diff --git a/README.md b/README.md index 80ca717..25628df 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,21 @@ 蓝猫KoKo下载器(BlueCatKoKo)是一个简单易用的抖音视频下载工具,具有简洁的界面,流畅的操作逻辑。可以下载几乎所有的抖音视频,并输出mp4格式的文件。 > 因为包含了LibVLC库播放视频,所以比较大 + ## 使用教程 + ![_20240824121629.png](https://s2.loli.net/2024/08/24/BRfOVi4X6bFY8EU.jpg) + ## 下载 + https://github.com/K12f/BlueCatKoKo/releases + ## 库版本 + - .net>=8.0 + ## 版本ChangeLog + > 请查看release中的log ## 免责申明⚠️ @@ -18,7 +26,8 @@ https://github.com/K12f/BlueCatKoKo/releases 1. 本软件只提供视频解析,不提供任何资源上传、存储到服务器的功能。 2. 本软件仅解析来自抖音的内容,不会对解析到的音视频进行二次编码,部分视频会进行有限的格式转换、拼接等操作。 3. 本软件解析得到的所有内容均来自抖音UP主上传、分享,其版权均归原作者所有。内容提供者、上传者(UP主)应对其提供、上传的内容承担全部责任。 -4. 🚫 **本软件提供的所有内容,仅可用作学习交流使用,未经原作者授权,禁止用于其他用途。请在下载24小时内删除。为尊重作者版权,请前往资源的原始发布网站观看,支持原创,谢谢。** +4. 🚫 **本软件提供的所有内容,仅可用作学习交流使用,未经原作者授权,禁止用于其他用途。请在下载24小时内删除。为尊重作者版权,请前往资源的原始发布网站观看,支持原创,谢谢。 + ** 5. 因使用本软件产生的版权问题,软件作者概不负责。 ## 喜欢就赞赏一下吧😄 diff --git a/src/BlueCatKoKo.Ui/App.xaml.cs b/src/BlueCatKoKo.Ui/App.xaml.cs index 6cfb5c9..2b7061f 100644 --- a/src/BlueCatKoKo.Ui/App.xaml.cs +++ b/src/BlueCatKoKo.Ui/App.xaml.cs @@ -1,4 +1,5 @@ -using System.Windows; +using System.Text; +using System.Windows; using System.Windows.Threading; using BlueCatKoKo.Ui.Models; @@ -35,7 +36,9 @@ public partial class App : Application logging.ClearProviders(); Log.Logger = new LoggerConfiguration() .WriteTo.Console() - .WriteTo.File("log.txt", rollingInterval: RollingInterval.Day) + .WriteTo.File( + "log.txt", rollingInterval: RollingInterval.Day,encoding:Encoding.UTF8 + ) .CreateLogger(); logging.Services.AddSingleton(Log.Logger); }) @@ -48,7 +51,7 @@ public partial class App : Application // Service containing container.AddSingleton(); - container.AddSingleton(); + container.AddSingleton(); // Main window with navigation container.AddSingleton(); diff --git a/src/BlueCatKoKo.Ui/BlueCatKoKo.Ui.csproj b/src/BlueCatKoKo.Ui/BlueCatKoKo.Ui.csproj index 63b0b0c..5ecfc84 100644 --- a/src/BlueCatKoKo.Ui/BlueCatKoKo.Ui.csproj +++ b/src/BlueCatKoKo.Ui/BlueCatKoKo.Ui.csproj @@ -53,7 +53,7 @@ - + @@ -68,7 +68,7 @@ BlueCatKoKo - + Always @@ -87,8 +87,8 @@ - - + + diff --git a/src/BlueCatKoKo.Ui/Models/Pages/HomePageModel.cs b/src/BlueCatKoKo.Ui/Models/VideoModel.cs similarity index 97% rename from src/BlueCatKoKo.Ui/Models/Pages/HomePageModel.cs rename to src/BlueCatKoKo.Ui/Models/VideoModel.cs index 558607f..57226f3 100644 --- a/src/BlueCatKoKo.Ui/Models/Pages/HomePageModel.cs +++ b/src/BlueCatKoKo.Ui/Models/VideoModel.cs @@ -3,7 +3,7 @@ namespace BlueCatKoKo.Ui.Models.Pages /// /// home page页面数据 /// - public class HomePageModel + public class VideoModel { // 视频ID public string? VideoId { get; set; } diff --git a/src/BlueCatKoKo.Ui/Services/DouyinDownloaderService.cs b/src/BlueCatKoKo.Ui/Services/DouyinDownloaderService.cs index 9a3beac..121862f 100644 --- a/src/BlueCatKoKo.Ui/Services/DouyinDownloaderService.cs +++ b/src/BlueCatKoKo.Ui/Services/DouyinDownloaderService.cs @@ -4,6 +4,7 @@ using System.Text.RegularExpressions; using BlueCatKoKo.Ui.Models; +using BlueCatKoKo.Ui.Models.Pages; using Downloader; @@ -15,7 +16,7 @@ namespace BlueCatKoKo.Ui.Services { - public class DouyinDownloaderService + public class DouyinDownloaderService : IDownloaderService { private static readonly Dictionary _defaultHeaders = new() { @@ -48,13 +49,13 @@ public DouyinDownloaderService(ILogger logger) _logger = logger; } - /// - /// 解析dy分享中的文本 6.17 05/18 U@L.wf fbn:/ 悬疑推理:亡者和自己的手机上午一同下葬,下午却给警察发来短信 本期的故事,来自于高分推理神剧《天堂岛疑云》中的谜案《亡灵的短信》。# 悬疑推理 # 每日推荐电影 - /// # 一剪到底 https://v.douyin.com/ircqoExo/ 复制此链接,打开Dou音搜索,直接观看视频! - /// https://v.douyin.com/ircqoExo/ - /// - /// - /// + /// + /// 解析dy分享中的文本 6.17 05/18 U@L.wf fbn:/ 悬疑推理:亡者和自己的手机上午一同下葬,下午却给警察发来短信 本期的故事,来自于高分推理神剧《天堂岛疑云》中的谜案《亡灵的短信》。# 悬疑推理 # 每日推荐电影 + /// # 一剪到底 https://v.douyin.com/ircqoExo/ 复制此链接,打开Dou音搜索,直接观看视频! + /// https://v.douyin.com/ircqoExo/ + /// + /// + /// public async Task ExtractUrlAsync(string text) { _logger.Information("开始解析文本 {text}", text); @@ -66,16 +67,16 @@ public async Task ExtractUrlAsync(string text) /// /// /// - public async Task ExtractVideoDataAsync(string url) + public async Task ExtractVideoDataAsync(string url) { try { _logger.Information("开始解析链接 {url}", url); // 创建RestClient - RestClient client = new RestClient(url); + RestClient client = new(url); // 创建请求对象 - RestRequest request = new RestRequest(); + RestRequest request = new(); // 设置 User-Agent 模拟手机浏览器 request.AddHeaders(_defaultHeaders); @@ -107,7 +108,34 @@ public async Task ExtractUrlAsync(string text) string videoJson = matchJson.Groups[1].Value; _logger.Information("开始解析匹配到的json {videoJson}", videoJson); // 反序列化JSON字符串为C#对象 - return JsonConvert.DeserializeObject(videoJson); + DouyinShareRouterData? videoData = JsonConvert.DeserializeObject(videoJson); + + if (videoData is null) + { + throw new InvalidDataException("JSON解析数据为空,请检查分享链接是否正确,如有更多问题请查看日志"); + } + + ItemList videoInfoData = videoData.LoaderData.VideoIdPage.VideoInfoRes.ItemList.First(); + + return new VideoModel + { + VideoId = videoInfoData.AwemeId, + AuthorName = videoInfoData.Author.Nickname, + AuthorAvatar = videoInfoData.Author.AvatarThumb.UrlList.First().ToString(), + Title = videoInfoData.Author.Signature, + Cover = videoInfoData.Video.Cover.UrlList.Last().ToString(), + VideoUrl = videoInfoData.Video.PlayAddr.UrlList.First().ToString().Replace("playwm", "play"), + Mp3Url = "", + CreatedTime = + DateTimeOffset.FromUnixTimeSeconds(videoInfoData.CreateTime) + .ToString("yyyy-MM-dd HH:mm:ss"), + Desc = videoInfoData.Desc, + Duration = "", + DiggCount = videoInfoData.Statistics.DiggCount, + CollectCount = videoInfoData.Statistics.CollectCount, + CommentCount = videoInfoData.Statistics.CommentCount, + ShareCount = videoInfoData.Statistics.ShareCount + }; } catch (Exception e) { @@ -116,23 +144,23 @@ public async Task ExtractUrlAsync(string text) } } - public async Task Download(string url, string savePath, string fileName, + public async Task DownloadAsync(string url, string savePath, string fileName, EventHandler onProgressChanged, EventHandler onProgressCompleted ) { - var downloadConfiguration = new DownloadConfiguration + DownloadConfiguration downloadConfiguration = new() { ChunkCount = 8, // Download in 8 chunks (increase for larger files) MaxTryAgainOnFailover = 5, // Retry up to 5 times on failure Timeout = 10000, // 10 seconds timeout for each request - RequestConfiguration = new RequestConfiguration() + RequestConfiguration = new RequestConfiguration { - UserAgent = _defaultHeaders.GetValueOrDefault("User-Agent"), + UserAgent = _defaultHeaders.GetValueOrDefault("User-Agent") } }; - var downloader = new DownloadService(downloadConfiguration); + DownloadService downloader = new(downloadConfiguration); downloader.DownloadProgressChanged += onProgressChanged; downloader.DownloadFileCompleted += onProgressCompleted; diff --git a/src/BlueCatKoKo.Ui/Services/IDownloaderService.cs b/src/BlueCatKoKo.Ui/Services/IDownloaderService.cs new file mode 100644 index 0000000..f130883 --- /dev/null +++ b/src/BlueCatKoKo.Ui/Services/IDownloaderService.cs @@ -0,0 +1,19 @@ +using System.ComponentModel; + +using BlueCatKoKo.Ui.Models.Pages; + +using Downloader; + +namespace BlueCatKoKo.Ui.Services +{ + public interface IDownloaderService + { + public Task ExtractUrlAsync(string text); + + public Task ExtractVideoDataAsync(string url); + + public Task DownloadAsync(string url, string savePath, string fileName, + EventHandler onProgressChanged, + EventHandler onProgressCompleted); + } +} \ No newline at end of file diff --git a/src/BlueCatKoKo.Ui/ViewModels/Pages/HomeViewModel.cs b/src/BlueCatKoKo.Ui/ViewModels/Pages/HomeViewModel.cs index f2fa47c..a13f4c3 100644 --- a/src/BlueCatKoKo.Ui/ViewModels/Pages/HomeViewModel.cs +++ b/src/BlueCatKoKo.Ui/ViewModels/Pages/HomeViewModel.cs @@ -25,11 +25,11 @@ namespace BlueCatKoKo.Ui.ViewModels.Pages public partial class HomeViewModel : ViewModelBase { private readonly IOptions _appConfig; - private readonly DouyinDownloaderService _douyinDownloaderService; + private readonly IDownloaderService _douyinDownloaderService; private readonly ILogger _logger; // 解析出的视频数据 - [ObservableProperty] private HomePageModel _data; + [ObservableProperty] private VideoModel _data; // 下载进度 [ObservableProperty] private double _downloadProcess; @@ -38,12 +38,12 @@ public partial class HomeViewModel : ViewModelBase [ObservableProperty] [Required(ErrorMessage = "缺少分享链接")] private string _downloadUrlText; - // 是否已经下载 - [ObservableProperty] private string _isDownload; - // 解析按钮状态,正在解析/下载时,禁止点击 [ObservableProperty] private bool _isDisableParsingBtn; + // 是否已经下载 + [ObservableProperty] private string _isDownload; + // 是否下载音频,默认false [ObservableProperty] private bool _isDownloadAudio; @@ -54,7 +54,8 @@ [ObservableProperty] [Required(ErrorMessage = "缺少分享链接")] // 视频是否已解析 [ObservableProperty] private string _isParsed; - public HomeViewModel(IMessenger messenger, ILogger logger, DouyinDownloaderService douyinDownloaderService, + public HomeViewModel(IMessenger messenger, ILogger logger, + IDownloaderService douyinDownloaderService, IOptions appConfig) { Messenger = messenger; @@ -108,39 +109,13 @@ private async Task Parse() throw new ValidationException("请输入正确的分享链接"); } - string downloadUrl = await _douyinDownloaderService.ExtractUrlAsync(DownloadUrlText); - DouyinShareRouterData? douYinRouterData = - await _douyinDownloaderService.ExtractVideoDataAsync(downloadUrl); - if (douYinRouterData is null) - { - throw new InvalidDataException("解析数据为空,请检查分享链接是否正确,如有更多问题请查看日志"); - } - - // 获取数据 - ItemList videoInfoData = douYinRouterData.LoaderData.VideoIdPage.VideoInfoRes.ItemList.First(); - - Data = new HomePageModel - { - VideoId = videoInfoData.AwemeId, - AuthorName = videoInfoData.Author.Nickname, - AuthorAvatar = videoInfoData.Author.AvatarThumb.UrlList.First().ToString(), - Title = videoInfoData.Author.Signature, - Cover = videoInfoData.Video.Cover.UrlList.Last().ToString(), - VideoUrl = videoInfoData.Video.PlayAddr.UrlList.First().ToString().Replace("playwm", "play"), - Mp3Url = "", - CreatedTime = - DateTimeOffset.FromUnixTimeSeconds(videoInfoData.CreateTime) - .ToString("yyyy-MM-dd HH:mm:ss"), - Desc = videoInfoData.Desc, - Duration = "", - DiggCount = videoInfoData.Statistics.DiggCount, - CollectCount = videoInfoData.Statistics.CollectCount, - CommentCount = videoInfoData.Statistics.CommentCount, - ShareCount = videoInfoData.Statistics.ShareCount - }; + var downloadUrl = await _douyinDownloaderService.ExtractUrlAsync(DownloadUrlText); + var douYinRouterData = await _douyinDownloaderService.ExtractVideoDataAsync(downloadUrl); + Data = douYinRouterData; + // 绑定视频 - using Media media = new(LibVlc, new Uri(Data.VideoUrl)); + using Media media = new(LibVlc, new Uri(douYinRouterData.VideoUrl)); // 这里设置选项,防止自动播放 MediaPlayer.Media = media; MediaPlayer.Pause(); @@ -193,7 +168,7 @@ private async Task DownloadAll() } string filename = _appConfig.Value.DownloadPath + Data.VideoId + ".mp4"; - await _douyinDownloaderService.Download(Data.VideoUrl, _appConfig.Value.DownloadPath, + await _douyinDownloaderService.DownloadAsync(Data.VideoUrl, _appConfig.Value.DownloadPath, Data.VideoId + ".mp4", (sender, e) => {