2022-04-06 16:41:29 +00:00
|
|
|
|
using System;
|
|
|
|
|
using YoutubeExplode;
|
|
|
|
|
using YoutubeExplode.Videos;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using System.Net.Http;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using YoutubeExplode.Playlists;
|
|
|
|
|
using YoutubeExplode.Channels;
|
2022-06-14 18:21:36 +00:00
|
|
|
|
using Newtonsoft.Json;
|
2022-04-06 16:41:29 +00:00
|
|
|
|
|
|
|
|
|
namespace Tesses.YouTubeDownloader
|
|
|
|
|
{
|
2022-06-14 18:21:36 +00:00
|
|
|
|
public abstract partial class TYTDStorage : TYTDBase, IStorage
|
2022-04-06 16:41:29 +00:00
|
|
|
|
{
|
2022-04-10 00:18:45 +00:00
|
|
|
|
private static readonly HttpClient _default = new HttpClient();
|
2022-04-06 16:41:29 +00:00
|
|
|
|
public abstract Task<Stream> CreateAsync(string path);
|
|
|
|
|
|
|
|
|
|
public abstract void CreateDirectory(string path);
|
|
|
|
|
|
|
|
|
|
public TYTDStorage(HttpClient clt)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
HttpClient=clt;
|
|
|
|
|
YoutubeClient=new YoutubeClient(HttpClient);
|
|
|
|
|
ExtensionContext=null;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
public TYTDStorage()
|
|
|
|
|
{
|
2022-04-10 00:18:45 +00:00
|
|
|
|
HttpClient=_default;
|
2022-04-06 16:41:29 +00:00
|
|
|
|
YoutubeClient=new YoutubeClient(HttpClient);
|
|
|
|
|
ExtensionContext=null;
|
|
|
|
|
}
|
|
|
|
|
public async Task WriteAllBytesAsync(string path,byte[] data,CancellationToken token=default(CancellationToken))
|
|
|
|
|
{
|
|
|
|
|
using(var s=await CreateAsync(path))
|
|
|
|
|
{
|
|
|
|
|
await s.WriteAsync(data,0,data.Length,token);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-07-13 13:59:23 +00:00
|
|
|
|
public static string TYTDTag {get {return _tytd_tag;}}
|
|
|
|
|
private static string _tytd_tag=_getTYTDTag();
|
|
|
|
|
private static string _getTYTDTag()
|
|
|
|
|
{
|
|
|
|
|
string tag=Environment.GetEnvironmentVariable("TYTD_TAG");
|
|
|
|
|
if(string.IsNullOrWhiteSpace(tag)) return "UnknownPC";
|
|
|
|
|
return tag;
|
|
|
|
|
}
|
2022-04-10 00:18:45 +00:00
|
|
|
|
bool can_download=true;
|
|
|
|
|
public bool CanDownload {get {return can_download;} set {can_download=value;}}
|
2022-06-14 18:21:36 +00:00
|
|
|
|
public IExtensionContext ExtensionContext {get;set;}
|
|
|
|
|
public HttpClient HttpClient {get;set;}
|
|
|
|
|
public YoutubeClient YoutubeClient {get;set;}
|
2022-04-06 16:41:29 +00:00
|
|
|
|
public abstract void MoveDirectory(string src,string dest);
|
|
|
|
|
public abstract void DeleteFile(string file);
|
|
|
|
|
public abstract void DeleteDirectory(string dir,bool recursive=false);
|
|
|
|
|
|
2022-07-06 22:59:50 +00:00
|
|
|
|
public virtual async Task WriteBestStreamInfoAsync(VideoId id,BestStreamInfo.BestStreamsSerialized serialized)
|
2022-06-14 18:21:36 +00:00
|
|
|
|
{
|
2022-07-06 22:59:50 +00:00
|
|
|
|
await WriteAllTextAsync($"StreamInfo/{id.Value}.json",JsonConvert.SerializeObject(serialized));
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public virtual async Task WriteVideoInfoAsync(SavedVideo info)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
string file = info.DownloadFrom.StartsWith("NormalDownload,Length=") ? $"FileInfo/{B64.Base64UrlEncodes(info.Id)}.json" : $"Info/{info.Id}.json";
|
2022-06-14 18:21:36 +00:00
|
|
|
|
if(!FileExists(file))
|
|
|
|
|
{
|
|
|
|
|
await WriteAllTextAsync(file,JsonConvert.SerializeObject(info));
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-07-06 22:59:50 +00:00
|
|
|
|
public virtual async Task WritePlaylistInfoAsync(SavedPlaylist info)
|
2022-06-14 18:21:36 +00:00
|
|
|
|
{
|
|
|
|
|
string file = $"Playlist/{info.Id}.json";
|
|
|
|
|
if(!FileExists(file))
|
|
|
|
|
{
|
|
|
|
|
await WriteAllTextAsync(file,JsonConvert.SerializeObject(info));
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-07-06 22:59:50 +00:00
|
|
|
|
public virtual async Task WriteChannelInfoAsync(SavedChannel info)
|
2022-06-14 18:21:36 +00:00
|
|
|
|
{
|
|
|
|
|
string file = $"Channel/{info.Id}.json";
|
|
|
|
|
if(!FileExists(file))
|
|
|
|
|
{
|
|
|
|
|
await WriteAllTextAsync(file,JsonConvert.SerializeObject(info));
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-04-06 16:41:29 +00:00
|
|
|
|
public async Task AddPlaylistAsync(PlaylistId id,Resolution resolution=Resolution.PreMuxed)
|
|
|
|
|
{
|
|
|
|
|
lock(Temporary)
|
|
|
|
|
{
|
|
|
|
|
Temporary.Add( new PlaylistMediaContext(id,resolution));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
await Task.FromResult(0);
|
|
|
|
|
}
|
|
|
|
|
public async Task AddChannelAsync(ChannelId id,Resolution resolution=Resolution.PreMuxed)
|
|
|
|
|
{
|
|
|
|
|
lock(Temporary)
|
|
|
|
|
{
|
|
|
|
|
Temporary.Add(new ChannelMediaContext(id,resolution));
|
|
|
|
|
}
|
|
|
|
|
await Task.FromResult(0);
|
|
|
|
|
}
|
|
|
|
|
public async Task AddUserAsync(UserName name,Resolution resolution=Resolution.PreMuxed)
|
|
|
|
|
{
|
|
|
|
|
lock(Temporary)
|
|
|
|
|
{
|
|
|
|
|
Temporary.Add(new ChannelMediaContext(name,resolution));
|
|
|
|
|
}
|
|
|
|
|
await Task.FromResult(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task AddVideoAsync(VideoId videoId,Resolution res=Resolution.PreMuxed)
|
|
|
|
|
{
|
|
|
|
|
lock(Temporary)
|
|
|
|
|
{
|
|
|
|
|
Temporary.Add(new VideoMediaContext(videoId,res));
|
|
|
|
|
}
|
|
|
|
|
await Task.FromResult(0);
|
|
|
|
|
}
|
2022-07-06 22:59:50 +00:00
|
|
|
|
public async Task AddFileAsync(string url,bool download=true)
|
|
|
|
|
{
|
|
|
|
|
lock(Temporary)
|
|
|
|
|
{
|
|
|
|
|
Temporary.Add(new NormalDownloadMediaContext(url,download));
|
|
|
|
|
}
|
|
|
|
|
await Task.FromResult(0);
|
|
|
|
|
}
|
2022-04-06 16:41:29 +00:00
|
|
|
|
public void CreateDirectoryIfNotExist(string dir)
|
|
|
|
|
{
|
|
|
|
|
if(!DirectoryExists(dir))
|
|
|
|
|
{
|
|
|
|
|
CreateDirectory(dir);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public async Task DownloadThumbnails(VideoId id)
|
|
|
|
|
{
|
2022-04-10 00:18:45 +00:00
|
|
|
|
if(!can_download) return;
|
2022-04-06 16:41:29 +00:00
|
|
|
|
string Id=id.Value;
|
|
|
|
|
string[] res=new string[] {"default.jpg","sddefault.jpg","mqdefault.jpg","hqdefault.jpg","maxresdefault.jpg"};
|
|
|
|
|
CreateDirectoryIfNotExist($"Thumbnails/{Id}");
|
|
|
|
|
foreach(var reso in res)
|
|
|
|
|
{
|
|
|
|
|
if(await Continue($"Thumbnails/{Id}/{reso}"))
|
|
|
|
|
{
|
|
|
|
|
try{
|
|
|
|
|
var data=await HttpClient.GetByteArrayAsync($"https://s.ytimg.com/vi/{Id}/{reso}");
|
|
|
|
|
await WriteAllBytesAsync($"Thumbnails/{Id}/{reso}",data);
|
|
|
|
|
}catch(Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_=ex;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-04-10 00:18:45 +00:00
|
|
|
|
public void CreateDirectories()
|
2022-04-06 16:41:29 +00:00
|
|
|
|
{
|
2022-05-11 23:14:16 +00:00
|
|
|
|
CreateDirectoryIfNotExist("Channel");
|
|
|
|
|
CreateDirectoryIfNotExist("Playlist");
|
2022-05-09 22:00:19 +00:00
|
|
|
|
CreateDirectoryIfNotExist("Subscriptions");
|
2022-04-10 00:18:45 +00:00
|
|
|
|
CreateDirectoryIfNotExist("VideoOnly");
|
2022-04-06 16:41:29 +00:00
|
|
|
|
CreateDirectoryIfNotExist("AudioOnly");
|
|
|
|
|
CreateDirectoryIfNotExist("Muxed");
|
|
|
|
|
CreateDirectoryIfNotExist("PreMuxed");
|
|
|
|
|
CreateDirectoryIfNotExist("Info");
|
|
|
|
|
CreateDirectoryIfNotExist("Thumbnails");
|
2022-05-11 23:14:16 +00:00
|
|
|
|
CreateDirectoryIfNotExist("config");
|
|
|
|
|
CreateDirectoryIfNotExist("config/logs");
|
2022-07-06 22:59:50 +00:00
|
|
|
|
CreateDirectoryIfNotExist("FileInfo");
|
|
|
|
|
CreateDirectoryIfNotExist("Download");
|
|
|
|
|
CreateDirectoryIfNotExist("StreamInfo");
|
2022-04-10 00:18:45 +00:00
|
|
|
|
}
|
|
|
|
|
public void StartLoop(CancellationToken token = default(CancellationToken))
|
|
|
|
|
{
|
|
|
|
|
CreateDirectories();
|
2022-04-06 16:41:29 +00:00
|
|
|
|
Thread thread0=new Thread(()=>{
|
|
|
|
|
DownloadLoop(token).Wait();
|
|
|
|
|
});
|
|
|
|
|
thread0.Start();
|
|
|
|
|
Thread thread1=new Thread(()=>{
|
|
|
|
|
QueueLoop(token).Wait();
|
|
|
|
|
});
|
|
|
|
|
thread1.Start();
|
|
|
|
|
}
|
2022-06-24 23:02:51 +00:00
|
|
|
|
internal void ThrowError(TYTDErrorEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
Error?.Invoke(this,e);
|
|
|
|
|
}
|
2022-04-06 16:41:29 +00:00
|
|
|
|
public async Task WriteAllTextAsync(string path,string data)
|
|
|
|
|
{
|
|
|
|
|
using(var dstStrm= await CreateAsync(path))
|
|
|
|
|
{
|
|
|
|
|
using(var sw = new StreamWriter(dstStrm))
|
|
|
|
|
{
|
|
|
|
|
await sw.WriteAsync(data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-06-24 23:02:51 +00:00
|
|
|
|
|
2022-07-06 22:59:50 +00:00
|
|
|
|
public virtual async Task AddToPersonalPlaylistAsync(string name, IEnumerable<ListContentItem> items)
|
2022-06-24 23:02:51 +00:00
|
|
|
|
{
|
|
|
|
|
List<ListContentItem> items0=new List<ListContentItem>();
|
|
|
|
|
await foreach(var item in GetPersonalPlaylistContentsAsync(name))
|
|
|
|
|
{
|
|
|
|
|
items0.Add(item);
|
|
|
|
|
}
|
|
|
|
|
items0.AddRange(items);
|
|
|
|
|
await WriteAllTextAsync($"PersonalPlaylist/{name}.json",JsonConvert.SerializeObject(items0));
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-06 22:59:50 +00:00
|
|
|
|
public virtual async Task ReplacePersonalPlaylistAsync(string name, IEnumerable<ListContentItem> items)
|
2022-06-24 23:02:51 +00:00
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
await WriteAllTextAsync($"PersonalPlaylist/{name}.json",JsonConvert.SerializeObject(items.ToList()));
|
|
|
|
|
|
|
|
|
|
}
|
2022-07-06 22:59:50 +00:00
|
|
|
|
public virtual void DeletePersonalPlaylist(string name)
|
2022-06-24 23:02:51 +00:00
|
|
|
|
{
|
|
|
|
|
DeleteFile($"PersonalPlaylist/{name}.json");
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-06 22:59:50 +00:00
|
|
|
|
public virtual async Task RemoveItemFromPersonalPlaylistAsync(string name, VideoId id)
|
2022-06-24 23:02:51 +00:00
|
|
|
|
{
|
|
|
|
|
List<ListContentItem> items0=new List<ListContentItem>();
|
|
|
|
|
await foreach(var item in GetPersonalPlaylistContentsAsync(name))
|
|
|
|
|
{
|
|
|
|
|
if(item.Id != id)
|
|
|
|
|
{
|
|
|
|
|
items0.Add(item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await WriteAllTextAsync($"PersonalPlaylist/{name}.json",JsonConvert.SerializeObject(items0));
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-06 22:59:50 +00:00
|
|
|
|
public virtual async Task SetResolutionForItemInPersonalPlaylistAsync(string name, VideoId id, Resolution resolution)
|
2022-06-24 23:02:51 +00:00
|
|
|
|
{
|
|
|
|
|
List<ListContentItem> items0=new List<ListContentItem>();
|
|
|
|
|
await foreach(var item in GetPersonalPlaylistContentsAsync(name))
|
|
|
|
|
{
|
|
|
|
|
if(item.Id != id)
|
|
|
|
|
{
|
|
|
|
|
items0.Add(item);
|
|
|
|
|
}else{
|
|
|
|
|
items0.Add(new ListContentItem(item.Id,resolution));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await WriteAllTextAsync($"PersonalPlaylist/{name}.json",JsonConvert.SerializeObject(items0));
|
|
|
|
|
|
|
|
|
|
}
|
2022-04-06 16:41:29 +00:00
|
|
|
|
}
|
|
|
|
|
}
|