-
-
-
-
-
-
-
{{ category.name }}
-
作品数: {{ category.count }}
+
+
+
+
+
+
+
+
+
{{ category.name }}
+
作品数: {{ category.count }}
+
+
+ {{ Math.round((category.count / totalVideos) * 100) }}%
-
+
-
+
@@ -584,16 +207,23 @@ interface Category {
icon: string;
}
-// 3. 初始Tab改为"视频作者"(currentTab默认值从'type'改为'author')
+// 状态管理 - 包含喜欢、收藏、关注的数量及空间统计
const totalVideos = ref
(0);
const totalAuthors = ref(0);
const categoryTotal = ref(0);
-const fileSizeTotal = ref(0);
+const fileSizeTotal = ref('0.00');
+const totalDiskSize = ref('0.00');
+
const favoriteCount = ref(0);
const collectCount = ref(0);
+const followCount = ref(0); // 新增:关注的数量
+const favoriteSize = ref('0.00'); // 喜欢占用空间
+const collectSize = ref('0.00'); // 收藏占用空间
+const followSize = ref('0.00'); // 新增:关注占用空间
const categories = ref([]);
const authors = ref([]);
-const currentTab = ref('author'); // 默认显示"视频作者"Tab
+const currentTab = ref('author');
+const tabCount = ref(0); // tab切换时显示数量
// 组件名称
defineOptions({
@@ -602,19 +232,34 @@ defineOptions({
// 加载数据
onMounted(() => {
+ // const html = document.documentElement;
+ // html.classList.add('dark-mode');
loadDashboardData();
});
+const changeTab = (e: any) => {
+ currentTab.value = e;
+ if (e == 'author') {
+ tabCount.value = totalAuthors.value;
+ } else {
+ tabCount.value = categoryTotal.value;
+ }
+};
const loadDashboardData = async () => {
try {
const res = await useApiStore().VideoStatics();
- totalVideos.value = res.data.videoCount;
totalAuthors.value = res.data.authorCount;
categoryTotal.value = res.data.categoryCount;
+ totalVideos.value = res.data.videoCount;
+ fileSizeTotal.value = res.data.videoSizeTotal || 0.0;
+ totalDiskSize.value = res.data.totalDiskSize || 0.0;
favoriteCount.value = res.data.favoriteCount;
collectCount.value = res.data.collectCount;
+ followCount.value = res.data.followCount || 0; // 新增:读取关注数量
+ favoriteSize.value = res.data.videoFavoriteSize || 0.0;
+ collectSize.value = res.data.videoCollectSize || 0.0;
+ followSize.value = res.data.videoFollowSize || 0.0; // 新增:读取关注占用空间
categories.value = res.data.categories;
- fileSizeTotal.value = res.data.viedoSizeTotal; // 注意:原代码"video"拼写错误(viedo),建议修正为"videoSizeTotal"
authors.value = res.data.authors;
// 分类图标和颜色随机分配
@@ -656,8 +301,6 @@ const categoriessss = ref([
'tv',
'watch',
'charger',
- 'router',
- 'printer',
]);
const colorArray = [
@@ -667,7 +310,6 @@ const colorArray = [
'#829399',
'#A63446',
'#4F6367',
- '#EEF4ED',
'#FE5F55',
'#C7EFCF',
'#78C0E0',
@@ -680,243 +322,431 @@ const colorArray = [
\ No newline at end of file
diff --git a/app/src/store/setting.ts b/app/src/store/setting.ts
index d903c01..88d1634 100644
--- a/app/src/store/setting.ts
+++ b/app/src/store/setting.ts
@@ -6,7 +6,7 @@ export type Navigation = 'side' | 'head' | 'mix';
export const useSettingStore = defineStore('setting', () => {
const navigation = ref('head');
const useTabs = ref(true);
- const theme = ref('header-purple');
+ const theme = ref('header-night');
const contentClass = ref('common');
const filterMenu = ref(false);
diff --git a/app/src/theme/index.ts b/app/src/theme/index.ts
index 81733b6..ef8c198 100644
--- a/app/src/theme/index.ts
+++ b/app/src/theme/index.ts
@@ -17,7 +17,8 @@ export function configTheme(key: string) {
const { setNavigation, setTheme } = useSettingStore();
switch (key) {
case 'night':
- setBgSeriesColors({ 'bg-base': '#1D1D1F' });
+ // setBgSeriesColors({ 'bg-base': '#1D1D1F' });
+ setBgSeriesColors({ 'bg-base': '#1a1a2e' });
break;
case 'header-dark':
setNavigation('head');
@@ -84,12 +85,20 @@ export const themeList: Theme.ThemeConfig[] = [
},
{
title: '暗夜紫',
- key: 'purple',
+ key: 'purple2',
imgUrl: PurpleImg,
config: {
color: { middle: { 'bg-base': '#361F68' } },
},
},
+ {
+ title: '暗夜',
+ key: 'purple',
+ imgUrl: PurpleImg,
+ config: {
+ color: { middle: { 'bg-base': '#1a1a2e' } },
+ },
+ },
{
title: '中国红',
key: 'china',
diff --git a/app/vite.config.ts b/app/vite.config.ts
index 8b9eda3..2056e57 100644
--- a/app/vite.config.ts
+++ b/app/vite.config.ts
@@ -26,7 +26,6 @@ const prodRollupOptions = {
export default ({ command, mode }) => {
// 获取环境变量
const env = loadEnv(mode, process.cwd());
-
return defineConfig({
server: {
host: "0.0.0.0",
@@ -85,7 +84,14 @@ export default ({ command, mode }) => {
preprocessorOptions: {
less: {
plugins: [AntdvLessPlugin],
- modifyVars: AntdvModifyVars,
+
+ modifyVars: {
+ ...AntdvModifyVars, // 保留默认变量(可选,按需添加)
+ '@primary-color': '#722ed1',
+ // 你的自定义主色调(核心:覆盖默认值)
+ // 其他需要修改的变量
+ // '@link-color': '#722ed1',
+ },
javascriptEnabled: true,
},
},
diff --git a/dto/VideoStaticsDto.cs b/dto/VideoStaticsDto.cs
index 80ba9cd..3f1897c 100644
--- a/dto/VideoStaticsDto.cs
+++ b/dto/VideoStaticsDto.cs
@@ -23,11 +23,19 @@
public long CategoryCount { get; set; }
- public double ViedoSizeTotal { get; set; }
+ public string VideoSizeTotal { get; set; }
+
+ public string VideoCollectSize { get; set; }
+ public string VideoFollowSize { get; set; }
+ public string VideoFavoriteSize { get; set; }
public long CollectCount { get; set; }
public long FavoriteCount { get; set; }
+ public long FollowCount { get; set; }
+
+
+ public string TotalDiskSize { get; set; }
public List Authors { get; set; } = new List();
public List Categories { get; set; } = new List();
}
diff --git a/extension/ServiceExtension.cs b/extension/ServiceExtension.cs
index 32f26aa..6471d5e 100644
--- a/extension/ServiceExtension.cs
+++ b/extension/ServiceExtension.cs
@@ -1,9 +1,12 @@
using dy.net.job;
using dy.net.service;
+using dy.net.utils;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.ResponseCompression;
+using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Quartz;
using Serilog;
@@ -16,6 +19,7 @@ using Swashbuckle.AspNetCore.SwaggerUI;
using System.IO.Compression;
using System.Net.Http;
using System.Reflection;
+using System.Text;
namespace dy.net.extension
{
@@ -72,6 +76,44 @@ namespace dy.net.extension
return conn;
}
+
+ ///
+ /// 配置JWT认证
+ ///
+ public static void ConfigureJwtAuthentication(this IServiceCollection services)
+ {
+ var key = Encoding.ASCII.GetBytes(Md5Util.JWT_TOKEN_KEY);
+
+ services.AddAuthentication(options =>
+ {
+ options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
+ options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
+ })
+ .AddJwtBearer(options =>
+ {
+ options.Events = new JwtBearerEvents
+ {
+ OnMessageReceived = context =>
+ {
+ context.Token = context.Request.Headers["Authorization"]
+ .FirstOrDefault()?.Split(" ").Last();
+ return Task.CompletedTask;
+ }
+ };
+ options.RequireHttpsMetadata = false;
+ options.SaveToken = true;
+ options.TokenValidationParameters = new TokenValidationParameters
+ {
+ ValidateIssuerSigningKey = true,
+ IssuerSigningKey = new SymmetricSecurityKey(key),
+ ValidateIssuer = false,
+ ValidateAudience = false,
+ ValidateLifetime = true,
+ ClockSkew = TimeSpan.FromSeconds(60)
+ };
+ });
+ }
+
public static void AddSqlsugar(this IServiceCollection services, IConfiguration configuration)
{
DbType dbtype = GetDBType(configuration);
diff --git a/job/DouYinCollectSyncJob.cs b/job/DouYinCollectSyncJob.cs
index 99f7a38..fdbd25f 100644
--- a/job/DouYinCollectSyncJob.cs
+++ b/job/DouYinCollectSyncJob.cs
@@ -65,6 +65,11 @@ namespace dy.net.job
Serilog.Log.Debug($"collect-Cookie-[{cookie.UserName}]无效,跳过");
continue;
}
+ if (string.IsNullOrWhiteSpace(cookie.SavePath))
+ {
+ Serilog.Log.Debug($"collect-Cookie-[{cookie.UserName}]未设置保存路径,跳过");
+ continue;
+ }
try
{
int syncCount = 0;// 记录本次Cookie同步的视频数量
diff --git a/service/CommonService.cs b/service/CommonService.cs
index 60537af..74be584 100644
--- a/service/CommonService.cs
+++ b/service/CommonService.cs
@@ -1,6 +1,8 @@
-using dy.net.dto;
+using ClockSnowFlake;
+using dy.net.dto;
using dy.net.model;
using SqlSugar;
+using System.Reflection;
using System.Threading.Tasks;
namespace dy.net.service
@@ -14,39 +16,49 @@ namespace dy.net.service
this.sqlSugarClient = sqlSugarClient;
}
- public AppConfig GetConfig()
+ public AppConfig GetConfig()
{
- return sqlSugarClient.Queryable().First();
+ return sqlSugarClient.Queryable().First();
}
- public AppConfig InitConfig(AppConfig config)
+ public AppConfig InitConfig()
{
var conf = GetConfig();
if (conf != null)
return conf;
else
{
- var add = sqlSugarClient.Insertable(config).ExecuteCommand();
+ AppConfig config = new AppConfig
+ {
+ Id = IdGener.GetLong().ToString(),
+ Cron = "30",
+ BatchCount = 10
+ };
+ sqlSugarClient.Insertable(config).ExecuteCommand();
return config;
}
+
+
}
- public async Task UpdateConfig(AppConfig config) {
+ public async Task UpdateConfig(AppConfig config)
+ {
- var cc = await sqlSugarClient.Queryable().FirstAsync(x=>x.Id== config.Id);
+ var cc = await sqlSugarClient.Queryable().FirstAsync(x => x.Id == config.Id);
if (cc == null)
return false;
- else {
+ else
+ {
cc.Cron = config.Cron;
cc.BatchCount = config.BatchCount;
}
var update = await sqlSugarClient.Updateable(cc).ExecuteCommandAsync();
- return update > 0 ;
+ return update > 0;
}
///
@@ -54,11 +66,12 @@ namespace dy.net.service
///
public void UpdateCollectViedoType()
{
- var collectViedos= sqlSugarClient.Queryable().Where(x=>string.IsNullOrWhiteSpace(x.ViedoType)).ToList();
+ var collectViedos = sqlSugarClient.Queryable().Where(x => string.IsNullOrWhiteSpace(x.ViedoType)).ToList();
if (collectViedos.Any())
{
- collectViedos.ForEach(x => {
+ collectViedos.ForEach(x =>
+ {
x.ViedoType = "2";
});
sqlSugarClient.Updateable(collectViedos).ExecuteCommand();
@@ -72,7 +85,7 @@ namespace dy.net.service
{
//var sql = "update dy_cookie set CollHasSyncd=0,FavHasSyncd=0,UperSyncd=0";
// sqlSugarClient.Ado.ExecuteCommand(sql) > 0;
- var cookies= sqlSugarClient.Queryable().ToList();
+ var cookies = sqlSugarClient.Queryable().ToList();
foreach (var cookie in cookies)
{
@@ -97,6 +110,67 @@ namespace dy.net.service
return sqlSugarClient.Updateable(cookies).ExecuteCommand() > 0;
}
+ #region 测试创建数据库
+
+ /////
+ ///// 创建SQLite数据库连接字符串
+ /////
+ //private string CreateSqliteDBConn()
+ //{
+ // var dbFolder = Path.Combine(Environment.CurrentDirectory, "db");
+ // Directory.CreateDirectory(dbFolder); // 不存在则创建,无需判断
+
+ // var dbPath = Path.Combine(dbFolder, "dy.sqlite");
+ // if (!File.Exists(dbPath))
+ // {
+ // using (File.Create(dbPath)) { } // 使用using确保文件流关闭
+ // }
+
+ // return $"DataSource={dbPath}";
+ //}
+ /////
+ ///// 初始化数据库
+ /////
+ //public ISqlSugarClient InitDataBase(DbType dbType, string connString)
+ //{
+ // // 处理SQLite连接字符串
+ // if (dbType == DbType.Sqlite)
+ // {
+ // connString = CreateSqliteDBConn();
+ // }
+
+ // if (string.IsNullOrWhiteSpace(connString))
+ // {
+ // return null;
+ // }
+
+ // return new SqlSugarClient(new ConnectionConfig
+ // {
+ // ConnectionString = connString,
+ // InitKeyType = InitKeyType.Attribute,
+ // DbType = dbType,
+ // IsAutoCloseConnection = true
+ // }, db =>
+ // {
+ // // 日志配置
+ // db.Aop.OnLogExecuting = (sql, pars) => Serilog.Log.Debug(sql);
+ // db.Aop.OnError = e =>
+ // {
+ // Serilog.Log.Error(e.Message);
+ // Serilog.Log.Error(e.Sql);
+ // };
+
+ // // 创建数据库和表
+ // db.DbMaintenance.CreateDatabase();
+ // var modelTypes = Assembly.GetExecutingAssembly()
+ // .GetTypes()
+ // .Where(t => t.Namespace?.StartsWith("dy.net.model") ?? false)
+ // .ToArray();
+ // db.CodeFirst.InitTables(modelTypes);
+ // });
+ //}
+ #endregion
}
+
}
diff --git a/service/DyCollectVideoService.cs b/service/DyCollectVideoService.cs
index c87ff06..a09e2ad 100644
--- a/service/DyCollectVideoService.cs
+++ b/service/DyCollectVideoService.cs
@@ -59,26 +59,33 @@ namespace dy.net.service
List list = await this._dyCollectVideoRepository.GetAllAsync();
if (!list.Any())
return new VideoStaticsDto();
- var Categories = list.GroupBy(x => x.Tag1).Select(x => new VideoStaticsItemDto { Name = x.Key, Count = x.LongCount() }).OrderByDescending(p=>p.Count).ToList();
+ var Categories = list.GroupBy(x => x.Tag1).Select(x => new VideoStaticsItemDto { Name = x.Key, Count = x.LongCount() }).OrderByDescending(p => p.Count).ToList();
Categories.Where(x => string.IsNullOrWhiteSpace(x.Name)).ToList().ForEach(x => x.Name = "其他");
var data = new VideoStaticsDto
{
AuthorCount = list.Select(x => x.AuthorId).Distinct().Count(),
- CategoryCount=list.Select(x=>x.Tag1).Distinct().Count(),
+ CategoryCount = list.Select(x => x.Tag1).Distinct().Count(),
VideoCount = list.Count,
- Categories= Categories,
- FavoriteCount= list.Count(x => x.ViedoType=="1"),
- CollectCount= list.Count(x => x.ViedoType=="2"),
- ViedoSizeTotal = ByteToGbConverter.ConvertBytesToGb(list.Sum(x=>x.FileSize))
+ Categories = Categories,
+ FavoriteCount = list.Count(x => x.ViedoType == "1"),
+ CollectCount = list.Count(x => x.ViedoType == "2"),
+ FollowCount = list.Count(x => x.ViedoType == "3"),
+
+ VideoSizeTotal = ByteToGbConverter.ConvertBytesToGb(list.Sum(x => x.FileSize)),
+ VideoFavoriteSize = ByteToGbConverter.ConvertBytesToGb(list.Where(x => x.ViedoType == "1").Sum(x => x.FileSize)),
+ VideoCollectSize = ByteToGbConverter.ConvertBytesToGb(list.Where(x => x.ViedoType == "2").Sum(x => x.FileSize)),
+ VideoFollowSize = ByteToGbConverter.ConvertBytesToGb(list.Where(x => x.ViedoType == "3").Sum(x => x.FileSize)),
+
+ TotalDiskSize= ByteToGbConverter.GetHostTotalDiskSpaceGB(),
};
- data.Authors = list.GroupBy(x => x.Author).Select(x => new VideoStaticsItemDto { Name = x.Key, Count = x.LongCount(), Icon=x.FirstOrDefault().AuthorAvatarUrl }).OrderByDescending(d=>d.Count).ToList();
+ data.Authors = list.GroupBy(x => x.Author).Select(x => new VideoStaticsItemDto { Name = x.Key, Count = x.LongCount(), Icon = x.FirstOrDefault().AuthorAvatarUrl }).OrderByDescending(d => d.Count).ToList();
return data;
}
//分页查询
- public async Task<(List list, int totalCount)> GetPagedAsync(int pageIndex, int pageSize, string tag = null, string author = null,string viedoType=null,List? dates=null)
+ public async Task<(List list, int totalCount)> GetPagedAsync(int pageIndex, int pageSize, string tag = null, string author = null, string viedoType = null, List? dates = null)
{
return await _dyCollectVideoRepository.GetPagedAsync(pageIndex, pageSize, tag, author, viedoType, dates);
}
diff --git a/service/DyCookieService.cs b/service/DyCookieService.cs
index 0b53303..9f270c1 100644
--- a/service/DyCookieService.cs
+++ b/service/DyCookieService.cs
@@ -26,14 +26,29 @@ namespace dy.net.service
return await _cookieRepository.InsertAsync(dyUserCookies);
}
- public bool Init(DyUserCookies dyUserCookies)
+ public bool Init()
{
- var exist = _cookieRepository.GetFirst(x => x.Id == dyUserCookies.Id);
- if(exist != null)
+ var initId= "2026";
+ var exist = _cookieRepository.GetFirst(x => x.Id == initId);
+ if (exist != null)
{
return false;
}
- return _cookieRepository.Insert(dyUserCookies);
+ var cookie = new DyUserCookies
+ {
+ UserName = "douyin",
+ Cookies = "--",
+ Id = initId,
+ SavePath = "/app/collect",
+ Status = 0,
+ SecUserId = "--",
+ FavSavePath = "/app/favorite",
+ UpSavePath = "/app/uper",
+ CollHasSyncd = 0,
+ FavHasSyncd = 0,
+ UperSyncd = 0
+ };
+ return _cookieRepository.Insert(cookie);
}
// 查询单个
diff --git a/service/UserService.cs b/service/UserService.cs
index e4ae5f6..29e79bd 100644
--- a/service/UserService.cs
+++ b/service/UserService.cs
@@ -30,8 +30,14 @@ namespace dy.net.service
return await _userRepository.UpdateAvatar(avatar);
}
- public (int code, string erro) InitUser(LoginUserInfo userInfo)
+ public (int code, string erro) InitUser()
{
+ LoginUserInfo userInfo = new LoginUserInfo
+ {
+ UserName = "douyin",
+ Password = "douyin2025",
+ CreateTime = DateTime.Now
+ };
return _userRepository.InitUser(userInfo);
}
}
diff --git a/utils/ByteToGbConverter.cs b/utils/ByteToGbConverter.cs
index 4d0e6a2..a544a2c 100644
--- a/utils/ByteToGbConverter.cs
+++ b/utils/ByteToGbConverter.cs
@@ -8,13 +8,52 @@
/// 字节数(需使用long类型避免大数值溢出)
/// 保留的小数位数
/// 转换后的GB值
- public static double ConvertBytesToGb(long bytes, int decimalPlaces = 2)
+ public static string ConvertBytesToGb(long bytes, int decimalPlaces = 2)
{
// 1GB = 1024 * 1024 * 1024 = 1073741824 Byte
double gb = (double)bytes / 1073741824;
// 四舍五入保留指定小数位数
- return Math.Round(gb, decimalPlaces);
+ return Math.Round(gb, decimalPlaces).ToString("F2");
+ }
+
+
+ ///
+ /// 获取宿主机所有本地固定磁盘的总空间(GB)
+ ///
+ /// 总空间字符串(如 "1408.35 GB"),失败时返回错误信息
+ public static string GetHostTotalDiskSpaceGB()
+ {
+ try
+ {
+ // 1. 获取所有驱动器
+ DriveInfo[] allDrives = DriveInfo.GetDrives();
+
+ // 2. 筛选:仅保留“就绪的本地固定磁盘”(DriveType.Fixed)
+ var fixedDrives = allDrives.Where(drive =>
+ drive.IsReady &&
+ drive.DriveType == DriveType.Fixed
+ ).ToList();
+
+ if (!fixedDrives.Any())
+ {
+ return "未找到可用的本地固定磁盘";
+ }
+
+ // 3. 累加所有固定磁盘的总空间(字节)
+ long totalBytes = fixedDrives.Sum(drive => drive.TotalSize);
+
+ // 4. 转换为GB并保留2位小数
+ return ConvertBytesToGb(totalBytes);
+ }
+ catch (UnauthorizedAccessException)
+ {
+ return "权限不足,无法访问部分磁盘";
+ }
+ catch (Exception ex)
+ {
+ return $"获取宿主机总磁盘空间失败:{ex.Message}";
+ }
}
}
}