Skip to content

Commit

Permalink
refactor: 重新设计,以满足多平台下载需求
Browse files Browse the repository at this point in the history
  • Loading branch information
K12f committed Sep 10, 2024
1 parent de469ec commit d33d003
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 64 deletions.
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,30 @@

蓝猫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
## 免责申明⚠️

1. 本软件只提供视频解析,不提供任何资源上传、存储到服务器的功能。
2. 本软件仅解析来自抖音的内容,不会对解析到的音视频进行二次编码,部分视频会进行有限的格式转换、拼接等操作。
3. 本软件解析得到的所有内容均来自抖音UP主上传、分享,其版权均归原作者所有。内容提供者、上传者(UP主)应对其提供、上传的内容承担全部责任。
4. 🚫 **本软件提供的所有内容,仅可用作学习交流使用,未经原作者授权,禁止用于其他用途。请在下载24小时内删除。为尊重作者版权,请前往资源的原始发布网站观看,支持原创,谢谢。**
4. 🚫 **本软件提供的所有内容,仅可用作学习交流使用,未经原作者授权,禁止用于其他用途。请在下载24小时内删除。为尊重作者版权,请前往资源的原始发布网站观看,支持原创,谢谢。
**
5. 因使用本软件产生的版权问题,软件作者概不负责。

## 喜欢就赞赏一下吧😄
Expand Down
9 changes: 6 additions & 3 deletions src/BlueCatKoKo.Ui/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Windows;
using System.Text;
using System.Windows;
using System.Windows.Threading;

using BlueCatKoKo.Ui.Models;
Expand Down Expand Up @@ -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);
})
Expand All @@ -48,7 +51,7 @@ public partial class App : Application

// Service containing
container.AddSingleton<INavigationService, NavigationService>();
container.AddSingleton<DouyinDownloaderService>();
container.AddSingleton<IDownloaderService, DouyinDownloaderService>();

// Main window with navigation
container.AddSingleton<MainWindowViewModel>();
Expand Down
8 changes: 4 additions & 4 deletions src/BlueCatKoKo.Ui/BlueCatKoKo.Ui.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0"/>
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0"/>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0"/>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3"/>
<PackageReference Include="nulastudio.NetBeauty" Version="2.1.4.5"/>
<PackageReference Include="RestSharp" Version="111.4.1"/>
<PackageReference Include="Serilog" Version="4.0.1"/>
Expand All @@ -68,7 +68,7 @@
<PropertyGroup>
<AssemblyName>BlueCatKoKo</AssemblyName>
</PropertyGroup>

<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
Expand All @@ -87,8 +87,8 @@
</ItemGroup>

<ItemGroup>
<None Remove="Assets\applicationicon.jpg" />
<Resource Include="Assets\applicationicon.jpg" />
<None Remove="Assets\applicationicon.jpg"/>
<Resource Include="Assets\applicationicon.jpg"/>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ namespace BlueCatKoKo.Ui.Models.Pages
/// <summary>
/// home page页面数据
/// </summary>
public class HomePageModel
public class VideoModel
{
// 视频ID
public string? VideoId { get; set; }
Expand Down
62 changes: 45 additions & 17 deletions src/BlueCatKoKo.Ui/Services/DouyinDownloaderService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Text.RegularExpressions;

using BlueCatKoKo.Ui.Models;
using BlueCatKoKo.Ui.Models.Pages;

using Downloader;

Expand All @@ -15,7 +16,7 @@

namespace BlueCatKoKo.Ui.Services
{
public class DouyinDownloaderService
public class DouyinDownloaderService : IDownloaderService
{
private static readonly Dictionary<string, string> _defaultHeaders = new()
{
Expand Down Expand Up @@ -48,13 +49,13 @@ public DouyinDownloaderService(ILogger logger)
_logger = logger;
}

/// <summary>
/// 解析dy分享中的文本 6.17 05/18 [email protected] fbn:/ 悬疑推理:亡者和自己的手机上午一同下葬,下午却给警察发来短信 本期的故事,来自于高分推理神剧《天堂岛疑云》中的谜案《亡灵的短信》。# 悬疑推理 # 每日推荐电影
/// # 一剪到底 https://v.douyin.com/ircqoExo/ 复制此链接,打开Dou音搜索,直接观看视频!
/// https://v.douyin.com/ircqoExo/
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
/// <summary>
/// 解析dy分享中的文本 6.17 05/18 [email protected] fbn:/ 悬疑推理:亡者和自己的手机上午一同下葬,下午却给警察发来短信 本期的故事,来自于高分推理神剧《天堂岛疑云》中的谜案《亡灵的短信》。# 悬疑推理 # 每日推荐电影
/// # 一剪到底 https://v.douyin.com/ircqoExo/ 复制此链接,打开Dou音搜索,直接观看视频!
/// https://v.douyin.com/ircqoExo/
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public async Task<string> ExtractUrlAsync(string text)
{
_logger.Information("开始解析文本 {text}", text);
Expand All @@ -66,16 +67,16 @@ public async Task<string> ExtractUrlAsync(string text)
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public async Task<DouyinShareRouterData?> ExtractVideoDataAsync(string url)
public async Task<VideoModel> 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);
Expand Down Expand Up @@ -107,7 +108,34 @@ public async Task<string> ExtractUrlAsync(string text)
string videoJson = matchJson.Groups[1].Value;
_logger.Information("开始解析匹配到的json {videoJson}", videoJson);
// 反序列化JSON字符串为C#对象
return JsonConvert.DeserializeObject<DouyinShareRouterData>(videoJson);
DouyinShareRouterData? videoData = JsonConvert.DeserializeObject<DouyinShareRouterData>(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)
{
Expand All @@ -116,23 +144,23 @@ public async Task<string> ExtractUrlAsync(string text)
}
}

public async Task Download(string url, string savePath, string fileName,
public async Task DownloadAsync(string url, string savePath, string fileName,
EventHandler<DownloadProgressChangedEventArgs> onProgressChanged,
EventHandler<AsyncCompletedEventArgs> 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;
Expand Down
19 changes: 19 additions & 0 deletions src/BlueCatKoKo.Ui/Services/IDownloaderService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.ComponentModel;

using BlueCatKoKo.Ui.Models.Pages;

using Downloader;

namespace BlueCatKoKo.Ui.Services
{
public interface IDownloaderService
{
public Task<string> ExtractUrlAsync(string text);

public Task<VideoModel> ExtractVideoDataAsync(string url);

public Task DownloadAsync(string url, string savePath, string fileName,
EventHandler<DownloadProgressChangedEventArgs> onProgressChanged,
EventHandler<AsyncCompletedEventArgs> onProgressCompleted);
}
}
51 changes: 13 additions & 38 deletions src/BlueCatKoKo.Ui/ViewModels/Pages/HomeViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ namespace BlueCatKoKo.Ui.ViewModels.Pages
public partial class HomeViewModel : ViewModelBase
{
private readonly IOptions<AppConfig> _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;
Expand All @@ -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;

Expand All @@ -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> appConfig)
{
Messenger = messenger;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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) =>
{
Expand Down

0 comments on commit d33d003

Please sign in to comment.