From c016041574141825f96f373f813b20b52b34adf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B3=E9=88=9E?= Date: Sun, 24 Dec 2023 19:05:30 +0800 Subject: [PATCH] refactor: Edited for CodeFactor suggestions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 陳鈞 --- Download.cs | 31 +- Extension/YoutubeDLHelper.cs | 17 +- GlobalSuppressions.cs | 8 + Models/YtdlpVideoData.cs | 572 +++++++++++++++++------------------ Program.cs | 1 - README.md | 2 + 6 files changed, 311 insertions(+), 320 deletions(-) create mode 100644 GlobalSuppressions.cs diff --git a/Download.cs b/Download.cs index f305ac0..64688e3 100644 --- a/Download.cs +++ b/Download.cs @@ -3,43 +3,28 @@ using YoutubeDLSharp; using YoutubeDLSharp.Options; using YoutubeSegmentDownloader.Extension; -using YoutubeSegmentDownloader.Models; +using YtdlpVideoData = YoutubeSegmentDownloader.Models.YtdlpVideoData.ytdlpVideoData; namespace YoutubeSegmentDownloader; -internal class Download +internal class Download(string id, + float start, + float end, + DirectoryInfo outputDirectory, + string format, + string browser) { - private readonly string id; private string link { get => id.Contains('/') ? id : @$"https://youtu.be/{id}"; } - private readonly float start; - private readonly float end; - private readonly DirectoryInfo outputDirectory; - private readonly string format; - private readonly string browser; + public bool finished = false; public bool successed = false; public string? outputFilePath = null; - public Download(string id, - float start, - float end, - DirectoryInfo outputDirectory, - string format, - string browser) - { - this.id = id; - this.start = start; - this.end = end; - this.outputDirectory = outputDirectory; - this.format = format; - this.browser = browser; - } - public async Task Start() { Log.Information("Start the download process..."); diff --git a/Extension/YoutubeDLHelper.cs b/Extension/YoutubeDLHelper.cs index 5f4fcd0..e3cb00d 100644 --- a/Extension/YoutubeDLHelper.cs +++ b/Extension/YoutubeDLHelper.cs @@ -3,7 +3,7 @@ using YoutubeDLSharp; using YoutubeDLSharp.Helpers; using YoutubeDLSharp.Options; -using YoutubeSegmentDownloader.Models; +using YtdlpVideoData = YoutubeSegmentDownloader.Models.YtdlpVideoData.ytdlpVideoData; namespace YoutubeSegmentDownloader.Extension; @@ -28,7 +28,6 @@ public static async Task> RunVideoDataFetch_Alt(this Y OptionSet overrideOptions = null) #pragma warning restore CA1068 // CancellationToken 參數必須位於最後 { -#pragma warning disable IDE0017 // 簡化物件初始化 OptionSet opts = new() { IgnoreErrors = ytdl.IgnoreDownloadErrors, @@ -42,13 +41,11 @@ public static async Task> RunVideoDataFetch_Alt(this Y NoOverwrites = !ytdl.OverwriteFiles, NoPart = true, FfmpegLocation = Utils.GetFullPath(ytdl.FFmpegPath), - Exec = "echo outfile: {}" + Exec = "echo outfile: {}", + DumpSingleJson = true, + FlatPlaylist = flat, + WriteComments = fetchComments }; - - opts.DumpSingleJson = true; - opts.FlatPlaylist = flat; - opts.WriteComments = fetchComments; -#pragma warning restore IDE0017 // 簡化物件初始化 if (overrideOptions != null) { opts = opts.OverrideOptions(overrideOptions); @@ -66,8 +63,8 @@ public static async Task> RunVideoDataFetch_Alt(this Y data = ChangeJsonStringSingleQuotesToDoubleQuotes().Replace(data, @"""$1"""); videoData = Newtonsoft.Json.JsonConvert.DeserializeObject(data); }; - FieldInfo fieldInfo = typeof(YoutubeDLSharp.YoutubeDL).GetField("runner", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField); - (int code, string[] errors) = await (fieldInfo.GetValue(ytdl) as ProcessRunner).RunThrottled(youtubeDLProcess, new[] { url }, opts, ct); + FieldInfo fieldInfo = typeof(YoutubeDL).GetField("runner", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField); + (int code, string[] errors) = await (fieldInfo.GetValue(ytdl) as ProcessRunner).RunThrottled(youtubeDLProcess, [url], opts, ct); return new RunResult(code == 0, errors, videoData); } #nullable enable diff --git a/GlobalSuppressions.cs b/GlobalSuppressions.cs new file mode 100644 index 0000000..30bcf7c --- /dev/null +++ b/GlobalSuppressions.cs @@ -0,0 +1,8 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Maintainability", "CA1507:使用 nameof 表示符號名稱", Justification = "<暫止>", Scope = "member", Target = "~P:YoutubeSegmentDownloader.Models.YtdlpVideoData.HttpHeaders.Accept")] diff --git a/Models/YtdlpVideoData.cs b/Models/YtdlpVideoData.cs index ecd981a..ebb3cfd 100644 --- a/Models/YtdlpVideoData.cs +++ b/Models/YtdlpVideoData.cs @@ -3,417 +3,417 @@ namespace YoutubeSegmentDownloader.Models; -// https://json2csharp.com/ - -public class AutomaticCaptions +public class YtdlpVideoData { -} + public class AutomaticCaptions + { + } -public class Chapter -{ - [JsonProperty("start_time")] - public double? StartTime { get; set; } + public class Chapter + { + [JsonProperty("start_time")] + public double? StartTime { get; set; } - [JsonProperty("title")] - public string Title { get; set; } + [JsonProperty("title")] + public string Title { get; set; } - [JsonProperty("end_time")] - public double? EndTime { get; set; } -} + [JsonProperty("end_time")] + public double? EndTime { get; set; } + } -public class DownloaderOptions -{ - [JsonProperty("http_chunk_size")] - public int? HttpChunkSize { get; set; } -} + public class DownloaderOptions + { + [JsonProperty("http_chunk_size")] + public int? HttpChunkSize { get; set; } + } -public class Format -{ - [JsonProperty("format_id")] - public string FormatId { get; set; } + public class Format + { + [JsonProperty("format_id")] + public string FormatId { get; set; } - [JsonProperty("url")] - public string Url { get; set; } + [JsonProperty("url")] + public string Url { get; set; } - [JsonProperty("manifest_url")] - public string ManifestUrl { get; set; } + [JsonProperty("manifest_url")] + public string ManifestUrl { get; set; } - [JsonProperty("tbr")] - public double? Tbr { get; set; } + [JsonProperty("tbr")] + public double? Tbr { get; set; } - [JsonProperty("ext")] - public string Ext { get; set; } + [JsonProperty("ext")] + public string Ext { get; set; } - [JsonProperty("fps")] - public double? Fps { get; set; } + [JsonProperty("fps")] + public double? Fps { get; set; } - [JsonProperty("protocol")] - public string Protocol { get; set; } + [JsonProperty("protocol")] + public string Protocol { get; set; } - [JsonProperty("quality")] - public double? Quality { get; set; } + [JsonProperty("quality")] + public double? Quality { get; set; } - [JsonProperty("width")] - public int? Width { get; set; } + [JsonProperty("width")] + public int? Width { get; set; } - [JsonProperty("height")] - public int? Height { get; set; } + [JsonProperty("height")] + public int? Height { get; set; } - [JsonProperty("vcodec")] - public string Vcodec { get; set; } + [JsonProperty("vcodec")] + public string Vcodec { get; set; } - [JsonProperty("acodec")] - public string Acodec { get; set; } + [JsonProperty("acodec")] + public string Acodec { get; set; } - [JsonProperty("dynamic_range")] - public string DynamicRange { get; set; } + [JsonProperty("dynamic_range")] + public string DynamicRange { get; set; } - [JsonProperty("video_ext")] - public string VideoExt { get; set; } + [JsonProperty("video_ext")] + public string VideoExt { get; set; } - [JsonProperty("audio_ext")] - public string AudioExt { get; set; } + [JsonProperty("audio_ext")] + public string AudioExt { get; set; } - [JsonProperty("vbr")] - public double? Vbr { get; set; } + [JsonProperty("vbr")] + public double? Vbr { get; set; } - [JsonProperty("abr")] - public double? Abr { get; set; } + [JsonProperty("abr")] + public double? Abr { get; set; } - [JsonProperty("format")] - public string _Format { get; set; } + [JsonProperty("format")] + public string _Format { get; set; } - [JsonProperty("resolution")] - public string Resolution { get; set; } + [JsonProperty("resolution")] + public string Resolution { get; set; } - [JsonProperty("http_headers")] - public HttpHeaders HttpHeaders { get; set; } + [JsonProperty("http_headers")] + public HttpHeaders HttpHeaders { get; set; } - [JsonProperty("format_note")] - public string FormatNote { get; set; } + [JsonProperty("format_note")] + public string FormatNote { get; set; } - [JsonProperty("rows")] - public int? Rows { get; set; } + [JsonProperty("rows")] + public int? Rows { get; set; } - [JsonProperty("columns")] - public int? Columns { get; set; } + [JsonProperty("columns")] + public int? Columns { get; set; } - [JsonProperty("fragments")] - public List Fragments { get; set; } + [JsonProperty("fragments")] + public List Fragments { get; set; } - [JsonProperty("asr")] - public int? Asr { get; set; } + [JsonProperty("asr")] + public int? Asr { get; set; } - [JsonProperty("filesize")] - public long? Filesize { get; set; } + [JsonProperty("filesize")] + public long? Filesize { get; set; } - [JsonProperty("source_preference")] - public int? SourcePreference { get; set; } + [JsonProperty("source_preference")] + public int? SourcePreference { get; set; } - [JsonProperty("audio_channels")] - public int? AudioChannels { get; set; } + [JsonProperty("audio_channels")] + public int? AudioChannels { get; set; } - [JsonProperty("has_drm")] - public bool? HasDrm { get; set; } + [JsonProperty("has_drm")] + public bool? HasDrm { get; set; } - [JsonProperty("language")] - public string Language { get; set; } + [JsonProperty("language")] + public string Language { get; set; } - [JsonProperty("language_preference")] - public int? LanguagePreference { get; set; } + [JsonProperty("language_preference")] + public int? LanguagePreference { get; set; } - [JsonProperty("downloader_options")] - public DownloaderOptions DownloaderOptions { get; set; } + [JsonProperty("downloader_options")] + public DownloaderOptions DownloaderOptions { get; set; } - [JsonProperty("container")] - public string Container { get; set; } + [JsonProperty("container")] + public string Container { get; set; } - [JsonProperty("preference")] - public int? Preference { get; set; } + [JsonProperty("preference")] + public int? Preference { get; set; } - [JsonProperty("filesize_approx")] - public long? FilesizeApprox { get; set; } -} + [JsonProperty("filesize_approx")] + public long? FilesizeApprox { get; set; } + } -public class Fragment -{ - [JsonProperty("url")] - public string Url { get; set; } + public class Fragment + { + [JsonProperty("url")] + public string Url { get; set; } - [JsonProperty("duration")] - public double? Duration { get; set; } -} + [JsonProperty("duration")] + public double? Duration { get; set; } + } -public class HttpHeaders -{ - [JsonProperty("User-Agent")] - public string UserAgent { get; set; } + public class HttpHeaders + { + [JsonProperty("User-Agent")] + public string UserAgent { get; set; } - [JsonProperty("Accept")] - public string Accept { get; set; } + [JsonProperty("Accept")] + public string Accept { get; set; } - [JsonProperty("Accept-Language")] - public string AcceptLanguage { get; set; } + [JsonProperty("Accept-Language")] + public string AcceptLanguage { get; set; } - [JsonProperty("Sec-Fetch-Mode")] - public string SecFetchMode { get; set; } -} + [JsonProperty("Sec-Fetch-Mode")] + public string SecFetchMode { get; set; } + } -public class LiveChat -{ - [JsonProperty("url")] - public string Url { get; set; } + public class LiveChat + { + [JsonProperty("url")] + public string Url { get; set; } - [JsonProperty("video_id")] - public string VideoId { get; set; } + [JsonProperty("video_id")] + public string VideoId { get; set; } - [JsonProperty("ext")] - public string Ext { get; set; } + [JsonProperty("ext")] + public string Ext { get; set; } - [JsonProperty("protocol")] - public string Protocol { get; set; } -} + [JsonProperty("protocol")] + public string Protocol { get; set; } + } -public class YtdlpVideoData -{ - [JsonProperty("id")] - public string Id { get; set; } + public class ytdlpVideoData + { + [JsonProperty("id")] + public string Id { get; set; } - [JsonProperty("title")] - public string Title { get; set; } + [JsonProperty("title")] + public string Title { get; set; } - [JsonProperty("formats")] - public List Formats { get; set; } + [JsonProperty("formats")] + public List Formats { get; set; } - [JsonProperty("thumbnails")] - public List Thumbnails { get; set; } + [JsonProperty("thumbnails")] + public List Thumbnails { get; set; } - [JsonProperty("thumbnail")] - public string Thumbnail { get; set; } + [JsonProperty("thumbnail")] + public string Thumbnail { get; set; } - [JsonProperty("description")] - public string Description { get; set; } + [JsonProperty("description")] + public string Description { get; set; } - [JsonProperty("uploader")] - public string Uploader { get; set; } + [JsonProperty("uploader")] + public string Uploader { get; set; } - [JsonProperty("uploader_id")] - public string UploaderId { get; set; } + [JsonProperty("uploader_id")] + public string UploaderId { get; set; } - [JsonProperty("uploader_url")] - public string UploaderUrl { get; set; } + [JsonProperty("uploader_url")] + public string UploaderUrl { get; set; } - [JsonProperty("channel_id")] - public string ChannelId { get; set; } + [JsonProperty("channel_id")] + public string ChannelId { get; set; } - [JsonProperty("channel_url")] - public string ChannelUrl { get; set; } + [JsonProperty("channel_url")] + public string ChannelUrl { get; set; } - [JsonProperty("view_count")] - public int? ViewCount { get; set; } + [JsonProperty("view_count")] + public int? ViewCount { get; set; } - [JsonProperty("age_limit")] - public int? AgeLimit { get; set; } + [JsonProperty("age_limit")] + public int? AgeLimit { get; set; } - [JsonProperty("webpage_url")] - public string WebpageUrl { get; set; } + [JsonProperty("webpage_url")] + public string WebpageUrl { get; set; } - [JsonProperty("categories")] - public List Categories { get; set; } + [JsonProperty("categories")] + public List Categories { get; set; } - [JsonProperty("tags")] - public List Tags { get; set; } + [JsonProperty("tags")] + public List Tags { get; set; } - [JsonProperty("playable_in_embed")] - public bool? PlayableInEmbed { get; set; } + [JsonProperty("playable_in_embed")] + public bool? PlayableInEmbed { get; set; } - [JsonProperty("live_status")] - public string LiveStatus { get; set; } + [JsonProperty("live_status")] + public string LiveStatus { get; set; } - [JsonProperty("release_timestamp")] - public long? ReleaseTimestamp { get; set; } + [JsonProperty("release_timestamp")] + public long? ReleaseTimestamp { get; set; } - [JsonProperty("automatic_captions")] - public AutomaticCaptions AutomaticCaptions { get; set; } + [JsonProperty("automatic_captions")] + public AutomaticCaptions AutomaticCaptions { get; set; } - [JsonProperty("subtitles")] - public Subtitles Subtitles { get; set; } + [JsonProperty("subtitles")] + public Subtitles Subtitles { get; set; } - [JsonProperty("like_count")] - public int? LikeCount { get; set; } + [JsonProperty("like_count")] + public int? LikeCount { get; set; } - [JsonProperty("concurrent_view_count")] - public int? ConcurrentViewCount { get; set; } + [JsonProperty("concurrent_view_count")] + public int? ConcurrentViewCount { get; set; } - [JsonProperty("channel")] - public string Channel { get; set; } + [JsonProperty("channel")] + public string Channel { get; set; } - [JsonProperty("channel_follower_count")] - public int? ChannelFollowerCount { get; set; } + [JsonProperty("channel_follower_count")] + public int? ChannelFollowerCount { get; set; } - [JsonProperty("upload_date")] - public string UploadDate { get; set; } + [JsonProperty("upload_date")] + public string UploadDate { get; set; } - [JsonProperty("availability")] - public string Availability { get; set; } + [JsonProperty("availability")] + public string Availability { get; set; } - [JsonProperty("webpage_url_basename")] - public string WebpageUrlBasename { get; set; } + [JsonProperty("webpage_url_basename")] + public string WebpageUrlBasename { get; set; } - [JsonProperty("webpage_url_domain")] - public string WebpageUrlDomain { get; set; } + [JsonProperty("webpage_url_domain")] + public string WebpageUrlDomain { get; set; } - [JsonProperty("extractor")] - public string Extractor { get; set; } + [JsonProperty("extractor")] + public string Extractor { get; set; } - [JsonProperty("extractor_key")] - public string ExtractorKey { get; set; } + [JsonProperty("extractor_key")] + public string ExtractorKey { get; set; } - [JsonProperty("display_id")] - public string DisplayId { get; set; } + [JsonProperty("display_id")] + public string DisplayId { get; set; } - [JsonProperty("fulltitle")] - public string Fulltitle { get; set; } + [JsonProperty("fulltitle")] + public string Fulltitle { get; set; } - [JsonProperty("release_date")] - public string ReleaseDate { get; set; } + [JsonProperty("release_date")] + public string ReleaseDate { get; set; } - [JsonProperty("is_live")] - public bool? IsLive { get; set; } + [JsonProperty("is_live")] + public bool? IsLive { get; set; } - [JsonProperty("was_live")] - public bool? WasLive { get; set; } + [JsonProperty("was_live")] + public bool? WasLive { get; set; } - [JsonProperty("format_id")] - public string FormatId { get; set; } + [JsonProperty("format_id")] + public string FormatId { get; set; } - [JsonProperty("url")] - public string Url { get; set; } + [JsonProperty("url")] + public string Url { get; set; } - [JsonProperty("manifest_url")] - public string ManifestUrl { get; set; } + [JsonProperty("manifest_url")] + public string ManifestUrl { get; set; } - [JsonProperty("tbr")] - public double? Tbr { get; set; } + [JsonProperty("tbr")] + public double? Tbr { get; set; } - [JsonProperty("ext")] - public string Ext { get; set; } + [JsonProperty("ext")] + public string Ext { get; set; } - [JsonProperty("fps")] - public double? Fps { get; set; } + [JsonProperty("fps")] + public double? Fps { get; set; } - [JsonProperty("protocol")] - public string Protocol { get; set; } + [JsonProperty("protocol")] + public string Protocol { get; set; } - [JsonProperty("quality")] - public double? Quality { get; set; } + [JsonProperty("quality")] + public double? Quality { get; set; } - [JsonProperty("width")] - public int? Width { get; set; } + [JsonProperty("width")] + public int? Width { get; set; } - [JsonProperty("height")] - public int? Height { get; set; } + [JsonProperty("height")] + public int? Height { get; set; } - [JsonProperty("vcodec")] - public string Vcodec { get; set; } + [JsonProperty("vcodec")] + public string Vcodec { get; set; } - [JsonProperty("acodec")] - public string Acodec { get; set; } + [JsonProperty("acodec")] + public string Acodec { get; set; } - [JsonProperty("dynamic_range")] - public string DynamicRange { get; set; } + [JsonProperty("dynamic_range")] + public string DynamicRange { get; set; } - [JsonProperty("video_ext")] - public string VideoExt { get; set; } + [JsonProperty("video_ext")] + public string VideoExt { get; set; } - [JsonProperty("audio_ext")] - public string AudioExt { get; set; } + [JsonProperty("audio_ext")] + public string AudioExt { get; set; } - [JsonProperty("vbr")] - public double? Vbr { get; set; } + [JsonProperty("vbr")] + public double? Vbr { get; set; } - [JsonProperty("abr")] - public double? Abr { get; set; } + [JsonProperty("abr")] + public double? Abr { get; set; } - [JsonProperty("format")] - public string Format { get; set; } + [JsonProperty("format")] + public string Format { get; set; } - [JsonProperty("resolution")] - public string Resolution { get; set; } + [JsonProperty("resolution")] + public string Resolution { get; set; } - [JsonProperty("http_headers")] - public HttpHeaders HttpHeaders { get; set; } + [JsonProperty("http_headers")] + public HttpHeaders HttpHeaders { get; set; } - [JsonProperty("epoch")] - public long? Epoch { get; set; } + [JsonProperty("epoch")] + public long? Epoch { get; set; } - [JsonProperty("_type")] - public string Type { get; set; } + [JsonProperty("_type")] + public string Type { get; set; } - [JsonProperty("_version")] - public Version Version { get; set; } + [JsonProperty("_version")] + public Version Version { get; set; } - [JsonProperty("duration")] - public long? Duration { get; set; } + [JsonProperty("duration")] + public long? Duration { get; set; } - [JsonProperty("comment_count")] - public int? CommentCount { get; set; } + [JsonProperty("comment_count")] + public int? CommentCount { get; set; } - [JsonProperty("chapters")] - public List Chapters { get; set; } + [JsonProperty("chapters")] + public List Chapters { get; set; } - [JsonProperty("duration_string")] - public string DurationString { get; set; } + [JsonProperty("duration_string")] + public string DurationString { get; set; } - [JsonProperty("format_note")] - public string FormatNote { get; set; } + [JsonProperty("format_note")] + public string FormatNote { get; set; } - [JsonProperty("filesize_approx")] - public long? FilesizeApprox { get; set; } + [JsonProperty("filesize_approx")] + public long? FilesizeApprox { get; set; } - [JsonProperty("asr")] - public int? Asr { get; set; } + [JsonProperty("asr")] + public int? Asr { get; set; } - [JsonProperty("audio_channels")] - public int? AudioChannels { get; set; } -} + [JsonProperty("audio_channels")] + public int? AudioChannels { get; set; } + } -public class Subtitles -{ - [JsonProperty("live_chat")] - public List LiveChat { get; set; } -} + public class Subtitles + { + [JsonProperty("live_chat")] + public List LiveChat { get; set; } + } -public class Thumbnail -{ - [JsonProperty("url")] - public string Url { get; set; } + public class Thumbnail + { + [JsonProperty("url")] + public string Url { get; set; } - [JsonProperty("preference")] - public int? Preference { get; set; } + [JsonProperty("preference")] + public int? Preference { get; set; } - [JsonProperty("id")] - public string Id { get; set; } + [JsonProperty("id")] + public string Id { get; set; } - [JsonProperty("height")] - public int? Height { get; set; } + [JsonProperty("height")] + public int? Height { get; set; } - [JsonProperty("width")] - public int? Width { get; set; } + [JsonProperty("width")] + public int? Width { get; set; } - [JsonProperty("resolution")] - public string Resolution { get; set; } -} + [JsonProperty("resolution")] + public string Resolution { get; set; } + } -public class Version -{ - [JsonProperty("version")] - public string _Version { get; set; } + public class Version + { + [JsonProperty("version")] + public string _Version { get; set; } - [JsonProperty("release_git_head")] - public string ReleaseGitHead { get; set; } + [JsonProperty("release_git_head")] + public string ReleaseGitHead { get; set; } - [JsonProperty("repository")] - public string Repository { get; set; } + [JsonProperty("repository")] + public string Repository { get; set; } + } } - diff --git a/Program.cs b/Program.cs index 27bbedb..a8a0234 100644 --- a/Program.cs +++ b/Program.cs @@ -64,4 +64,3 @@ private static void SetAddRemoveProgramsIcon() } #pragma warning restore CS8602 // 可能 null 參考的取值 (dereference)。 } - diff --git a/README.md b/README.md index 09de25e..a29f1e0 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ A lifesaver for anyone who is doing video clipping/translating or any need to download segments from Youtube. +[![CodeFactor](https://www.codefactor.io/repository/github/jim60105/youtubesegmentdownloader/badge?style=for-the-badge)](https://www.codefactor.io/repository/github/jim60105/youtubesegmentdownloader) + [![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/jim60105/YoutubeSegmentDownloader?style=for-the-badge)](https://github.com/jim60105/YoutubeSegmentDownloader/releases/latest) ![GitHub Release Date](https://img.shields.io/github/release-date/jim60105/YoutubeSegmentDownloader?style=for-the-badge) ![GitHub all releases](https://img.shields.io/github/downloads/jim60105/YoutubeSegmentDownloader/total?style=for-the-badge)