Release
This commit is contained in:
		
							parent
							
								
									d972d03e7f
								
							
						
					
					
						commit
						4ef759a9d1
					
				| 
						 | 
				
			
			@ -0,0 +1,146 @@
 | 
			
		|||
## Ignore Visual Studio temporary files, build results, and
 | 
			
		||||
## files generated by popular Visual Studio add-ons.
 | 
			
		||||
 | 
			
		||||
# User-specific files
 | 
			
		||||
*.suo
 | 
			
		||||
*.user
 | 
			
		||||
*.sln.docstates
 | 
			
		||||
 | 
			
		||||
# Build results
 | 
			
		||||
 | 
			
		||||
[Dd]ebug/
 | 
			
		||||
[Rr]elease/
 | 
			
		||||
x64/
 | 
			
		||||
[Bb]in/
 | 
			
		||||
[Oo]bj/
 | 
			
		||||
 | 
			
		||||
# MSTest test Results
 | 
			
		||||
[Tt]est[Rr]esult*/
 | 
			
		||||
[Bb]uild[Ll]og.*
 | 
			
		||||
 | 
			
		||||
*_i.c
 | 
			
		||||
*_p.c
 | 
			
		||||
*_i.h
 | 
			
		||||
*.ilk
 | 
			
		||||
*.meta
 | 
			
		||||
*.obj
 | 
			
		||||
*.pch
 | 
			
		||||
*.pdb
 | 
			
		||||
*.pgc
 | 
			
		||||
*.pgd
 | 
			
		||||
*.rsp
 | 
			
		||||
*.sbr
 | 
			
		||||
*.tlb
 | 
			
		||||
*.tli
 | 
			
		||||
*.tlh
 | 
			
		||||
*.tmp
 | 
			
		||||
*.tmp_proj
 | 
			
		||||
*.log
 | 
			
		||||
*.vspscc
 | 
			
		||||
*.vssscc
 | 
			
		||||
.builds
 | 
			
		||||
*.pidb
 | 
			
		||||
*.log
 | 
			
		||||
*.svclog
 | 
			
		||||
*.scc
 | 
			
		||||
 | 
			
		||||
# Visual C++ cache files
 | 
			
		||||
ipch/
 | 
			
		||||
*.aps
 | 
			
		||||
*.ncb
 | 
			
		||||
*.opensdf
 | 
			
		||||
*.sdf
 | 
			
		||||
*.cachefile
 | 
			
		||||
 | 
			
		||||
# Visual Studio profiler
 | 
			
		||||
*.psess
 | 
			
		||||
*.vsp
 | 
			
		||||
*.vspx
 | 
			
		||||
 | 
			
		||||
# Guidance Automation Toolkit
 | 
			
		||||
*.gpState
 | 
			
		||||
 | 
			
		||||
# ReSharper is a .NET coding add-in
 | 
			
		||||
_ReSharper*/
 | 
			
		||||
*.[Rr]e[Ss]harper
 | 
			
		||||
*.DotSettings.user
 | 
			
		||||
 | 
			
		||||
# Click-Once directory
 | 
			
		||||
publish/
 | 
			
		||||
 | 
			
		||||
# Publish Web Output
 | 
			
		||||
*.Publish.xml
 | 
			
		||||
*.pubxml
 | 
			
		||||
*.azurePubxml
 | 
			
		||||
 | 
			
		||||
# NuGet Packages Directory
 | 
			
		||||
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
 | 
			
		||||
packages/
 | 
			
		||||
## TODO: If the tool you use requires repositories.config, also uncomment the next line
 | 
			
		||||
!packages/repositories.config
 | 
			
		||||
 | 
			
		||||
# Windows Azure Build Output
 | 
			
		||||
csx/
 | 
			
		||||
*.build.csdef
 | 
			
		||||
 | 
			
		||||
# Windows Store app package directory
 | 
			
		||||
AppPackages/
 | 
			
		||||
 | 
			
		||||
# Others
 | 
			
		||||
sql/
 | 
			
		||||
*.Cache
 | 
			
		||||
ClientBin/
 | 
			
		||||
[Ss]tyle[Cc]op.*
 | 
			
		||||
![Ss]tyle[Cc]op.targets
 | 
			
		||||
~$*
 | 
			
		||||
*~
 | 
			
		||||
*.dbmdl
 | 
			
		||||
*.[Pp]ublish.xml
 | 
			
		||||
 | 
			
		||||
*.publishsettings
 | 
			
		||||
 | 
			
		||||
# RIA/Silverlight projects
 | 
			
		||||
Generated_Code/
 | 
			
		||||
 | 
			
		||||
# Backup & report files from converting an old project file to a newer
 | 
			
		||||
# Visual Studio version. Backup files are not needed, because we have git ;-)
 | 
			
		||||
_UpgradeReport_Files/
 | 
			
		||||
Backup*/
 | 
			
		||||
UpgradeLog*.XML
 | 
			
		||||
UpgradeLog*.htm
 | 
			
		||||
 | 
			
		||||
# SQL Server files
 | 
			
		||||
App_Data/*.mdf
 | 
			
		||||
App_Data/*.ldf
 | 
			
		||||
 | 
			
		||||
# =========================
 | 
			
		||||
# Windows detritus
 | 
			
		||||
# =========================
 | 
			
		||||
 | 
			
		||||
# Windows image file caches
 | 
			
		||||
Thumbs.db
 | 
			
		||||
ehthumbs.db
 | 
			
		||||
 | 
			
		||||
# Folder config file
 | 
			
		||||
Desktop.ini
 | 
			
		||||
 | 
			
		||||
# Recycle Bin used on file shares
 | 
			
		||||
$RECYCLE.BIN/
 | 
			
		||||
 | 
			
		||||
# Mac desktop service store files
 | 
			
		||||
.DS_Store
 | 
			
		||||
 | 
			
		||||
_NCrunch*
 | 
			
		||||
 | 
			
		||||
Tesses.YouTubeDownloader.Net6/AudioOnly/
 | 
			
		||||
Tesses.YouTubeDownloader.Net6/VideoOnly/
 | 
			
		||||
Tesses.YouTubeDownloader.Net6/Muxed/
 | 
			
		||||
Tesses.YouTubeDownloader.Net6/Info/
 | 
			
		||||
Tesses.YouTubeDownloader.Net6/Thumbnails/
 | 
			
		||||
Tesses.YouTubeDownloader.Net6/StreamInfo/
 | 
			
		||||
Tesses.YouTubeDownloader.Net6/PreMuxed/
 | 
			
		||||
Tesses.YouTubeDownloader.Net6/config/
 | 
			
		||||
Tesses.YouTubeDownloader.Net6/Playlist/
 | 
			
		||||
Tesses.YouTubeDownloader.Net6/Channel/
 | 
			
		||||
Tesses.YouTubeDownloader.Net6/Subscriptions/
 | 
			
		||||
push
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env
 | 
			
		||||
 | 
			
		||||
WORKDIR /app
 | 
			
		||||
RUN git clone https://gitlab.tesses.net/tesses50/tytd_proxy .
 | 
			
		||||
WORKDIR /app/TYTDProxy
 | 
			
		||||
# Restore as distinct layers
 | 
			
		||||
RUN dotnet restore
 | 
			
		||||
# Build and publish a release
 | 
			
		||||
RUN dotnet publish -c Release -o /app/out
 | 
			
		||||
 | 
			
		||||
# Build runtime image
 | 
			
		||||
FROM mcr.microsoft.com/dotnet/runtime:6.0
 | 
			
		||||
WORKDIR /app
 | 
			
		||||
EXPOSE 3252
 | 
			
		||||
COPY --from=build-env /app/out .
 | 
			
		||||
ENTRYPOINT ["dotnet", "TYTDProxy.dll","--docker"]
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
The MIT License (MIT)
 | 
			
		||||
 | 
			
		||||
Copyright (c) Darko Jurić
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
 | 
			
		||||
this software and associated documentation files (the "Software"), to deal in
 | 
			
		||||
the Software without restriction, including without limitation the rights to
 | 
			
		||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 | 
			
		||||
the Software, and to permit persons to whom the Software is furnished to do so,
 | 
			
		||||
subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 | 
			
		||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 | 
			
		||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 | 
			
		||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 | 
			
		||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Licensing for JSON.NET dependency also applies: https://www.newtonsoft.com/json
 | 
			
		||||
Licensing for SimpleHttp also applies: https://github.com/dajuric/simple-http
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,183 @@
 | 
			
		|||
using System.Net;
 | 
			
		||||
using Tesses.WebServer;
 | 
			
		||||
using YoutubeExplode;
 | 
			
		||||
using YoutubeExplode.Search;
 | 
			
		||||
using YoutubeExplode.Videos;
 | 
			
		||||
using YoutubeExplode.Videos.Streams;
 | 
			
		||||
public class TYTDProxy
 | 
			
		||||
{
 | 
			
		||||
    static YoutubeClient Youtube = new YoutubeClient();
 | 
			
		||||
    public static async Task Main(string[] args)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        using (var token = new CancellationTokenSource())
 | 
			
		||||
        {
 | 
			
		||||
            Console.CancelKeyPress += (sender,e)=>{
 | 
			
		||||
                token.Cancel();
 | 
			
		||||
            };
 | 
			
		||||
            StaticServer static_server=new StaticServer("WebSite");
 | 
			
		||||
            RouteServer api_server=new RouteServer();
 | 
			
		||||
            RouteApi(api_server);
 | 
			
		||||
            MountableServer mountable_server=new MountableServer(static_server);
 | 
			
		||||
            mountable_server.Mount("/api/",api_server);
 | 
			
		||||
 | 
			
		||||
            HttpServerListener listener=new HttpServerListener(new System.Net.IPEndPoint(System.Net.IPAddress.Any,3253),mountable_server);
 | 
			
		||||
            await listener.ListenAsync(token.Token);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void RouteApi(RouteServer api)
 | 
			
		||||
    {
 | 
			
		||||
        api.Add("/Video.json",Video);
 | 
			
		||||
        api.Add("/Video.json",Video,"POST");
 | 
			
		||||
        api.Add("/PreMuxed",PreMuxed);
 | 
			
		||||
        api.Add("/VideoOnly",VideoOnly);
 | 
			
		||||
        api.Add("/AudioOnly",AudioOnly);
 | 
			
		||||
        api.Add("/PreMuxed",PreMuxed,"POST");
 | 
			
		||||
        api.Add("/VideoOnly",VideoOnly,"POST");
 | 
			
		||||
        api.Add("/AudioOnly",AudioOnly,"POST");
 | 
			
		||||
        api.Add("/DownloadYTStream",DownloadYTStream,"POST");
 | 
			
		||||
        api.Add("/Search.json",Search,"POST");
 | 
			
		||||
        api.Add("/Search.json",Search);
 | 
			
		||||
    }
 | 
			
		||||
    public class StreamResult 
 | 
			
		||||
    {
 | 
			
		||||
        public StreamResult(IStreamInfo info)
 | 
			
		||||
        {
 | 
			
		||||
            url = info.Url;
 | 
			
		||||
            ext=info.Container.Name;
 | 
			
		||||
            size = info.Size.Bytes;
 | 
			
		||||
        }
 | 
			
		||||
        public string url {get;set;}
 | 
			
		||||
 | 
			
		||||
        public string ext {get;set;}
 | 
			
		||||
 | 
			
		||||
        public long size {get;set;}
 | 
			
		||||
    }
 | 
			
		||||
    public class VideoResult
 | 
			
		||||
    {
 | 
			
		||||
        public VideoResult(StreamResult muxed,StreamResult video_only,StreamResult audio_only,SavedVideo video)
 | 
			
		||||
        {
 | 
			
		||||
            this.muxed=muxed;
 | 
			
		||||
            this.video_only=video_only;
 | 
			
		||||
            this.audio_only = audio_only;
 | 
			
		||||
            this.video_info=video;
 | 
			
		||||
        }
 | 
			
		||||
        public StreamResult muxed {get;set;}
 | 
			
		||||
 | 
			
		||||
        public StreamResult video_only {get;set;}
 | 
			
		||||
 | 
			
		||||
        public StreamResult audio_only {get;set;}
 | 
			
		||||
 | 
			
		||||
        public SavedVideo video_info {get;set;}
 | 
			
		||||
    }
 | 
			
		||||
     public static async Task Search(ServerContext ctx)
 | 
			
		||||
    {
 | 
			
		||||
           string q;
 | 
			
		||||
        if(ctx.QueryParams.TryGetFirst("q",out q)){
 | 
			
		||||
        if(ctx.Method == "POST") ctx.ParseBody();
 | 
			
		||||
        if(ctx.Method != "POST") q=WebUtility.UrlDecode(q);
 | 
			
		||||
        {
 | 
			
		||||
            List<VideoSearchResult> searchResults=new List<VideoSearchResult>();
 | 
			
		||||
            await foreach(var item in Youtube.Search.GetVideosAsync(q))
 | 
			
		||||
            {
 | 
			
		||||
               searchResults.Add(item);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            await ctx.SendJsonAsync(searchResults);
 | 
			
		||||
        }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    public static async Task PreMuxed(ServerContext ctx)
 | 
			
		||||
    {
 | 
			
		||||
         string v;
 | 
			
		||||
        if(ctx.QueryParams.TryGetFirst("v",out v)){
 | 
			
		||||
        if(ctx.Method == "POST") ctx.ParseBody();
 | 
			
		||||
        if(ctx.Method != "POST") v=WebUtility.UrlDecode(v);
 | 
			
		||||
            var vid=await Youtube.Videos.GetAsync(v);
 | 
			
		||||
            var res= await Youtube.Videos.Streams.GetManifestAsync(v);
 | 
			
		||||
            var bestMuxed=res.GetMuxedStreams().GetWithHighestVideoQuality();
 | 
			
		||||
            System.Net.Mime.ContentDisposition contentDisposition=new System.Net.Mime.ContentDisposition();
 | 
			
		||||
            contentDisposition.FileName = vid.Title;
 | 
			
		||||
            await ctx.SendStreamAsync(await Youtube.Videos.Streams.GetAsync(bestMuxed),$"file.{bestMuxed.Container.Name}");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
      public static async Task VideoOnly(ServerContext ctx)
 | 
			
		||||
    {
 | 
			
		||||
         string v;
 | 
			
		||||
        if(ctx.QueryParams.TryGetFirst("v",out v)){
 | 
			
		||||
        if(ctx.Method == "POST") ctx.ParseBody();
 | 
			
		||||
        if(ctx.Method != "POST") v=WebUtility.UrlDecode(v);
 | 
			
		||||
            var vid=await Youtube.Videos.GetAsync(v);
 | 
			
		||||
            var res= await Youtube.Videos.Streams.GetManifestAsync(v);
 | 
			
		||||
            var bestVideoOnly=res.GetVideoOnlyStreams().GetWithHighestVideoQuality();
 | 
			
		||||
            System.Net.Mime.ContentDisposition contentDisposition=new System.Net.Mime.ContentDisposition();
 | 
			
		||||
            contentDisposition.FileName = vid.Title;
 | 
			
		||||
            await ctx.SendStreamAsync(await Youtube.Videos.Streams.GetAsync(bestVideoOnly),$"file.{bestVideoOnly.Container.Name}");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
     public static async Task AudioOnly(ServerContext ctx)
 | 
			
		||||
    {
 | 
			
		||||
         string v;
 | 
			
		||||
        if(ctx.QueryParams.TryGetFirst("v",out v)){
 | 
			
		||||
        if(ctx.Method == "POST") ctx.ParseBody();
 | 
			
		||||
        if(ctx.Method != "POST") v=WebUtility.UrlDecode(v);
 | 
			
		||||
            var vid=await Youtube.Videos.GetAsync(v);
 | 
			
		||||
            var res= await Youtube.Videos.Streams.GetManifestAsync(v);
 | 
			
		||||
            var bestAudioOnly=res.GetAudioOnlyStreams().GetWithHighestBitrate();
 | 
			
		||||
            System.Net.Mime.ContentDisposition contentDisposition=new System.Net.Mime.ContentDisposition();
 | 
			
		||||
            contentDisposition.FileName = vid.Title;
 | 
			
		||||
            await ctx.SendStreamAsync(await Youtube.Videos.Streams.GetAsync(bestAudioOnly),$"file.{bestAudioOnly.Container.Name}");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    public class DummyStr : IStreamInfo
 | 
			
		||||
    {
 | 
			
		||||
        public DummyStr(string url,long len)
 | 
			
		||||
        {
 | 
			
		||||
            this.url = url;
 | 
			
		||||
            this.len=len;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        long len;
 | 
			
		||||
        string url;
 | 
			
		||||
        public string Url => url;
 | 
			
		||||
 | 
			
		||||
        public Container Container => Container.Mp4;
 | 
			
		||||
        public FileSize Size => new FileSize(len);
 | 
			
		||||
 | 
			
		||||
        public Bitrate Bitrate => new Bitrate(420000);
 | 
			
		||||
    }
 | 
			
		||||
    public static async Task DownloadYTStream(ServerContext ctx)
 | 
			
		||||
    {
 | 
			
		||||
         string v;
 | 
			
		||||
         string lenTxt;
 | 
			
		||||
        if(ctx.QueryParams.TryGetFirst("url",out v)){
 | 
			
		||||
            if(ctx.QueryParams.TryGetFirst("flen",out lenTxt)){
 | 
			
		||||
        if(ctx.Method == "POST") ctx.ParseBody();
 | 
			
		||||
        if(ctx.Method != "POST") v=WebUtility.UrlDecode(v);
 | 
			
		||||
           long len;
 | 
			
		||||
           if(long.TryParse(lenTxt,out len)){
 | 
			
		||||
 | 
			
		||||
            await ctx.SendStreamAsync(await Youtube.Videos.Streams.GetAsync(new DummyStr(v,len)),HeyRed.Mime.MimeTypesMap.GetMimeType("file.bin"));
 | 
			
		||||
           }
 | 
			
		||||
        }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    public static async Task Video(ServerContext ctx)
 | 
			
		||||
    {
 | 
			
		||||
        string v;
 | 
			
		||||
        if(ctx.QueryParams.TryGetFirst("v",out v)){
 | 
			
		||||
        if(ctx.Method == "POST") ctx.ParseBody();
 | 
			
		||||
        if(ctx.Method != "POST") v=WebUtility.UrlDecode(v);
 | 
			
		||||
 | 
			
		||||
           var res= await Youtube.Videos.Streams.GetManifestAsync(v);
 | 
			
		||||
           var bestMuxed=new StreamResult(res.GetMuxedStreams().GetWithHighestVideoQuality());
 | 
			
		||||
           var bestVideoOnly=new StreamResult(res.GetVideoOnlyStreams().GetWithHighestVideoQuality());
 | 
			
		||||
           var bestAudioOnly=new StreamResult(res.GetAudioOnlyStreams().GetWithHighestBitrate());
 | 
			
		||||
           SavedVideo video = new SavedVideo(await Youtube.Videos.GetAsync(v));
 | 
			
		||||
           VideoResult result = new VideoResult(bestMuxed,bestVideoOnly,bestAudioOnly,video);
 | 
			
		||||
           await ctx.SendJsonAsync(result);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,88 @@
 | 
			
		|||
using System.Text;
 | 
			
		||||
using YoutubeExplode.Common;
 | 
			
		||||
using YoutubeExplode.Videos;
 | 
			
		||||
 | 
			
		||||
public class SavedVideo
 | 
			
		||||
    {
 | 
			
		||||
        public SavedVideo()
 | 
			
		||||
        {
 | 
			
		||||
            Id = "";
 | 
			
		||||
            Title = "";
 | 
			
		||||
            AuthorChannelId = "";
 | 
			
		||||
            AuthorTitle = "";
 | 
			
		||||
            Description = "";
 | 
			
		||||
            Keywords = new string[0];
 | 
			
		||||
            Likes = 0;
 | 
			
		||||
            Dislikes = 0;
 | 
			
		||||
            Views = 0;
 | 
			
		||||
            Duration = TimeSpan.Zero;
 | 
			
		||||
            UploadDate=new DateTime(1992,8,20);
 | 
			
		||||
            AddDate=DateTime.Now;
 | 
			
		||||
            LegacyVideo=false;
 | 
			
		||||
        }
 | 
			
		||||
        public SavedVideo(Video video)
 | 
			
		||||
        {
 | 
			
		||||
            Id=video.Id;
 | 
			
		||||
            Title = video.Title;
 | 
			
		||||
            AuthorChannelId = video.Author.ChannelId;
 | 
			
		||||
            AuthorTitle = video.Author.ChannelTitle;
 | 
			
		||||
            Description = video.Description;
 | 
			
		||||
            Keywords=video.Keywords.ToArray();
 | 
			
		||||
            Likes=video.Engagement.LikeCount;
 | 
			
		||||
            Dislikes = video.Engagement.DislikeCount;
 | 
			
		||||
            Views = video.Engagement.ViewCount;
 | 
			
		||||
            Duration = video.Duration.Value;
 | 
			
		||||
            UploadDate = video.UploadDate.DateTime;
 | 
			
		||||
            AddDate=DateTime.Now;
 | 
			
		||||
            LegacyVideo=false;
 | 
			
		||||
        }
 | 
			
		||||
        public bool LegacyVideo {get;set;}
 | 
			
		||||
 | 
			
		||||
     
 | 
			
		||||
        public Video ToVideo()
 | 
			
		||||
        {
 | 
			
		||||
            List<Thumbnail> thumbnails=new List<Thumbnail>();
 | 
			
		||||
            thumbnails.Add(new Thumbnail($"https://s.ytimg.com/vi/{Id}/default.jpg",new YoutubeExplode.Common.Resolution(120,90)));
 | 
			
		||||
            thumbnails.Add(new Thumbnail($"https://s.ytimg.com/vi/{Id}/hqdefault.jpg",new YoutubeExplode.Common.Resolution(480,360)));
 | 
			
		||||
            thumbnails.Add(new Thumbnail($"https://s.ytimg.com/vi/{Id}/mqdefault.jpg",new YoutubeExplode.Common.Resolution(320,180)));
 | 
			
		||||
            
 | 
			
		||||
            return new Video(Id,Title,new YoutubeExplode.Common.Author(AuthorChannelId,AuthorTitle),new DateTimeOffset(UploadDate),Description,Duration,thumbnails,Keywords.ToList(),new Engagement(Views,Likes,Dislikes));
 | 
			
		||||
        }
 | 
			
		||||
      
 | 
			
		||||
   
 | 
			
		||||
 | 
			
		||||
        public DateTime AddDate {get;set;}
 | 
			
		||||
         public string Title { get; set; }
 | 
			
		||||
        public DateTime UploadDate { get; set; }
 | 
			
		||||
        public string[] Keywords { get; set; }
 | 
			
		||||
        public string Id { get; set; }
 | 
			
		||||
        public string AuthorTitle { get; set; }
 | 
			
		||||
        public string AuthorChannelId { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Description { get; set; }
 | 
			
		||||
 | 
			
		||||
        public TimeSpan Duration { get; set; }
 | 
			
		||||
 | 
			
		||||
        public long Views { get; set; }
 | 
			
		||||
        public long Likes { get; set; }
 | 
			
		||||
        public long Dislikes { get; set; }
 | 
			
		||||
 | 
			
		||||
      
 | 
			
		||||
       
 | 
			
		||||
        public override string ToString()
 | 
			
		||||
        {
 | 
			
		||||
            StringBuilder b=new StringBuilder();
 | 
			
		||||
            b.AppendLine($"Title: {Title}");
 | 
			
		||||
            b.AppendLine($"AuthorTitle: {AuthorTitle}");
 | 
			
		||||
            DateTime date=UploadDate;
 | 
			
		||||
            
 | 
			
		||||
                 b.AppendLine($"Upload Date: {date.ToShortDateString()}");
 | 
			
		||||
            
 | 
			
		||||
            b.AppendLine($"Likes: {Likes}, Dislikes: {Dislikes}, Views: {Views}");
 | 
			
		||||
            b.AppendLine($"Duration: {Duration.ToString()}");
 | 
			
		||||
            b.AppendLine($"Tags: {string.Join(", ",Keywords)}");
 | 
			
		||||
            b.AppendLine("Description:");
 | 
			
		||||
            b.AppendLine(Description);
 | 
			
		||||
            return b.ToString();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
  <PropertyGroup>
 | 
			
		||||
    <OutputType>Exe</OutputType>
 | 
			
		||||
    <TargetFramework>net6.0</TargetFramework>
 | 
			
		||||
    <ImplicitUsings>enable</ImplicitUsings>
 | 
			
		||||
    <Nullable>enable</Nullable>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <PackageReference Include="Tesses.WebServer" Version="1.0.3.3" />
 | 
			
		||||
    <PackageReference Include="YouTubeExplode" Version="6.1.2" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
</Project>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
    <title>YouTube Downloader</title>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    <h1>Download YouTube Videos</h1>
 | 
			
		||||
 | 
			
		||||
    <form method="POST" action="api/PreMuxed">
 | 
			
		||||
        <input type="text" name="v" placeholder="Url or Id">
 | 
			
		||||
        <input type="submit" value="Download">
 | 
			
		||||
    </form>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
version: '2'
 | 
			
		||||
services:
 | 
			
		||||
  tytd:
 | 
			
		||||
      build: Dockerfile
 | 
			
		||||
      image: tytd-proxy-ogc
 | 
			
		||||
      ports:
 | 
			
		||||
        - 3253:3253
 | 
			
		||||
      volumes:
 | 
			
		||||
        - tytd-data:/data
 | 
			
		||||
volumes:
 | 
			
		||||
  tytd-data:
 | 
			
		||||
		Loading…
	
		Reference in New Issue