1047 lines
27 KiB
C#
1047 lines
27 KiB
C#
namespace Tesses.YouTubeDownloader.Droid;
|
|
using Tesses.YouTubeDownloader;
|
|
using Tesses.YouTubeDownloader.Server;
|
|
using Tesses.WebServer;
|
|
using Tesses.WebServer.Swagme;
|
|
using Android.Content;
|
|
using Android.Net;
|
|
using Android.Database;
|
|
using Android.OS;
|
|
using Tesses.YouTubeDownloader.SFTP;
|
|
using Renci.SshNet;
|
|
using System.Net;
|
|
using Android.Preferences;
|
|
using AndroidX.Preference;
|
|
using Android.Runtime;
|
|
using Java.Interop;
|
|
|
|
public class TYTDPreferences : PreferenceDataStore
|
|
{
|
|
|
|
public override bool GetBoolean(string? key, bool defValue)
|
|
{
|
|
if(key == TYTDCommon.TYTD_SERVER_ACCESSABLE)
|
|
{
|
|
return TYTDCommon.ServerAccessable;
|
|
}
|
|
if(key == TYTDCommon.TYTD_NO_EXTERNAL_INFO_THUMB)
|
|
{
|
|
//no scanning server for info or thumb
|
|
return TYTDCommon.NoExtenalInfoThumbnail;
|
|
}
|
|
if(key == TYTDCommon.TYTD_CACHE_THUMBNAILS_ON_START)
|
|
{
|
|
return TYTDCommon.CacheThumbnailsFromServersOnStartup;
|
|
}
|
|
if(key == TYTDCommon.TYTD_CACHE_INFO_ON_START)
|
|
{
|
|
return TYTDCommon.CacheInfoFromServersOnStartup;
|
|
}
|
|
if(key == TYTDCommon.TYTD_CACHE_LAZY)
|
|
{
|
|
return TYTDCommon.CacheLazy;
|
|
}
|
|
if(key == TYTDCommon.TYTD_FETCH_THUMB_YOUTUBE)
|
|
{
|
|
return TYTDCommon.FetchThumbYouTube;
|
|
}
|
|
return defValue;
|
|
}
|
|
|
|
public override float GetFloat(string? key, float defValue)
|
|
{
|
|
return defValue;
|
|
}
|
|
|
|
|
|
|
|
public override int GetInt(string? key, int defValue)
|
|
{
|
|
if(key == TYTDCommon.TYTD_SERVER_PORT)
|
|
{
|
|
return TYTDCommon.ServerPort;
|
|
}
|
|
return defValue;
|
|
}
|
|
|
|
public override long GetLong(string? key, long defValue)
|
|
{
|
|
return defValue;
|
|
}
|
|
|
|
public override string? GetString(string? key, string? defValue)
|
|
{
|
|
if(key == TYTDCommon.TYTD_SERVER_PORT)
|
|
{
|
|
return TYTDCommon.ServerPort.ToString();
|
|
}
|
|
if(key == TYTDCommon.TYTD_TAG)
|
|
{
|
|
return TYTDCommon.Tag;
|
|
}
|
|
return defValue;
|
|
}
|
|
|
|
public override ICollection<string>? GetStringSet(string? key, ICollection<string>? defValues)
|
|
{
|
|
return defValues;
|
|
}
|
|
|
|
public override void PutBoolean(string? key, bool value)
|
|
{
|
|
if(key == TYTDCommon.TYTD_SERVER_ACCESSABLE)
|
|
{
|
|
TYTDCommon.ServerAccessable=value;
|
|
}
|
|
if(key == TYTDCommon.TYTD_NO_EXTERNAL_INFO_THUMB)
|
|
{
|
|
//no scanning server for info or thumb
|
|
TYTDCommon.NoExtenalInfoThumbnail = value;
|
|
}
|
|
if(key == TYTDCommon.TYTD_CACHE_THUMBNAILS_ON_START)
|
|
{
|
|
TYTDCommon.CacheThumbnailsFromServersOnStartup=value;
|
|
}
|
|
if(key == TYTDCommon.TYTD_CACHE_INFO_ON_START)
|
|
{
|
|
TYTDCommon.CacheInfoFromServersOnStartup=value;
|
|
}
|
|
if(key == TYTDCommon.TYTD_CACHE_LAZY)
|
|
{
|
|
TYTDCommon.CacheLazy=value;
|
|
}
|
|
if(key == TYTDCommon.TYTD_FETCH_THUMB_YOUTUBE)
|
|
{
|
|
TYTDCommon.FetchThumbYouTube=value;
|
|
}
|
|
|
|
}
|
|
|
|
public override void PutFloat(string? key, float value)
|
|
{
|
|
|
|
}
|
|
|
|
public override void PutInt(string? key, int value)
|
|
{
|
|
if(key == TYTDCommon.TYTD_SERVER_PORT)
|
|
{
|
|
if(value <= ushort.MaxValue)
|
|
{
|
|
TYTDCommon.ServerPort=(ushort)value;
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void PutLong(string? key, long value)
|
|
{
|
|
base.PutLong(key, value);
|
|
}
|
|
|
|
public override void PutString(string? key, string? value)
|
|
{
|
|
if(key == TYTDCommon.TYTD_SERVER_PORT)
|
|
{
|
|
ushort value2;
|
|
if(ushort.TryParse(value,out value2))
|
|
{
|
|
TYTDCommon.ServerPort=value2;
|
|
}
|
|
}
|
|
if(key == TYTDCommon.TYTD_TAG)
|
|
{
|
|
if(!string.IsNullOrWhiteSpace(value)){
|
|
TYTDCommon.Tag = value;
|
|
}else{
|
|
TYTDCommon.Tag="UnknownPC";
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void PutStringSet(string? key, ICollection<string>? values)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
public static class CastExtensions
|
|
{
|
|
public static async Task CopyThumbnailsFromYouTubeAsync(this IStorage storage,string id)
|
|
{
|
|
if(id.Length == 11)
|
|
{
|
|
storage.CreateDirectoryIfNotExist($"Thumbnails/{id}");
|
|
await storage.CopyThumbnailFromYouTubeAsync(id,"default");
|
|
await storage.CopyThumbnailFromYouTubeAsync(id,"hqdefault");
|
|
await storage.CopyThumbnailFromYouTubeAsync(id,"maxresdefault");
|
|
await storage.CopyThumbnailFromYouTubeAsync(id,"mqdefault");
|
|
await storage.CopyThumbnailFromYouTubeAsync(id,"sddefault");
|
|
}
|
|
}
|
|
private static async Task CopyThumbnailFromYouTubeAsync(this IStorage storage,string id,string thumbnail)
|
|
{
|
|
string name=$"Thumbnails/{id}/{thumbnail}.jpg";
|
|
if(!storage.FileExists(name))
|
|
{
|
|
try{
|
|
using(var src=await storage.HttpClient.GetStreamAsync($"https://i.ytimg.com/vi/{id}/{thumbnail}.jpg")){
|
|
using(var dest=await storage.CreateAsync(name))
|
|
{
|
|
await src.CopyToAsync(dest);
|
|
}
|
|
}}catch(Exception ex)
|
|
{
|
|
_=ex;
|
|
}
|
|
}
|
|
}
|
|
private static async Task CopyThumbnailAsync(this ITYTDBase baseCtl,string id,string thumbnail,IStorage storage)
|
|
{
|
|
string name=$"Thumbnails/{id}/{thumbnail}.jpg";
|
|
if(!storage.FileExists(name) && baseCtl.FileExists(name))
|
|
{
|
|
using(var src=await baseCtl.OpenReadAsync(name)){
|
|
using(var dest=await storage.CreateAsync(name))
|
|
{
|
|
await src.CopyToAsync(dest);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
public static async Task CopyThumbnailsAsync(this ITYTDBase baseCtl,string id,IStorage storage)
|
|
{
|
|
storage.CreateDirectoryIfNotExist($"Thumbnails/{id}");
|
|
await baseCtl.CopyThumbnailAsync(id,"default",storage);
|
|
await baseCtl.CopyThumbnailAsync(id,"hqdefault",storage);
|
|
await baseCtl.CopyThumbnailAsync(id,"maxresdefault",storage);
|
|
await baseCtl.CopyThumbnailAsync(id,"mqdefault",storage);
|
|
await baseCtl.CopyThumbnailAsync(id,"sddefault",storage);
|
|
await baseCtl.CopyThumbnailAsync(id,"900x900",storage);
|
|
}
|
|
public static async Task CastIfAsync<T1,T2>(this T1? src,Func<T2,Task> cb) where T2 : class
|
|
{
|
|
var type = src as T2;
|
|
if(type != null && cb != null)
|
|
{
|
|
await cb(type);
|
|
}
|
|
}
|
|
public static void CastIf<T1,T2>(this T1? src,Action<T2> cb) where T2 : class
|
|
{
|
|
var type = src as T2;
|
|
if(type != null && cb != null)
|
|
{
|
|
cb(type);
|
|
}
|
|
}
|
|
}
|
|
public static class TYTDCommon
|
|
{
|
|
|
|
|
|
#region PREFENCES
|
|
public static string Tag {
|
|
get{
|
|
if(ctx==null) return "UnknownPC";
|
|
var res=ctx.GetSharedPreferences(TYTD_CONFIG,FileCreationMode.Private);
|
|
if(res != null)
|
|
{
|
|
var res2=res.GetString(TYTD_TAG,"UnknownPC");
|
|
if(!string.IsNullOrWhiteSpace(res2))
|
|
{
|
|
return res2;
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
set{
|
|
if(ctx==null) return;
|
|
var res=ctx.GetSharedPreferences(TYTD_CONFIG,FileCreationMode.Private);
|
|
if(res != null)
|
|
{
|
|
var res2=res.Edit();
|
|
if(res2 != null)
|
|
{
|
|
res2.PutString(TYTD_TAG,value);
|
|
|
|
if(res2.Commit())
|
|
{
|
|
Restart();
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
public static bool ServerAccessable
|
|
{
|
|
get{
|
|
if(ctx==null) return false;
|
|
var res=ctx.GetSharedPreferences(TYTD_CONFIG,FileCreationMode.Private);
|
|
if(res != null)
|
|
{
|
|
var res2=res.GetBoolean(TYTD_SERVER_ACCESSABLE,false);
|
|
return res2;
|
|
}
|
|
return false;
|
|
}
|
|
set{
|
|
if(ctx==null) return;
|
|
var res=ctx.GetSharedPreferences(TYTD_CONFIG,FileCreationMode.Private);
|
|
if(res != null)
|
|
{
|
|
var res2=res.Edit();
|
|
if(res2 != null)
|
|
{
|
|
res2.PutBoolean(TYTD_SERVER_ACCESSABLE,value);
|
|
|
|
if(res2.Commit())
|
|
{
|
|
Restart();
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
public static bool CacheLazy
|
|
{
|
|
get{
|
|
if(ctx==null) return false;
|
|
var res=ctx.GetSharedPreferences(TYTD_CONFIG,FileCreationMode.Private);
|
|
if(res != null)
|
|
{
|
|
var res2=res.GetBoolean(TYTD_CACHE_LAZY,false);
|
|
return res2;
|
|
}
|
|
return false;
|
|
}
|
|
set{
|
|
if(ctx==null) return;
|
|
var res=ctx.GetSharedPreferences(TYTD_CONFIG,FileCreationMode.Private);
|
|
if(res != null)
|
|
{
|
|
var res2=res.Edit();
|
|
if(res2 != null)
|
|
{
|
|
res2.PutBoolean(TYTD_CACHE_LAZY,value);
|
|
|
|
res2.Apply();
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
public static bool CacheThumbnailsFromServersOnStartup
|
|
{
|
|
get{
|
|
if(ctx==null) return false;
|
|
var res=ctx.GetSharedPreferences(TYTD_CONFIG,FileCreationMode.Private);
|
|
if(res != null)
|
|
{
|
|
var res2=res.GetBoolean(TYTD_CACHE_THUMBNAILS_ON_START,false);
|
|
return res2;
|
|
}
|
|
return false;
|
|
}
|
|
set{
|
|
if(ctx==null) return;
|
|
var res=ctx.GetSharedPreferences(TYTD_CONFIG,FileCreationMode.Private);
|
|
if(res != null)
|
|
{
|
|
var res2=res.Edit();
|
|
if(res2 != null)
|
|
{
|
|
res2.PutBoolean(TYTD_CACHE_THUMBNAILS_ON_START,value);
|
|
|
|
res2.Apply();
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public static bool CacheInfoFromServersOnStartup
|
|
{
|
|
get{
|
|
if(ctx==null) return false;
|
|
var res=ctx.GetSharedPreferences(TYTD_CONFIG,FileCreationMode.Private);
|
|
if(res != null)
|
|
{
|
|
var res2=res.GetBoolean(TYTD_CACHE_INFO_ON_START,false);
|
|
return res2;
|
|
}
|
|
return false;
|
|
}
|
|
set{
|
|
if(ctx==null) return;
|
|
var res=ctx.GetSharedPreferences(TYTD_CONFIG,FileCreationMode.Private);
|
|
if(res != null)
|
|
{
|
|
var res2=res.Edit();
|
|
if(res2 != null)
|
|
{
|
|
res2.PutBoolean(TYTD_CACHE_INFO_ON_START,value);
|
|
|
|
res2.Apply();
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
public static bool NoExtenalInfoThumbnail
|
|
{
|
|
get{
|
|
if(ctx==null) return false;
|
|
var res=ctx.GetSharedPreferences(TYTD_CONFIG,FileCreationMode.Private);
|
|
if(res != null)
|
|
{
|
|
var res2=res.GetBoolean(TYTD_NO_EXTERNAL_INFO_THUMB,false);
|
|
return res2;
|
|
}
|
|
return false;
|
|
}
|
|
set{
|
|
if(ctx==null) return;
|
|
var res=ctx.GetSharedPreferences(TYTD_CONFIG,FileCreationMode.Private);
|
|
if(res != null)
|
|
{
|
|
var res2=res.Edit();
|
|
if(res2 != null)
|
|
{
|
|
res2.PutBoolean(TYTD_NO_EXTERNAL_INFO_THUMB,value);
|
|
|
|
res2.Apply();
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
public static bool FetchThumbYouTube
|
|
{
|
|
get{
|
|
if(ctx==null) return false;
|
|
var res=ctx.GetSharedPreferences(TYTD_CONFIG,FileCreationMode.Private);
|
|
if(res != null)
|
|
{
|
|
var res2=res.GetBoolean(TYTD_FETCH_THUMB_YOUTUBE,false);
|
|
return res2;
|
|
}
|
|
return false;
|
|
}
|
|
set{
|
|
if(ctx==null) return;
|
|
var res=ctx.GetSharedPreferences(TYTD_CONFIG,FileCreationMode.Private);
|
|
if(res != null)
|
|
{
|
|
var res2=res.Edit();
|
|
if(res2 != null)
|
|
{
|
|
res2.PutBoolean(TYTD_FETCH_THUMB_YOUTUBE,value);
|
|
|
|
res2.Apply();
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public static ushort ServerPort
|
|
{
|
|
get{
|
|
if(ctx==null) return 3252;
|
|
var res=ctx.GetSharedPreferences(TYTD_CONFIG,FileCreationMode.Private);
|
|
if(res != null)
|
|
{
|
|
var res2=res.GetInt(TYTD_SERVER_PORT,3252);
|
|
if(res2 < 1025 || res2 > ushort.MaxValue)
|
|
return 1025;
|
|
|
|
return (ushort)res2;
|
|
}
|
|
return 3252;
|
|
}
|
|
set{
|
|
if(ctx==null) return;
|
|
var res=ctx.GetSharedPreferences(TYTD_CONFIG,FileCreationMode.Private);
|
|
if(res != null)
|
|
{
|
|
var res2=res.Edit();
|
|
if(res2 != null)
|
|
{
|
|
res2.PutInt(TYTD_SERVER_PORT,value);
|
|
|
|
if(res2.Commit())
|
|
{
|
|
Restart();
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
public static string AppDownloadLocation
|
|
{
|
|
get{
|
|
if(ctx==null) return "";
|
|
var res=ctx.GetSharedPreferences(TYTD_CONFIG,FileCreationMode.Private);
|
|
if(res != null)
|
|
{
|
|
var res2=res.GetString(TYTD_DOWNLOAD_LOCATION,"");
|
|
if(!string.IsNullOrWhiteSpace(res2))
|
|
{
|
|
return res2;
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
set{
|
|
if(ctx==null) return;
|
|
var res=ctx.GetSharedPreferences(TYTD_CONFIG,FileCreationMode.Private);
|
|
if(res != null)
|
|
{
|
|
var res2=res.Edit();
|
|
if(res2 != null)
|
|
{
|
|
res2.PutString(TYTD_DOWNLOAD_LOCATION,value);
|
|
|
|
if(res2.Commit())
|
|
{
|
|
Restart();
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
public static string AppStorageLocation
|
|
{
|
|
get{
|
|
if(ctx==null) return "";
|
|
var res=ctx.GetSharedPreferences(TYTD_CONFIG,FileCreationMode.Private);
|
|
if(res != null)
|
|
{
|
|
var res2=res.GetString(TYTD_STORE_LOCATION,"");
|
|
if(!string.IsNullOrWhiteSpace(res2))
|
|
{
|
|
return res2;
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
set{
|
|
if(ctx==null) return;
|
|
var res=ctx.GetSharedPreferences(TYTD_CONFIG,FileCreationMode.Private);
|
|
if(res != null)
|
|
{
|
|
var res2=res.Edit();
|
|
if(res2 != null)
|
|
{
|
|
res2.PutString(TYTD_STORE_LOCATION,value);
|
|
if(res2.Commit())
|
|
{
|
|
Restart();
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
#region CONSTANTS
|
|
public const string TYTD_CONFIG="tytdConfig";
|
|
public const string TYTD_TAG = "tytdTag";
|
|
public const string TYTD_STORE_LOCATION = "tytdStorageLocation";
|
|
public const string TYTD_DOWNLOAD_LOCATION = "tytdDownloadLocation";
|
|
|
|
public const string TYTD_SERVER_ACCESSABLE = "tytdServerAccessable";
|
|
public const string TYTD_SERVER_PORT= "tytdServerPort";
|
|
public const string TYTD_NO_EXTERNAL_INFO_THUMB= "tytdNoExternalInfoThumb";
|
|
public const string TYTD_CACHE_THUMBNAILS_ON_START="tytdCacheThumbnailsOnStart";
|
|
public const string TYTD_CACHE_INFO_ON_START = "tytdCacheInfoOnStart";
|
|
|
|
public const string TYTD_FETCH_THUMB_YOUTUBE = "tytdFetchThumbYouTube";
|
|
|
|
public const string TYTD_CACHE_LAZY="tytdCacheLazy";
|
|
#endregion
|
|
#region PRIVATE_FIELDS
|
|
private static string data_dir_internal="";
|
|
private static CancellationTokenSource? src=null;
|
|
private static Context? ctx=null;
|
|
private static CancellationToken token=default(CancellationToken);
|
|
private static string server_url_file="";
|
|
private static List<PrivateKeyFile> privateKeys=new List<PrivateKeyFile>();
|
|
|
|
private static Dictionary<string,ITYTDBase> svrs=new Dictionary<string, ITYTDBase>();
|
|
#endregion
|
|
#region PUBLIC_PROPERTIES
|
|
|
|
public static Type? MainActivity {get;set;}
|
|
public static Type? SettingsActivity {get;set;}
|
|
public static ITYTDBase? TYTD {get;private set;}
|
|
public static Context? Context {get{return ctx;}}
|
|
|
|
public static ITYTDBase? LocalStorage {get;private set;}
|
|
public static bool HasContext {get{return ctx != null;}}
|
|
#endregion
|
|
|
|
#region PUBLIC_METHODS
|
|
|
|
public static Dictionary<string,string> GetServers()
|
|
{
|
|
Dictionary<string,string> servers=new Dictionary<string, string>();
|
|
if(File.Exists(server_url_file)){
|
|
var lines=File.ReadAllLines(server_url_file);
|
|
foreach(var line in lines)
|
|
{
|
|
string[] item=line.Split(':',2,StringSplitOptions.RemoveEmptyEntries);
|
|
if(item.Length ==2)
|
|
{
|
|
servers.Add(item[0],item[1]);
|
|
}
|
|
}
|
|
}
|
|
return servers;
|
|
}
|
|
public static void SaveServers(Dictionary<string,string> servers)
|
|
{
|
|
List<string> servers_list=new List<string>();
|
|
foreach(var svr in servers)
|
|
{
|
|
servers_list.Add($"{svr.Key.Replace(":","").Replace("\n","").Replace("\r","")}:{svr.Value.Replace("\n","").Replace("\r","")}");
|
|
}
|
|
File.WriteAllLines(server_url_file,servers_list);
|
|
}
|
|
public static async Task<byte[]> GetBestThumbnailArrayAsync(string id,string thumbRes)
|
|
{
|
|
string thumb = $"Thumbnails/{id}/{thumbRes}.jpg";
|
|
if(LocalStorage!= null && LocalStorage.FileExists(thumb)) return await LocalStorage.ReadAllBytesAsync(thumb);
|
|
|
|
if(TYTD != null && TYTD.FileExists(thumb)) {
|
|
|
|
|
|
var bytes=await TYTD.ReadAllBytesAsync(thumb);
|
|
|
|
if(CacheLazy && LocalStorage != null)
|
|
{
|
|
await LocalStorage.CastIfAsync<ITYTDBase,IStorage>(async(e)=>{
|
|
using(var f = await e.CreateAsync(thumb))
|
|
{
|
|
await f.WriteAsync(bytes,0,bytes.Length);
|
|
}
|
|
});
|
|
}
|
|
return bytes;
|
|
}
|
|
|
|
|
|
return Convert.FromBase64String("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");
|
|
}
|
|
public static byte[] GetBestThumbnailArray(string id,string thumbRes)
|
|
{
|
|
return GetBestThumbnailArrayAsync(id,thumbRes).Result;
|
|
}
|
|
public static async IAsyncEnumerable<SavedVideo> EnumerateVideos()
|
|
{
|
|
|
|
if(NoExtenalInfoThumbnail)
|
|
{
|
|
if(LocalStorage == null) yield break;
|
|
await foreach(var item in LocalStorage.GetVideosAsync())
|
|
{
|
|
if(FetchThumbYouTube)
|
|
{
|
|
await LocalStorage.CastIfAsync<ITYTDBase,IStorage>(async(e)=>{
|
|
|
|
await e.CopyThumbnailsFromYouTubeAsync(item.Id);
|
|
});
|
|
}
|
|
yield return await Task.FromResult(item);
|
|
}
|
|
}else{
|
|
if(TYTD != null){
|
|
await foreach(var item in TYTD.GetVideosAsync())
|
|
{
|
|
if(CacheLazy && LocalStorage != null && !LocalStorage.VideoInfoExists(item.Id))
|
|
{
|
|
await LocalStorage.CastIfAsync<ITYTDBase,IStorage>(async(e)=>{
|
|
await e.WriteVideoInfoAsync(item);
|
|
});
|
|
}
|
|
yield return await Task.FromResult(item);
|
|
}
|
|
}else{
|
|
|
|
if(LocalStorage == null) yield break;
|
|
await foreach(var item in LocalStorage.GetVideosAsync())
|
|
{
|
|
if(FetchThumbYouTube || CacheLazy)
|
|
{
|
|
await LocalStorage.CastIfAsync<ITYTDBase,IStorage>(async(e)=>{
|
|
|
|
await e.CopyThumbnailsFromYouTubeAsync(item.Id);
|
|
});
|
|
}
|
|
yield return await Task.FromResult(item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void StartBootCache<T>(Func<T> startCacheCb,Action<T> stopCacheCb)
|
|
{
|
|
if(CacheThumbnailsFromServersOnStartup || CacheInfoFromServersOnStartup)
|
|
{
|
|
T t = startCacheCb();
|
|
Thread thd=new Thread(async()=>{
|
|
if(LocalStorage != null ){
|
|
foreach(var server in GetServers())
|
|
{
|
|
try{
|
|
|
|
var tytddb = getTYTDBase(server.Value,Path.Combine(data_dir_internal,"working"),privateKeys);
|
|
await foreach(var item in tytddb.GetVideoIdsAsync())
|
|
{
|
|
|
|
if(!LocalStorage.VideoInfoExists(item) && CacheInfoFromServersOnStartup)
|
|
{
|
|
await LocalStorage.CastIfAsync<ITYTDBase,IStorage>(async (e)=>{
|
|
await e.WriteVideoInfoAsync(await tytddb.GetVideoInfoAsync(item));
|
|
});
|
|
}
|
|
if(CacheThumbnailsFromServersOnStartup)
|
|
{
|
|
await LocalStorage.CastIfAsync<ITYTDBase,IStorage>(async (e)=>{
|
|
|
|
await tytddb.CopyThumbnailsAsync(item,e);
|
|
});
|
|
}
|
|
|
|
}
|
|
await foreach(var item in tytddb.GetPlaylistIdsAsync())
|
|
{
|
|
if(!LocalStorage.PlaylistInfoExists(item) && CacheInfoFromServersOnStartup)
|
|
{
|
|
await LocalStorage.CastIfAsync<ITYTDBase,IStorage>(async (e)=>{
|
|
await e.WritePlaylistInfoAsync(await tytddb.GetPlaylistInfoAsync(item));
|
|
});
|
|
}
|
|
|
|
if(LocalStorage.PlaylistInfoExists(item))
|
|
{
|
|
var pl=await LocalStorage.GetPlaylistInfoAsync(item);
|
|
foreach(var id in pl.Videos)
|
|
{
|
|
if(!LocalStorage.VideoInfoExists(id) && tytddb.VideoInfoExists(id) && CacheInfoFromServersOnStartup)
|
|
{
|
|
await LocalStorage.CastIfAsync<ITYTDBase,IStorage>(async (e)=>{
|
|
await e.WriteVideoInfoAsync(await tytddb.GetVideoInfoAsync(id));
|
|
});
|
|
}
|
|
if(CacheThumbnailsFromServersOnStartup)
|
|
{
|
|
await LocalStorage.CastIfAsync<ITYTDBase,IStorage>(async (e)=>{
|
|
|
|
await tytddb.CopyThumbnailsAsync(id,e);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
await foreach(var channel in tytddb.GetChannelIdsAsync())
|
|
{
|
|
if(!LocalStorage.ChannelInfoExists(channel) && CacheInfoFromServersOnStartup)
|
|
{
|
|
await LocalStorage.CastIfAsync<ITYTDBase,IStorage>(async (e)=>{
|
|
await e.WriteChannelInfoAsync(await tytddb.GetChannelInfoAsync(channel));
|
|
});
|
|
}
|
|
if(CacheThumbnailsFromServersOnStartup)
|
|
{
|
|
await LocalStorage.CastIfAsync<ITYTDBase,IStorage>(async (e)=>{
|
|
|
|
await tytddb.CopyThumbnailsAsync(channel,e);
|
|
});
|
|
}
|
|
}
|
|
|
|
}catch(Exception ex)
|
|
{
|
|
_=ex;
|
|
}
|
|
}
|
|
}
|
|
stopCacheCb(t);
|
|
});
|
|
thd.Start();
|
|
}
|
|
}
|
|
|
|
public static List<(string Label,string Path)> GetStorageDevices()
|
|
{
|
|
List<(string Label,string Path)> paths=new List<(string Label, string Path)>();
|
|
if(ctx != null){
|
|
var dirs=ctx.GetExternalFilesDirs(null);
|
|
if(dirs != null)
|
|
foreach(var item in dirs)
|
|
{
|
|
string path=item.ToString();
|
|
if(path.StartsWith("/storage/emulated/"))
|
|
{
|
|
paths.Add(("Internal Storage",path));
|
|
}else{
|
|
if(path.StartsWith("/storage/"))
|
|
{
|
|
string[] path0=path.Split('/',3,StringSplitOptions.RemoveEmptyEntries);
|
|
if(path0.Length >= 2)
|
|
{
|
|
paths.Add((path0[1],path));
|
|
|
|
}else{
|
|
paths.Add((path,path));
|
|
|
|
}
|
|
}else{
|
|
paths.Add((path,path));
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return paths;
|
|
}
|
|
public static void Close()
|
|
{
|
|
if(src != null)
|
|
{
|
|
src.Cancel();
|
|
src=null;
|
|
}
|
|
}
|
|
public static void Restart()
|
|
{
|
|
|
|
if(ctx != null)
|
|
reInit(ctx);
|
|
}
|
|
|
|
public static void Init(Android.Content.Context ctx)
|
|
{
|
|
if(!HasContext)
|
|
{
|
|
reInit(ctx);
|
|
}
|
|
|
|
}
|
|
|
|
#endregion
|
|
#region PRIVATE_METHODS
|
|
private static void reInit(Android.Content.Context ctx)
|
|
{
|
|
|
|
Close();
|
|
src=new CancellationTokenSource();
|
|
var dir=ctx.GetExternalFilesDir(null);
|
|
|
|
var token = src.Token;
|
|
|
|
|
|
if(dir == null) return;
|
|
TYTDCommon.ctx=ctx;
|
|
|
|
TYTDCommon.token = token;
|
|
data_dir_internal=dir.ToString();
|
|
server_url_file = Path.Combine(data_dir_internal,"servers.txt");
|
|
|
|
|
|
string applocationInternal = Path.Combine(data_dir_internal, "working");
|
|
string privKeyDir = Path.Combine(data_dir_internal,"privkeys");
|
|
Directory.CreateDirectory(privKeyDir);
|
|
foreach(var priv in Directory.GetFiles(privKeyDir))
|
|
{
|
|
privateKeys.Add(new PrivateKeyFile(priv));
|
|
}
|
|
|
|
Directory.CreateDirectory(applocationInternal);
|
|
System.Environment.CurrentDirectory = applocationInternal;
|
|
|
|
|
|
string appStorageLocation = AppStorageLocation;
|
|
string appDownloadLocation = AppDownloadLocation;
|
|
|
|
|
|
|
|
|
|
TYTDStorage.SetTYTDTag(Tag);
|
|
MountableServer svr=new MountableServer(new StaticServer(Path.Combine(data_dir_internal,"site")));
|
|
svr.Mount("/serveradmin/",getServerApi(data_dir_internal));
|
|
LocalStorage=getTYTDBase("",applocationInternal,privateKeys);
|
|
var storage=getTYTDBase(appStorageLocation,applocationInternal,privateKeys);
|
|
var downloader = getTYTDBase(appDownloadLocation,applocationInternal,privateKeys);
|
|
ITYTDBase baseCtl=storage;
|
|
|
|
if(storage != downloader)
|
|
{
|
|
var proxy = new TYTDDownloaderStorageProxy();
|
|
proxy.Storage = storage;
|
|
downloader.CastIf<ITYTDBase,IDownloader>((dl)=>{
|
|
proxy.Downloader = dl;
|
|
});
|
|
|
|
baseCtl=proxy;
|
|
}
|
|
|
|
TYTD=baseCtl;
|
|
|
|
TYTDServer server=new TYTDServer(baseCtl);
|
|
server.RootServer.Server = svr;
|
|
|
|
var svrCtl = baseCtl as IStorage;
|
|
|
|
if(svrCtl != null){
|
|
ExtensionLoader.Loader loader=new ExtensionLoader.Loader(svrCtl);
|
|
loader.SetServer(server);
|
|
|
|
}
|
|
HttpServerListener listener = new HttpServerListener(new IPEndPoint(ServerAccessable ? IPAddress.Any : IPAddress.Loopback,ServerPort),server.InnerServer);
|
|
listener.ListenAsync(token).Wait(0);
|
|
|
|
SetupChannels();
|
|
Intent intent=new Intent(ctx,typeof(TYTDServerService));
|
|
ctx.StartForegroundService(intent);
|
|
}
|
|
private static void SetupChannels()
|
|
{
|
|
if (Build.VERSION.SdkInt < BuildVersionCodes.O || ctx == null)
|
|
{
|
|
// Notification channels are new in API 26 (and not a part of the
|
|
// support library). There is no need to create a notification
|
|
// channel on older versions of Android.
|
|
return;
|
|
}
|
|
NotificationChannel serverChannel = new NotificationChannel(TYTDServerService.SERVER_CHANNEL_ID, "TYTD Server", NotificationImportance.Default);
|
|
|
|
NotificationManager? manager = (NotificationManager?)ctx.GetSystemService(Context.NotificationService);
|
|
if (manager != null)
|
|
{
|
|
manager.CreateNotificationChannel(serverChannel);
|
|
|
|
}
|
|
}
|
|
private static IServer getServerApi(string appDir)
|
|
{
|
|
StaticServer ssvr=new StaticServer(Path.Combine(appDir,"adminpanel"));
|
|
MountableServer svr=new MountableServer(ssvr);
|
|
SwagmeServer swagme = new SwagmeServer();
|
|
svr.Mount("/api/",swagme);
|
|
|
|
return svr;
|
|
}
|
|
private static ITYTDBase getTYTDBase(string url,string internalUrl,IEnumerable<PrivateKeyFile> privKeys)
|
|
{
|
|
if(!string.IsNullOrWhiteSpace(url)){
|
|
if(url.StartsWith('/'))
|
|
{
|
|
//file
|
|
if(svrs.ContainsKey(url)) return svrs[url];
|
|
var item=new TYTDPathDirectory(url);
|
|
|
|
item.CreateDirectories();
|
|
item.StartLoop(token);
|
|
svrs.Add(url,item);
|
|
return item;
|
|
}else{
|
|
System.Uri uri = new(url);
|
|
if(uri.Scheme == "http" || uri.Scheme == "https")
|
|
{
|
|
if(svrs.ContainsKey(url)) return svrs[url];
|
|
var item = new TYTDClient(url);
|
|
svrs.Add(url,item);
|
|
return item;
|
|
}
|
|
if(uri.Scheme == "sftp")
|
|
{
|
|
//sftp
|
|
//we need to get username password
|
|
string username = "root";
|
|
string? password = null;
|
|
string userInfo=uri.UserInfo;
|
|
|
|
if(!string.IsNullOrWhiteSpace(userInfo))
|
|
{
|
|
string[] userInfoSplit = userInfo.Split(':',2,StringSplitOptions.RemoveEmptyEntries);
|
|
if(userInfoSplit.Length == 2)
|
|
{
|
|
//username:password
|
|
username = userInfoSplit[0];
|
|
password = userInfoSplit[1];
|
|
}else if(userInfoSplit.Length == 1)
|
|
{
|
|
//username only
|
|
username = userInfoSplit[0];
|
|
}
|
|
}
|
|
if(string.IsNullOrWhiteSpace(password))
|
|
{
|
|
try{
|
|
var item= new SSHFS(url,username,privKeys.ToArray());
|
|
item.CreateDirectories();
|
|
item.StartLoop(token);
|
|
svrs.Add(url,item);
|
|
return item;
|
|
}catch(Exception ex)
|
|
{
|
|
_=ex;
|
|
}
|
|
|
|
}else{
|
|
try{
|
|
var item= new SSHFS(url,username,password);
|
|
item.CreateDirectories();
|
|
item.StartLoop(token);
|
|
svrs.Add(url,item);
|
|
return item;
|
|
}catch(Exception ex)
|
|
{
|
|
_=ex;
|
|
}
|
|
|
|
}
|
|
//SSHFS ssh=new SSHFS(url,);
|
|
}
|
|
}
|
|
}
|
|
if(svrs.ContainsKey(internalUrl)) return svrs[internalUrl];
|
|
var item2= new TYTDPathDirectory(internalUrl);
|
|
item2.CreateDirectories();
|
|
item2.StartLoop(token);
|
|
svrs.Add(internalUrl,item2);
|
|
return item2;
|
|
}
|
|
|
|
#endregion
|
|
}
|