mirror of
https://github.com/zoriya/Opus.git
synced 2025-12-06 06:26:15 +00:00
Replaced search engine with official one
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
228
MusicApp/Resources/Portable Class/YoutubeEngine.cs
Normal file
228
MusicApp/Resources/Portable Class/YoutubeEngine.cs
Normal 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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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("&", "&").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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
45
MusicApp/Resources/Portable Class/YtAdapter.cs
Normal file
45
MusicApp/Resources/Portable Class/YtAdapter.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
6406
MusicApp/Resources/Resource.Designer.cs
generated
6406
MusicApp/Resources/Resource.Designer.cs
generated
File diff suppressed because it is too large
Load Diff
32
MusicApp/Resources/layout/YtList.xml
Normal file
32
MusicApp/Resources/layout/YtList.xml
Normal 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 >
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user