Replaced search engine with official one

This commit is contained in:
Gboy9155
2017-11-05 19:01:22 +01:00
parent 20ddc69780
commit 9def03b469
14 changed files with 3679 additions and 4207 deletions

View File

@@ -113,6 +113,8 @@ namespace MusicApp
{
if (DownloadList.instance != null)
DownloadList.instance.Search(e.Query);
if (YoutubeEngine.instance != null)
YoutubeEngine.instance.Search(e.Query);
e.Handled = true;
};
@@ -147,6 +149,8 @@ namespace MusicApp
PlaylistTracks.instance.result = null;
if (FolderTracks.instance != null)
FolderTracks.instance.result = null;
if (YoutubeEngine.instance != null)
YoutubeEngine.instance.result = null;
}
public void HideSearch()
@@ -213,7 +217,7 @@ namespace MusicApp
case Resource.Id.downloadLayout:
HideTabs();
DisplaySearch();
fragment = DownloadList.NewInstance();
fragment = YoutubeEngine.NewInstance();
break;
case Resource.Id.playlistLayout:

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Xamarin.Build.Download.0.4.2\build\Xamarin.Build.Download.props" Condition="Exists('..\packages\Xamarin.Build.Download.0.4.2\build\Xamarin.Build.Download.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
@@ -55,9 +56,6 @@
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
</PropertyGroup>
<ItemGroup>
<Reference Include="FFMpeg.Xamarin, Version=1.0.31.22165, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.Android.FFmpeg.1.0.32\lib\FFMpeg.Xamarin.dll</HintPath>
</Reference>
<Reference Include="Google.Apis, Version=1.30.0.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<HintPath>..\packages\Google.Apis.1.30.0\lib\netstandard1.3\Google.Apis.dll</HintPath>
</Reference>
@@ -149,6 +147,24 @@
<Reference Include="Xamarin.Android.Support.Vector.Drawable, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.Android.Support.Vector.Drawable.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.Vector.Drawable.dll</HintPath>
</Reference>
<Reference Include="Xamarin.GooglePlayServices.Auth, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.GooglePlayServices.Auth.42.1021.1\lib\MonoAndroid70\Xamarin.GooglePlayServices.Auth.dll</HintPath>
</Reference>
<Reference Include="Xamarin.GooglePlayServices.Auth.Base, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.GooglePlayServices.Auth.Base.42.1021.1\lib\MonoAndroid70\Xamarin.GooglePlayServices.Auth.Base.dll</HintPath>
</Reference>
<Reference Include="Xamarin.GooglePlayServices.Base, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.GooglePlayServices.Base.42.1021.1\lib\MonoAndroid70\Xamarin.GooglePlayServices.Base.dll</HintPath>
</Reference>
<Reference Include="Xamarin.GooglePlayServices.Basement, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.GooglePlayServices.Basement.42.1021.1\lib\MonoAndroid70\Xamarin.GooglePlayServices.Basement.dll</HintPath>
</Reference>
<Reference Include="Xamarin.GooglePlayServices.Identity, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.GooglePlayServices.Identity.42.1021.1\lib\MonoAndroid70\Xamarin.GooglePlayServices.Identity.dll</HintPath>
</Reference>
<Reference Include="Xamarin.GooglePlayServices.Tasks, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Xamarin.GooglePlayServices.Tasks.42.1021.1\lib\MonoAndroid70\Xamarin.GooglePlayServices.Tasks.dll</HintPath>
</Reference>
<Reference Include="YoutubeExplode, Version=3.2.5.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\YoutubeExplode.3.2.5\lib\netstandard1.1\YoutubeExplode.dll</HintPath>
</Reference>
@@ -169,7 +185,6 @@
<Compile Include="Resources\Portable Class\FolderAdapter.cs" />
<Compile Include="Resources\Portable Class\FolderBrowse.cs" />
<Compile Include="Resources\Portable Class\FolderTracks.cs" />
<Compile Include="Resources\Portable Class\Format.cs" />
<Compile Include="Resources\Portable Class\MusicPlayer.cs" />
<Compile Include="Resources\Portable Class\Player.cs" />
<Compile Include="Resources\Portable Class\Playlist.cs" />
@@ -178,9 +193,9 @@
<Compile Include="Resources\Portable Class\Queue.cs" />
<Compile Include="Resources\Portable Class\SleepManager.cs" />
<Compile Include="Resources\Portable Class\TwoLineAdapter.cs" />
<Compile Include="Resources\Portable Class\VideoMeta.cs" />
<Compile Include="Resources\Portable Class\ViewPagerAdapter.cs" />
<Compile Include="Resources\Portable Class\YoutubeExtractor.cs" />
<Compile Include="Resources\Portable Class\YoutubeEngine.cs" />
<Compile Include="Resources\Portable Class\YtAdapter.cs" />
<Compile Include="Resources\Portable Class\YtFile.cs" />
<Compile Include="Resources\Portable Class\YTitemToSong.cs" />
<Compile Include="Resources\Resource.Designer.cs" />
@@ -368,6 +383,9 @@
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\layout\YtList.xml" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
@@ -387,6 +405,14 @@
<Error Condition="!Exists('..\packages\Xamarin.Android.Support.v7.AppCompat.25.4.0.2\build\MonoAndroid70\Xamarin.Android.Support.v7.AppCompat.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Android.Support.v7.AppCompat.25.4.0.2\build\MonoAndroid70\Xamarin.Android.Support.v7.AppCompat.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.Android.Support.Design.25.4.0.2\build\MonoAndroid70\Xamarin.Android.Support.Design.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Android.Support.Design.25.4.0.2\build\MonoAndroid70\Xamarin.Android.Support.Design.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.Android.Support.v7.Preference.25.4.0.2\build\MonoAndroid70\Xamarin.Android.Support.v7.Preference.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Android.Support.v7.Preference.25.4.0.2\build\MonoAndroid70\Xamarin.Android.Support.v7.Preference.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.Build.Download.0.4.2\build\Xamarin.Build.Download.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Build.Download.0.4.2\build\Xamarin.Build.Download.props'))" />
<Error Condition="!Exists('..\packages\Xamarin.Build.Download.0.4.2\build\Xamarin.Build.Download.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Build.Download.0.4.2\build\Xamarin.Build.Download.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.GooglePlayServices.Basement.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Basement.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.GooglePlayServices.Basement.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Basement.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.GooglePlayServices.Tasks.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Tasks.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.GooglePlayServices.Tasks.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Tasks.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.GooglePlayServices.Base.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Base.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.GooglePlayServices.Base.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Base.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.GooglePlayServices.Identity.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Identity.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.GooglePlayServices.Identity.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Identity.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.GooglePlayServices.Auth.Base.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Auth.Base.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.GooglePlayServices.Auth.Base.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Auth.Base.targets'))" />
<Error Condition="!Exists('..\packages\Xamarin.GooglePlayServices.Auth.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Auth.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.GooglePlayServices.Auth.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Auth.targets'))" />
</Target>
<Import Project="..\packages\NETStandard.Library.2.0.1\build\netstandard2.0\NETStandard.Library.targets" Condition="Exists('..\packages\NETStandard.Library.2.0.1\build\netstandard2.0\NETStandard.Library.targets')" />
<Import Project="..\packages\Xamarin.Android.Support.Compat.25.4.0.2\build\MonoAndroid70\Xamarin.Android.Support.Compat.targets" Condition="Exists('..\packages\Xamarin.Android.Support.Compat.25.4.0.2\build\MonoAndroid70\Xamarin.Android.Support.Compat.targets')" />
@@ -402,6 +428,13 @@
<Import Project="..\packages\Xamarin.Android.Support.v7.AppCompat.25.4.0.2\build\MonoAndroid70\Xamarin.Android.Support.v7.AppCompat.targets" Condition="Exists('..\packages\Xamarin.Android.Support.v7.AppCompat.25.4.0.2\build\MonoAndroid70\Xamarin.Android.Support.v7.AppCompat.targets')" />
<Import Project="..\packages\Xamarin.Android.Support.Design.25.4.0.2\build\MonoAndroid70\Xamarin.Android.Support.Design.targets" Condition="Exists('..\packages\Xamarin.Android.Support.Design.25.4.0.2\build\MonoAndroid70\Xamarin.Android.Support.Design.targets')" />
<Import Project="..\packages\Xamarin.Android.Support.v7.Preference.25.4.0.2\build\MonoAndroid70\Xamarin.Android.Support.v7.Preference.targets" Condition="Exists('..\packages\Xamarin.Android.Support.v7.Preference.25.4.0.2\build\MonoAndroid70\Xamarin.Android.Support.v7.Preference.targets')" />
<Import Project="..\packages\Xamarin.Build.Download.0.4.2\build\Xamarin.Build.Download.targets" Condition="Exists('..\packages\Xamarin.Build.Download.0.4.2\build\Xamarin.Build.Download.targets')" />
<Import Project="..\packages\Xamarin.GooglePlayServices.Basement.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Basement.targets" Condition="Exists('..\packages\Xamarin.GooglePlayServices.Basement.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Basement.targets')" />
<Import Project="..\packages\Xamarin.GooglePlayServices.Tasks.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Tasks.targets" Condition="Exists('..\packages\Xamarin.GooglePlayServices.Tasks.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Tasks.targets')" />
<Import Project="..\packages\Xamarin.GooglePlayServices.Base.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Base.targets" Condition="Exists('..\packages\Xamarin.GooglePlayServices.Base.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Base.targets')" />
<Import Project="..\packages\Xamarin.GooglePlayServices.Identity.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Identity.targets" Condition="Exists('..\packages\Xamarin.GooglePlayServices.Identity.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Identity.targets')" />
<Import Project="..\packages\Xamarin.GooglePlayServices.Auth.Base.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Auth.Base.targets" Condition="Exists('..\packages\Xamarin.GooglePlayServices.Auth.Base.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Auth.Base.targets')" />
<Import Project="..\packages\Xamarin.GooglePlayServices.Auth.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Auth.targets" Condition="Exists('..\packages\Xamarin.GooglePlayServices.Auth.42.1021.1\build\MonoAndroid70\Xamarin.GooglePlayServices.Auth.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@@ -1,8 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="MusicApp.MusicApp" android:versionCode="1" android:versionName="1.0" android:installLocation="preferExternal">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="24" />
<uses-sdk android:minSdkVersion="20" android:targetSdkVersion="24" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<application android:allowBackup="true" android:label="@string/app_name" android:icon="@drawable/MusicIcon"></application>
</manifest>

View File

@@ -3,7 +3,6 @@ using Android.Content;
using Android.Views;
using Android.Widget;
using MusicApp.Resources.values;
using Android.Graphics;
using Android.App;
using Square.Picasso;

View File

@@ -8,7 +8,6 @@ using Android.Support.Design.Widget;
using YoutubeSearch;
using MusicApp.Resources.values;
using Android.Support.V7.Preferences;
using static MusicApp.Resources.Portable_Class.YoutubeExtractor;
using Android.Util;
namespace MusicApp.Resources.Portable_Class
@@ -86,83 +85,7 @@ namespace MusicApp.Resources.Portable_Class
private void ListView_ItemClick(object sender, AdapterView.ItemClickEventArgs e)
{
Song song = list[e.Position];
YoutubeExtractor extractor = new YoutubeExtractor();
extractor.OnExtractionComplete += DownloadList_OnExtractionComplete;
extractor.Extract(song.GetPath(), true, true, song);
}
private void DownloadList_OnExtractionComplete(SparseArray<YtFile> ytFiles, Song song)
{
System.Console.WriteLine("Extraction complete : " + ytFiles.Size());
if(ytFiles == null || ytFiles.Size() < 1)
{
Toast.MakeText(Android.App.Application.Context, "Download failed, try again", ToastLength.Short).Show();
return;
}
YtFile ytFile = GetBestStream(ytFiles);
Intent intent = new Intent(Android.App.Application.Context, typeof(MusicPlayer));
intent.SetAction("YTPlay");
intent.PutExtra("file", ytFile.url);
intent.PutStringArrayListExtra("song", new string[] { song.GetName(), song.GetArtist(), song.GetAlbum() });
Activity.StartService(intent);
}
private YtFile GetBestStream(SparseArray<YtFile> ytFiles)
{
List<int> keys = new List<int>();
for (int i = 0; i < ytFiles.Size(); i++)
{
System.Console.WriteLine("Key " + i + " : " + ytFiles.KeyAt(i));
keys.Add(ytFiles.KeyAt(i));
}
// WEBM Dash Audio
if (keys.Contains(251))
return ytFiles.Get(251);
if (keys.Contains(171))
return ytFiles.Get(171);
if (keys.Contains(250))
return ytFiles.Get(250);
if (keys.Contains(249))
return ytFiles.Get(249);
// Dash Audio
if (keys.Contains(141))
return ytFiles.Get(141);
if (keys.Contains(140))
return ytFiles.Get(140);
// Video and Audio
if (keys.Contains(22))
return ytFiles.Get(22);
if (keys.Contains(43))
return ytFiles.Get(43);
if (keys.Contains(18))
return ytFiles.Get(18);
if (keys.Contains(5))
return ytFiles.Get(5);
if (keys.Contains(36))
return ytFiles.Get(36);
if (keys.Contains(17))
return ytFiles.Get(17);
// HLS Live Stream
if (keys.Contains(96))
return ytFiles.Get(96);
if (keys.Contains(95))
return ytFiles.Get(95);
if (keys.Contains(94))
return ytFiles.Get(94);
if (keys.Contains(93))
return ytFiles.Get(93);
if (keys.Contains(92))
return ytFiles.Get(92);
if (keys.Contains(91))
return ytFiles.Get(91);
return null;
}
private void ListView_ItemLongClick(object sender, AdapterView.ItemLongClickEventArgs e)

View File

@@ -1,168 +0,0 @@
namespace MusicApp.Resources.Portable_Class
{
[System.Serializable]
public class Format
{
public enum VCodec
{
H263, H264, MPEG4, VP8, VP9, NONE
}
public enum ACodec
{
MP3, AAC, VORBIS, OPUS, NONE
}
private int itag;
private string ext;
public int height;
private int fps;
private VCodec vCodec;
private ACodec aCodec;
private int audioBitrate;
public bool isDashContainer;
public bool isHlsContent;
public Format(int itag, string ext, int height, VCodec vCodec, ACodec aCodec, bool isDashContainer)
{
this.itag = itag;
this.ext = ext;
this.height = height;
this.fps = 30;
this.audioBitrate = -1;
this.isDashContainer = isDashContainer;
this.isHlsContent = false;
}
public Format(int itag, string ext, VCodec vCodec, ACodec aCodec, int audioBitrate, bool isDashContainer)
{
this.itag = itag;
this.ext = ext;
this.height = -1;
this.fps = 30;
this.audioBitrate = audioBitrate;
this.isDashContainer = isDashContainer;
this.isHlsContent = false;
}
public Format(int itag, string ext, int height, VCodec vCodec, ACodec aCodec, int audioBitrate, bool isDashContainer)
{
this.itag = itag;
this.ext = ext;
this.height = height;
this.fps = 30;
this.audioBitrate = audioBitrate;
this.isDashContainer = isDashContainer;
this.isHlsContent = false;
}
public Format(int itag, string ext, int height, VCodec vCodec, ACodec aCodec, int audioBitrate, bool isDashContainer, bool isHlsContent)
{
this.itag = itag;
this.ext = ext;
this.height = height;
this.fps = 30;
this.audioBitrate = audioBitrate;
this.isDashContainer = isDashContainer;
this.isHlsContent = isHlsContent;
}
public Format(int itag, string ext, int height, VCodec vCodec, int fps, ACodec aCodec, bool isDashContainer)
{
this.itag = itag;
this.ext = ext;
this.height = height;
this.audioBitrate = -1;
this.fps = fps;
this.isDashContainer = isDashContainer;
this.isHlsContent = false;
}
public int GetFPS()
{
return fps;
}
public int GetAudioBitrate()
{
return audioBitrate;
}
public int GetItag()
{
return itag;
}
public string GetExt()
{
return ext;
}
public ACodec GetAudioCodec()
{
return aCodec;
}
public VCodec GetVideoCodec()
{
return vCodec;
}
public override bool Equals(object obj)
{
if (this == obj)
return true;
if (obj == null || obj.GetType() != GetType())
return false;
Format format = (Format)obj;
if (itag != format.itag)
return false;
if (height != format.height)
return false;
if (fps != format.fps)
return false;
if (audioBitrate != format.audioBitrate)
return false;
if (isDashContainer != format.isDashContainer)
return false;
if (isHlsContent != format.isHlsContent)
return false;
if (ext != null ? !ext.Equals(format.ext) : format.ext != null)
return false;
if (vCodec != format.vCodec)
return false;
return aCodec == format.aCodec;
}
public override int GetHashCode()
{
int result = itag;
result = 31 * result + (ext != null ? ext.GetHashCode() : 0);
result = 31 * result + height;
result = 31 * result + fps;
result = 31 * result + vCodec.GetHashCode();
result = 31 * result + aCodec.GetHashCode();
result = 31 * result + audioBitrate;
result = 31 * result + (isDashContainer ? 1 : 0);
result = 31 * result + (isHlsContent ? 1 : 0);
return result;
}
public override string ToString()
{
return "Format{" +
"itag=" + itag +
", ext='" + ext + '\'' +
", height=" + height +
", fps=" + fps +
", vCodec=" + vCodec +
", aCodec=" + aCodec +
", audioBitrate=" + audioBitrate +
", isDashContainer=" + isDashContainer +
", isHlsContent=" + isHlsContent +
'}';
}
}
}

View File

@@ -1,56 +0,0 @@
namespace MusicApp.Resources.Portable_Class
{
public class VideoMeta
{
private const string IMAGE_BASE_URL = "http://i.ytimg.com/vi/";
public string videoID;
public string title;
public string author;
public object channelId;
public object length;
public long viewCount;
public bool isLiveStream;
public VideoMeta(string videoID, string title, string author, object channelId, object length, long viewCount, bool isLiveStream)
{
this.videoID = videoID;
this.title = title;
this.author = author;
this.channelId = channelId;
this.length = length;
this.viewCount = viewCount;
this.isLiveStream = isLiveStream;
}
// 120 x 90
public string GetThumbUrl()
{
return IMAGE_BASE_URL + videoID + "/default.jpg";
}
// 320 x 180
public string GetMqImageUrl()
{
return IMAGE_BASE_URL + videoID + "/mqdefault.jpg";
}
// 480 x 360
public string GetHqImageUrl()
{
return IMAGE_BASE_URL + videoID + "/hqdefault.jpg";
}
// 640 x 480
public string GetSdImageUrl()
{
return IMAGE_BASE_URL + videoID + "/sddefault.jpg";
}
// Max Res
public string GetMaxResImageUrl()
{
return IMAGE_BASE_URL + videoID + "/maxresdefault.jpg";
}
}
}

View File

@@ -0,0 +1,228 @@
using Android.Support.V4.App;
using Android.Content;
using Android.OS;
using Android.Views;
using Android.Widget;
using Android;
using Android.Content.PM;
using Android.Support.Design.Widget;
using Google.Apis.YouTube.v3;
using Android.Gms.Auth.Api.SignIn;
using Android.Gms.Common.Apis;
using Android.Gms.Common;
using Android.Gms.Auth.Api;
using System.Collections.Generic;
using Google.Apis.Services;
using Android.Accounts;
namespace MusicApp.Resources.Portable_Class
{
public class YoutubeEngine : ListFragment
{
public static YoutubeEngine instance;
public List<YtFile> result;
private View emptyView;
private bool isEmpty = true;
private string ApiKey = "AIzaSyBOQyZVnBAKjur0ztBuYPSopS725Qudgc4";
private YouTubeService youtubeService;
//private string oAuthKey;
//private GoogleApiClient googleClient;
//private const int signPickerID = 9001;
//private const int playServiceID = 9002;
//private const string accountName = "accountName";
//private static readonly string[] permissions = new string[]
//{
// Manifest.Permission.GetAccounts
//};
public override void OnActivityCreated(Bundle savedInstanceState)
{
base.OnActivityCreated(savedInstanceState);
emptyView = LayoutInflater.Inflate(Resource.Layout.DownloadLayout, null);
ListView.EmptyView = emptyView;
Activity.AddContentView(emptyView, View.LayoutParameters);
ListAdapter = null;
if (youtubeService == null)
{
youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
ApiKey = ApiKey,
ApplicationName = "MusicApp"
});
}
}
public override void OnDestroy()
{
if (isEmpty)
{
ViewGroup rootView = Activity.FindViewById<ViewGroup>(Android.Resource.Id.Content);
rootView.RemoveView(emptyView);
}
base.OnDestroy();
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = base.OnCreateView(inflater, container, savedInstanceState);
view.SetPadding(0, 100, 0, MainActivity.paddingBot);
return view;
}
public static Fragment NewInstance()
{
instance = new YoutubeEngine { Arguments = new Bundle() };
return instance;
}
#region AccountChooser
//void CheckInitializationState()
//{
// if (!GooglePlayServiceAvailable())
// GetGooglePlayServices();
// else if (oAuthKey == null)
// ChooseAccount();
// else
// GetPermissions(permissions);
//}
//private bool GooglePlayServiceAvailable()
//{
// GoogleApiAvailability availability = GoogleApiAvailability.Instance;
// int result = availability.IsGooglePlayServicesAvailable(Android.App.Application.Context);
// return result == ConnectionResult.Success;
//}
//private void GetGooglePlayServices()
//{
// GoogleApiAvailability availability = GoogleApiAvailability.Instance;
// int result = availability.IsGooglePlayServicesAvailable(Android.App.Application.Context);
// if(availability.IsUserResolvableError(result))
// availability.GetErrorDialog(MainActivity.instance, result, playServiceID).Show();
//}
//void ChooseAccount()
//{
// bool hasPermissions = CheckPermissions(permissions);
// if (hasPermissions)
// {
// string account = Activity.GetPreferences(FileCreationMode.Private).GetString(accountName, null);
// oAuthKey = Activity.GetPreferences(FileCreationMode.Private).GetString("oAuth", null);
// if (account != null)
// {
// CheckInitializationState();
// }
// else
// {
// GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DefaultSignIn).RequestEmail().Build();
// googleClient = new GoogleApiClient.Builder(Android.App.Application.Context).EnableAutoManage(Activity, (result) => { }).AddApi(Auth.GOOGLE_SIGN_IN_API, gso).Build();
// Intent intent = Auth.GoogleSignInApi.GetSignInIntent(googleClient);
// StartActivityForResult(intent, signPickerID);
// }
// }
// else
// {
// GetPermissions(permissions);
// }
//}
//public override void OnActivityResult(int requestCode, int resultCode, Intent data)
//{
// base.OnActivityResult(requestCode, resultCode, data);
// if(requestCode == signPickerID)
// {
// GoogleSignInResult result = Auth.GoogleSignInApi.GetSignInResultFromIntent(data);
// if (result.IsSuccess)
// {
// GoogleSignInAccount signInAccount = result.SignInAccount;
// Account account = signInAccount.Account;
// ISharedPreferences settings = Activity.GetPreferences(FileCreationMode.Private);
// ISharedPreferencesEditor editor = settings.Edit();
// editor.PutString(accountName, signInAccount.DisplayName);
// editor.Apply();
// }
// }
//}
#endregion
#region Permissions
void GetPermissions(string[] permissions)
{
bool hasPermissions = CheckPermissions(permissions);
if (!hasPermissions)
RequestPermissions(permissions, 0);
//else
// PopulateList();
}
bool CheckPermissions(string[] permissions)
{
bool hasPermissions = true;
foreach (string permission in permissions)
{
if (Android.Support.V4.Content.ContextCompat.CheckSelfPermission(Android.App.Application.Context, permission) != (int)Permission.Granted)
{
hasPermissions = false;
break;
}
}
return hasPermissions;
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
switch (requestCode)
{
case 0:
{
if (grantResults[0] == Permission.Granted)
{
PopulateList();
}
else
{
Snackbar.Make(View, "Permission denied, can't list search on youtube.", Snackbar.LengthShort).Show();
}
}
break;
}
}
#endregion
public async void Search(string search)
{
if (search == null || search == "")
return;
SearchResource.ListRequest searchResult = youtubeService.Search.List("snippet");
searchResult.Fields = "items(id/videoId,snippet/title,snippet/thumbnails/default/url,snippet/channelTitle)";
searchResult.Q = search;
searchResult.Type = "video";
searchResult.MaxResults = 20;
var searchReponse = await searchResult.ExecuteAsync();
result = new List<YtFile>();
foreach(var video in searchReponse.Items)
{
YtFile videoInfo = new YtFile(video.Snippet.Title, video.Snippet.ChannelTitle, video.Id.VideoId, video.Snippet.Thumbnails.Default__.Url);
result.Add(videoInfo);
}
ListAdapter = new YtAdapter(Android.App.Application.Context, Resource.Layout.YtList, result);
}
void PopulateList()
{
}
}
}

View File

@@ -1,765 +0,0 @@
using System;
using System.Collections.Generic;
using Android.Runtime;
using Android.Util;
using Java.Lang;
using Java.Util.Regex;
using Java.Net;
using Java.IO;
using Java.Util.Concurrent.Locks;
using Android.Webkit;
using MusicApp.Resources.values;
namespace MusicApp.Resources.Portable_Class
{
public class YoutubeExtractor : Android.OS.AsyncTask<string, int, SparseArray<YtFile>>, IValueCallback
{
private const int dashRetries = 5;
private bool parseDashManifest;
private bool includeWebM;
private Song song = null;
private string videoID;
private bool useHttps = true;
private /*volatile*/ string decipheredSignature;
private string curJsFileName;
private const string cacheFileName = "decipher_js_funct";
private static string decipherJsFileName;
private static string decipherFunctions;
private static string decipherFunctionName;
private static ILock Ilock = new ReentrantLock();
private ICondition jsExecution = Ilock.NewCondition();
private const string USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36";
private Pattern patYouTubePageLink = Pattern.Compile("(http|https)://(www\\.|m.|)youtube\\.com/watch\\?v=(.+?)( |\\z|&)");
private Pattern patYouTubeShortLink = Pattern.Compile("(http|https)://(www\\.|)youtu.be/(.+?)( |\\z|&)");
private Pattern patTitle = Pattern.Compile("title=(.*?)(&|\\z)");
private Pattern patHlsvp = Pattern.Compile("hlsvp=(.+?)(&|\\z)");
private Pattern patAuthor = Pattern.Compile("author=(.+?)(&|\\z)");
private Pattern patChannelId = Pattern.Compile("ucid=(.+?)(&|\\z)");
private Pattern patLength = Pattern.Compile("length_seconds=(\\d+?)(&|\\z)");
private Pattern patViewCount = Pattern.Compile("view_count=(\\d+?)(&|\\z)");
private Pattern patHlsItag = Pattern.Compile("/itag/(\\d+?)/");
private Pattern patDecryptionJsFile = Pattern.Compile("jsbin\\\\/(player-(.+?).js)");
private Pattern patDashManifest1 = Pattern.Compile("dashmpd=(.+?)(&|\\z)");
private Pattern patDashManifest2 = Pattern.Compile("\"dashmpd\":\"(.+?)\"");
private Pattern patDashManifestEncSig = Pattern.Compile("/s/([0-9A-F|\\.]{10,}?)(/|\\z)");
private Pattern patItag = Pattern.Compile("itag=([0-9]+?)(&|,)");
private Pattern patEncSig = Pattern.Compile("s=([0-9A-F|\\.]{10,}?)(&|,|\")");
private Pattern patUrl = Pattern.Compile("url=(.+?)(&|,)");
private Pattern patVariableFunction = Pattern.Compile("(\\{|;| |=)([a-zA-Z$][a-zA-Z0-9$]{0,2})\\.([a-zA-Z$][a-zA-Z0-9$]{0,2})\\(");
private Pattern patFunction = Pattern.Compile("(\\{|;| |=)([a-zA-Z$_][a-zA-Z0-9$]{0,2})\\(");
private Pattern patSignatureDecFunction = Pattern.Compile("\\(\"signature\",(.{1,3}?)\\(.{1,10}?\\)");
#region formats
private Dictionary<int, Format> formats = new Dictionary<int, Format>
{
// http://en.wikipedia.org/wiki/YouTube#Quality_and_formats
// Video and Audio
{ 17, new Format(17, "3gp", 144, Format.VCodec.MPEG4, Format.ACodec.AAC, 24, false) },
{36, new Format(36, "3gp", 240, Format.VCodec.MPEG4, Format.ACodec.AAC, 32, false)},
{5, new Format(5, "flv", 240, Format.VCodec.H263, Format.ACodec.MP3, 64, false)},
{43, new Format(43, "webm", 360, Format.VCodec.VP8, Format.ACodec.VORBIS, 128, false)},
{18, new Format(18, "mp4", 360, Format.VCodec.H264, Format.ACodec.AAC, 96, false)},
{22, new Format(22, "mp4", 720, Format.VCodec.H264, Format.ACodec.AAC, 192, false)},
// Dash Video
{160, new Format(160, "mp4", 144, Format.VCodec.H264, Format.ACodec.NONE, true)},
{133, new Format(133, "mp4", 240, Format.VCodec.H264, Format.ACodec.NONE, true)},
{134, new Format(134, "mp4", 360, Format.VCodec.H264, Format.ACodec.NONE, true)},
{135, new Format(135, "mp4", 480, Format.VCodec.H264, Format.ACodec.NONE, true)},
{136, new Format(136, "mp4", 720, Format.VCodec.H264, Format.ACodec.NONE, true)},
{137, new Format(137, "mp4", 1080, Format.VCodec.H264, Format.ACodec.NONE, true)},
{264, new Format(264, "mp4", 1440, Format.VCodec.H264, Format.ACodec.NONE, true)},
{266, new Format(266, "mp4", 2160, Format.VCodec.H264, Format.ACodec.NONE, true)},
{298, new Format(298, "mp4", 720, Format.VCodec.H264, 60, Format.ACodec.NONE, true)},
{299, new Format(299, "mp4", 1080, Format.VCodec.H264, 60, Format.ACodec.NONE, true)},
// Dash Audio
{140, new Format(140, "m4a", Format.VCodec.NONE, Format.ACodec.AAC, 128, true)},
{141, new Format(141, "m4a", Format.VCodec.NONE, Format.ACodec.AAC, 256, true)},
// WEBM Dash Video
{278, new Format(278, "webm", 144, Format.VCodec.VP9, Format.ACodec.NONE, true)},
{242, new Format(242, "webm", 240, Format.VCodec.VP9, Format.ACodec.NONE, true)},
{243, new Format(243, "webm", 360, Format.VCodec.VP9, Format.ACodec.NONE, true)},
{244, new Format(244, "webm", 480, Format.VCodec.VP9, Format.ACodec.NONE, true)},
{247, new Format(247, "webm", 720, Format.VCodec.VP9, Format.ACodec.NONE, true)},
{248, new Format(248, "webm", 1080, Format.VCodec.VP9, Format.ACodec.NONE, true)},
{271, new Format(271, "webm", 1440, Format.VCodec.VP9, Format.ACodec.NONE, true)},
{313, new Format(313, "webm", 2160, Format.VCodec.VP9, Format.ACodec.NONE, true)},
{302, new Format(302, "webm", 720, Format.VCodec.VP9, 60, Format.ACodec.NONE, true)},
{308, new Format(308, "webm", 1440, Format.VCodec.VP9, 60, Format.ACodec.NONE, true)},
{303, new Format(303, "webm", 1080, Format.VCodec.VP9, 60, Format.ACodec.NONE, true)},
{315, new Format(315, "webm", 2160, Format.VCodec.VP9, 60, Format.ACodec.NONE, true)},
// WEBM Dash Audio
{171, new Format(171, "webm", Format.VCodec.NONE, Format.ACodec.VORBIS, 128, true)},
{249, new Format(249, "webm", Format.VCodec.NONE, Format.ACodec.OPUS, 48, true)},
{250, new Format(250, "webm", Format.VCodec.NONE, Format.ACodec.OPUS, 64, true)},
{251, new Format(251, "webm", Format.VCodec.NONE, Format.ACodec.OPUS, 160, true)},
// HLS Live Stream
{91, new Format(91, "mp4", 144 , Format.VCodec.H264, Format.ACodec.AAC, 48, false, true)},
{92, new Format(92, "mp4", 240 , Format.VCodec.H264, Format.ACodec.AAC, 48, false, true)},
{93, new Format(93, "mp4", 360 , Format.VCodec.H264, Format.ACodec.AAC, 128, false, true)},
{94, new Format(94, "mp4", 480 , Format.VCodec.H264, Format.ACodec.AAC, 128, false, true)},
{95, new Format(95, "mp4", 720 , Format.VCodec.H264, Format.ACodec.AAC, 256, false, true)},
{96, new Format(96, "mp4", 1080 , Format.VCodec.H264, Format.ACodec.AAC, 256, false, true)},
};
#endregion
public YoutubeExtractor()
{
}
public YoutubeExtractor(IntPtr doNotUse, JniHandleOwnership transfer) : base(doNotUse, transfer)
{
}
public void Extract(string youtubeURL, bool parseDashManifest, bool includeWebM, Song song = null)
{
this.parseDashManifest = parseDashManifest;
this.includeWebM = includeWebM;
this.song = song;
this.Execute(youtubeURL);
}
protected override void OnPreExecute()
{
base.OnPreExecute();
}
protected override void OnPostExecute(Java.Lang.Object result)
{
base.OnPostExecute(result);
OnExtractionComplete((SparseArray<YtFile>) result, song);
}
protected override void OnPostExecute(SparseArray<YtFile> ytFiles)
{
System.Console.WriteLine("Post Execute");
base.OnPostExecute(ytFiles);
OnExtractionComplete(ytFiles, song);
}
public delegate void ExtractionComplete(SparseArray<YtFile> ytFiles, Song song);
public event ExtractionComplete OnExtractionComplete;
protected override Java.Lang.Object DoInBackground(params Java.Lang.Object[] native_parms)
{
videoID = null;
string ytUrl = native_parms[0].ToString();
if (ytUrl == null)
return null;
Matcher matcher = patYouTubePageLink.Matcher(ytUrl);
if (matcher.Find())
videoID = matcher.Group(3);
else
{
matcher = patYouTubeShortLink.Matcher(ytUrl);
if (matcher.Find())
videoID = matcher.Group(3);
else if (new Java.Lang.String(videoID).Matches("\\p{Graph}+?"))
videoID = ytUrl;
}
if (videoID != null)
{
try
{
var urls = GetStreamUrls();
System.Console.WriteLine("Finial Size: " + urls.Size());
return urls;
}
catch (Java.Lang.Exception e)
{
e.PrintStackTrace();
}
}
else
System.Console.WriteLine("Youtube link not supported");
return null;
}
private SparseArray<YtFile> GetStreamUrls()
{
string ytInfoUrl = (useHttps) ? "https://" : "http://";
ytInfoUrl += "www.youtube.com/get_video_info?video_id=" + videoID + "&eurl=" + URLEncoder.Encode("https://youtube.googleapis.com/v/" + videoID, "UTF-8");
//string dashMpdUrl = null;
string streamMap = null;
BufferedReader reader = null;
URL url = new URL(ytInfoUrl);
HttpURLConnection urlConnection = (HttpURLConnection)url.OpenConnection();
urlConnection.SetRequestProperty("User-Agent", USER_AGENT);
try
{
reader = new BufferedReader(new InputStreamReader(urlConnection.InputStream));
streamMap = reader.ReadLine();
}
finally
{
if (reader != null)
reader.Close();
urlConnection.Disconnect();
}
VideoMeta videoMeta = ParseVideoMeta(streamMap);
System.Console.WriteLine(videoMeta.title);
if (videoMeta.isLiveStream)
{
return GetLiveStreamUrls(streamMap);
}
SparseArray<string> encSignatures = new SparseArray<string>();
string dashMpdUrl = null;
if (streamMap == null || !streamMap.Contains("use_cipher_signature=False"))
encSignatures = DecipherJsFile(streamMap);
else
{
if (parseDashManifest)
{
Matcher matcher = patDashManifest1.Matcher(streamMap);
if (matcher.Find())
{
dashMpdUrl = URLDecoder.Decode(matcher.Group(1), "UTF-8");
}
}
streamMap = URLDecoder.Decode(streamMap, "UTF-8");
}
Java.Lang.String streamMapJL = new Java.Lang.String(streamMap);
string[] streams = streamMapJL.Split(",|url_encoded_fmt_stream_map|&adaptive_fmts=");
SparseArray<YtFile> ytFiles = new SparseArray<YtFile>();
foreach (string foo in streams)
{
string encStream = foo + ",";
if (!encStream.Contains("itag%3D"))
continue;
string stream = URLDecoder.Decode(encStream, "UTF-8");
Matcher matcher = patItag.Matcher(stream);
int itag;
if (matcher.Find())
{
itag = int.Parse(matcher.Group(1));
if (formats[itag] == null)
continue;
else if (!includeWebM && formats[itag].GetExt().Equals("webm"))
continue;
}
else
continue;
if (curJsFileName != null)
{
matcher = patEncSig.Matcher(stream);
if (matcher.Find())
{
encSignatures.Append(itag, matcher.Group(1));
}
}
matcher = patUrl.Matcher(encStream);
string uri = null;
if (matcher.Find())
uri = matcher.Group(1);
if(uri != null)
{
Format format = formats[itag];
string finalUrl = URLDecoder.Decode(uri, "UTF-8");
YtFile newVideo = new YtFile(format, finalUrl);
ytFiles.Put(itag, newVideo);
}
}
System.Console.WriteLine("Size: " + ytFiles.Size());
if(encSignatures != null)
{
decipheredSignature = null;
bool boo = DecipherSignature(encSignatures);
System.Console.WriteLine("booleanbOO : " + boo);
if (boo)
{
Ilock.Lock();
try
{
jsExecution.Await(7, Java.Util.Concurrent.TimeUnit.Seconds);
}
finally
{
Ilock.Unlock();
}
}
if (decipheredSignature == null)
return null;
else
{
string[] signatures = new Java.Lang.String(decipheredSignature).Split("\n");
for (int i = 0; i < encSignatures.Size() && i < signatures.Length; i++)
{
int key = encSignatures.KeyAt(i);
if (key == 0)
dashMpdUrl = dashMpdUrl.Replace("/s/" + encSignatures.Get(key), "/signature/" + signatures[i]);
else
{
string uri = ytFiles.Get(key).url;
uri += "&signature=" + signatures[i];
YtFile newFile = new YtFile(formats[key], uri);
ytFiles.Put(key, newFile);
}
}
}
}
if(parseDashManifest && dashMpdUrl != null)
{
for (int i = 0; i < dashRetries; i++)
{
try
{
ParseDashManifest(dashMpdUrl, ytFiles);
break;
}
catch(IOException)
{
Thread.Sleep(5);
}
}
}
if (ytFiles.Size() == 0)
return null;
return ytFiles;
}
private bool DecipherSignature(SparseArray<string> encSignatures)
{
bool dfn = decipherFunctionName == null;
bool dfs = decipherFunctions == null;
System.Console.WriteLine("DecipherFunctionName: " + dfn + " DecipherFunctions: " + dfs);
if(decipherFunctionName == null || decipherFunctions == null)
{
string decipherFunctUrl = "https://s.ytimg.com/yts/jsbin/" + decipherJsFileName;
URL url = new URL(decipherFunctUrl);
HttpURLConnection urlConnection = (HttpURLConnection)url.OpenConnection();
urlConnection.SetRequestProperty("User-Agent", USER_AGENT);
BufferedReader reader = null;
string javascriptFile = null;
try
{
reader = new BufferedReader(new InputStreamReader(urlConnection.InputStream));
StringBuilder sb = new StringBuilder("");
string line;
while ((line = reader.ReadLine()) != null)
{
sb.Append(line);
sb.Append(" ");
}
javascriptFile = sb.ToString();
}
finally
{
if (reader != null)
reader.Close();
urlConnection.Disconnect();
}
Matcher matcher = patSignatureDecFunction.Matcher(javascriptFile);
if (matcher.Find())
{
decipherFunctionName = matcher.Group(1);
Pattern patMainVariable = Pattern.Compile("(var |\\s|,|;)" + decipherFunctionName.Replace("$", "\\$") + "(=function\\((.{1,3})\\)\\{)");
string mainDecipherFunct;
matcher = patMainVariable.Matcher(javascriptFile);
if (matcher.Find())
mainDecipherFunct = "var " + decipherFunctionName + matcher.Group(2);
else
{
Pattern patMainFunction = Pattern.Compile("function " + decipherFunctionName.Replace("$", "\\$") + "(\\((.{1,3})\\)\\{)");
matcher = patMainFunction.Matcher(javascriptFile);
if (!matcher.Find())
return false;
mainDecipherFunct = "function " + decipherFunctionName + matcher.Group(2);
}
System.Console.WriteLine("Etap 1 succed");
int startIndex = matcher.End();
char[] javascriptChars = javascriptFile.ToCharArray();
for (int braces = 1, i = 0; i < javascriptFile.Length; i++)
{
if (braces == 0 && startIndex + 5 < i)
{
mainDecipherFunct += javascriptFile.Substring(startIndex, i) + ";";
break;
}
if (javascriptChars[i] == '{')
braces++;
else if (javascriptChars[i] == '}')
braces--;
}
decipherFunctions = mainDecipherFunct;
System.Console.WriteLine("Etap 2 succed");
matcher = patVariableFunction.Matcher(mainDecipherFunct);
while (matcher.Find())
{
string variableDef = "var " + matcher.Group(2) + "={";
if (decipherFunctions.Contains(variableDef))
continue;
startIndex = javascriptFile.IndexOf(variableDef) + variableDef.Length;
for (int braces = 1, i = 0; i < javascriptFile.Length - startIndex; i++)
{
if (braces == 0)
{
decipherFunctions += variableDef + javascriptFile.Substring(startIndex, i) + ";";
break;
}
if (javascriptFile[i] == '{')
braces++;
else if (javascriptFile[i] == '}')
braces--;
}
}
System.Console.WriteLine("Etap 3 succed");
matcher = patFunction.Matcher(mainDecipherFunct);
while (matcher.Find())
{
string functionDef = "function " + matcher.Group(2) + "(";
if (decipherFunctions.Contains(functionDef))
continue;
startIndex = javascriptFile.IndexOf(functionDef) + functionDef.Length;
for (int braces = 0, i = 0; i < javascriptFile.Length - startIndex; i++)
{
if (braces == 0 && startIndex + 5 < i)
{
decipherFunctions += functionDef + javascriptFile.Substring(startIndex, i) + ";";
break;
}
if (javascriptFile[i] == '{')
braces++;
else if (javascriptFile[i] == '}')
braces--;
}
}
System.Console.WriteLine("Work fine");
DecipherViaWebView(encSignatures);
System.Console.WriteLine("Deciphered via Web");
WriteDeciperFunctToCache();
}
else
return false;
}
else
DecipherViaWebView(encSignatures);
return true;
}
private void DecipherViaWebView(SparseArray<string> encSignatures)
{
StringBuilder stringBuilder = new StringBuilder(decipherFunctions + " function decipher(");
stringBuilder.Append("){return ");
for (int i = 0; i < encSignatures.Size(); i++)
{
int key = encSignatures.KeyAt(i);
if (i < encSignatures.Size() - 1)
stringBuilder.Append(decipherFunctionName).Append("('").Append(encSignatures.Get(key)).Append("')+\"\\n\"+");
else
stringBuilder.Append(decipherFunctionName).Append("('").Append(encSignatures.Get(key)).Append("')");
}
stringBuilder.Append("};decipher();");
Android.OS.Handler handler = new Android.OS.Handler(MainActivity.instance.MainLooper);
handler.Post(() =>
{
WebView webView = new WebView(Android.App.Application.Context);
webView.EvaluateJavascript(stringBuilder.ToString(), this);
});
//Android.OS.Handler handler = new Android.OS.Handler((sender) =>
//{
// WebView webView = new WebView(Android.App.Application.Context);
// webView.EvaluateJavascript(stringBuilder.ToString(), this);
//});
}
public void OnReceiveValue(Java.Lang.Object value)
{
System.Console.WriteLine("Value receive");
Ilock.Lock();
try
{
decipheredSignature = value.ToString();
}
finally
{
Ilock.Unlock();
}
}
private void WriteDeciperFunctToCache()
{
File cacheFile = new File(Android.App.Application.Context.CacheDir.AbsolutePath + "/" + cacheFileName);
BufferedWriter writer = null;
try
{
writer = new BufferedWriter(new FileWriter(cacheFile));
writer.Write(decipherJsFileName + "\n");
writer.Write(decipherFunctionName + "\n");
writer.Write(decipherFunctions);
}
catch (Java.Lang.Exception e)
{
e.PrintStackTrace();
}
finally
{
if (writer != null)
writer.Close();
}
}
private void ParseDashManifest(string dashMpdUrl, SparseArray<YtFile> ytFiles)
{
Pattern patBaseUrl = Pattern.Compile("<BaseURL yt:contentLength=\"[0-9]+?\">(.+?)</BaseURL>");
URL url = new URL(dashMpdUrl);
HttpURLConnection urlConnection = (HttpURLConnection)url.OpenConnection();
urlConnection.SetRequestProperty("User-Agent", USER_AGENT);
BufferedReader reader = null;
string dashManifest;
try
{
reader = new BufferedReader(new InputStreamReader(urlConnection.InputStream));
reader.ReadLine();
dashManifest = reader.ReadLine();
}
finally
{
if (reader != null)
reader.Close();
urlConnection.Disconnect();
}
if (dashManifest == null)
return;
Matcher matcher = patBaseUrl.Matcher(dashManifest);
while (matcher.Find())
{
string foo = matcher.Group(1);
Matcher matcherBis = patItag.Matcher(foo);
int itag;
if (matcherBis.Find())
{
itag = int.Parse(matcherBis.Group(1));
if (formats[itag] != null)
continue;
if (!includeWebM && formats[itag].GetExt().Equals("webm"))
continue;
}
else
continue;
foo = foo.Replace("&amp;", "&").Replace(",", "%2C").Replace("mime=audio/", "mime=audio%2F").Replace("mime=video/", "mime=video%2F");
YtFile newFile = new YtFile(formats[itag], foo);
ytFiles.Append(itag, newFile);
}
}
private void ReadDecipherFunctFromCache()
{
File cacheFile = new File(Android.App.Application.Context.CacheDir.AbsolutePath + "/" + cacheFileName);
if(cacheFile.Exists() && JavaSystem.CurrentTimeMillis() - cacheFile.LastModified() < 1209600000)
{
BufferedReader reader = null;
try
{
reader = new BufferedReader(new FileReader(cacheFile));
decipherJsFileName = reader.ReadLine();
decipherFunctionName = reader.ReadLine();
decipherFunctions = reader.ReadLine();
}
catch(Java.Lang.Exception ex)
{
ex.PrintStackTrace();
}
finally
{
if(reader != null)
reader.Close();
}
}
}
private SparseArray<string> DecipherJsFile(string streamMap)
{
if (decipherJsFileName == null || decipherFunctions == null || decipherFunctionName == null)
ReadDecipherFunctFromCache();
URL url = new URL("https://youtube.com/watch?v=" + videoID);
HttpURLConnection urlConnection = (HttpURLConnection)url.OpenConnection();
urlConnection.SetRequestProperty("User-Agent", USER_AGENT);
BufferedReader reader = null;
try
{
reader = new BufferedReader(new InputStreamReader(urlConnection.InputStream));
string line;
while ((line = reader.ReadLine()) != null)
{
if (line.Contains("url_encoded_fmt_stream_map"))
{
streamMap = line.Replace("\\u0026", "&");
break;
}
}
}
finally
{
if (reader != null)
reader.Close();
urlConnection.Disconnect();
}
SparseArray<string> encSignatures = new SparseArray<string>();
Matcher matcher = patDecryptionJsFile.Matcher(streamMap);
if (matcher.Find())
{
curJsFileName = matcher.Group(1).Replace("\\/", "/");
if (decipherJsFileName == null || !decipherJsFileName.Equals(curJsFileName))
{
decipherFunctions = null;
decipherFunctionName = null;
}
decipherJsFileName = curJsFileName;
}
if (parseDashManifest)
{
matcher = patDashManifest2.Matcher(streamMap);
if (matcher.Find())
{
string dashMpdUrl = matcher.Group(1).Replace("\\/", "/");
matcher = patDashManifestEncSig.Matcher(dashMpdUrl);
if (matcher.Find())
{
encSignatures.Append(0, matcher.Group(1));
}
else
{
dashMpdUrl = null;
}
}
}
return encSignatures;
}
private SparseArray<YtFile> GetLiveStreamUrls(string streamMap)
{
Matcher matcher = patHlsvp.Matcher(streamMap);
if (matcher.Find())
{
string hlsvp = URLDecoder.Decode(matcher.Group(1), "UTF-8");
SparseArray<YtFile> ytFiles = new SparseArray<YtFile>();
URL url = new URL(hlsvp);
HttpURLConnection urlConnection = (HttpURLConnection)url.OpenConnection();
urlConnection.SetRequestProperty("User-Agent", USER_AGENT);
BufferedReader reader = null;
try
{
reader = new BufferedReader(new InputStreamReader(urlConnection.InputStream));
string line;
while ((line = reader.ReadLine()) != null)
{
if (line.StartsWith("https://") || line.StartsWith("http://"))
{
matcher = patHlsItag.Matcher(line);
if (matcher.Find())
{
int itag = int.Parse(matcher.Group(1));
YtFile newFile = new YtFile(formats[itag], line);
ytFiles.Put(itag, newFile);
}
}
}
}
finally
{
if (reader != null)
reader.Close();
urlConnection.Disconnect();
}
if (ytFiles.Size() == 0)
return null;
return ytFiles;
}
return null;
}
private VideoMeta ParseVideoMeta(string videoInfo)
{
bool isLiveStream = false;
string title = null;
string author = null;
string channelID = null;
long viewCount = 0;
long length = 0;
Matcher matcher = patTitle.Matcher(videoInfo);
if (matcher.Find())
title = URLDecoder.Decode(matcher.Group(1), "UTF-8");
matcher = patHlsvp.Matcher(videoInfo);
if (matcher.Find())
isLiveStream = true;
matcher = patAuthor.Matcher(videoInfo);
if (matcher.Find())
author = URLDecoder.Decode(matcher.Group(1), "UTF-8");
matcher = patChannelId.Matcher(videoInfo);
if (matcher.Find())
channelID = matcher.Group(1);
matcher = patLength.Matcher(videoInfo);
if (matcher.Find())
length = Long.ParseLong(matcher.Group(1));
matcher = patViewCount.Matcher(videoInfo);
if (matcher.Find())
viewCount = Long.ParseLong(matcher.Group(1));
return new VideoMeta(videoID, title, author, channelID, length, viewCount, isLiveStream);
}
protected override SparseArray<YtFile> RunInBackground(params string[] @params)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,45 @@
using System.Collections.Generic;
using Android.Content;
using Android.Views;
using Android.Widget;
using MusicApp.Resources.values;
using Android.App;
using Square.Picasso;
namespace MusicApp.Resources.Portable_Class
{
public class YtAdapter : ArrayAdapter
{
private Context context;
private List<YtFile> ytList;
private LayoutInflater inflater;
private int resource;
public YtAdapter(Context context, int resource, List<YtFile> ytList) : base(context, resource, ytList)
{
this.context = context;
this.resource = resource;
this.ytList = ytList;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
if (inflater == null)
{
inflater = (LayoutInflater)context.GetSystemService(Context.LayoutInflaterService);
}
if (convertView == null)
{
convertView = inflater.Inflate(resource, parent, false);
}
Holder holder = new Holder(convertView)
{
Title = { Text = ytList[position].Title },
Artist = { Text = ytList[position].channelTitle },
};
Picasso.With(Application.Context).Load(ytList[position].thumbnailUrl).Placeholder(Resource.Drawable.MusicIcon).Resize(400, 400).CenterCrop().Into(holder.AlbumArt);
return convertView;
}
}
}

View File

@@ -1,43 +1,19 @@
namespace MusicApp.Resources.Portable_Class
{
[System.Serializable]
public class YtFile
{
public Format format;
public string url;
public string Title;
public string channelTitle;
public string Id;
public string thumbnailUrl;
public YtFile(Format format, string url)
public YtFile(string title, string channelTitle, string id, string thumbnailUrl)
{
this.format = format;
this.url = url;
}
public override bool Equals(object obj)
{
if (this == obj)
return true;
if (obj == null || GetType() != obj.GetType())
return false;
YtFile ytFile = (YtFile)obj;
if (format != null ? !format.Equals(ytFile.format) : ytFile.format != null)
return false;
return url != null ? url.Equals(ytFile.url) : ytFile.url == null;
}
public override int GetHashCode()
{
int result = format != null ? format.GetHashCode() : 0;
result = 31 * result + (url != null ? url.GetHashCode() : 0);
return result;
}
public override string ToString()
{
return "YtFile{" +
"format=" + format +
", url='" + url + '\'' +
'}';
Title = title;
this.channelTitle = channelTitle;
Id = id;
this.thumbnailUrl = thumbnailUrl;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8" ?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="8dp">
<ImageView
android:id="@+id/albumArt"
android:layout_width="120dp"
android:layout_height="90dp"
android:padding="5dp"
android:src="@drawable/MusicIcon"
android:layout_alignParentLeft="true" />
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="100dp">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#000"
android:textSize="16dip"
android:textStyle="bold" />
<TextView
android:id="@+id/artist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14dip"
android:textColor="#000" />
</LinearLayout>
</RelativeLayout >

View File

@@ -64,7 +64,6 @@
<package id="System.Xml.ReaderWriter" version="4.3.0" targetFramework="monoandroid70" />
<package id="System.Xml.XDocument" version="4.3.0" targetFramework="monoandroid70" />
<package id="System.Xml.XmlDocument" version="4.3.0" targetFramework="monoandroid70" />
<package id="Xamarin.Android.FFmpeg" version="1.0.32" targetFramework="monoandroid70" />
<package id="Xamarin.Android.Support.Animated.Vector.Drawable" version="25.4.0.2" targetFramework="monoandroid70" />
<package id="Xamarin.Android.Support.Annotations" version="25.4.0.2" targetFramework="monoandroid70" />
<package id="Xamarin.Android.Support.Compat" version="25.4.0.2" targetFramework="monoandroid70" />
@@ -79,6 +78,13 @@
<package id="Xamarin.Android.Support.v7.Preference" version="25.4.0.2" targetFramework="monoandroid70" />
<package id="Xamarin.Android.Support.v7.RecyclerView" version="25.4.0.2" targetFramework="monoandroid70" />
<package id="Xamarin.Android.Support.Vector.Drawable" version="25.4.0.2" targetFramework="monoandroid70" />
<package id="Xamarin.Build.Download" version="0.4.2" targetFramework="monoandroid70" />
<package id="Xamarin.GooglePlayServices.Auth" version="42.1021.1" targetFramework="monoandroid70" />
<package id="Xamarin.GooglePlayServices.Auth.Base" version="42.1021.1" targetFramework="monoandroid70" />
<package id="Xamarin.GooglePlayServices.Base" version="42.1021.1" targetFramework="monoandroid70" />
<package id="Xamarin.GooglePlayServices.Basement" version="42.1021.1" targetFramework="monoandroid70" />
<package id="Xamarin.GooglePlayServices.Identity" version="42.1021.1" targetFramework="monoandroid70" />
<package id="Xamarin.GooglePlayServices.Tasks" version="42.1021.1" targetFramework="monoandroid70" />
<package id="YoutubeExplode" version="3.2.5" targetFramework="monoandroid70" />
<package id="YoutubeSearch.dll" version="1.1" targetFramework="monoandroid70" />
</packages>