提交功能

This commit is contained in:
Bob.Song
2026-02-08 16:58:32 +08:00
commit 9dd1e6c278
67 changed files with 4588 additions and 0 deletions

613
.gitattributes vendored Normal file
View File

@@ -0,0 +1,613 @@
# 视频video
# oggtheora
*.[oO][gG][gG][tT][hH][eE][oO][rR][aA] filter=lfs diff=lfs merge=binary -text
# m2t
*.[mM]2[tT] filter=lfs diff=lfs merge=binary -text
# mts
*.[mM][tT][sS] filter=lfs diff=lfs merge=binary -text
# mp4
*.[mM][pP]4 filter=lfs diff=lfs merge=binary -text
# avi
*.[aA][vV][iI] filter=lfs diff=lfs merge=binary -text
# mkv
*.[mM][kK][vV] filter=lfs diff=lfs merge=binary -text
# wmv
*.[wW][mM][vV] filter=lfs diff=lfs merge=binary -text
# asf
*.[aA][sS][fF] filter=lfs diff=lfs merge=binary -text
# asx
*.[aA][sS][xX] filter=lfs diff=lfs merge=binary -text
# rm
*.[rR][mM] filter=lfs diff=lfs merge=binary -text
# rmvb
*.[rR][mM][vV][bB] filter=lfs diff=lfs merge=binary -text
# 3gp
*.3[gG][pP] filter=lfs diff=lfs merge=binary -text
# 3gpp
*.3[gG][pP][pP] filter=lfs diff=lfs merge=binary -text
# 3gpp2
*.3[gG][pP][pP]2 filter=lfs diff=lfs merge=binary -text
# mov
*.[mM][oO][vV] filter=lfs diff=lfs merge=binary -text
# m4v
*.[mM]4[vV] filter=lfs diff=lfs merge=binary -text
# dat
*.[dD][aA][tT] filter=lfs diff=lfs merge=binary -text
# vob
*.[vV][oO][bB] filter=lfs diff=lfs merge=binary -text
# dv
*.[dD][vV] filter=lfs diff=lfs merge=binary -text
# mpeg
*.[mM][pP][eE][gG] filter=lfs diff=lfs merge=binary -text
# mpg
*.[mM][pP][gG] filter=lfs diff=lfs merge=binary -text
# mpe
*.[mM][pP][eE] filter=lfs diff=lfs merge=binary -text
# m2v
*.[mM]2[vV] filter=lfs diff=lfs merge=binary -text
# webm
*.[wW][eE][bB][mM] filter=lfs diff=lfs merge=binary -text
# flv
*.[fF][lL][vV] filter=lfs diff=lfs merge=binary -text
# swf
*.[sS][wW][fF] filter=lfs diff=lfs merge=binary -text
# avc
*.[aA][vV][cC] filter=lfs diff=lfs merge=binary -text
# arf
*.[aA][rR][fF] filter=lfs diff=lfs merge=binary -text
# vcr
*.[vV][cC][rR] filter=lfs diff=lfs merge=binary -text
# ogv
*.[oO][gG][vV] filter=lfs diff=lfs merge=binary -text
# 音频, audio
# ape
*.[aA][pP][eE] filter=lfs diff=lfs merge=binary -text
# wav
*.[wW][aA][vV] filter=lfs diff=lfs merge=binary -text
# m4a
*.[mM]4[aA] filter=lfs diff=lfs merge=binary -text
# mp3
*.[mM][pP]3 filter=lfs diff=lfs merge=binary -text
# flac
*.[fF][lL][aA][cC] filter=lfs diff=lfs merge=binary -text
# aif
*.[aA][iI][fF] filter=lfs diff=lfs merge=binary -text
# aiff
*.[aA][iI][fF][fF] filter=lfs diff=lfs merge=binary -text
# aac
*.[aA][aA][cC] filter=lfs diff=lfs merge=binary -text
# cpa
*.[cC][pP][aA] filter=lfs diff=lfs merge=binary -text
# swa
*.[sS][wW][aA] filter=lfs diff=lfs merge=binary -text
# sesx
*.[sS][eE][sS][xX] filter=lfs diff=lfs merge=binary -text
# ses
*.[sS][eE][sS] filter=lfs diff=lfs merge=binary -text
# bnk, Wwise audio
*.[bB][nN][kK] filter=lfs diff=lfs merge=binary -text
# wem, Wwise
*.[wW][eE][mM] filter=lfs diff=lfs merge=binary -text
# pca
*.[pP][cC][aA] filter=lfs diff=lfs merge=binary -text
# 库文件Library
# a
*.[aA] filter=lfs diff=lfs merge=binary -text
# o
*.[oO] filter=lfs diff=lfs merge=binary -text
# so
*.[sS][oO] filter=lfs diff=lfs merge=binary -text
# lib
*.[lL][iI][bB] filter=lfs diff=lfs merge=binary -text
# dll
*.[dD][lL][lL] filter=lfs diff=lfs merge=binary -text
# lbr
*.[lL][bB][rR] filter=lfs diff=lfs merge=binary -text
# tlb
*.[tT][lL][bB] filter=lfs diff=lfs merge=binary -text
# cab
*.[cC][aA][bB] filter=lfs diff=lfs merge=binary -text
# dylib
*.[dD][yY][lL][iI][bB] filter=lfs diff=lfs merge=binary -text
# dsym
*.[dD][sS][yY][mM] filter=lfs diff=lfs merge=binary -text
# app
*.[aA][pP][pP] filter=lfs diff=lfs merge=binary -text
# ipa
*.[iI][pP][aA] filter=lfs diff=lfs merge=binary -text
# dmg
*.[dD][mM][gG] filter=lfs diff=lfs merge=binary -text
# exe
*.[eE][xX][eE] filter=lfs diff=lfs merge=binary -text
# pdb
*.[pP][dD][bB] filter=lfs diff=lfs merge=binary -text
# dbg
*.[dD][bB][gG] filter=lfs diff=lfs merge=binary -text
# run
*.[rR][uU][nN] filter=lfs diff=lfs merge=binary -text
# pyd
*.[pP][yY][dD] filter=lfs diff=lfs merge=binary -text
# pyc
*.[pP][yY][cC] filter=lfs diff=lfs merge=binary -text
# nupkg, NuGet package
*.[nN][uU][pP][kK][gG] filter=lfs diff=lfs merge=binary -text
# pch
*.[pP][cC][hH] filter=lfs diff=lfs merge=binary -text
# ilk
*.[iI][lL][kK] filter=lfs diff=lfs merge=binary -text
# debug
*.[dD][eE][bB][uU][gG] filter=lfs diff=lfs merge=binary -text
# obj
*.[oO][bB][jJ] filter=lfs diff=lfs merge=binary -text
# stub
*.[sS][tT][uU][bB] filter=lfs diff=lfs merge=binary -text
# ddp
*.[dD][dD][pP] filter=lfs diff=lfs merge=binary -text
# sym
*.[sS][yY][mM] filter=lfs diff=lfs merge=binary -text
# lld
*.[lL][lL][dD] filter=lfs diff=lfs merge=binary -text
# res
*.[rR][eE][sS] filter=lfs diff=lfs merge=binary -text
# locres
*.[lL][oO][cC][rR][eE][sS] filter=lfs diff=lfs merge=binary -text
# aar
*.[aA][aA][rR] filter=lfs diff=lfs merge=binary -text
# udd
*.[uU][dD][dD] filter=lfs diff=lfs merge=binary -text
# mdb
*.[mM][dD][bB] filter=lfs diff=lfs merge=binary -text
# ddc
*.[dD][dD][cC] filter=lfs diff=lfs merge=binary -text
# udn
*.[uU][dD][nN] filter=lfs diff=lfs merge=binary -text
# h5
*.[hH]5 filter=lfs diff=lfs merge=binary -text
# 压缩包Archive format
# =Archiving only=
# ar
*.[aA][rR] filter=lfs diff=lfs merge=binary -text
# cpio
*.[cC][pP][iI][oO] filter=lfs diff=lfs merge=binary -text
# shar
*.[sS][hH][aA][rR] filter=lfs diff=lfs merge=binary -text
# tar
*.[tT][aA][rR] filter=lfs diff=lfs merge=binary -text
# lbr
*.[lL][bB][rR] filter=lfs diff=lfs merge=binary -text
# =Compression only=
# Brotli
*.[bB][rR][oO][tT][lL][iI] filter=lfs diff=lfs merge=binary -text
# zip
*.[zZ][iI][pP] filter=lfs diff=lfs merge=binary -text
# bzip2
*.[bB][zZ][iI][pP]2 filter=lfs diff=lfs merge=binary -text
# compress
*.[cC][oO][mM][pP][rR][eE][sS][sS] filter=lfs diff=lfs merge=binary -text
# gzip
*.[gG][zZ][iI][pP] filter=lfs diff=lfs merge=binary -text
# zopfli
*.[zZ][oO][pP][fF][lL][iI] filter=lfs diff=lfs merge=binary -text
# LZMA
*.[lL][zZ][mM][aA] filter=lfs diff=lfs merge=binary -text
# LZ4
*.[lL][zZ]4 filter=lfs diff=lfs merge=binary -text
# lzip
*.[lL][zZ][iI][pP] filter=lfs diff=lfs merge=binary -text
# lzop
*.[lL][zZ][oO][pP] filter=lfs diff=lfs merge=binary -text
# SQ
*.[sS][qQ] filter=lfs diff=lfs merge=binary -text
# xz
*.[xX][zZ] filter=lfs diff=lfs merge=binary -text
# Zstandard
*.[zZ][sS][tT][aA][nN][dD][aA][rR][dD] filter=lfs diff=lfs merge=binary -text
# =Archiving and compression=
# 7z
*.7[zZ] filter=lfs diff=lfs merge=binary -text
# ace
*.[aA][cC][eE] filter=lfs diff=lfs merge=binary -text
# arc
*.[aA][rR][cC] filter=lfs diff=lfs merge=binary -text
# arj
*.[aA][rR][jJ] filter=lfs diff=lfs merge=binary -text
# b1
*.[bB]1 filter=lfs diff=lfs merge=binary -text
# cabinet
*.[cC][aA][bB][iI][nN][eE][tT] filter=lfs diff=lfs merge=binary -text
# cfs
*.[cC][fF][sS] filter=lfs diff=lfs merge=binary -text
# cpt
*.[cC][pP][tT] filter=lfs diff=lfs merge=binary -text
# dar
*.[dD][aA][rR] filter=lfs diff=lfs merge=binary -text
# dgca
*.[dD][gG][cC][aA] filter=lfs diff=lfs merge=binary -text
# dmg
*.[dD][mM][gG] filter=lfs diff=lfs merge=binary -text
# egg
*.[eE][gG][gG] filter=lfs diff=lfs merge=binary -text
# kgb
*.[kK][gG][bB] filter=lfs diff=lfs merge=binary -text
# lha
*.[lL][hH][aA] filter=lfs diff=lfs merge=binary -text
# lzx
*.[lL][zZ][xX] filter=lfs diff=lfs merge=binary -text
# mpq
*.[mM][pP][qQ] filter=lfs diff=lfs merge=binary -text
# pea
*.[pP][eE][aA] filter=lfs diff=lfs merge=binary -text
# rar
*.[rR][aA][rR] filter=lfs diff=lfs merge=binary -text
# rzip
*.[rR][zZ][iI][pP] filter=lfs diff=lfs merge=binary -text
# sit
*.[sS][iI][tT] filter=lfs diff=lfs merge=binary -text
# sitx
*.[sS][iI][tT][xX] filter=lfs diff=lfs merge=binary -text
# sqx
*.[sS][qQ][xX] filter=lfs diff=lfs merge=binary -text
# uda
*.[uU][dD][aA] filter=lfs diff=lfs merge=binary -text
# xar
*.[xX][aA][rR] filter=lfs diff=lfs merge=binary -text
# zoo
*.[zZ][oO][oO] filter=lfs diff=lfs merge=binary -text
# zpaq
*.[zZ][pP][aA][qQ] filter=lfs diff=lfs merge=binary -text
# =Software packaging and distribution=
# apk
*.[aA][pP][kK] filter=lfs diff=lfs merge=binary -text
# appx
*.[aA][pP][pP][xX] filter=lfs diff=lfs merge=binary -text
# deb
*.[dD][eE][bB] filter=lfs diff=lfs merge=binary -text
# rpm
*.[rR][pP][mM] filter=lfs diff=lfs merge=binary -text
# msi
*.[mM][sS][iI] filter=lfs diff=lfs merge=binary -text
# ipa
*.[iI][pP][aA] filter=lfs diff=lfs merge=binary -text
# jar
*.[jJ][aA][rR] filter=lfs diff=lfs merge=binary -text
# war
*.[wW][aA][rR] filter=lfs diff=lfs merge=binary -text
# ear
*.[eE][aA][rR] filter=lfs diff=lfs merge=binary -text
# xap
*.[xX][aA][pP] filter=lfs diff=lfs merge=binary -text
# xbap
*.[xX][bB][aA][pP] filter=lfs diff=lfs merge=binary -text
# hap
*.[hH][aA][pP] filter=lfs diff=lfs merge=binary -text
# app
*.[aA][pP][pP] filter=lfs diff=lfs merge=binary -text
# gz
*.[gG][zZ] filter=lfs diff=lfs merge=binary -text
# tgz
*.[tT][gG][zZ] filter=lfs diff=lfs merge=binary -text
# bz2
*.[bB][zZ]2 filter=lfs diff=lfs merge=binary -text
# z
*.[zZ] filter=lfs diff=lfs merge=binary -text
# pak
*.[pP][aA][kK] filter=lfs diff=lfs merge=binary -text
# archive
*.[aA][rR][cC][hH][iI][vV][eE] filter=lfs diff=lfs merge=binary -text
# vsix
*.[vV][sS][iI][xX] filter=lfs diff=lfs merge=binary -text
# disk image
# iso
*.[iI][sS][oO] filter=lfs diff=lfs merge=binary -text
# bin
*.[bB][iI][nN] filter=lfs diff=lfs merge=binary -text
# cue
*.[cC][uU][eE] filter=lfs diff=lfs merge=binary -text
# raw
*.[rR][aA][wW] filter=lfs diff=lfs merge=binary -text
# Adobe
# Photoshop
# psd
*.[pP][sS][dD] filter=lfs diff=lfs merge=binary -text
# Illustrator
# ai
*.[aA][iI] filter=lfs diff=lfs merge=binary -text
# eps
*.[eE][pP][sS] filter=lfs diff=lfs merge=binary -text
# pdf
*.[pP][dD][fF] filter=lfs diff=lfs merge=binary -text
# 原始图片Raw image
# cr2
*.[cC][rR]2 filter=lfs diff=lfs merge=binary -text
# crw
*.[cC][rR][wW] filter=lfs diff=lfs merge=binary -text
# nef
*.[nN][eE][fF] filter=lfs diff=lfs merge=binary -text
# nrw
*.[nN][rR][wW] filter=lfs diff=lfs merge=binary -text
# sr2
*.[sS][rR]2 filter=lfs diff=lfs merge=binary -text
# dng
*.[dD][nN][gG] filter=lfs diff=lfs merge=binary -text
# arw
*.[aA][rR][wW] filter=lfs diff=lfs merge=binary -text
# ort
*.[oO][rR][fF] filter=lfs diff=lfs merge=binary -text
# fbx
*.[fF][bB][xX] filter=lfs diff=lfs merge=binary -text
# 3ds
*.3[dD][sS] filter=lfs diff=lfs merge=binary -text
# xcf
*.[xX][cC][fF] filter=lfs diff=lfs merge=binary -text
# hdr
*.[hH][dD][rR] filter=lfs diff=lfs merge=binary -text
# duf
*.[dD][uU][fF] filter=lfs diff=lfs merge=binary -text
# mb, maya
*.[mM][bB] filter=lfs diff=lfs merge=binary -text
# cubemapunity 贴图
*.[cC][uU][bB][eE][mM][aA][pP] filter=lfs diff=lfs merge=binary -text
# navmeshunity
*.[nN][aA][vV][mM][eE][sS][hH] filter=lfs diff=lfs merge=binary -text
# osm地理数据
*.[oO][sS][mM] filter=lfs diff=lfs merge=binary -text
# hip, houdini
*.[hH][iI][pP] filter=lfs diff=lfs merge=binary -text
# cdr
*.[cC][dD][rR] filter=lfs diff=lfs merge=binary -text
# raw
*.[rR][aA][wW] filter=lfs diff=lfs merge=binary -text
# dae
*.[dD][aA][eE] filter=lfs diff=lfs merge=binary -text
# hda, houdini
*.[hH][dD][aA] filter=lfs diff=lfs merge=binary -text
# geo, houdini
*.[gG][eE][oO] filter=lfs diff=lfs merge=binary -text
# bgeo, houdini
*.[bB][gG][eE][oO] filter=lfs diff=lfs merge=binary -text
# ma, 3dmax
*.[mM][aA] filter=lfs diff=lfs merge=binary -text
# max, 3dmax
*.[mM][aA][xX] filter=lfs diff=lfs merge=binary -text
# 3dm, 3d模型
*.3[dD][mM] filter=lfs diff=lfs merge=binary -text
# blend
*.[bB][lL][eE][nN][dD] filter=lfs diff=lfs merge=binary -text
# c4d
*.[cC]4[dD] filter=lfs diff=lfs merge=binary -text
# collada
*.[cC][oO][lL][lL][aA][dD][aA] filter=lfs diff=lfs merge=binary -text
# dxf
*.[dD][xX][fF] filter=lfs diff=lfs merge=binary -text
# jas
*.[jJ][aA][sS] filter=lfs diff=lfs merge=binary -text
# lws
*.[lL][wW][sS] filter=lfs diff=lfs merge=binary -text
# lxo
*.[lL][xX][oO] filter=lfs diff=lfs merge=binary -text
# ply
*.[pP][lL][yY] filter=lfs diff=lfs merge=binary -text
# skp
*.[sS][kK][pP] filter=lfs diff=lfs merge=binary -text
# stl
*.[sS][tT][lL] filter=lfs diff=lfs merge=binary -text
# ztl
*.[zZ][tT][lL] filter=lfs diff=lfs merge=binary -text
# it
*.[iI][tT] filter=lfs diff=lfs merge=binary -text
# mod
*.[mM][oO][dD] filter=lfs diff=lfs merge=binary -text
# ogg
*.[oO][gG][gG] filter=lfs diff=lfs merge=binary -text
# s3m
*.[sS]3[mM] filter=lfs diff=lfs merge=binary -text
# xm
*.[xX][mM] filter=lfs diff=lfs merge=binary -text
# glb
*.[gG][lL][bB] filter=lfs diff=lfs merge=binary -text
# gltf
*.[gG][lL][tT][fF] filter=lfs diff=lfs merge=binary -text
# off
*.[oO][fF][fF] filter=lfs diff=lfs merge=binary -text
# wrl
*.[wW][rR][lL] filter=lfs diff=lfs merge=binary -text
# 3mf
*.3[mM][fF] filter=lfs diff=lfs merge=binary -text
# amf
*.[aA][mM][fF] filter=lfs diff=lfs merge=binary -text
# ifc
*.[iI][fF][cC] filter=lfs diff=lfs merge=binary -text
# brep
*.[bB][rR][eE][pP] filter=lfs diff=lfs merge=binary -text
# step
*.[sS][tT][eE][pP] filter=lfs diff=lfs merge=binary -text
# fcstd
*.[fF][cC][sS][tT][dD] filter=lfs diff=lfs merge=binary -text
# bim
*.[bB][iI][mM] filter=lfs diff=lfs merge=binary -text
# 图像Image
# jpg
*.[jJ][pP][gG] filter=lfs diff=lfs merge=binary -text
# jpeg
*.[jJ][pP][eE][gG] filter=lfs diff=lfs merge=binary -text
# tiff
*.[tT][iI][fF][fF] filter=lfs diff=lfs merge=binary -text
# gif
*.[gG][iI][fF] filter=lfs diff=lfs merge=binary -text
# svg
*.[sS][vV][gG] filter=lfs diff=lfs merge=binary -text
# svgz
*.[sS][vV][gG][zZ] filter=lfs diff=lfs merge=binary -text
# bmp
*.[bB][mM][pP] filter=lfs diff=lfs merge=binary -text
# png
*.[pP][nN][gG] filter=lfs diff=lfs merge=binary -text
# tif
*.[tT][iI][fF] filter=lfs diff=lfs merge=binary -text
# tga
*.[tT][gG][aA] filter=lfs diff=lfs merge=binary -text
# prj
*.[pP][rR][jJ] filter=lfs diff=lfs merge=binary -text
# dwg
*.[dD][wW][gG] filter=lfs diff=lfs merge=binary -text
# flt
*.[fF][lL][tT] filter=lfs diff=lfs merge=binary -text
# htr
*.[hH][tT][rR] filter=lfs diff=lfs merge=binary -text
# iges
*.[iI][gG][eE][sS] filter=lfs diff=lfs merge=binary -text
# igs
*.[iI][gG][sS] filter=lfs diff=lfs merge=binary -text
# ige
*.[iI][gG][eE] filter=lfs diff=lfs merge=binary -text
# ipt
*.[iI][pP][tT] filter=lfs diff=lfs merge=binary -text
# iam
*.[iI][aA][mM] filter=lfs diff=lfs merge=binary -text
# lp
*.[lL][pP] filter=lfs diff=lfs merge=binary -text
# ls
*.[lL][sS] filter=lfs diff=lfs merge=binary -text
# shp
*.[sS][hH][pP] filter=lfs diff=lfs merge=binary -text
# aep
*.[aA][eE][pP] filter=lfs diff=lfs merge=binary -text
# psb
*.[pP][sS][bB] filter=lfs diff=lfs merge=binary -text
# edx
*.[eE][dD][xX] filter=lfs diff=lfs merge=binary -text
# cds
*.[cC][dD][sS] filter=lfs diff=lfs merge=binary -text
# exr
*.[eE][xX][rR] filter=lfs diff=lfs merge=binary -text
# bc
*.[bB][cC] filter=lfs diff=lfs merge=binary -text
# 文档Document
# Microsoft Excel
# xls
*.[xX][lL][sS] filter=lfs diff=lfs merge=binary -text
# xlsx
*.[xX][lL][sS][xX] filter=lfs diff=lfs merge=binary -text
# xslsm
*.[xX][sS][lL][sS][mM] filter=lfs diff=lfs merge=binary -text
# xlt
*.[xX][lL][tT] filter=lfs diff=lfs merge=binary -text
# xltx
*.[xX][lL][tT][xX] filter=lfs diff=lfs merge=binary -text
# xltm
*.[xX][lL][tT][mM] filter=lfs diff=lfs merge=binary -text
# Microsoft powperpoint
# ppt
*.[pP][pP][tT] filter=lfs diff=lfs merge=binary -text
# pptx
*.[pP][pP][tT][xX] filter=lfs diff=lfs merge=binary -text
# pps
*.[pP][pP][sS] filter=lfs diff=lfs merge=binary -text
# ppsx
*.[pP][pP][sS][xX] filter=lfs diff=lfs merge=binary -text
# ppsm
*.[pP][pP][sS][mM] filter=lfs diff=lfs merge=binary -text
# pptm
*.[pP][pP][tT][mM] filter=lfs diff=lfs merge=binary -text
# pot
*.[pP][oO][tT] filter=lfs diff=lfs merge=binary -text
# potm
*.[pP][oO][tT][mM] filter=lfs diff=lfs merge=binary -text
# Microsoft word
# doc
*.[dD][oO][cC] filter=lfs diff=lfs merge=binary -text
# docx
*.[dD][oO][cC][xX] filter=lfs diff=lfs merge=binary -text
# docm
*.[dD][oO][cC][mM] filter=lfs diff=lfs merge=binary -text
# dot
*.[dD][oO][tT] filter=lfs diff=lfs merge=binary -text
# dotx
*.[dD][oO][tT][xX] filter=lfs diff=lfs merge=binary -text
# dotm
*.[dD][oO][tT][mM] filter=lfs diff=lfs merge=binary -text
# Apple keynotes
# key
*.[kK][eE][yY] filter=lfs diff=lfs merge=binary -text
# Apple pages
# pages, apple
*.[pP][aA][gG][eE][sS] filter=lfs diff=lfs merge=binary -text
# Apple numbers
# numbers, apple
*.[nN][uU][mM][bB][eE][rR][sS] filter=lfs diff=lfs merge=binary -text
# 电子书Book
# chm
*.[cC][hH][mM] filter=lfs diff=lfs merge=binary -text
# mobi
*.[mM][oO][bB][iI] filter=lfs diff=lfs merge=binary -text
# epub
*.[eE][pP][uU][bB] filter=lfs diff=lfs merge=binary -text
# azw
*.[aA][zZ][wW] filter=lfs diff=lfs merge=binary -text
# azw3
*.[aA][zZ][wW]3 filter=lfs diff=lfs merge=binary -text
# iba
*.[iI][bB][aA] filter=lfs diff=lfs merge=binary -text
# lrs
*.[lL][rR][sS] filter=lfs diff=lfs merge=binary -text
# lrf
*.[lL][rR][fF] filter=lfs diff=lfs merge=binary -text
# lrx
*.[lL][rR][xX] filter=lfs diff=lfs merge=binary -text
# djvu
*.[dD][jJ][vV][uU] filter=lfs diff=lfs merge=binary -text
# lit
*.[lL][iI][tT] filter=lfs diff=lfs merge=binary -text
# rft
*.[rR][fF][tT] filter=lfs diff=lfs merge=binary -text
# cbr
*.[cC][bB][rR] filter=lfs diff=lfs merge=binary -text
# cbz
*.[cC][bB][zZ] filter=lfs diff=lfs merge=binary -text
# cb7
*.[cC][bB]7 filter=lfs diff=lfs merge=binary -text
# cbt
*.[cC][bB][tT] filter=lfs diff=lfs merge=binary -text
# cba
*.[cC][bB][aA] filter=lfs diff=lfs merge=binary -text
# pdb
*.[pP][dD][bB] filter=lfs diff=lfs merge=binary -text
# 字体font
# ttf
*.[tT][tT][fF] filter=lfs diff=lfs merge=binary -text
# otf
*.[oO][tT][fF] filter=lfs diff=lfs merge=binary -text
# woff
*.[wW][oO][fF][fF] filter=lfs diff=lfs merge=binary -text
# woff2
*.[wW][oO][fF][fF]2 filter=lfs diff=lfs merge=binary -text
# 翻译translate
# po
*.[pP][oO] filter=lfs diff=lfs merge=binary -text
# auto generated by UGit
*.bundle filter=lfs diff=lfs merge=binary -text
*.dll filter=lfs diff=lfs merge=binary -text
*.exe filter=lfs diff=lfs merge=binary -text
*.zip filter=lfs diff=lfs merge=binary -text
*.mp4 filter=lfs diff=lfs merge=binary -text
*.so filter=lfs diff=lfs merge=binary -text
*.dylib filter=lfs diff=lfs merge=binary -text
*.a filter=lfs diff=lfs merge=binary -text
*.pdb filter=lfs diff=lfs merge=binary -text
*.cache filter=lfs diff=lfs merge=binary -text
*.png filter=lfs diff=lfs merge=binary -text
*.db filter=lfs diff=lfs merge=binary -text

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
bin/
obj/
/packages/
riderModule.iml
/_ReSharper.Caches/

View File

@@ -0,0 +1,211 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoGeneratedRunConfigurationManager">
<projectFile>ACBuildService.csproj</projectFile>
</component>
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="1ed80655-443d-428d-8747-9708bc7b56ea" name="更改" comment="">
<change afterPath="$PROJECT_DIR$/Db/DB.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Db/Tables/DeviceTable.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Db/Tables/VideoTable.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Http/Extensions/DateTimeExtensions.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Http/Extensions/FileNameExtensions.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Http/Page/ApiController.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Http/Page/Args.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Http/Page/HomeController.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Http/Page/NBControllerBase.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Http/SignValidationAttribute.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/VideoDownload/DouyinShareRouterData.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/VideoDownload/Platforms/DouYin.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/VideoDownload/Platforms/DownloadPlatforms.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/VideoDownload/Platforms/KuaiShou.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/VideoDownload/VideoDownload.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/VideoDownload/VideoDownloadService.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/VideoDownload/VideoModel.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/.idea.ACBuildService/.idea/.gitignore" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/.idea.ACBuildService/.idea/.name" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/.idea.ACBuildService/.idea/encodings.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/.idea.ACBuildService/.idea/indexLayout.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/.idea.ACBuildService/.idea/jsLibraryMappings.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/.idea.ACBuildService/.idea/riderPublish.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/.idea.ACBuildService/.idea/vcs.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ACBuildService.csproj" beforeDir="false" afterPath="$PROJECT_DIR$/BabyVideoService.csproj" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ACBuildService.sln" beforeDir="false" afterPath="$PROJECT_DIR$/BabyVideoService.sln" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ACBuildService.sln.DotSettings.user" beforeDir="false" afterPath="$PROJECT_DIR$/ACBuildService.sln.DotSettings.user" afterDir="false" />
<change beforePath="$PROJECT_DIR$/App.cs" beforeDir="false" afterPath="$PROJECT_DIR$/App.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/AppSettingsConfigs.cs" beforeDir="false" afterPath="$PROJECT_DIR$/AppSettingsConfigs.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/GoogleCloud/GoogleCloud.cs" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/GoogleCloud/UploadFileTask.cs" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/GoogleCloud/UploadGoogleCloudTask.cs" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/Http/ErrorCode.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Http/ErrorCode.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Http/Extensions/HttpContextExtension.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Http/Extensions/HttpContextExtension.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Http/HttpHandler.cs" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/Http/HttpPage.cs" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/Http/HttpService.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Http/HttpService.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Http/HttpSmbHandler.cs" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/Utils/FileUtil.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Utils/FileUtil.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/appsettings.json" beforeDir="false" afterPath="$PROJECT_DIR$/appsettings.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/wwwroot/cdn.html" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/wwwroot/clipboard.html" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/wwwroot/file_list.html" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/wwwroot/home.html" beforeDir="false" afterPath="$PROJECT_DIR$/wwwroot/home.html" afterDir="false" />
<change beforePath="$PROJECT_DIR$/wwwroot/log.html" beforeDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="DpaMonitoringSettings">
<option name="firstShow" value="false" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="HTML File" />
</list>
</option>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="HighlightingSettingsPerFile">
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/56fad711d44d4d47bc9f3128de5ae30ac90920/14/78d2f3c4/CultureInfo.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/759e68a39ef64bf8b91a2ed761bcd8a92e3400/05/7b368181/ISugarQueryable`1.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/759e68a39ef64bf8b91a2ed761bcd8a92e3400/59/e6a3fb28/IInsertable`1.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/83d0691b2ff94d50bae98091380d202e27e00/40/dca6e351/StorageClient.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/9a4eaacbc57349edb521efd6830794901f4910/b4/311bb478/MD5.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/ba9546ad90c246358f902e6522c620ddc9910/71/404fbeda/EndpointRouteBuilderExtensions.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/bbcd33dffc534f7b961b0763775ac78b1de938/f9/08f50080/ControllerBase.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/fb4ef246b9c147be90cf03bb4a45b1ce20938/a1/2cc30e09/Channel`2.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/59cd7a17e4fcffda1ba3687b386939b4e155ba482f85c8fe9993bc8373d7f93/ApiBehaviorApplicationModelProvider.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/9492f6e132137ff2698b3268c9c422a578e49c5893a8371ed19fb0695fd5925/AbstractDownloadService.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Db/DB.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Db/Tables/DeviceTable.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Db/Tables/VideoTable.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Http/Extensions/DateTimeExtensions.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Http/Extensions/FileNameExtensions.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Http/HttpService.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Http/Page/ApiController.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Http/Page/Args.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Http/Page/HomeController.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Http/Page/NBControllerBase.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Http/SignValidationAttribute.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/VideoDownload/DouyinShareRouterData.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/VideoDownload/Platforms/DouYin.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/VideoDownload/Platforms/DownloadPlatforms.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/VideoDownload/Platforms/KuaiShou.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/VideoDownload/VideoDownload.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/VideoDownload/VideoModel.cs" root0="FORCE_HIGHLIGHTING" />
</component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 8
}</component>
<component name="ProjectId" id="38V6rHTZQlHzbOTn9ymyVhZ9eqD" />
<component name="ProjectLevelVcsManager">
<ConfirmationsSetting value="2" id="Add" />
</component>
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;.NET 项目.ACBuildService.executor&quot;: &quot;Debug&quot;,
&quot;.NET 项目.BabyVideoService.executor&quot;: &quot;Debug&quot;,
&quot;DefaultHtmlFileTemplate&quot;: &quot;HTML File&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;RunOnceActivity.typescript.service.memoryLimit.init&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;master&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;configurable.group.editor&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;,
&quot;发布到文件夹.Publish ACBuildService to folder.executor&quot;: &quot;Run&quot;
}
}</component>
<component name="RunManager" selected=".NET 项目.BabyVideoService">
<configuration name="Publish ACBuildService to folder" type="DotNetFolderPublish" factoryName="Publish to folder">
<riderPublish configuration="Release" delete_existing_files="true" platform="Any CPU" produce_single_file="true" self_contained="true" target_folder="$PROJECT_DIR$/bin/Release/net8.0/win-x64/publish" target_framework="net8.0" uuid_high="-3297046778019885416" uuid_low="-4999719437998725852">
<runtimes>
<item value="win-x64" />
</runtimes>
</riderPublish>
<method v="2" />
</configuration>
<configuration name="BabyVideoService" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/bin/Debug/net8.0/BabyVideoService.exe" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/bin/Debug/net8.0" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="ENV_FILE_PATHS" value="" />
<option name="REDIRECT_INPUT_PATH" value="" />
<option name="MIXED_MODE_DEBUG" value="0" />
<option name="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" />
<option name="AUTO_ATTACH_CHILDREN" value="0" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/BabyVideoService.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value="net8.0" />
<method v="2">
<option name="Build" />
</method>
</configuration>
<list>
<item itemvalue=".NET 项目.BabyVideoService" />
<item itemvalue="发布到文件夹.Publish ACBuildService to folder" />
</list>
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="默认任务">
<changelist id="1ed80655-443d-428d-8747-9708bc7b56ea" name="更改" comment="" />
<created>1768872588133</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1768872588133</updated>
<workItem from="1768872589085" duration="31265000" />
<workItem from="1769505886159" duration="1492000" />
<workItem from="1770512793250" duration="25168000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="UnityProjectConfiguration" hasMinimizedUI="false" />
<component name="VcsManagerConfiguration">
<option name="CLEAR_INITIAL_COMMIT_MESSAGE" value="true" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<breakpoint enabled="true" type="DotNet_Exception_Breakpoints">
<properties exception="System.OperationCanceledException" breakIfHandledByOtherCode="false" displayValue="System.OperationCanceledException" />
<option name="timeStamp" value="1" />
</breakpoint>
<breakpoint enabled="true" type="DotNet_Exception_Breakpoints">
<properties exception="System.Threading.Tasks.TaskCanceledException" breakIfHandledByOtherCode="false" displayValue="System.Threading.Tasks.TaskCanceledException" />
<option name="timeStamp" value="2" />
</breakpoint>
<breakpoint enabled="true" type="DotNet_Exception_Breakpoints">
<properties exception="System.Threading.ThreadAbortException" breakIfHandledByOtherCode="false" displayValue="System.Threading.ThreadAbortException" />
<option name="timeStamp" value="3" />
</breakpoint>
</breakpoints>
</breakpoint-manager>
</component>
<component name="XSLT-Support.FileAssociations.UIState">
<expand />
<select />
</component>
</project>

13
.idea/.idea.BabyVideoService/.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,13 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# Rider 忽略的文件
/.idea.S3Update.iml
/contentModel.xml
/projectSettingsUpdater.xml
/modules.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<file url="PROJECT" libraries="{jquery}" />
</component>
</project>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="LocationsWithSilentDeleteHolder">
<option name="locations">
<list>
<option value="$PROJECT_DIR$/bin/Release/net7.0/win-x64/publish" />
</list>
</option>
</component>
</project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

151
.idea/.idea.S3Update/.idea/workspace.xml generated Normal file
View File

@@ -0,0 +1,151 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoGeneratedRunConfigurationManager">
<projectFile>S3Update.csproj</projectFile>
</component>
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="a22442a9-c22e-4925-9d61-0e7b123dd791" name="更改" comment="">
<change afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
<change afterPath="$PROJECT_DIR$/ACBuildService.sln" afterDir="false" />
<change afterPath="$PROJECT_DIR$/AmazonS3/AmazonS3Uploader.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/App.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/AppSettingsConfigs.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/DAO/BuildInfo.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/DingTalk/DingTalkService.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/DingTalk/DingTalkTestData.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Http/HttpHandler.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Http/HttpService.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Http/Test.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Program.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/S3Update.csproj" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Template/AppNotice.md" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Utils.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/appsettings.json" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="JSON File" />
</list>
</option>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="HighlightingSettingsPerFile">
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/125a503bdb47420aa159999f50c6f427b16a0/63/cd0e14c5/AmazonS3Client.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/125a503bdb47420aa159999f50c6f427b16a0/a5/d29319ae/IAmazonS3.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/272f80b23bc042ef81ba3ab7ea4ecddb16b880/94/999b8d74/JsonSerializer.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/4153965b14f14480b9485bd6302acddec8a0/48/c9be6245/JsonConfigurationExtensions.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/53e60aab468843af866c5896ad5ea72d2178a0/42/0bd35d8f/KestrelServerLimits.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/53e60aab468843af866c5896ad5ea72d2178a0/51/b874eb72/KestrelServerOptions.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/5f8fb2e16be44b11b2ee143f4b77491e408a0/f3/2d0d331a/FormOptions.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/6eb64a83264b490faecaf5d9f80c71fd1e38b0/a7/e37813ca/StatusCodeResult.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/aa813094324f408e8047b0dda3853621b1d878/8c/9ddbd145/FileStream.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/c7c18550c3384b22ad98cb17afcdcb8ff880/c3/fe8586e4/HttpUtility.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/d029ce1eb56a43518a6f641c75e05c69c08a0/8d/04decdcd/EndpointRouteBuilderExtensions.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/d0903c2b0b994c838944152abd1164d04b8a0/27/cee45528/HttpRequest.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/df62c8d50e2f42c8be5b0988aa88459d118b0/0e/2ef98457/IFormCollection.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/df62c8d50e2f42c8be5b0988aa88459d118b0/54/bba95950/IFormFileCollection.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/e59a47c0febb43d581d8863e7367eb611ab878/d5/6c014dc6/HttpClient.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/fe558445f2644cc7bce69050fb3e956c1b7ca0/4c/6616a00b/ClientConfig.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/fe558445f2644cc7bce69050fb3e956c1b7ca0/a3/f9fafb87/RegionEndpoint.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/3bd14eb0aff9eef09c97aab46a4e2efb4447d1d32344eb947ffac2f9ab8/WebHostBuilderExtensions.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/4dc731a2825b3b282c3a7fc5adbea47e8720eadf5445558010f8488ec05eb64f/HttpClient.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/bd1d5c50194fea68ff3559c160230b0ab50f5acf4ce3061bffd6d62958e2182/ExceptionDispatchInfo.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/c7a4689271bd1d191f6aaf905f158992a7d36de3f142326e6f6c892997a2dd3/ByteArrayContent.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/d97a692897faa346d6c839abe34fef633777e8a5199569218619a7e48769c65f/StreamReader.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/e60b8a28a674789a73bf80f2349c8fd7881cb1ed21fea431b814f1e08598/Convert.cs" root0="SKIP_HIGHLIGHTING" />
</component>
<component name="MarkdownSettingsMigration">
<option name="stateVersion" value="1" />
</component>
<component name="ProjectId" id="2R5esRCxK3XGnIL0YuG3qDdD3DH" />
<component name="ProjectLevelVcsManager" settingsEditedManually="true">
<ConfirmationsSetting value="2" id="Add" />
</component>
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;RunOnceActivity.OpenProjectViewOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;WebServerToolWindowFactoryState&quot;: &quot;false&quot;,
&quot;git-widget-placeholder&quot;: &quot;master&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
},
&quot;keyToStringList&quot;: {
&quot;rider.external.source.directories&quot;: [
&quot;C:\\Users\\Bob\\AppData\\Roaming\\JetBrains\\Rider2023.1\\resharper-host\\DecompilerCache&quot;,
&quot;C:\\Users\\Bob\\AppData\\Roaming\\JetBrains\\Rider2023.1\\resharper-host\\SourcesCache&quot;,
&quot;C:\\Users\\Bob\\AppData\\Local\\Symbols\\src&quot;
]
}
}</component>
<component name="RunManager" selected="发布到文件夹.Win">
<configuration name="Win" type="DotNetFolderPublish" factoryName="Publish to folder">
<riderPublish configuration="Release" delete_existing_files="true" platform="Any CPU" produce_single_file="true" runtime="win-x64" target_folder="$PROJECT_DIR$/bin/Release/net7.0/win-x64/publish" target_framework="net7.0" uuid_high="-3297046778019885416" uuid_low="-4999719437998725852" />
<method v="2" />
</configuration>
<configuration name="S3Update" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/bin/Debug/net7.0/S3Update.exe" />
<option name="PROGRAM_PARAMETERS" value="http src=1 test=2" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/bin/Debug/net7.0" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/S3Update.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value="net7.0" />
<method v="2">
<option name="Build" />
</method>
</configuration>
<list>
<item itemvalue=".NET 项目.S3Update" />
<item itemvalue="发布到文件夹.Win" />
</list>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="应用程序级" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="默认任务">
<changelist id="a22442a9-c22e-4925-9d61-0e7b123dd791" name="更改" comment="" />
<created>1686544575807</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1686544575807</updated>
<workItem from="1686544577339" duration="14232000" />
<workItem from="1686624244302" duration="14867000" />
<workItem from="1686708589206" duration="4021000" />
<workItem from="1686879973338" duration="137000" />
<workItem from="1686880397386" duration="11663000" />
<workItem from="1687137892768" duration="7503000" />
<workItem from="1687742766796" duration="2228000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="VcsManagerConfiguration">
<option name="CLEAR_INITIAL_COMMIT_MESSAGE" value="true" />
</component>
</project>

BIN
.vs/ACBuildService/v17/.suo Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,10 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACultureInfo_002Ecs_002Fl_003AC_0021_003FUsers_003FFIREBAT_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F56fad711d44d4d47bc9f3128de5ae30ac90920_003F14_003F78d2f3c4_003FCultureInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFile_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F5147b10c5a8c4522b56fba0a889139cfc8f908_003F1b_003F1029da80_003FFile_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFormOptions_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fbe70df8bcbbf4fdbb727e7194c8cd235e510_003F8a_003Fab2ee68d_003FFormOptions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AHttpUtility_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fbb2917bd97f04a3bbe43bb4d35dae565e908_003Fda_003F1ba0545f_003FHttpUtility_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALogLevel_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F3928f30d38a74d899dcf80c80e3944ccddc00_003F98_003F04c78b53_003FLogLevel_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ARoutePatternParser_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F3831116b66390bb3a7f83dd22df6aab756e1807336836727888e69a5b413a_003FRoutePatternParser_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AStorageClient_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F83d0691b2ff94d50bae98091380d202e27e00_003F8c_003Ff8d922d0_003FStorageClient_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AStorageClient_002Ecs_002Fl_003AC_0021_003FUsers_003FFIREBAT_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F83d0691b2ff94d50bae98091380d202e27e00_003F40_003Fdca6e351_003FStorageClient_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AString_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F5147b10c5a8c4522b56fba0a889139cfc8f908_003Fd9_003F39b4342b_003FString_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>

51
App.cs Normal file
View File

@@ -0,0 +1,51 @@
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Configuration;
using SqlSugar;
namespace ACBuildService;
public static class App
{
public static bool Running { get; set; }
public static AppSettingsConfigs Settings = new AppSettingsConfigs();
public static void Init()
{
Running = true;
LoadSetting();
DB.InitDb();
}
public static void LoadSetting()
{
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.Build();
var config = configuration.Get<AppSettingsConfigs>(); //绑定到实体
if (config != null)
{
Settings = config;
}
else
{
throw new IOException("配置文件读取失败");
}
}
#region Update
internal static void Update()
{
Time.Update();
}
internal static void LateUpdate()
{
Time.LateUpdate();
}
#endregion
}

29
AppSettingsConfigs.cs Normal file
View File

@@ -0,0 +1,29 @@
namespace ACBuildService;
public class AppSettingsConfigs
{
/// <summary>
/// 是否开启Debug模式
/// </summary>
public bool Debug { get; set; } = true;
/// <summary>
/// http服务地址
/// </summary>
public int HttpPort { get; set; }
/// <summary>
/// 是否开启代理
/// </summary>
public bool OpenProxy { get; set; }
/// <summary>
/// 代理地址
/// </summary>
public string ProxyHost { get; set; }
/// <summary>
/// 密码
/// </summary>
public string Password { get; set; }
}

47
BabyVideoService.csproj Normal file
View File

@@ -0,0 +1,47 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<RootNamespace>S3Update</RootNamespace>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<None Update="Temp\AppNotice.md">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="project-game-x-f720ef30f678.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="wwwroot\assets\bootstrap.min.css">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="wwwroot\assets\bootstrap.min.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="wwwroot\assets\jquery.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="wwwroot\home.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Downloader" Version="4.0.3" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="NLog" Version="5.2.0" />
<PackageReference Include="NLog.Web" Version="5.3.0" />
<PackageReference Include="RestSharp" Version="113.1.0" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.211" />
</ItemGroup>
</Project>

16
BabyVideoService.sln Normal file
View File

@@ -0,0 +1,16 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BabyVideoService", "BabyVideoService.csproj", "{D23E896C-8447-4E98-BA9D-6DA929786524}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D23E896C-8447-4E98-BA9D-6DA929786524}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D23E896C-8447-4E98-BA9D-6DA929786524}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D23E896C-8447-4E98-BA9D-6DA929786524}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D23E896C-8447-4E98-BA9D-6DA929786524}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,16 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAbstractDownloadService_002Ecs_002Fl_003AC_0021_003FUsers_003FFIREBAT_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F9492f6e132137ff2698b3268c9c422a578e49c5893a8371ed19fb0695fd5925_003FAbstractDownloadService_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AApiBehaviorApplicationModelProvider_002Ecs_002Fl_003AC_0021_003FUsers_003FFIREBAT_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F59cd7a17e4fcffda1ba3687b386939b4e155ba482f85c8fe9993bc8373d7f93_003FApiBehaviorApplicationModelProvider_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AChannel_00602_002Ecs_002Fl_003AC_0021_003FUsers_003FFIREBAT_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ffb4ef246b9c147be90cf03bb4a45b1ce20938_003Fa1_003F2cc30e09_003FChannel_00602_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AControllerBase_002Ecs_002Fl_003AC_0021_003FUsers_003FFIREBAT_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fbbcd33dffc534f7b961b0763775ac78b1de938_003Ff9_003F08f50080_003FControllerBase_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACultureInfo_002Ecs_002Fl_003AC_0021_003FUsers_003FFIREBAT_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F56fad711d44d4d47bc9f3128de5ae30ac90920_003F14_003F78d2f3c4_003FCultureInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEndpointRouteBuilderExtensions_002Ecs_002Fl_003AC_0021_003FUsers_003FFIREBAT_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fba9546ad90c246358f902e6522c620ddc9910_003F71_003F404fbeda_003FEndpointRouteBuilderExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFile_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F5147b10c5a8c4522b56fba0a889139cfc8f908_003F1b_003F1029da80_003FFile_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFormOptions_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fbe70df8bcbbf4fdbb727e7194c8cd235e510_003F8a_003Fab2ee68d_003FFormOptions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AHttpUtility_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fbb2917bd97f04a3bbe43bb4d35dae565e908_003Fda_003F1ba0545f_003FHttpUtility_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AISugarQueryable_00601_002Ecs_002Fl_003AC_0021_003FUsers_003FFIREBAT_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F759e68a39ef64bf8b91a2ed761bcd8a92e3400_003F05_003F7b368181_003FISugarQueryable_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALogLevel_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F3928f30d38a74d899dcf80c80e3944ccddc00_003F98_003F04c78b53_003FLogLevel_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ARoutePatternParser_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F3831116b66390bb3a7f83dd22df6aab756e1807336836727888e69a5b413a_003FRoutePatternParser_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AStorageClient_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F83d0691b2ff94d50bae98091380d202e27e00_003F8c_003Ff8d922d0_003FStorageClient_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AStorageClient_002Ecs_002Fl_003AC_0021_003FUsers_003FFIREBAT_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F83d0691b2ff94d50bae98091380d202e27e00_003F40_003Fdca6e351_003FStorageClient_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AString_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F5147b10c5a8c4522b56fba0a889139cfc8f908_003Fd9_003F39b4342b_003FString_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>

View File

@@ -0,0 +1,106 @@
namespace NBC
{
public class ParallelTaskCollection : TaskCollection
{
private int _currentIndex;
/// <summary>
/// 最大并行数量 (默认为9)
/// </summary>
public int ParallelNum = 9;
protected override NTaskStatus RunTasksAndCheckIfDone()
{
var st = NTaskStatus.Running;
if (CurRunTask.Count < ParallelNum && _currentIndex < RawList.Count)
{
var num = ParallelNum - CurRunTask.Count;
for (var index = 0; index < num; index++)
if (_currentIndex < RawList.Count)
{
CurRunTask.Add(RawList[_currentIndex]);
_currentIndex += 1;
}
}
for (var index = 0; index < CurrentTask.Count; index++)
{
var element = CurrentTask[index];
var childSt = element.Process();
if (FailBreak && childSt == NTaskStatus.Fail)
{
_errorMsg = element.ErrorMsg;
st = NTaskStatus.Fail;
break;
}
if (childSt == NTaskStatus.Success || childSt == NTaskStatus.Fail)
{
CurrentTask.RemoveAt(index);
index--;
FinishList.Add(element);
}
}
if (FinishList.Count >= RawList.Count) st = NTaskStatus.Success;
// for (var index = 0; index < CurrentTask.Count; index++)
// {
// var element = CurrentTask[index];
// var childSt = element.Process();
//
// if (childSt >= Status.Success)
// {
// if (FailBreak && childSt == Status.Fail)
// {
// _errorMsg = element.ErrorMsg;
// st = Status.Fail;
// }
//
// CurrentTask.RemoveAt(index);
// index--;
// FinishList.Add(element);
// }
// }
//
// if (FinishList.Count >= RawList.Count)
// {
// st = Status.Success;
// }
// else if (CurRunTask.Count < ParallelNum && _currentIndex < RawList.Count)
// {
// var num = ParallelNum - CurRunTask.Count;
// for (var index = 0; index < num; index++)
// {
// if (_currentIndex < RawList.Count)
// {
// CurRunTask.Add(RawList[_currentIndex]);
// _currentIndex += 1;
// }
// }
// }
return st;
}
public override void Reset()
{
base.Reset();
_currentIndex = 0;
}
public override void Stop()
{
base.Stop();
_currentIndex = 0;
}
public override void Clear()
{
base.Clear();
_currentIndex = 0;
}
}
}

View File

@@ -0,0 +1,113 @@
namespace NBC
{
public class SequenceTaskCollection : TaskCollection
{
private int _currentIndex;
public override string Info
{
get
{
if (CurrentTask != null && CurrentTask.Count > 0) return CurrentTask[0].Info;
return TaskInfo;
}
}
protected override NTaskStatus RunTasksAndCheckIfDone()
{
var st = NTaskStatus.Running;
if (RawList.Count > 0)
{
if (CurRunTask.Count == 0 && _currentIndex < RawList.Count) CurRunTask.Add(RawList[_currentIndex]);
NTaskStatus curSt;
do
{
var childTask = CurRunTask[0];
curSt = childTask.Process();
if (curSt >= NTaskStatus.Success)
{
if (FailBreak && curSt == NTaskStatus.Fail)
{
_errorMsg = childTask.ErrorMsg;
st = NTaskStatus.Fail;
break;
}
FinishList.Add(childTask);
CurRunTask.RemoveAt(0);
_currentIndex++;
if (_currentIndex < RawList.Count)
CurRunTask.Add(RawList[_currentIndex]);
}
} while (curSt >= NTaskStatus.Success && CurRunTask.Count > 0);
if (FinishList.Count == RawList.Count)
st = NTaskStatus.Success;
}
else
{
st = NTaskStatus.Success;
}
// if (RawList.Count > 0)
// {
// if (FinishList.Count == RawList.Count)
// {
// st = Status.Success;
// }
// else
// {
// if (CurRunTask.Count == 0)
// {
// CurRunTask.Add(RawList[_currentIndex]);
// }
//
// var childTask = CurRunTask[0];
// var curSt = childTask.Process();
//
//
// if (curSt >= Status.Success)
// {
// if (FailBreak && curSt == Status.Fail)
// {
// _errorMsg = childTask.ErrorMsg;
// st = Status.Fail;
// }
//
// FinishList.Add(childTask);
// CurRunTask.RemoveAt(0);
// _currentIndex++;
// }
// }
// }
// else
// {
// st = Status.Success;
// }
return st;
}
public override void Reset()
{
base.Reset();
_currentIndex = 0;
}
public override void Stop()
{
base.Stop();
_currentIndex = 0;
}
public override void Clear()
{
base.Clear();
_currentIndex = 0;
}
}
}

View File

@@ -0,0 +1,88 @@
using System.Collections.Generic;
namespace NBC
{
public abstract class TaskCollection : NTask, ITaskCollection
{
protected readonly List<ITask> CurRunTask;
protected readonly List<ITask> FinishList;
protected readonly List<ITask> RawList;
public TaskCollection(string taskInfo = "")
{
RawList = new List<ITask>();
FinishList = new List<ITask>();
CurRunTask = new List<ITask>();
TaskInfo = taskInfo;
Status = NTaskStatus.None;
}
public virtual int Count => RawList.Count + FinishList.Count;
/// <summary>
/// 任务失败中断任务链
/// </summary>
public virtual bool FailBreak { get; set; }
public List<ITask> CurrentTask => CurRunTask;
public override float Progress
{
get
{
if (Status == NTaskStatus.Success) return 1;
if (Status == NTaskStatus.None) return 0;
var finishCount = FinishList.Count;
for (var index = 0; index < CurRunTask.Count; index++)
{
var element = CurRunTask[index];
finishCount += (int)element.Progress;
}
return finishCount * 1f / RawList.Count;
}
}
public ITaskCollection AddTask(ITask task)
{
RawList.Add(task);
return this;
}
public override void Reset()
{
FinishList.Clear();
CurrentTask.Clear();
Status = NTaskStatus.None;
for (int i = 0, len = RawList.Count; i < len; i++) RawList[i].Reset();
}
public override void Stop()
{
FinishList.Clear();
Status = NTaskStatus.None;
for (var i = 0; i < CurRunTask.Count; i++)
{
var task = CurrentTask[i];
task.Stop();
}
}
public virtual void Clear()
{
FinishList.Clear();
RawList.Clear();
Status = NTaskStatus.None;
}
protected override NTaskStatus OnProcess()
{
return RunTasksAndCheckIfDone();
}
protected abstract NTaskStatus RunTasksAndCheckIfDone();
}
}

View File

@@ -0,0 +1,10 @@
namespace NBC
{
/// <summary>
/// 进度
/// </summary>
public interface IProcess
{
NTaskStatus Process();
}
}

View File

@@ -0,0 +1,49 @@
namespace NBC
{
public interface IRunner
{
/// <summary>
/// 是否暂停
/// </summary>
bool IsPaused { get; set; }
/// <summary>
/// 是否已经终止了
/// </summary>
bool IsKilled { get; }
/// <summary>
/// 当前运行的任务数量
/// </summary>
int RunningTaskNum { get; }
/// <summary>
/// 准备执行的任务数量
/// </summary>
int NeedRunTaskNum { get; }
/// <summary>
/// 执行一个任务
/// </summary>
/// <param name="task">任务对象</param>
void Run(ITask task);
void Process();
/// <summary>
/// 停止任务
/// </summary>
/// <param name="task">任务对象</param>
void StopTask(ITask task);
/// <summary>
/// 停止所有任务
/// </summary>
void StopAllTask();
/// <summary>
/// 终止任务
/// </summary>
void ShutDown();
}
}

View File

@@ -0,0 +1,77 @@
using System;
using System.Collections;
namespace NBC
{
public interface ITask : IProcess, IEnumerator
{
NTaskStatus Status { get; }
/// <summary>
/// 当前任务的信息
/// </summary>
string Info { get; }
/// <summary>
/// 错误信息
/// </summary>
string ErrorMsg { get; }
/// <summary>
/// 当前任务的进度
/// </summary>
float Progress { get; }
/// <summary>
/// 任务正在执行
/// </summary>
bool IsRunning { get; }
/// <summary>
/// 任务是否执行完成
/// </summary>
bool IsDone { get; }
/// <summary>
/// 任务参数
/// </summary>
/// <param name="argsName"></param>
object this[string argsName] { get; set; }
/// <summary>
/// 停止任务
/// </summary>
void Stop();
/// <summary>
/// 任务开始回调
/// </summary>
/// <param name="callback"></param>
/// <param name="cover"></param>
/// <returns></returns>
ITask OnStarted(Action<ITask> callback, bool cover = false);
/// <summary>
/// 任务执行回调
/// </summary>
/// <param name="callback"></param>
/// <param name="cover"></param>
/// <returns></returns>
ITask OnUpdated(Action<ITask> callback, bool cover = false);
/// <summary>
/// 任务完成回调
/// </summary>
/// <param name="callback"></param>
/// <param name="cover"></param>
/// <returns></returns>
ITask OnCompleted(Action<ITask> callback, bool cover = false);
/// <summary>
/// 运行任务
/// </summary>
/// <param name="runner">任务运行器</param>
void Run(IRunner runner);
}
}

View File

@@ -0,0 +1,24 @@
using System.Collections.Generic;
namespace NBC
{
public interface ITaskCollection : ITask
{
/// <summary>
/// 当前运行的任务堆栈
/// </summary>
List<ITask> CurrentTask { get; }
/// <summary>
/// 添加一个任务
/// </summary>
/// <param name="task"></param>
/// <returns></returns>
ITaskCollection AddTask(ITask task);
/// <summary>
/// 清理任务列表
/// </summary>
void Clear();
}
}

View File

@@ -0,0 +1,7 @@
namespace NBC
{
public interface ITaskRun
{
void Run(IRunner runner);
}
}

198
Core/Task/NTask.cs Normal file
View File

@@ -0,0 +1,198 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace NBC
{
public abstract class NTask : ITask
{
protected readonly Dictionary<string, object> _argsDic = new();
protected string _errorMsg;
protected float _progress;
protected string TaskInfo;
public virtual string Info
{
get => TaskInfo;
set => TaskInfo = value;
}
public NTaskStatus Status { get; protected set; } = NTaskStatus.None;
public virtual string ErrorMsg => _errorMsg;
public virtual float Progress => _progress;
public virtual bool IsRunning => Status == NTaskStatus.Running;
public virtual bool IsDone => Status == NTaskStatus.Success || Status == NTaskStatus.Fail;
public object this[string argsName]
{
get
{
if (_argsDic.TryGetValue(argsName, out var args)) return args;
return null;
}
set => _argsDic[argsName] = value;
}
public virtual void Stop()
{
Status = NTaskStatus.None;
}
public virtual void Run(IRunner runner)
{
Reset();
runner?.Run(this);
}
public NTaskStatus Process()
{
if (Status == NTaskStatus.None) Start();
Status = OnProcess();
CallUpdateListener();
if (Status == NTaskStatus.Success)
{
_progress = 1;
CallCompleteListener(Status);
}
else if (Status == NTaskStatus.Fail)
{
_progress = 1;
CallCompleteListener(Status);
}
return Status;
}
protected virtual void OnStart()
{
}
protected virtual NTaskStatus OnProcess()
{
return Status;
}
protected void Finish()
{
_progress = 1;
Status = NTaskStatus.Success;
}
protected void Fail(string message)
{
_progress = 1;
Status = NTaskStatus.Fail;
_errorMsg = message;
}
private void Start()
{
Reset();
Status = NTaskStatus.Running;
_progress = 0;
OnStart();
CallStartListener();
}
#region
protected event Action<ITask> OnStartListener;
protected event Action<ITask> OnCompleteListener;
protected event Action<ITask> OnUpdateListener;
public ITask OnStarted(Action<ITask> callback, bool cover = false)
{
if (cover)
OnStartListener = callback;
else
OnStartListener += callback;
return this;
}
public ITask OnUpdated(Action<ITask> callback, bool cover = false)
{
if (cover)
OnUpdateListener = callback;
else
OnUpdateListener += callback;
return this;
}
public ITask OnCompleted(Action<ITask> callback, bool cover = false)
{
if (cover)
OnCompleteListener = callback;
else
OnCompleteListener += callback;
return this;
}
protected void CallStartListener()
{
OnStartListener?.Invoke(this);
}
protected void CallCompleteListener(NTaskStatus taskStatus)
{
OnCompleteListener?.Invoke(this);
_taskCompletionSource?.TrySetResult(null);
}
protected void CallUpdateListener()
{
OnUpdateListener?.Invoke(this);
}
#endregion
#region
private TaskCompletionSource<object> _taskCompletionSource;
/// <summary>
/// 异步操作任务
/// </summary>
public Task Task
{
get
{
if (_taskCompletionSource == null)
{
_taskCompletionSource = new TaskCompletionSource<object>();
if (IsDone)
_taskCompletionSource.SetResult(null);
}
return _taskCompletionSource.Task;
}
}
#endregion
#region IEnumerator
bool IEnumerator.MoveNext()
{
return !IsDone;
}
public virtual void Reset()
{
Status = NTaskStatus.None;
}
object IEnumerator.Current => null;
#endregion
}
}

25
Core/Task/NTaskStatus.cs Normal file
View File

@@ -0,0 +1,25 @@
namespace NBC
{
public enum NTaskStatus
{
/// <summary>
/// 任务还未执行
/// </summary>
None = 0,
/// <summary>
/// 任务运行
/// </summary>
Running,
/// <summary>
/// 任务执行成功
/// </summary>
Success,
/// <summary>
/// 任务执行失败
/// </summary>
Fail
}
}

View File

@@ -0,0 +1,18 @@
namespace NBC
{
/// <summary>
/// 操作信息类
/// </summary>
public class FlushingOperation
{
/// <summary>
/// 是否被终结
/// </summary>
public bool Kill;
/// <summary>
/// 是否暂停
/// </summary>
public bool Paused;
}
}

View File

@@ -0,0 +1,89 @@
using System.Collections.Generic;
namespace NBC
{
public class Runner : IRunner
{
/// <summary>
/// 当前运行的任务
/// </summary>
protected readonly List<ITask> Coroutines = new();
/// <summary>
/// 当前操作的信息
/// </summary>
protected readonly FlushingOperation FlushingOperation = new();
/// <summary>
/// 准备要运行的任务
/// </summary>
protected readonly Queue<ITask> ReadyTask = new();
public bool IsPaused
{
get => FlushingOperation.Paused;
set => FlushingOperation.Paused = value;
}
public bool IsKilled => FlushingOperation.Kill;
public int RunningTaskNum => Coroutines.Count;
public int NeedRunTaskNum => ReadyTask.Count;
public virtual void Run(ITask task)
{
ReadyTask.Enqueue(task);
}
public virtual void Process()
{
var count = ReadyTask.Count;
for (var i = 0; i < count; i++)
{
var task = ReadyTask.Dequeue();
Coroutines.Add(task);
}
if (Coroutines.Count < 1) return;
var index = 0;
bool mustExit;
do
{
var childTask = Coroutines[index];
var st = childTask.Process();
if (st >= NTaskStatus.Success)
Coroutines.Remove(childTask);
else
index++;
mustExit = Coroutines.Count == 0 || index >= Coroutines.Count;
} while (!mustExit);
}
public virtual void StopTask(ITask task)
{
var index = Coroutines.IndexOf(task);
if (index != -1)
{
var t = Coroutines[index];
t.Stop();
Coroutines.RemoveAt(index);
}
}
public virtual void StopAllTask()
{
ReadyTask.Clear();
for (var i = 0; i < Coroutines.Count; i++) Coroutines[i].Stop();
Coroutines.Clear();
}
public virtual void ShutDown()
{
IsPaused = false;
FlushingOperation.Kill = true;
StopAllTask();
}
}
}

View File

@@ -0,0 +1,77 @@
using System.Collections.Generic;
namespace NBC
{
public class RunnerProcess : IProcess
{
/// <summary>
/// 当前运行的任务
/// </summary>
protected readonly List<ITask> Coroutines;
/// <summary>
/// 当前操作的信息
/// </summary>
protected readonly FlushingOperation FlushingOperation;
/// <summary>
/// 准备要运行的任务
/// </summary>
protected readonly Queue<ITask> ReadyTask;
/// <summary>
/// 进程名称
/// </summary>
protected string Name;
public RunnerProcess(string name, List<ITask> coroutines, Queue<ITask> readyTask, FlushingOperation op)
{
Name = name;
Coroutines = coroutines;
ReadyTask = readyTask;
FlushingOperation = op;
}
public NTaskStatus Process()
{
var flag = false;
if (FlushingOperation.Kill)
{
flag = true;
}
else
{
for (var index = 0; index < ReadyTask.Count; index++)
{
// var task = ReadyTask[0];
var task = ReadyTask.Dequeue();
Coroutines.Add(task);
// ReadyTask.RemoveAt(0);
}
if (Coroutines.Count == 0 || FlushingOperation.Paused)
{
flag = false;
}
else
{
var index = 0;
var mustExit = false;
do
{
var childTask = Coroutines[index];
var st = childTask.Process();
if (st >= NTaskStatus.Success)
Coroutines.RemoveAt(index); //.splice(index, 1);
else
index++;
mustExit = Coroutines.Count == 0 || index >= Coroutines.Count;
} while (!mustExit);
}
}
return flag ? NTaskStatus.Success : NTaskStatus.Running;
}
}
}

54
Core/TaskRunner.cs Normal file
View File

@@ -0,0 +1,54 @@
using NBC;
namespace ACBuildService;
/// <summary>
/// 默认任务执行器
/// </summary>
public class TaskRunner : Runner
{
private static TaskRunner mUpdateRunner;
public static TaskRunner Scheduler => mUpdateRunner ??= new TaskRunner();
private readonly List<IProcess> _updateRoutines = new List<IProcess>();
public event Action OnUpdate;
public TaskRunner()
{
Time.OnUpdate += Update;
StartCoroutine(new RunnerProcess("TaskRunner", Coroutines, ReadyTask, FlushingOperation));
}
private void StartCoroutine(IProcess process)
{
var routines = _updateRoutines;
if (!routines.Contains(process))
{
routines.Add(process);
}
}
private void Update()
{
ExecuteRoutines(_updateRoutines);
OnUpdate?.Invoke();
}
private void ExecuteRoutines(List<IProcess> arr)
{
if (arr != null && arr.Count > 0)
{
for (var index = 0; index < arr.Count; index++)
{
var task = arr[index];
var st = task.Process();
if (st == NTaskStatus.Success)
{
arr.RemoveAt(index);
index--;
}
}
}
}
}

88
Core/Time.cs Normal file
View File

@@ -0,0 +1,88 @@
namespace ACBuildService;
public static class Time
{
public const long OneDay = 86400000;
public const long Hour = 3600000;
public const long Minute = 60000;
private static TimeInfo _timeInfo = new TimeInfo();
public static long Now => _timeInfo.Now();
public static long NowSeconds => Now / 1000;
public static DateTime DateTimeNow => DateTime.Now;
public static long FrameTime => _timeInfo.FrameTime;
public static event Action OnUpdate;
public static event Action OnLateUpdate;
internal static void Awake()
{
}
internal static void Update()
{
_timeInfo.Update();
OnUpdate?.Invoke();
}
internal static void LateUpdate()
{
OnLateUpdate?.Invoke();
}
}
public class TimeInfo
{
private int timeZone;
public int TimeZone
{
get => timeZone;
set
{
timeZone = value;
dt = dt1970.AddHours(TimeZone);
}
}
private readonly DateTime dt1970;
private DateTime dt;
public long FrameTime;
public TimeInfo()
{
dt1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
dt = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
FrameTime = Now();
}
public void Update()
{
FrameTime = Now();
}
/// <summary>
/// 根据时间戳获取时间
/// </summary>
public DateTime ToDateTime(long timeStamp)
{
return dt.AddTicks(timeStamp * 10000);
}
// 线程安全
public long Now()
{
return (DateTime.UtcNow.Ticks - dt1970.Ticks) / 10000;
}
public long Transition(DateTime d)
{
return (d.Ticks - dt.Ticks) / 10000;
}
}

34
Db/DB.cs Normal file
View File

@@ -0,0 +1,34 @@
using SqlSugar;
namespace ACBuildService;
public static class DB
{
public static SqlSugarClient Main { get; private set; }
public static void InitDb()
{
//创建数据库对象 (用法和EF Dappper一样通过new保证线程安全)
Main = new SqlSugarClient(new ConnectionConfig()
{
ConnectionString = "datasource=data.db",
DbType = DbType.Sqlite,
IsAutoCloseConnection = true
},
db =>
{
// db.Aop.OnLogExecuting = (sql, pars) =>
// {
// //获取原生SQL推荐 5.1.4.63 性能OK
// Console.WriteLine(UtilMethods.GetNativeSql(sql, pars));
// };
});
//建库
Main.DbMaintenance.CreateDatabase();
//建表
Main.CodeFirst.InitTables<VideoTable>();
Main.CodeFirst.InitTables<DeviceTable>();
}
}

38
Db/Tables/DeviceTable.cs Normal file
View File

@@ -0,0 +1,38 @@
using SqlSugar;
namespace ACBuildService;
[SugarTable("device")]
public class DeviceTable
{
/// <summary>
/// 设备id
/// </summary>
[SugarColumn(IsPrimaryKey = true)]
public string Id { get; set; }
/// <summary>
/// 观看视频
/// </summary>
public int WatchId { get; set; }
/// <summary>
/// 设备名字
/// </summary>
public string Name { get; set; }
/// <summary>
/// 总观看时长
/// </summary>
public long TotalTime { get; set; }
/// <summary>
/// 今日观看时长
/// </summary>
public long TodayTime { get; set; }
/// <summary>
/// 最后更新时间,用于判定今日时长和跨天
/// </summary>
public DateTime LastTime { get; set; }
}

59
Db/Tables/VideoTable.cs Normal file
View File

@@ -0,0 +1,59 @@
using System.ComponentModel.DataAnnotations.Schema;
using SqlSugar;
namespace ACBuildService;
[SugarTable("video")]
public class VideoTable
{
//数据是自增需要加上IsIdentity
//数据库是主键需要加上IsPrimaryKey
//注意要完全和数据库一致2个属性
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
/// <summary>
/// 源视频地址
/// </summary>
[SugarColumn(ColumnDataType = "text")]
public string SourcePath { get; set; }
/// <summary>
/// 本地存储地址
/// </summary>
[SugarColumn(ColumnDataType = "text")]
public string FilePath { get; set; }
/// <summary>
/// 作者
/// </summary>
[SugarColumn(ColumnDataType = "text")]
public string AuthorName { get; set; }
/// <summary>
/// 标题
/// </summary>
[SugarColumn(ColumnDataType = "text")]
public string Title { get; set; }
/// <summary>
/// 简介
/// </summary>
[SugarColumn(ColumnDataType = "text")]
public string Desc { get; set; }
/// <summary>
/// 视频时长
/// </summary>
[SugarColumn(ColumnDataType = "text")]
public string Duration { get; set; }
public int Like { get; set; }
public int Collect { get; set; }
public int Message { get; set; }
public int Share { get; set; }
public DateTime CreateTime { get; set; }
}

11
Http/ErrorCode.cs Normal file
View File

@@ -0,0 +1,11 @@
namespace ACBuildService;
public enum ErrorCode
{
/// <summary>
/// 成功
/// </summary>
Success = 0,
ArgsError = 1,
}

View File

@@ -0,0 +1,24 @@
namespace ACBuildService;
public static class DateTimeExtensions
{
public static bool IsSameDay(this DateTime date1, DateTime date2)
{
return date1.Date == date2.Date;
}
public static bool IsToday(this DateTime date)
{
return date.Date == DateTime.Today;
}
public static bool IsYesterday(this DateTime date)
{
return date.Date == DateTime.Today.AddDays(-1);
}
public static bool IsTomorrow(this DateTime date)
{
return date.Date == DateTime.Today.AddDays(1);
}
}

View File

@@ -0,0 +1,25 @@
using System.Text;
namespace ACBuildService;
public static class FileNameExtensions
{
/// <summary>
/// 替换非法字符
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
public static string ReplaceInvalidCharacters(this string fileName)
{
var invalidChars = Path.GetInvalidFileNameChars();
var replacedFileName = new StringBuilder();
foreach (var c in fileName)
{
replacedFileName.Append(!invalidChars.Contains(c) ? c : '#');
}
return replacedFileName.ToString();
}
}

View File

@@ -0,0 +1,49 @@
using Microsoft.AspNetCore.Http;
namespace ACBuildService;
public static class HttpContextExtension
{
public static async Task Error(this HttpContext context, ErrorCode code, string msg = "")
{
var res = new ResponseData<string>
{
Code = (int)code,
Data = msg
};
await context.WriteJson(res);
}
public static async Task Success(this HttpContext context)
{
var res = new ResponseData<string>
{
Code = 0,
Data = string.Empty
};
await context.WriteJson(res);
}
public static async Task Success<T>(this HttpContext context, T data)
{
var res = new ResponseData<T>
{
Code = 0,
Data = data
};
await context.WriteJson(res);
}
public static async Task WriteJson<T>(this HttpContext context, T obj)
{
context.Response.ContentType = "application/json";
if (obj is string st)
{
await context.Response.WriteAsync(st);
}
else
{
await context.Response.WriteAsync(JSON.Serialize(obj));
}
}
}

75
Http/HttpService.cs Normal file
View File

@@ -0,0 +1,75 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
namespace ACBuildService;
public static class HttpService
{
public static WebApplication App { get; private set; }
public static readonly string BaseRootPath = $"{AppContext.BaseDirectory}wwwroot/";
public static async Task Start()
{
var builder = WebApplication.CreateBuilder();
// 添加控制器服务
builder.Services.AddControllers();
builder.WebHost.UseKestrel(options =>
{
//设置最大1G, 这里的单位是byte
options.Limits.MaxRequestBodySize = int.MaxValue;
// HTTP
if (ACBuildService.App.Settings.HttpPort > 0)
{
options.ListenAnyIP(ACBuildService.App.Settings.HttpPort);
}
})
.ConfigureLogging(logging => { logging.SetMinimumLevel(LogLevel.Error); });
builder.Services.Configure<FormOptions>(x =>
{
x.ValueLengthLimit = int.MaxValue;
x.MultipartBodyLengthLimit = int.MaxValue;
});
var contentTypeProvider = new FileExtensionContentTypeProvider();
contentTypeProvider.Mappings.Add(".bundle", "application/octet-stream");
contentTypeProvider.Mappings.Add(".meta", "application/octet-stream");
contentTypeProvider.Mappings.Add(".bytes", "application/octet-stream");
Log.Info($"wwwroot={BaseRootPath}");
App = builder.Build();
// 针对 files 文件夹配置
var fileServerOptions = new FileServerOptions
{
RequestPath = "/files",
EnableDirectoryBrowsing = true,
FileProvider = new PhysicalFileProvider(BaseRootPath),
StaticFileOptions =
{
ContentTypeProvider = contentTypeProvider
}
};
App.UseFileServer(fileServerOptions);
// 使用控制器路由
App.MapControllers();
// 不再需要手动映射路由
// App.MapGet("/", HttpHandler.Index);
// App.MapPost("/api", HttpHandler.Api);
await App.StartAsync();
Log.Info("http 服务启动成功");
}
}

161
Http/Page/ApiController.cs Normal file
View File

@@ -0,0 +1,161 @@
// Controllers/ApiController.cs
using ACBuildService.Filters;
using Microsoft.AspNetCore.Mvc;
namespace ACBuildService.Controllers;
[ApiController]
[Route("api")]
[SignValidation]
public class ApiController : NBControllerBase
{
#region
[HttpPost("list")]
public async Task<IActionResult> List([FromForm] PageFormArgs data)
{
try
{
if (data.Page < 1) data.Page = 1;
if (data.PageSize < 1) data.PageSize = 50;
int totalCount = 0;
var list = DB.Main.Queryable<VideoTable>().ToPageList(data.Page, data.PageSize, ref totalCount);
List<VideoRetData> retList = [];
var retData = new PageListData<VideoRetData>
{
TotalCount = totalCount,
Page = data.Page,
List = retList,
PageSize = data.PageSize
};
foreach (var video in list)
{
var r = new VideoRetData
{
Id = video.Id,
FilePath = video.FilePath,
Title = video.Title,
Desc = video.Desc,
Like = video.Like,
Message = video.Message,
Share = video.Share,
Collect = video.Collect,
AuthorName = video.AuthorName
};
retList.Add(r);
}
await Task.CompletedTask;
return Success(retData);
}
catch (Exception ex)
{
return StatusCode(500, new { error = ex.Message });
}
}
[HttpPost("add")]
public async Task<IActionResult> Add([FromForm] AddFormArgs data)
{
VideoDownload.Add(data.Text);
await Task.CompletedTask;
return Success();
}
[HttpPost("edit")]
public async Task<IActionResult> Edit([FromForm] EditFormArgs data)
{
await Task.CompletedTask;
DB.Main.Updateable(new VideoTable()
{
Id = data.Id,
Title = data.Title,
Desc = data.Desc,
Like = data.Like,
Message = data.Message,
Share = data.Share,
Collect = data.Collect,
AuthorName = data.AuthorName
}).ExecuteCommand();
return Success();
}
[HttpPost("remove")]
public async Task<IActionResult> Remove([FromForm] IdFormArgs data)
{
var video = await DB.Main.Queryable<VideoTable>().FirstAsync(t => t.Id == data.Id);
if (video != null)
{
//删除本地文件
var filePath = FileUtil.GetVideoPath(video.FilePath);
if (System.IO.File.Exists(filePath))
{
System.IO.File.Delete(filePath);
}
}
await DB.Main.Deleteable<VideoTable>().Where(it => it.Id == data.Id).ExecuteCommandAsync();
return Success();
}
[HttpPost("info")]
public async Task<IActionResult> Info()
{
await Task.CompletedTask;
var list = DB.Main.Queryable<DeviceTable>().ToList();
return Success(list);
}
#endregion
#region
[HttpPost("device")]
public async Task<IActionResult> Device([FromForm] DeviceFormArgs data)
{
var now = DateTime.Now.AddDays(-1);
var device = await DB.Main.Queryable<DeviceTable>().FirstAsync(t => t.Id == data.Device);
if (device == null)
{
device = new DeviceTable
{
Id = data.Device,
Name = data.DeviceName,
WatchId = data.Id,
TotalTime = 0,
TodayTime = data.Time,
LastTime = now
};
await DB.Main.Insertable(device).ExecuteCommandAsync();
}
if (!now.IsSameDay(device.LastTime))
{
device.TodayTime = data.Time; //重置今日时长
}
else
{
device.TodayTime += data.Time; //今日时长增加
}
device.WatchId = data.Id;
device.TotalTime += data.Time;
device.LastTime = now;
await DB.Main.Updateable(device).ExecuteCommandAsync();
return Success();
}
#endregion
}

68
Http/Page/Args.cs Normal file
View File

@@ -0,0 +1,68 @@
namespace ACBuildService.Controllers;
public class BaseFormArgs
{
public string Sign { get; set; } = string.Empty;
}
public class PageFormArgs : BaseFormArgs
{
public int Page { get; set; }
public int PageSize { get; set; }
}
public class AddFormArgs : BaseFormArgs
{
public string Text { get; set; }
}
public class IdFormArgs : BaseFormArgs
{
public int Id { get; set; }
}
public class EditFormArgs : VideoRetData
{
public string Sign { get; set; } = string.Empty;
}
public class DeviceFormArgs : IdFormArgs
{
/// <summary>
/// 设备id
/// </summary>
public string Device { get; set; }
/// <summary>
/// 设备名称
/// </summary>
public string DeviceName { get; set; }
/// <summary>
/// 观看时长,离上次
/// </summary>
public long Time { get; set; }
}
[Serializable]
public class PageListData<T>
{
public int TotalCount { get; set; }
public int Page { get; set; }
public int PageSize { get; set; }
public List<T> List { get; set; }
}
[Serializable]
public class VideoRetData
{
public int Id { get; set; }
public string FilePath { get; set; }
public string Title { get; set; }
public string Desc { get; set; }
public int Like { get; set; }
public int Message { get; set; }
public int Share { get; set; }
public int Collect { get; set; }
public string AuthorName { get; set; }
}

View File

@@ -0,0 +1,14 @@
using Microsoft.AspNetCore.Mvc;
namespace ACBuildService.Controllers;
[ApiController]
[Route("/")]
public class HomeController : NBControllerBase
{
[HttpGet("/")]
public IActionResult Index()
{
return Content("service running");
}
}

View File

@@ -0,0 +1,36 @@
using Microsoft.AspNetCore.Mvc;
namespace ACBuildService.Controllers;
public abstract class NBControllerBase : ControllerBase
{
public OkObjectResult Error(ErrorCode code, string msg = "")
{
var res = new ResponseData<string>
{
Code = (int)code,
Data = msg
};
return Ok(res);
}
public OkObjectResult Success()
{
var res = new ResponseData<string>
{
Code = 0,
Data = string.Empty
};
return Ok(res);
}
public OkObjectResult Success<T>(T data)
{
var res = new ResponseData<T>
{
Code = 0,
Data = data
};
return Ok(res);
}
}

9
Http/ResponseData.cs Normal file
View File

@@ -0,0 +1,9 @@
using System.Text.Json.Serialization;
namespace ACBuildService;
public class ResponseData<T>
{
[JsonPropertyName("code")] public int Code { get; set; }
[JsonPropertyName("data")] public T Data { get; set; }
}

View File

@@ -0,0 +1,58 @@
// Filters/SignValidationFilter.cs
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
namespace ACBuildService.Filters;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class SignValidationAttribute : Attribute, IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// 检查请求是否包含 form-data
if (context.HttpContext.Request.HasFormContentType)
{
var form = context.HttpContext.Request.Form;
// 尝试从表单中获取 Sign
if (form.TryGetValue("Sign", out var signValue))
{
if (!signValue.ToString().Equals(App.Settings.Password))
{
// 验证失败,直接返回错误
context.Result = new ObjectResult(new ResponseData<string>
{
Code = (int)ErrorCode.ArgsError,
Data = "sign is error"
});
return;
}
}
else
{
// 没有 Sign 字段
context.Result = new ObjectResult(new ResponseData<string>
{
Code = (int)ErrorCode.ArgsError,
Data = "sign is null"
});
return;
}
}
else
{
context.Result = new ObjectResult(new ResponseData<string>
{
Code = (int)ErrorCode.ArgsError,
Data = "请求格式必须是 form-data"
});
return;
}
// 验证通过,继续执行 Action
await next();
}
}

64
JSON/JSON.cs Normal file
View File

@@ -0,0 +1,64 @@
// MIT License
//
// Copyright (c) 2020-2023 BobSong, nobug.cn Co.,Ltd and Contributors
//
// 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.
using System.Text.Json;
namespace ACBuildService;
/// <summary>
/// JSON 静态帮助类
/// </summary>
public static class JSON
{
/// <summary>
/// 序列化对象
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string Serialize(object value)
{
return JsonSerializer.Serialize(value);
}
/// <summary>
/// 反序列化字符串
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="json"></param>
/// <returns></returns>
public static T Deserialize<T>(string json)
{
return JsonSerializer.Deserialize<T>(json);
}
/// <summary>
/// 反序列化字符串
/// </summary>
/// <param name="type"></param>
/// <param name="json"></param>
/// <returns></returns>
public static object Deserialize<T>(Type type, string json)
{
return JsonSerializer.Deserialize(json, type);
}
}

17
Log/ILog.cs Normal file
View File

@@ -0,0 +1,17 @@
namespace ACBuildService;
public interface ILog
{
void Info(string message);
void Info(string message, params object[] args);
void Warning(string message);
void Warning(string message, params object[] args);
void Error(string message);
void Error(string message, params object[] args);
void Trace(string message);
void Trace(string message, params object[] args);
void Debug(string message);
void Debug(string message, params object[] args);
void Fatal(string message);
void Fatal(string message, params object[] args);
}

165
Log/Log.cs Normal file
View File

@@ -0,0 +1,165 @@
using System.Runtime.CompilerServices;
namespace ACBuildService;
/// <summary>
/// 全局日志静态类
/// </summary>
public static class Log
{
private static ILog _log;
private static Func<ILog> _createLogger = NLogger.CreateInstance;
public static readonly List<LogInfo> LogList = new List<LogInfo>();
public static Func<ILog> CreateLogger
{
get => _createLogger;
set
{
_createLogger = value;
_log = _createLogger();
}
}
static Log()
{
_log = CreateLogger();
}
#region Info
public static void Info(string msg)
{
_log.Info(msg);
AddLog(msg);
}
public static void Info(ref DefaultInterpolatedStringHandler message)
{
var msg = message.ToStringAndClear();
_log.Info(msg);
AddLog(msg);
}
#endregion
#region Warning
public static void Warning(string msg)
{
_log.Warning(msg);
AddLog(msg, 1);
}
public static void Warning(ref DefaultInterpolatedStringHandler message)
{
var msg = message.ToStringAndClear();
_log.Warning(msg);
AddLog(msg, 1);
}
#endregion
#region Error
public static void Error(string msg)
{
_log.Error(msg);
AddLog(msg, 2);
}
public static void Error(Exception msg)
{
_log.Error(msg.ToString());
AddLog(msg.ToString(), 2);
}
public static void Error(ref DefaultInterpolatedStringHandler message)
{
var msg = message.ToStringAndClear();
_log.Error(msg);
AddLog(msg, 2);
}
#endregion
#region Trace
public static void Trace(string msg)
{
_log.Trace(msg);
AddLog(msg, 4);
}
public static void Trace(ref DefaultInterpolatedStringHandler message)
{
_log.Trace(message.ToStringAndClear());
AddLog(message.ToStringAndClear(), 4);
}
public static void TraceInfo(string msg)
{
_log.Trace(msg);
AddLog(msg, 4);
}
#endregion
#region Debug
public static void Debug(string msg)
{
_log.Debug(msg);
AddLog(msg, 4);
}
public static void Debug(ref DefaultInterpolatedStringHandler message)
{
_log.Debug(message.ToStringAndClear());
AddLog(message.ToStringAndClear(), 4);
}
#endregion
#region Fatal
public static void Fatal(string message)
{
_log.Fatal(message);
AddLog(message, 5);
}
public static void Fatal(string message, params object[] args)
{
_log.Fatal(message, args);
AddLog(string.Format(message, args), 5);
}
#endregion
private static void AddLog(string msg, int level = 0)
{
if (LogList.Count > 1000)
{
LogList.RemoveAt(0);
}
LogInfo log = new LogInfo
{
message = msg,
level = level,
timestamp = DateTime.Now.ToString("yyyy.MM.dd HH:mm:ss.fff")
};
LogList.Add(log);
}
}
[Serializable]
public class LogInfo
{
public string timestamp { get; set; }
public int level { get; set; }
public string message { get; set; }
}

249
Log/NLog.cs Normal file
View File

@@ -0,0 +1,249 @@
using NLog;
using NLog.Conditions;
using NLog.Config;
using NLog.Targets;
using NLog.Targets.Wrappers;
namespace ACBuildService;
public class NLogger : ILog
{
private readonly Logger _log;
public static ILog CreateInstance()
{
return new NLogger();
}
public NLogger()
{
var path = "NLog.config";
if (File.Exists(path))
{
LogManager.Configuration = new XmlLoggingConfiguration(path);
}
else
{
var config = new LoggingConfiguration();
var serverDebug = new AsyncTargetWrapper();
serverDebug.WrappedTarget = new FileTarget("serverDebug")
{
OpenFileCacheTimeout = 10,
Layout =
"${longdate} | ${message} ${onexception:${exception:format=message} ${newline} ${stacktrace} ${newline}",
FileName = "${basedir}/Logs/${logger}.${var:appIdFormat}.${date:format=yyyyMMddHH}.Debug.log",
ArchiveFileName = "${basedir}/Logs/${logger}.${var:appIdFormat}.{#}.Debug.log",
ArchiveNumbering = ArchiveNumberingMode.Date,
ArchiveEvery = FileArchivePeriod.Hour,
ArchiveDateFormat = "yyyyMMddHH",
KeepFileOpen = true,
};
var serverInfo = new AsyncTargetWrapper();
serverInfo.WrappedTarget = new FileTarget("serverInfo")
{
OpenFileCacheTimeout = 10,
Layout =
"${longdate} ${message}",
FileName = "${basedir}/Logs/${logger}.${var:appIdFormat}.${date:format=yyyyMMddHH}.Info.log",
ArchiveFileName = "${basedir}/Logs/${logger}.${var:appIdFormat}.{#}.Info.log",
ArchiveNumbering = ArchiveNumberingMode.Date,
ArchiveEvery = FileArchivePeriod.Hour,
ArchiveDateFormat = "yyyyMMddHH",
KeepFileOpen = true,
};
var serverError = new AsyncTargetWrapper();
serverError.WrappedTarget = new FileTarget("serverError")
{
OpenFileCacheTimeout = 10,
Layout =
"${longdate} | ${message} ${onexception:${exception:format=message} ${newline} ${stacktrace} ${newline}",
FileName = "${basedir}/Logs/${logger}.${var:appIdFormat}.${date:format=yyyyMMddHH}.Error.log",
ArchiveFileName = "${basedir}/Logs/${logger}.${var:appIdFormat}.{#}.Error.log",
ArchiveNumbering = ArchiveNumberingMode.Date,
ArchiveEvery = FileArchivePeriod.Hour,
ArchiveDateFormat = "yyyyMMddHH",
KeepFileOpen = true,
};
var serverWarn = new AsyncTargetWrapper();
serverWarn.WrappedTarget = new FileTarget("serverWarn")
{
OpenFileCacheTimeout = 10,
Layout =
"${longdate} ${message}",
FileName = "${basedir}/Logs/${logger}.${var:appIdFormat}.${date:format=yyyyMMddHH}.Warn.log",
ArchiveFileName = "${basedir}/Logs/${logger}.${var:appIdFormat}.{#}.Warn.log",
ArchiveNumbering = ArchiveNumberingMode.Date,
ArchiveEvery = FileArchivePeriod.Hour,
ArchiveDateFormat = "yyyyMMddHH",
KeepFileOpen = true,
};
var serverFatal = new AsyncTargetWrapper();
serverFatal.WrappedTarget = new FileTarget("serverFatal")
{
OpenFileCacheTimeout = 10,
Layout =
"${longdate} | ${message} ${onexception:${exception:format=message} ${newline} ${stacktrace} ${newline}",
FileName = "${basedir}/Logs/${logger}.${var:appIdFormat}.${date:format=yyyyMMddHH}.Fatal.log",
ArchiveFileName = "${basedir}/Logs/${logger}.${var:appIdFormat}.{#}.Fatal.log",
ArchiveNumbering = ArchiveNumberingMode.Date,
ArchiveEvery = FileArchivePeriod.Hour,
ArchiveDateFormat = "yyyyMMddHH",
KeepFileOpen = true,
};
var serverTrace = new AsyncTargetWrapper();
serverTrace.WrappedTarget = new FileTarget("serverTrace")
{
OpenFileCacheTimeout = 10,
Layout =
"${longdate} | ${message} ${onexception:${exception:format=message} ${newline} ${stacktrace} ${newline}",
FileName = "${basedir}/Logs/${logger}.${var:appIdFormat}.${date:format=yyyyMMddHH}.Trace.log",
ArchiveFileName = "${basedir}/Logs/${logger}.${var:appIdFormat}.{#}.Trace.log",
ArchiveNumbering = ArchiveNumberingMode.Date,
ArchiveEvery = FileArchivePeriod.Hour,
ArchiveDateFormat = "yyyyMMddHH",
KeepFileOpen = true,
};
var consoleTarget = new ColoredConsoleTarget()
{
Layout = "[${date:format=HH\\:mm\\:ss}]:${message} ${exception:format=message}"
};
var highlightRuleDebug = new ConsoleRowHighlightingRule
{
Condition = ConditionParser.ParseExpression("level == LogLevel.Debug"),
ForegroundColor = ConsoleOutputColor.Gray
};
consoleTarget.RowHighlightingRules.Add(highlightRuleDebug);
var highlightRuleTrace = new ConsoleRowHighlightingRule
{
Condition = ConditionParser.ParseExpression("level == LogLevel.Trace"),
ForegroundColor = ConsoleOutputColor.DarkGray
};
consoleTarget.RowHighlightingRules.Add(highlightRuleTrace);
var highlightRuleWarn = new ConsoleRowHighlightingRule
{
Condition = ConditionParser.ParseExpression("level == LogLevel.Warn"),
ForegroundColor = ConsoleOutputColor.Yellow
};
consoleTarget.RowHighlightingRules.Add(highlightRuleWarn);
var highlightRuleError = new ConsoleRowHighlightingRule
{
Condition = ConditionParser.ParseExpression("level == LogLevel.Error"),
ForegroundColor = ConsoleOutputColor.Red
};
consoleTarget.RowHighlightingRules.Add(highlightRuleError);
var highlightRuleFatal = new ConsoleRowHighlightingRule
{
Condition = ConditionParser.ParseExpression("level == LogLevel.Fatal"),
ForegroundColor = ConsoleOutputColor.Red,
BackgroundColor = ConsoleOutputColor.White
};
consoleTarget.RowHighlightingRules.Add(highlightRuleFatal);
config.AddRule(LogLevel.Trace, LogLevel.Trace, serverTrace);
config.AddRule(LogLevel.Error, LogLevel.Error, serverError);
config.AddRule(LogLevel.Warn, LogLevel.Warn, serverWarn);
config.AddRule(LogLevel.Debug, LogLevel.Debug, serverDebug);
config.AddRule(LogLevel.Fatal, LogLevel.Fatal, serverFatal);
config.AddRule(LogLevel.Info, LogLevel.Info, serverInfo);
//console
if (App.Settings.Debug)
{
config.AddRule(LogLevel.Trace, LogLevel.Fatal, consoleTarget);
}
else
{
config.AddRule(LogLevel.Trace, LogLevel.Trace, consoleTarget);
config.AddRule(LogLevel.Error, LogLevel.Error, consoleTarget);
config.AddRule(LogLevel.Warn, LogLevel.Warn, consoleTarget);
config.AddRule(LogLevel.Debug, LogLevel.Debug, consoleTarget);
config.AddRule(LogLevel.Fatal, LogLevel.Fatal, consoleTarget);
}
LogManager.Configuration = config;
}
LogManager.Configuration.Variables["appIdFormat"] = $"1";
LogManager.Configuration.Variables["currentDir"] = Environment.CurrentDirectory;
_log = LogManager.GetLogger("nbc");
}
public void Info(string message)
{
_log.Info(message);
}
public void Info(string message, params object[] args)
{
_log.Info(message, args);
}
public void Warning(string message)
{
_log.Warn(message);
}
public void Warning(string message, params object[] args)
{
_log.Warn(message, args);
}
public void Error(string message)
{
_log.Error(message);
}
public void Error(string message, params object[] args)
{
_log.Error(message, args);
}
public void Trace(string message)
{
_log.Trace(message);
}
public void Trace(string message, params object[] args)
{
_log.Trace(message, args);
}
public void Debug(string message)
{
_log.Debug(message);
}
public void Debug(string message, params object[] args)
{
_log.Debug(message, args);
}
public void Fatal(string message)
{
_log.Fatal(message);
}
public void Fatal(string message, params object[] args)
{
_log.Fatal(message, args);
}
}

60
Program.cs Normal file
View File

@@ -0,0 +1,60 @@
using System.Diagnostics;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.DependencyInjection;
using ACBuildService;
public class Program
{
private static volatile Task _loopTask;
private static volatile Task _shutDownTask;
public static async Task Main(string[] args)
{
App.Init();
Test();
await HttpService.Start();
ExitHandle();
_loopTask = EnterAsync();
await _loopTask;
}
private static async Task EnterAsync()
{
while (App.Running)
{
await Task.Delay(10);
App.Update();
App.LateUpdate();
}
Log.Info("Stop");
}
private static void ExitHandle()
{
//退出监听
AppDomain.CurrentDomain.ProcessExit += (_, _) => { ServerExitConfirm(); };
//Fetal异常监听
AppDomain.CurrentDomain.UnhandledException += (_, e) => { ServerExitConfirm(); };
//ctrl+c
Console.CancelKeyPress += (_, _) => { ServerExitConfirm(); };
}
private static void ServerExitConfirm()
{
Log.Info("开始执行退出程序");
_shutDownTask = Task.Run(() =>
{
App.Running = false;
_loopTask?.Wait();
Console.WriteLine("退出完成");
Process.GetCurrentProcess().Kill();
});
_shutDownTask.Wait();
}
private static void Test()
{
}
}

27
Utils.cs Normal file
View File

@@ -0,0 +1,27 @@
namespace ACBuildService;
public class Utils
{
public static string GetFileSizeTitle(long length)
{
if (length < 1024)
{
return length + "B";
}
length /= 1024;
if (length < 1024)
{
return (length * 100 / 100) + "KB";
}
length /= 1024;
if (length < 1024)
{
return (length * 100 / 100.0f) + "MB";
}
return ((length / 1024 * 100) / 100.0f) + "GB";
}
}

44
Utils/FileUtil.cs Normal file
View File

@@ -0,0 +1,44 @@
namespace ACBuildService;
public class FileUtil
{
public static readonly string BaseRootPath = $"{AppContext.BaseDirectory}wwwroot/";
public static string GetFileSizeTitle(long length)
{
if (length < 1024)
{
return length + "B";
}
length /= 1024;
if (length < 1024)
{
return (length * 100 / 100) + "KB";
}
length /= 1024;
if (length < 1024)
{
return (length * 100 / 100.0f) + "MB";
}
return ((length / 1024 * 100) / 100.0f) + "GB";
}
/// <summary>
/// 获得文件夹下所有的文件(类库调用)
/// </summary>
/// <param name="dir"></param>
/// <returns></returns>
public static FileInfo[] GetAllFileInfo(DirectoryInfo dir)
{
return dir.GetFiles(".", SearchOption.AllDirectories);
}
public static string GetVideoPath(string fileName)
{
return Path.Combine(BaseRootPath, fileName);
}
}

50
Utils/HttpHelper.cs Normal file
View File

@@ -0,0 +1,50 @@
using System.Text;
using System.Text.Json;
namespace ACBuildService;
public static class HttpHelper
{
private static readonly HttpClient _httpClient = new HttpClient();
public static async Task<string> GetAsync(string url)
{
try
{
HttpResponseMessage response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
// 使用 UTF-8 编码读取响应内容
byte[] byteArray = await response.Content.ReadAsByteArrayAsync();
return Encoding.UTF8.GetString(byteArray);
}
catch (HttpRequestException e)
{
// 处理请求异常
Console.WriteLine($"Request error: {e.Message}");
throw;
}
}
public static async Task<string> PostAsync<T>(string url, T data)
{
try
{
string json = JsonSerializer.Serialize(data);
StringContent content = new StringContent(json, Encoding.UTF8, "application/json");
HttpResponseMessage response = await _httpClient.PostAsync(url, content);
response.EnsureSuccessStatusCode();
// 使用 UTF-8 编码读取响应内容
byte[] byteArray = await response.Content.ReadAsByteArrayAsync();
return Encoding.UTF8.GetString(byteArray);
}
catch (HttpRequestException e)
{
// 处理请求异常
Console.WriteLine($"Request error: {e.Message}");
throw;
}
}
}

View File

@@ -0,0 +1,296 @@
using Newtonsoft.Json;
namespace ACBuildService;
/// <summary>
/// 抖音 分享文本中的视频数据
/// </summary>
public class DouYinShareRouterData
{
[JsonProperty("loaderData")] public LoaderData LoaderData { get; set; }
}
public class LoaderData
{
[JsonProperty("video_(id)/page")] public VideoIdPage VideoIdPage { get; set; }
}
public class VideoIdPage
{
[JsonProperty("isAutoOpenApp")] public bool IsAutoOpenApp { get; set; }
[JsonProperty("appName")] public string AppName { get; set; }
[JsonProperty("query")] public Query Query { get; set; }
[JsonProperty("isSpider")] public bool IsSpider { get; set; }
[JsonProperty("ua")] public string Ua { get; set; }
[JsonProperty("isVideoOptimize")] public bool IsVideoOptimize { get; set; }
[JsonProperty("commonContext")] public CommonContext CommonContext { get; set; }
[JsonProperty("itemId")] public string ItemId { get; set; }
[JsonProperty("webId")] public string WebId { get; set; }
[JsonProperty("renderInSSR")] public long RenderInSsr { get; set; }
[JsonProperty("host")] public string Host { get; set; }
[JsonProperty("videoInfoRes")] public VideoInfoRes VideoInfoRes { get; set; }
[JsonProperty("darkModeAdaptation")] public bool DarkModeAdaptation { get; set; }
[JsonProperty("lastPath")] public string LastPath { get; set; }
[JsonProperty("isNotSupportWebp")] public bool IsNotSupportWebp { get; set; }
[JsonProperty("serverToken")] public string ServerToken { get; set; }
}
public class CommonContext
{
[JsonProperty("webId")] public string WebId { get; set; }
[JsonProperty("renderInSSR")] public long RenderInSsr { get; set; }
[JsonProperty("appName")] public string AppName { get; set; }
[JsonProperty("query")] public Query Query { get; set; }
[JsonProperty("host")] public string Host { get; set; }
[JsonProperty("isSpider")] public bool IsSpider { get; set; }
[JsonProperty("lastPath")] public string LastPath { get; set; }
[JsonProperty("isNotSupportWebp")] public bool IsNotSupportWebp { get; set; }
[JsonProperty("ua")] public string Ua { get; set; }
}
public class Query
{
[JsonProperty("titleType")] public string TitleType { get; set; }
[JsonProperty("iid")] public string Iid { get; set; }
[JsonProperty("u_code")] public string UCode { get; set; }
[JsonProperty("from_ssr")] public long FromSsr { get; set; }
[JsonProperty("mid")] public string Mid { get; set; }
[JsonProperty("share_sign")] public string ShareSign { get; set; }
[JsonProperty("with_sec_did")] public long WithSecDid { get; set; }
[JsonProperty("from_aid")] public long FromAid { get; set; }
[JsonProperty("from")] public string From { get; set; }
[JsonProperty("region")] public string Region { get; set; }
[JsonProperty("did")] public string Did { get; set; }
[JsonProperty("share_version")] public long ShareVersion { get; set; }
[JsonProperty("ts")] public long Ts { get; set; }
}
public class VideoInfoRes
{
[JsonProperty("status_code")] public long StatusCode { get; set; }
[JsonProperty("filter_list")] public List<object> FilterList { get; set; }
[JsonProperty("item_list")] public List<ItemList> ItemList { get; set; }
[JsonProperty("extra")] public Extra Extra { get; set; }
}
public class Extra
{
[JsonProperty("now")] public long Now { get; set; }
}
public class ItemList
{
[JsonProperty("video")] public Video Video { get; set; }
[JsonProperty("music")] public Music Music { get; set; }
[JsonProperty("create_time")] public long CreateTime { get; set; }
[JsonProperty("author")] public Author Author { get; set; }
[JsonProperty("risk_infos")] public RiskInfos RiskInfos { get; set; }
[JsonProperty("mix_info")] public MixInfo MixInfo { get; set; }
[JsonProperty("aweme_id")] public string AwemeId { get; set; }
[JsonProperty("group_id_str")] public string GroupIdStr { get; set; }
[JsonProperty("text_extra")] public List<TextExtra> TextExtra { get; set; }
[JsonProperty("aweme_type")] public long AwemeType { get; set; }
[JsonProperty("desc")] public string Desc { get; set; }
[JsonProperty("statistics")] public Statistics Statistics { get; set; }
}
public class Author
{
[JsonProperty("avatar_medium")] public AvatarMedium AvatarMedium { get; set; }
[JsonProperty("unique_id")] public string UniqueId { get; set; }
[JsonProperty("sec_uid")] public string SecUid { get; set; }
[JsonProperty("signature")] public string Signature { get; set; }
[JsonProperty("mplatform_followers_count")]
public long MplatformFollowersCount { get; set; }
[JsonProperty("avatar_thumb")] public AvatarMedium AvatarThumb { get; set; }
[JsonProperty("follow_status")] public long FollowStatus { get; set; }
[JsonProperty("short_id")] public string ShortId { get; set; }
[JsonProperty("following_count")] public long FollowingCount { get; set; }
[JsonProperty("nickname")] public string Nickname { get; set; }
[JsonProperty("favoriting_count")] public long FavoritingCount { get; set; }
}
public class AvatarMedium
{
[JsonProperty("url_list")] public List<Uri> UrlList { get; set; }
[JsonProperty("uri")] public string Uri { get; set; }
}
public class MixInfo
{
[JsonProperty("mix_id")] public string MixId { get; set; }
[JsonProperty("cover_url")] public AvatarMedium CoverUrl { get; set; }
[JsonProperty("next_info")] public NextInfo NextInfo { get; set; }
[JsonProperty("statis")] public Statis Statis { get; set; }
[JsonProperty("create_time")] public long CreateTime { get; set; }
[JsonProperty("extra")] public string Extra { get; set; }
[JsonProperty("mix_name")] public string MixName { get; set; }
[JsonProperty("status")] public Status Status { get; set; }
[JsonProperty("desc")] public string Desc { get; set; }
}
public class NextInfo
{
[JsonProperty("cover_url")] public AvatarMedium CoverUrl { get; set; }
[JsonProperty("mix_name")] public string MixName { get; set; }
[JsonProperty("desc")] public string Desc { get; set; }
}
public class Statis
{
[JsonProperty("collect_vv")] public long CollectVv { get; set; }
[JsonProperty("current_episode")] public long CurrentEpisode { get; set; }
[JsonProperty("play_vv")] public long PlayVv { get; set; }
[JsonProperty("updated_to_episode")] public long UpdatedToEpisode { get; set; }
}
public class Status
{
[JsonProperty("is_collected")] public long IsCollected { get; set; }
[JsonProperty("status")] public long StatusStatus { get; set; }
}
public class Music
{
[JsonProperty("duration")] public long Duration { get; set; }
[JsonProperty("author")] public string Author { get; set; }
[JsonProperty("cover_medium")] public AvatarMedium CoverMedium { get; set; }
[JsonProperty("mid")] public string Mid { get; set; }
[JsonProperty("title")] public string Title { get; set; }
[JsonProperty("cover_hd")] public AvatarMedium CoverHd { get; set; }
[JsonProperty("cover_large")] public AvatarMedium CoverLarge { get; set; }
[JsonProperty("cover_thumb")] public AvatarMedium CoverThumb { get; set; }
[JsonProperty("status")] public long Status { get; set; }
}
public class RiskInfos
{
[JsonProperty("warn")] public bool Warn { get; set; }
[JsonProperty("type")] public long Type { get; set; }
[JsonProperty("content")] public string Content { get; set; }
[JsonProperty("reflow_unplayable")] public long ReflowUnplayable { get; set; }
}
public class Statistics
{
[JsonProperty("comment_count")] public long CommentCount { get; set; }
[JsonProperty("share_count")] public long ShareCount { get; set; }
[JsonProperty("aweme_id")] public string AwemeId { get; set; }
[JsonProperty("digg_count")] public long DiggCount { get; set; }
[JsonProperty("play_count")] public long PlayCount { get; set; }
[JsonProperty("collect_count")] public long CollectCount { get; set; }
}
public class TextExtra
{
[JsonProperty("hashtag_id")] public long HashtagId { get; set; }
[JsonProperty("start")] public long Start { get; set; }
[JsonProperty("end")] public long End { get; set; }
[JsonProperty("type")] public long Type { get; set; }
[JsonProperty("hashtag_name")] public string HashtagName { get; set; }
}
public class Video
{
[JsonProperty("cover")] public AvatarMedium Cover { get; set; }
[JsonProperty("play_addr")] public AvatarMedium PlayAddr { get; set; }
[JsonProperty("width")] public long Width { get; set; }
[JsonProperty("height")] public long Height { get; set; }
}

View File

@@ -0,0 +1,177 @@
using System.ComponentModel;
using System.Text.RegularExpressions;
using Downloader;
using Newtonsoft.Json;
using RestSharp;
namespace ACBuildService;
public class DouYin : DownloadPlatforms
{
private static readonly Dictionary<string, string> _defaultHeaders = new()
{
{
"User-Agent",
"Mozilla/5.0 (Linux; Android 8.0.0; SM-G955U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36"
},
{ "sec-fetch-site", "same-origin" },
{ "sec-fetch-mode", "cors" },
{ "sec-fetch-dest", "empty" },
{ "sec-ch-ua-platform", "Windows" },
{ "sec-ch-ua-mobile", "?0" },
{ "sec-ch-ua", "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google Chrome\";v=\"126\"" },
{ "referer", "https://www.douyin.com/?recommend=1" },
{ "priority", "u=1, i" },
{ "pragma", "no-cache" },
{ "cache-control", "no-cache" },
{ "accept-language", "zh-CN,zh;q=0.9,en;q=0.8" },
{
"accept",
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"
},
{ "dnt", "1" }
};
public async override Task<string> ExtractUrlAsync(string text)
{
Log.Info($"开始解析抖音链接 {text}");
return Regex.Match(text, @"https?://[^\s]+").Value;
}
public override async Task<VideoModel> ParseShare(string DownloadUrlText)
{
if (!DownloadUrlText.Contains("https://"))
{
Log.Error("请输入正确的分享链接");
return null;
}
var downloadUrl = await ExtractUrlAsync(DownloadUrlText);
VideoModel Data = await ExtractVideoDataAsync(downloadUrl);
Data.ShareId = downloadUrl.Replace("https://v.douyin.com/", "").Replace("/", "");
return Data;
}
/// <summary>
/// 根据链接 https://v.douyin.com/ircqoExo/ 解析出页面中的数据
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public override Task<VideoModel> ExtractVideoDataAsync(string url)
{
try
{
Log.Info($"开始解析链接 {url}");
// 创建RestClient
RestClient client = new(url);
// 创建请求对象
RestRequest request = new();
// 设置 User-Agent 模拟手机浏览器
request.AddHeaders(_defaultHeaders);
// 发送请求并获取响应
var response = client.Execute(request);
if (!response.IsSuccessful) throw new HttpRequestException("request is fail");
var content = response.Content;
Log.Info($"开始解析响应内容 {content}");
if (content is null) throw new InvalidDataException("content is null");
const string routerDataPattern = @"_ROUTER_DATA\s*=\s*(\{.*?\})<";
var matchJson = Regex.Match(content, routerDataPattern);
Log.Info($"开始解析匹配到的json {matchJson}");
if (matchJson.Groups.Count < 2) throw new InvalidDataException("未匹配到合法的数据matchJson.Groups.Count < 2");
var videoJson = matchJson.Groups[1].Value;
Log.Info($"开始解析匹配到的json {videoJson}");
// 反序列化JSON字符串为C#对象
var videoData = JsonConvert.DeserializeObject<DouYinShareRouterData>(videoJson);
if (videoData is null) throw new InvalidDataException("JSON解析数据为空请检查分享链接是否正确如有更多问题请查看日志");
var videoInfoData = videoData.LoaderData.VideoIdPage.VideoInfoRes.ItemList.First();
var video = new VideoModel
{
Platform = ShortVideoPlatformEnum.DouYin,
VideoId = videoInfoData.AwemeId,
AuthorName = videoInfoData.Author.Nickname,
UniqueId = videoInfoData.Author.UniqueId == ""
? videoInfoData.Author.ShortId
: videoInfoData.Author.UniqueId,
AuthorAvatar = videoInfoData.Author.AvatarThumb.UrlList.First().ToString(),
Title = videoInfoData.Author.Signature,
Cover = videoInfoData.Video.Cover.UrlList.Last().ToString(),
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
};
switch (videoInfoData.AwemeType)
{
case 2:
video.VideoUrl = videoInfoData.Video.Cover.UrlList.First().ToString();
break;
default:
video.VideoUrl = videoInfoData.Video.PlayAddr.UrlList.First().ToString().Replace("playwm", "play");
break;
}
if (video.VideoUrl.Contains("ratio=720p"))
{
video.VideoUrl = video.VideoUrl.Replace("ratio=720p", "ratio=1080p");
}
return Task.FromResult(video);
}
catch (Exception e)
{
Log.Error(e.Message);
throw;
}
}
public override async Task<bool> DownloadAsync(string url, string savePath, string fileName,
EventHandler<DownloadProgressChangedEventArgs> onProgressChanged,
EventHandler<AsyncCompletedEventArgs> onProgressCompleted)
{
DownloadConfiguration downloadConfiguration = new()
{
ChunkCount = 8, // Download in 8 chunks (increase for larger files)
MaxTryAgainOnFailure = 5,
Timeout = 10000, // 10 seconds timeout for each request
RequestConfiguration = new RequestConfiguration
{
UserAgent = _defaultHeaders.GetValueOrDefault("User-Agent")
}
};
DownloadService downloader = new(downloadConfiguration);
downloader.DownloadProgressChanged += onProgressChanged;
downloader.DownloadFileCompleted += onProgressCompleted;
try
{
await downloader.DownloadFileTaskAsync(url, Path.Combine(savePath, fileName));
return true;
}
catch (Exception ex)
{
Log.Info($"Download failed: {ex}");
}
return false;
}
}

View File

@@ -0,0 +1,15 @@
using System.ComponentModel;
using Downloader;
namespace ACBuildService;
public abstract class DownloadPlatforms
{
public abstract Task<string> ExtractUrlAsync(string text);
public abstract Task<VideoModel> ParseShare(string DownloadUrlText);
public abstract Task<VideoModel> ExtractVideoDataAsync(string url);
public abstract Task<bool> DownloadAsync(string url, string savePath, string fileName,
EventHandler<DownloadProgressChangedEventArgs> onProgressChanged,
EventHandler<AsyncCompletedEventArgs> onProgressCompleted);
}

View File

@@ -0,0 +1,53 @@
using System.ComponentModel;
using Downloader;
namespace ACBuildService;
public class KuaiShou : DownloadPlatforms
{
private static readonly Dictionary<string, string> _defaultHeaders = new()
{
{
"User-Agent",
"Mozilla/5.0 (Linux; Android 8.0.0; SM-G955U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36"
},
{ "sec-fetch-site", "same-origin" },
{ "sec-fetch-mode", "cors" },
{ "sec-fetch-dest", "empty" },
{ "sec-ch-ua-platform", "Windows" },
{ "sec-ch-ua-mobile", "?0" },
{ "sec-ch-ua", "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google Chrome\";v=\"126\"" },
{ "referer", "https://www.douyin.com/?recommend=1" },
{ "priority", "u=1, i" },
{ "pragma", "no-cache" },
{ "cache-control", "no-cache" },
{ "accept-language", "zh-CN,zh;q=0.9,en;q=0.8" },
{
"accept",
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"
},
{ "dnt", "1" }
};
public override Task<string> ExtractUrlAsync(string text)
{
throw new NotImplementedException();
}
public override Task<VideoModel> ParseShare(string DownloadUrlText)
{
throw new NotImplementedException();
}
public override Task<VideoModel> ExtractVideoDataAsync(string url)
{
throw new NotImplementedException();
}
public override Task<bool> DownloadAsync(string url, string savePath, string fileName,
EventHandler<DownloadProgressChangedEventArgs> onProgressChanged,
EventHandler<AsyncCompletedEventArgs> onProgressCompleted)
{
throw new NotImplementedException();
}
}

View File

@@ -0,0 +1,151 @@
using System.Threading.Channels;
namespace ACBuildService;
public static class VideoDownload
{
private static readonly Channel<string> _queue = Channel.CreateUnbounded<string>();
private static readonly CancellationTokenSource _cts = new CancellationTokenSource();
private static readonly TimeSpan _interval = TimeSpan.FromSeconds(20);
static VideoDownload()
{
Task.Run(ProcessQueueAsync);
}
public static void Add(string shareText)
{
_queue.Writer.TryWrite(shareText);
}
private static async Task ProcessQueueAsync()
{
var timer = new PeriodicTimer(_interval);
try
{
while (await _queue.Reader.WaitToReadAsync(_cts.Token) &&
await timer.WaitForNextTickAsync(_cts.Token))
{
if (_queue.Reader.TryRead(out var shareText))
{
// 这里开始处理任务,这个处理过程本身的时间不计入间隔
// PeriodicTimer 会保证两次 WaitForNextTickAsync 之间间隔10秒
await ProcessVideoDownloadAsync(shareText);
}
}
}
catch (OperationCanceledException)
{
// 正常取消
}
}
private static async Task ProcessVideoDownloadAsync(string shareText)
{
try
{
var service = GetService(shareText);
if (service != null)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] 开始处理: {shareText}");
await DownloadVideoAsync(service, shareText);
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] 处理完成: {shareText}");
}
}
catch (Exception ex)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] 处理失败: {shareText}, 错误: {ex.Message}");
}
}
// 模拟下载视频的方法
private static async Task DownloadVideoAsync(DownloadPlatforms service, string shareText)
{
//开始解析数据
var videoModel = await service.ParseShare(shareText);
if (videoModel == null) return;
var rootPath = $"{HttpService.BaseRootPath}videos/";
var t = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
var fileName = $"{t}.mp4";
var filePath = Path.Combine(rootPath, fileName);
if (File.Exists(filePath))
{
Log.Error("文件已存在");
return;
}
var ret = await service.DownloadAsync(videoModel.VideoUrl, rootPath, fileName, null, null);
if (ret)
{
var dbItem = new VideoTable
{
SourcePath = shareText,
FilePath = fileName,
AuthorName = videoModel.AuthorName,
Title = videoModel.Title,
Desc = videoModel.Desc,
Duration = videoModel.Duration,
Like = (int)videoModel.DiggCount,
Collect = (int)videoModel.CollectCount,
Message = (int)videoModel.CommentCount,
Share = (int)videoModel.ShareCount,
CreateTime = DateTime.Now
};
DB.Main.Insertable(dbItem).ExecuteCommand();
}
//解析数据后等待一会
await Task.Delay(3000);
}
#region Service
private static Dictionary<ShortVideoPlatformEnum, DownloadPlatforms> _services =
new Dictionary<ShortVideoPlatformEnum, DownloadPlatforms>();
private static DownloadPlatforms GetService(string text)
{
var platform = GetPlatform(text);
if (_services.TryGetValue(platform, out var value))
{
return value;
}
if (platform == ShortVideoPlatformEnum.DouYin)
{
value = new DouYin();
}
else if (platform == ShortVideoPlatformEnum.KuaiShou)
{
value = new KuaiShou();
}
if (value != null)
{
_services[platform] = value;
}
return value;
}
private static ShortVideoPlatformEnum GetPlatform(string text)
{
if (text.Contains("douyin"))
{
return ShortVideoPlatformEnum.DouYin;
}
if (text.Contains("kuaishou"))
{
return ShortVideoPlatformEnum.KuaiShou;
}
return ShortVideoPlatformEnum.None;
}
#endregion
}

View File

@@ -0,0 +1,200 @@
// using System.ComponentModel;
// using System.Text.RegularExpressions;
// using Downloader;
// using Newtonsoft.Json;
// using RestSharp;
//
// namespace ACBuildService;
//
// public class VideoDownloadService
// {
// private static readonly Dictionary<string, string> _defaultHeaders = new()
// {
// {
// "User-Agent",
// "Mozilla/5.0 (Linux; Android 8.0.0; SM-G955U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36"
// },
// { "sec-fetch-site", "same-origin" },
// { "sec-fetch-mode", "cors" },
// { "sec-fetch-dest", "empty" },
// { "sec-ch-ua-platform", "Windows" },
// { "sec-ch-ua-mobile", "?0" },
// { "sec-ch-ua", "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google Chrome\";v=\"126\"" },
// { "referer", "https://www.douyin.com/?recommend=1" },
// { "priority", "u=1, i" },
// { "pragma", "no-cache" },
// { "cache-control", "no-cache" },
// { "accept-language", "zh-CN,zh;q=0.9,en;q=0.8" },
// {
// "accept",
// "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"
// },
// { "dnt", "1" }
// };
//
// public async Task<VideoModel> ParseShare(string DownloadUrlText)
// {
// if (!DownloadUrlText.Contains("https://"))
// {
// // throw new ValidationException("请输入正确的分享链接");
// Log.Error("请输入正确的分享链接");
// return null;
// }
//
// VideoModel Data = null;
//
// if (DownloadUrlText.Contains(ShortVideoPlatformEnum.DouYin.ToString().ToLower()))
// {
// var downloadUrl = await ExtractUrlAsync(DownloadUrlText);
// Data = await ExtractVideoDataAsync(downloadUrl);
// Data.ShareId = downloadUrl.Replace("https://v.douyin.com/", "").Replace("/", "");
// }
//
// // else if (DownloadUrlText.Contains(ShortVideoPlatformEnum.KuaiShou.ToString().ToLower()))
// // {
// // var downloadUrl = await _kuaiShortVideoService.ExtractUrlAsync(DownloadUrlText);
// // Data = await _kuaiShortVideoService.ExtractVideoDataAsync(downloadUrl);
// // }
// // else
// // {
// // throw new ValidationException("暂不支持该平台");
// // }
// // Log.Info($"解析结果 data={JSON.Serialize(Data)}");
// return Data;
// }
//
// /// <summary>
// /// 解析dy分享中的文本 6.17 05/18 U@L.wf 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)
// {
// Log.Info($"开始解析抖音链接 {text}");
// return Regex.Match(text, @"https?://[^\s]+").Value;
// }
//
// /// <summary>
// /// 根据链接 https://v.douyin.com/ircqoExo/ 解析出页面中的数据
// /// </summary>
// /// <param name="url"></param>
// /// <returns></returns>
// public Task<VideoModel> ExtractVideoDataAsync(string url)
// {
// try
// {
// Log.Info($"开始解析链接 {url}");
// // 创建RestClient
// RestClient client = new(url);
//
// // 创建请求对象
// RestRequest request = new();
//
// // 设置 User-Agent 模拟手机浏览器
// request.AddHeaders(_defaultHeaders);
//
// // 发送请求并获取响应
// var response = client.Execute(request);
// if (!response.IsSuccessful) throw new HttpRequestException("request is fail");
//
// var content = response.Content;
// Log.Info($"开始解析响应内容 {content}");
// if (content is null) throw new InvalidDataException("content is null");
//
// const string routerDataPattern = @"_ROUTER_DATA\s*=\s*(\{.*?\})<";
//
// var matchJson = Regex.Match(content, routerDataPattern);
//
// Log.Info($"开始解析匹配到的json {matchJson}");
// if (matchJson.Groups.Count < 2) throw new InvalidDataException("未匹配到合法的数据matchJson.Groups.Count < 2");
//
// var videoJson = matchJson.Groups[1].Value;
// Log.Info($"开始解析匹配到的json {videoJson}");
// // 反序列化JSON字符串为C#对象
// var videoData = JsonConvert.DeserializeObject<DouYinShareRouterData>(videoJson);
//
// if (videoData is null) throw new InvalidDataException("JSON解析数据为空请检查分享链接是否正确如有更多问题请查看日志");
//
// var videoInfoData = videoData.LoaderData.VideoIdPage.VideoInfoRes.ItemList.First();
//
// var video = new VideoModel
// {
// Platform = ShortVideoPlatformEnum.DouYin,
// VideoId = videoInfoData.AwemeId,
// AuthorName = videoInfoData.Author.Nickname,
// UniqueId = videoInfoData.Author.UniqueId == ""
// ? videoInfoData.Author.ShortId
// : videoInfoData.Author.UniqueId,
// AuthorAvatar = videoInfoData.Author.AvatarThumb.UrlList.First().ToString(),
// Title = videoInfoData.Author.Signature,
// Cover = videoInfoData.Video.Cover.UrlList.Last().ToString(),
// 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
// };
// switch (videoInfoData.AwemeType)
// {
// case 2:
// video.VideoUrl = videoInfoData.Video.Cover.UrlList.First().ToString();
// break;
// default:
// video.VideoUrl = videoInfoData.Video.PlayAddr.UrlList.First().ToString().Replace("playwm", "play");
// break;
// }
//
// if (video.VideoUrl.Contains("ratio=720p"))
// {
// video.VideoUrl = video.VideoUrl.Replace("ratio=720p", "ratio=1080p");
// }
//
// return Task.FromResult(video);
// }
// catch (Exception e)
// {
// Log.Error(e.Message);
// throw;
// }
// }
//
//
// public async Task DownloadAsync(string url, string savePath, string fileName,
// EventHandler<DownloadProgressChangedEventArgs> onProgressChanged,
// EventHandler<AsyncCompletedEventArgs> onProgressCompleted
// )
// {
// DownloadConfiguration downloadConfiguration = new()
// {
// ChunkCount = 8, // Download in 8 chunks (increase for larger files)
// MaxTryAgainOnFailure = 5,
// Timeout = 10000, // 10 seconds timeout for each request
// RequestConfiguration = new RequestConfiguration
// {
// UserAgent = _defaultHeaders.GetValueOrDefault("User-Agent")
// }
// };
//
// DownloadService downloader = new(downloadConfiguration);
//
// downloader.DownloadProgressChanged += onProgressChanged;
// downloader.DownloadFileCompleted += onProgressCompleted;
//
// try
// {
// await downloader.DownloadFileTaskAsync(url, savePath + fileName);
// }
// catch (Exception ex)
// {
// Log.Info($"Download failed: {ex}");
// throw;
// }
// }
// }

View File

@@ -0,0 +1,70 @@
namespace ACBuildService;
public enum ShortVideoPlatformEnum
{
None,
// 抖音
DouYin,
// 快手
KuaiShou,
/// <summary>
/// B站
/// </summary>
Bilibili,
}
[Serializable]
public class VideoModel
{
// 短视频所属的平台
public ShortVideoPlatformEnum Platform { get; set; }
// 视频ID
public string VideoId { get; set; }
// 作者
public string AuthorName { get; set; }
// Unique Id
public string UniqueId { get; set; }
// 头像
public string AuthorAvatar { get; set; }
// 视频标题
public string Title { get; set; }
// 封面
public string Cover { get; set; }
// 收藏数
public long CollectCount { get; set; }
// 点赞数
public long DiggCount { get; set; }
// 分享数
public long ShareCount { get; set; }
// 评论数
public long CommentCount { get; set; }
// 视频地址
public string VideoUrl { get; set; }
// 音频地址
public string Mp3Url { get; set; }
// 创建时间
public string CreatedTime { get; set; }
// 视频描述
public string Desc { get; set; }
// 视频时长
public string Duration { get; set; }
public string ShareId { get; set; }
}

7
appsettings.json Normal file
View File

@@ -0,0 +1,7 @@
{
"Debug": true,
"HttpPort": 9888,
"OpenProxy": true,
"ProxyHost": "http://127.0.0.1:10808",
"Password": "123456"
}

7
global.json Normal file
View File

@@ -0,0 +1,7 @@
{
"sdk": {
"version": "8.0.0",
"rollForward": "latestMajor",
"allowPrerelease": true
}
}

View File

@@ -0,0 +1,13 @@
{
"type": "service_account",
"project_id": "project-game-x",
"private_key_id": "f720ef30f678d5c76f101b3eebdcb578460b27e2",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCtT0hy5GxzK+LI\nl0si8K7e8UrKDj+39YEti2hTrnA5NLed+zKOBMKl7vTJLCweE3RMPamkpYPtbwXc\neERB7s5+Bg4Bqq4uCkNov3uHBt19nyGPFhK3i6nIcm8tk+v8LLEMCnXy5R2hwU9J\naRtJoymP2fPbDeOEuSl8eeQw7kla2CXASrmNS8sTSnX3GAjqKIGmByRHQSa+Hn4R\nxVsqtL1sck9XAgITZQ9ciiWJAfX0caciU9merZHmTXI8lpJgE+7IjLi5RTlVQYg9\nkjY3CjZJAlDvFp/BN0laNzhSu8Z0bO/J1JrpGDn/jc+k0VZKK0JIDMUorvimFneh\n9kJwN5W7AgMBAAECggEAEsOhF3dt2oZni8pTA0RwhTOLd8GqhR4FT3YRj0XgDAGO\nLoJSYJgEBHfmdEWJEp85RJaZ4HlfMFIzSJc+OgPuGCz1pUPij0TjNDB54mO25XFY\n7ny4UrvCCobWohEASDxagEWTAapsK+WZ0lvumBQcsuDT4JAUuyZaZzTB+nOXk53i\nG0bj2CTCm3IJK0u/6gSuPIiMDjrMGOhYlATCeEY3kL6MJBiU96UGacuwBxXwHVO1\ntSH9vac7Nen1V9PSDZAhQQIldt6gFEjj5trKt4QMty0prbQCJvec4dXEVtO6HaLe\n1ZaUC2hyqWyL3kd14T/dlo6ZBCx3kp8j+UiBFQ6r4QKBgQDv9VXds+lKyH8V50xh\nrPLElg2ovpQ0d7LjBwopznNWIGSD0LFB+ihVI3itf5CXS3vIGztvEooRB6BVFW15\na9P5gfVmwVO/xZ5kZz/d0W8Hk6WoE2JFbNSGDj4eHRYSdbvzSZnwPfx1A+bmuXeL\nCA33dlMInl7k7P1w6EbPwRGpawKBgQC45VFfEJ97iNBhRsIAMVGU8pQX2G0TbgGg\nP4OBnARprf2Tb4ky3iENoWpM2OUHng8f80yaIrTqhqzNz/cb9XU3Y+6yatyTRrMw\nKUC369mjRPt1LL9vIr0JTwdANve/Uom3UneINsxSiPm2Dh5Rr0XNVeLI9aDjwnn4\nrPHIbBNI8QKBgHlSMlLAdtBfpJl/c0fjOG8aateJW8dXSbOtSH8wqXG/OPLIXga8\now3AvHAEEifcez4GPrt+xR2tHGxIRCxizy7UfS2xy1UnZljfNxRAooFTLitXeZUe\nXClXJCL3k8RLkHaGHPWxcWX8Tg8TcJRzwxP92CeAYvwdsloWpk6+D973AoGAJPD8\nTwgdNStipuziOfOgnyfQWutM78Lc5E2MUsr/PrYaoeh4+wbSh8ymVnBHYjw8PV/5\nABrLFsiNohlY/+cM4mI/ALrFE0/e4VJ8scKXmz1fGEw2e1fvePqnMjdJTJqLFWuO\nolKAhEUFz1AG0r84LQxp4UjiOl9Sy1KShD7Do6ECgYAkWZ6vXsvxNAfD2BnbPJ2j\nAB5r0RgN6PfPF4c/aJ8NOc/Dfi+myaKs5JEdtDIibO1+W6dfPWmaMMD/TkU5KgUU\nk4oGLEyWKD++dyt0y3PiKfoGsA9Xhhn9SK8pkqVphwfA1NSRrof5Jpo4OMZeB/ZF\nmRU+h5VqRGxR5eeSvOJinQ==\n-----END PRIVATE KEY-----\n",
"client_email": "ac-auto-update@project-game-x.iam.gserviceaccount.com",
"client_id": "108075195906651298317",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/ac-auto-update%40project-game-x.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}

43
wwwroot/home.html Normal file
View File

@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>导航页</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f4f4f4;
margin: 0;
}
.container {
text-align: center;
}
.button {
display: inline-block;
margin: 10px;
padding: 15px 25px;
font-size: 16px;
color: #fff;
background-color: #007BFF;
border: none;
border-radius: 5px;
text-decoration: none;
transition: background-color 0.3s ease;
}
.button:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
<div class="container">
<a href="/files" class="button">文件列表</a>
<a href="/page/log" class="button">日志列表</a>
</div>
</body>
</html>