Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 17 additions & 23 deletions scripts/optimize-media.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ const MP4_AUDIO_BITRATE = process.env.MP4_AUDIO_BITRATE || '128k'
const MIN_SAVINGS_PCT = Number(process.env.MEDIA_MIN_SAVINGS_PCT || '5')
const SKIP_SMALL_KB = Number(process.env.MEDIA_SKIP_SMALL_KB || '256')

function bytesToMiB(bytes) {
function bytesToMiB (bytes) {
return (bytes / 1024 / 1024).toFixed(2)
}

function runFfmpeg(args) {
function runFfmpeg (args) {
const result = spawnSync('ffmpeg', args, {
stdio: 'inherit',
windowsHide: true,
windowsHide: true
})
if (result.error) {
throw result.error
Expand All @@ -38,7 +38,7 @@ function runFfmpeg(args) {
}
}

function isFileExists(filePath) {
function isFileExists (filePath) {
try {
fsSync.accessSync(filePath)
return true
Expand All @@ -47,29 +47,29 @@ function isFileExists(filePath) {
}
}

async function ensureDir(dirPath) {
async function ensureDir (dirPath) {
await fs.mkdir(dirPath, { recursive: true })
}

async function cleanDir(dirPath) {
async function cleanDir (dirPath) {
await fs.rm(dirPath, { recursive: true, force: true })
await ensureDir(dirPath)
}

async function* walkFiles(dirPath) {
async function * walkFiles (dirPath) {
if (!isFileExists(dirPath)) return
const entries = await fs.readdir(dirPath, { withFileTypes: true })
for (const entry of entries) {
const fullPath = path.join(dirPath, entry.name)
if (entry.isDirectory()) {
yield* walkFiles(fullPath)
yield * walkFiles(fullPath)
} else if (entry.isFile()) {
yield fullPath
}
}
}

async function optimizeOne({ inputPath, inputBaseDir, outputBaseDir }) {
async function optimizeOne ({ inputPath, inputBaseDir, outputBaseDir }) {
const relPath = path.relative(inputBaseDir, inputPath)
const outputPath = path.join(outputBaseDir, relPath)

Expand Down Expand Up @@ -111,7 +111,7 @@ async function optimizeOne({ inputPath, inputBaseDir, outputBaseDir }) {
MP3_BITRATE,
'-id3v2_version',
'3',
tmpPath,
tmpPath
])
} else {
// Re-encode MP4 (H.264 + AAC) and enable faststart
Expand All @@ -133,7 +133,7 @@ async function optimizeOne({ inputPath, inputBaseDir, outputBaseDir }) {
MP4_AUDIO_BITRATE,
'-movflags',
'+faststart',
tmpPath,
tmpPath
])
}

Expand All @@ -151,12 +151,12 @@ async function optimizeOne({ inputPath, inputBaseDir, outputBaseDir }) {
return { changed: true, reason: 'optimized', before: stat.size, after: outStat.size, outputPath }
}

function checkFfmpegAvailable() {
function checkFfmpegAvailable () {
const result = spawnSync('ffmpeg', ['-version'], { stdio: 'ignore', windowsHide: true })
return result.status === 0
}

async function main() {
async function main () {
if (!checkFfmpegAvailable()) {
console.error('[optimize:media] ffmpeg not found in PATH.')
console.error('Install ffmpeg and ensure `ffmpeg` is available in your terminal, then re-run.')
Expand All @@ -167,21 +167,15 @@ async function main() {
const targets = [
{ inputDir: SRC_AUDIO_DIR, outputDir: OUT_AUDIO_DIR },
{ inputDir: SRC_VIDEO_DIR, outputDir: OUT_VIDEO_DIR },
{ inputDir: SRC_PUBLIC_AUDIO_DIR, outputDir: OUT_PUBLIC_AUDIO_DIR },
{ inputDir: SRC_PUBLIC_AUDIO_DIR, outputDir: OUT_PUBLIC_AUDIO_DIR }
]

let changedCount = 0
let scannedCount = 0
let savedBytes = 0

console.log(
'[optimize:media] scanning:',
targets.map((t) => path.relative(projectRoot, t.inputDir)).join(', ')
)
console.log(
'[optimize:media] output:',
targets.map((t) => path.relative(projectRoot, t.outputDir)).join(', ')
)
console.log('[optimize:media] scanning:', targets.map((t) => path.relative(projectRoot, t.inputDir)).join(', '))
console.log('[optimize:media] output:', targets.map((t) => path.relative(projectRoot, t.outputDir)).join(', '))
console.log(
`[optimize:media] settings: MP3_BITRATE=${MP3_BITRATE}, MP4_CRF=${MP4_CRF}, MP4_PRESET=${MP4_PRESET}, MIN_SAVINGS_PCT=${MIN_SAVINGS_PCT}%, SKIP_SMALL_KB=${SKIP_SMALL_KB}KB`
)
Expand All @@ -194,7 +188,7 @@ async function main() {
const result = await optimizeOne({
inputPath,
inputBaseDir: target.inputDir,
outputBaseDir: target.outputDir,
outputBaseDir: target.outputDir
})
if (result.changed) {
changedCount += 1
Expand Down
96 changes: 48 additions & 48 deletions webpack/webpack.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ switch (process.env.BUILD_GOAL) {

const config = {
entry: {
app: `${paths.src}/index.tsx`,
app: `${paths.src}/index.tsx`
},
output: {
path: paths.build,
Expand All @@ -48,7 +48,7 @@ const config = {
// library: '',
// libraryTarget: 'umd',
// chunkLoadingGlobal: '',
clean: true,
clean: true
// contentBase: path.join(__dirname, "public"), // 配置额外的静态文件内容的访问路径
},
resolve: {
Expand All @@ -67,16 +67,16 @@ const config = {
'@pages': path.resolve('./src/pages'),
'@routers': path.resolve('./src/routers'),
'@utils': path.resolve('./src/utils'),
'@theme': path.resolve('./src/theme'),
'@theme': path.resolve('./src/theme')
},
symlinks: false,
symlinks: false
},
plugins: [
new Dotenv({
path: path.resolve(__dirname, '..', dotEnv),
path: path.resolve(__dirname, '..', dotEnv)
}),
codeInspectorPlugin({
bundler: 'webpack',
bundler: 'webpack'
}),
new HtmlWebpackPlugin({
title: isDev ? 'Pro React Dev' : 'Pro React Admin',
Expand All @@ -100,8 +100,8 @@ const config = {
minifyCSS: true,
minifyJS: true,
minifyURLs: true,
useShortDoctype: true,
},
useShortDoctype: true
}
}),
new AntdDayjsWebpackPlugin(),
new CaseSensitivePathsPlugin(),
Expand All @@ -115,16 +115,16 @@ const config = {
new NodePolyfillPlugin(),
new WebpackBar(),
new ForkTsCheckerWebpackPlugin({
async: true,
async: true
}),
new ESLintWebpackPlugin({
// 指定检查文件的根目录
context: path.resolve(__dirname, '../src'),
exclude: 'node_modules', // 默认值
cache: true, // 开启缓存
// 缓存目录
cacheLocation: path.resolve(__dirname, '../node_modules/.cache/.eslintcache'),
}),
cacheLocation: path.resolve(__dirname, '../node_modules/.cache/.eslintcache')
})
],
module: {
// 将缺失的导出提示成错误而不是警告
Expand All @@ -133,12 +133,12 @@ const config = {
{
test: /\.m?js$/,
resolve: {
fullySpecified: false,
},
fullySpecified: false
}
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader'],
use: ['style-loader', 'css-loader', 'postcss-loader']
},
{
test: /\.less$/i,
Expand All @@ -155,10 +155,10 @@ const config = {
localIdentName: isDev ? '[path][name]__[local]--[hash:base64:5]' : '[local]--[hash:base64:5]',
localIdentContext: paths.src,
namedExport: false,
exportLocalsConvention: 'camelCase',
exportLocalsConvention: 'camelCase'
},
importLoaders: 2,
},
importLoaders: 2
}
},
{
loader: require.resolve('postcss-loader'),
Expand All @@ -172,18 +172,18 @@ const config = {
'postcss-preset-env',
{
autoprefixer: {
flexbox: 'no-2009',
flexbox: 'no-2009'
},
stage: 3,
},
stage: 3
}
],
'postcss-normalize',
],
'postcss-normalize'
]
},
sourceMap: true,
},
},
],
sourceMap: true
}
}
]
},
{
test: /\.(js|jsx|ts|tsx)$/,
Expand All @@ -193,47 +193,47 @@ const config = {
loader: 'esbuild-loader',
options: {
// loader: 'tsx',
target: 'es2020',
},
target: 'es2020'
}
},
{
loader: 'babel-loader',
options: {
presets: [['@babel/preset-env', { modules: false }], '@babel/preset-react'],
plugins: ['@babel/plugin-transform-object-rest-spread', '@babel/plugin-transform-runtime'],
},
},
],
plugins: ['@babel/plugin-transform-object-rest-spread', '@babel/plugin-transform-runtime']
}
}
]
},
{
test: /\.(png|jpe?g|gif|webp|eot|ttf|woff|woff2|mp4|mp3|mkv|pdf)$/i,
type: 'asset',
parser: {
// Conditions for converting to base64
dataUrlCondition: {
maxSize: 25 * 1024, // 25kb
},
maxSize: 25 * 1024 // 25kb
}
},
generator: {
filename: 'images/[contenthash][ext][query]',
},
filename: 'images/[contenthash][ext][query]'
}
},
{
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'babel-loader',
loader: 'babel-loader'
},
{
loader: '@svgr/webpack',
options: {
babel: false,
icon: true,
},
},
],
},
],
icon: true
}
}
]
}
]
},
stats: {
...(isAnalyze
Expand All @@ -250,23 +250,23 @@ const config = {
dependentModules: true,
reasons: true,
ids: true,
errorDetails: true,
errorDetails: true
}
: {
all: false,
errors: true,
warnings: true,
errorDetails: true,
moduleTrace: true,
excludeAssets: /node_modules/,
}),
excludeAssets: /node_modules/
})
},
// 性能提示
performance: {
hints: isDev ? false : 'warning',
maxEntrypointSize: 512000,
maxAssetSize: 512000,
},
maxAssetSize: 512000
}
}

if (USE_ANALYZE) {
Expand Down
Loading
Loading