From d94b4157ea929d05d7d1d58a395a8647c532e485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BC=8D=E8=BD=BB=E9=B8=A3?= Date: Mon, 4 Mar 2024 16:27:35 +0800 Subject: [PATCH] dev merge (#18) * test: add get videos list test error * remove rs action * doc: update * feat: try add web ui and web server --- .github/workflows/release_rs.yml | 15 -- README.md | 2 - docs/ffmpeg.md | 100 ++++--- docs/ffmpeg_rs.md | 94 ------- garage-ui/package.json | 4 +- garage-ui/src/App.vue | 5 + garage-ui/src/api/user.ts | 12 +- garage-ui/src/components/HelloWorld.vue | 12 + garage-ui/src/layout/DashboardLayout.vue | 36 ++- garage-ui/src/main.ts | 7 + garage-ui/src/theme.ts | 19 ++ garage-ui/src/utils/http.ts | 56 ++-- garage-ui/vite.config.ts | 13 +- garage-ui/yarn.lock | 202 ++++++++++---- garage_cmd/crawl_jav.go | 26 +- garage_cmd/ffmpeg.go | 228 +++++++--------- garage_cmd/main.go | 10 +- garage_di/app.go | 22 ++ garage_ffmpeg/ffmpeg.go | 327 ++++++++++++----------- garage_ffmpeg/ffmpeg_test.go | 48 +++- garage_jav/javbus.go | 237 ++++++++-------- garage_jav/model.go | 1 + go.mod | 2 + go.sum | 10 + taskfile.yaml | 2 + utils/config.go | 15 +- utils/logger.go | 65 +++-- 27 files changed, 837 insertions(+), 733 deletions(-) delete mode 100644 .github/workflows/release_rs.yml delete mode 100644 docs/ffmpeg_rs.md create mode 100644 garage-ui/src/theme.ts diff --git a/.github/workflows/release_rs.yml b/.github/workflows/release_rs.yml deleted file mode 100644 index 0054d7d..0000000 --- a/.github/workflows/release_rs.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Release Rust - -on: - push: - -jobs: - build: - name: cargo test - runs-on: ubuntu-latest - steps: - - name: Check out code - uses: actions/checkout@v4 - - name: Add Rust - uses: dtolnay/rust-toolchain@stable - - run: cargo version diff --git a/README.md b/README.md index 6a27774..c864d48 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,6 @@ # Garage -Unstable version,change to rust - `garage`是一个命令行工具,提供爬虫和批处理等功能。 [更新日志](./CHANGELOG.md) diff --git a/docs/ffmpeg.md b/docs/ffmpeg.md index 106391d..a82bf82 100644 --- a/docs/ffmpeg.md +++ b/docs/ffmpeg.md @@ -4,11 +4,42 @@ 使用 FFMPEG 对多个视频快速转码或其他操作 +- [x] convert 批量视频转码 +- [x] add_sub 批量视频添加字幕 +- [x] add_fonts 批量视频添加字体 + ```shell -# 视频批量转码 -$ garage ffmpeg-batch convert -# 视频批量添加字幕和字体 -$ garage ffmpeg-batch add-sub +# 子命令 +convert batch video convert other format +add_sub batch video add one suffix subtittle +add_fonts batch video add fonts from dir +``` + +## 视频转码 + +```shell +# 硬件加速 https://trac.ffmpeg.org/wiki/HWAccelIntro#VideoToolbox +# h264 转码 +ffmpeg.exe -i input.mp4 -c:v h264_nvenc output.mkv +# h265 cpu 10bit 转码 +ffmpeg -i INPUT.mp4 -c:v libx265 -crf 20 -pix_fmt yuv420p10le OUTPUT.mkv +# h265 nvida加速 10bit 转码 +ffmpeg -i INPUT.mp4 -c:v hevc_nvenc -pix_fmt p010le -rc vbr -cq:v 27 OUTPUT.mkv +# h264/h265 apple silicon 硬件加速转码 +# q:v 1-100 1 lowest, 100 highest +ffmpeg -i INPUT.mp4 -c:v h264_videotoolbox -pix_fmt yuv444p -cq:v 27 OUTPUT.mkv +ffmpeg -i INPUT.mp4 -c:v hevc_videotoolbox -pix_fmt yuv444p -cq:v 27 OUTPUT.mkv +``` + +```shell +$ ffmpeg-batch convert --help + +--input_path <> input directory path +--input_format <> input directory path [default: mp4] +--output_path <> output directory path [default: ./dest] +--output_format <> output directory path [default: mkv] +--advance <> advance string +--exec exec command ``` ## 字幕添加 @@ -18,16 +49,23 @@ $ garage ffmpeg-batch add-sub ```shell ## ffmpeg 对应命令 ffmpeg.exe -i INPUT -c copy -i INPUT.ass -sub_charenc UTF-8 -c copy -map 0 -map -0:s -map 1 -metadata:s:s:0 language=chi -metadata:s:s:0 title="jp&sc" OUTPUT +``` + +```shell ## garage 对应命令 -garage ffmpeg-batch add-sub \ ---input-path="queue" \ ---input-type=".mkv" \ ---input-sub-suffix=".ass" \ ---output-path="result/" \ ---exec=true +$ garage ffmpeg-batch add-sub + +--input_path input directory path +--input_format input directory path [default: mkv] +--sub_suffix sub suffix and extension [default: ass] +--sub_number sub number [default: 0] +--output_path output directory path [default: ./dest] +--output_format output directory path [default: mkv] +--advance <> advance string +--exec exec command ``` -添加字幕同时添加多个字体 +## 添加多个字体 ```shell ## ffmpeg 对应命令 @@ -43,46 +81,6 @@ garage ffmpeg-batch add-sub \ --exec=true ``` -## 视频转码 - -```shell -## ffmpeg 对应命令 -ffmpeg.exe -i input.mp4 -c:v h264_nvenc output.mkv - -## garage 对应命令 -garage.exe ffmpeg-batch convert --input-path="queue" \ ---input-type=".mp4" \ ---output-path="result/" \ ---output-type=".mkv" \ ---advance="-c:v h264_nvenc" --exec -``` - -h256_10bit 转码 - -```shell -ffmpeg -i INPUT.mp4 -c:v libx265 -crf 20 -pix_fmt yuv420p10le OUTPUT.mkv - -## garage 对应命令 -garage.exe ffmpeg-batch convert --input-path="queue" \ ---input-type=".mp4" \ ---output-path="result/" \ ---output-type=".mkv" \ ---advance="-c:v libx265 -crf 20 -pix_fmt yuv420p10le" --exec -``` - -h256_10bit 转码 nvdia 硬件加速 - -```shell -ffmpeg -i INPUT.mp4 -c:v hevc_nvenc -pix_fmt p010le -rc vbr -cq:v 27 OUTPUT.mkv - -## garage 对应命令 -garage.exe ffmpeg-batch convert --input-path="queue" \ ---input-type=".mp4" \ ---output-path="result/" \ ---output-type=".mkv" \ ---advance="-c:v hevc_nvenc -pix_fmt p010le -rc vbr -cq:v 25" --exec -``` - ## 切割视频 && 导出字幕文件 推荐使用 [Lossless Cut](https://github.com/mifi/lossless-cut)软件 diff --git a/docs/ffmpeg_rs.md b/docs/ffmpeg_rs.md deleted file mode 100644 index f294145..0000000 --- a/docs/ffmpeg_rs.md +++ /dev/null @@ -1,94 +0,0 @@ -# 视频批量处理 - -> 系统环境中需要预先安装 ffmpeg - -使用 FFMPEG 对多个视频快速转码或其他操作 - -- [x] convert 批量视频转码 -- [ ] add_sub 批量视频添加字幕 -- [ ] addfonts 批量视频添加字体 - -```shell -# 子命令 -convert batch video convert other format -add_sub batch video add one suffix subtittle -add_fonts batch video add fonts from dir -``` - -## 视频转码 - -```shell -# 硬件加速 https://trac.ffmpeg.org/wiki/HWAccelIntro#VideoToolbox -# h264 转码 -ffmpeg.exe -i input.mp4 -c:v h264_nvenc output.mkv -# h265 cpu 10bit 转码 -ffmpeg -i INPUT.mp4 -c:v libx265 -crf 20 -pix_fmt yuv420p10le OUTPUT.mkv -# h265 nvida加速 10bit 转码 -ffmpeg -i INPUT.mp4 -c:v hevc_nvenc -pix_fmt p010le -rc vbr -cq:v 27 OUTPUT.mkv -# h264/h265 apple silicon 硬件加速转码 -# q:v 1-100 1 lowest, 100 highest -ffmpeg -i INPUT.mp4 -c:v h264_videotoolbox -pix_fmt yuv444p -cq:v 27 OUTPUT.mkv -ffmpeg -i INPUT.mp4 -c:v hevc_videotoolbox -pix_fmt yuv444p -cq:v 27 OUTPUT.mkv -``` - -```shell -$ ffmpeg-batch convert --help - ---input_path <> input directory path ---input_format <> input directory path [default: mp4] ---output_path <> output directory path [default: ./dest] ---output_format <> output directory path [default: mkv] ---advance <> advance string ---exec exec command -``` - -## 字幕添加 - -添加字幕文件替换掉已有字幕, 只支持插入单条字幕。 - -```shell -## ffmpeg 对应命令 -ffmpeg.exe -i INPUT -c copy -i INPUT.ass -sub_charenc UTF-8 -c copy -map 0 -map -0:s -map 1 -metadata:s:s:0 language=chi -metadata:s:s:0 title="jp&sc" OUTPUT -``` - -```shell -## garage 对应命令 -$ garage ffmpeg-batch add-sub - ---input_path input directory path ---input_format input directory path [default: mkv] ---sub_suffix sub suffix and extension [default: ass] ---sub_number sub number [default: 0] ---output_path output directory path [default: ./dest] ---output_format output directory path [default: mkv] ---advance <> advance string ---exec exec command -``` - -## 添加多个字体 - -```shell -## ffmpeg 对应命令 -ffmpeg.exe -i INPUT -c copy -attach INPUT_FONT -metadata:s:t:0 mimetype=application/x-truetype-font - -## garage 对应命令 -garage ffmpeg-batch add-sub \ ---input-path="queue" \ ---input-type=".mkv" \ ---input-sub-suffix=".ass" \ ---input-fonts-path="fonts" \ ---output-path="result/" \ ---exec=true -``` - -## 切割视频 && 导出字幕文件 - -推荐使用 [Lossless Cut](https://github.com/mifi/lossless-cut)软件 - -```shell -ffmpeg -i input.wmv -ss 00:00:30.0 -c copy -t 00:00:10.0 output.wmv -``` - -```shell -ffmpeg -i input.mkv -map 0:s:0 subs.ass -``` diff --git a/garage-ui/package.json b/garage-ui/package.json index 0634ebf..2ad1257 100644 --- a/garage-ui/package.json +++ b/garage-ui/package.json @@ -6,10 +6,11 @@ "scripts": { "dev": "vite", "build": "vue-tsc && vite build", + "build:report": "vue-tsc && vite build", "preview": "vite preview" }, "dependencies": { - "axios": "^1.6.7", + "ky": "^1.2.0", "pinia": "^2.1.7", "vue": "^3.4.15", "vue-router": "^4.2.5", @@ -20,6 +21,7 @@ "@types/node": "^20.11.19", "@vitejs/plugin-vue": "^5.0.3", "less": "^4.2.0", + "rollup-plugin-visualizer": "^5.12.0", "typescript": "^5.2.2", "vite": "^5.1.0", "vite-plugin-vuetify": "^2.0.1", diff --git a/garage-ui/src/App.vue b/garage-ui/src/App.vue index edcdd1b..bdee649 100644 --- a/garage-ui/src/App.vue +++ b/garage-ui/src/App.vue @@ -1,8 +1,13 @@ diff --git a/garage-ui/src/layout/DashboardLayout.vue b/garage-ui/src/layout/DashboardLayout.vue index e5fa3ad..217ff55 100644 --- a/garage-ui/src/layout/DashboardLayout.vue +++ b/garage-ui/src/layout/DashboardLayout.vue @@ -10,28 +10,34 @@ - - Application Bar - + + + + + - - + @@ -41,4 +47,8 @@ import { useLayoutStore } from "@/stores/layout"; const layoutStore = useLayoutStore(); - + diff --git a/garage-ui/src/main.ts b/garage-ui/src/main.ts index 4ec9d0c..af0a0d3 100644 --- a/garage-ui/src/main.ts +++ b/garage-ui/src/main.ts @@ -8,6 +8,7 @@ import "vuetify/styles"; import { createVuetify } from "vuetify"; import { aliases, mdi } from "vuetify/iconsets/mdi"; import "@mdi/font/css/materialdesignicons.css"; +import theme from "./theme"; const vuetify = createVuetify({ icons: { @@ -15,6 +16,12 @@ const vuetify = createVuetify({ aliases, sets: { mdi }, }, + theme: { + defaultTheme: "myCustomLightTheme", + themes: { + myCustomLightTheme: theme, + }, + }, }); const pinia = createPinia(); diff --git a/garage-ui/src/theme.ts b/garage-ui/src/theme.ts new file mode 100644 index 0000000..e36a8fb --- /dev/null +++ b/garage-ui/src/theme.ts @@ -0,0 +1,19 @@ +import { type ThemeDefinition } from "vuetify"; + +const myCustomLightTheme: ThemeDefinition = { + dark: false, + colors: { + background: "#FAFAFA", + surface: "#F5F5F5", + primary: "#6200EE", + "primary-darken-1": "#3700B3", + secondary: "#03DAC6", + "secondary-darken-1": "#018786", + error: "#B00020", + info: "#2196F3", + success: "#4CAF50", + warning: "#FB8C00", + }, +}; + +export default myCustomLightTheme; diff --git a/garage-ui/src/utils/http.ts b/garage-ui/src/utils/http.ts index a64ecd7..551bab8 100644 --- a/garage-ui/src/utils/http.ts +++ b/garage-ui/src/utils/http.ts @@ -1,33 +1,39 @@ -import axios from "axios"; -import { InternalAxiosRequestConfig, AxiosResponse } from "axios"; +import ky from "ky"; -const http = axios.create({ - baseURL: "", +const httpv2 = ky.create({ + prefixUrl: "https://example.com/api", timeout: 5000, headers: { "Content-Type": "application/json", }, -}); - -http.interceptors.request.use( - (config: InternalAxiosRequestConfig) => { - console.log(config.baseURL); - return config; - }, - (error: any) => { - console.error(error); - return Promise.reject(error); - } -); + retry: 1, + hooks: { + beforeRequest: [ + (options) => { + console.log("before request", options); + }, + ], + beforeRetry: [ + async ({ error }) => { + console.log("before retry", error); + }, + ], + beforeError: [ + (error) => { + console.log("before error:", error); -http.interceptors.response.use( - (resp: AxiosResponse) => { - return resp; + return error; + }, + ], + afterResponse: [ + (request, _options, response) => { + console.log("after reponse:", response.status); + console.log("after reponse:", request.headers); + return new Response("A different response", { status: 200 }); + }, + ], }, - (error: any) => { - console.error("resp error: ", error); - return Promise.reject(error); - } -); + throwHttpErrors: false, +}); -export default http; +export { httpv2 }; diff --git a/garage-ui/vite.config.ts b/garage-ui/vite.config.ts index 089ef6e..25dc95b 100644 --- a/garage-ui/vite.config.ts +++ b/garage-ui/vite.config.ts @@ -3,10 +3,21 @@ import { splitVendorChunkPlugin } from "vite"; import vuetify from "vite-plugin-vuetify"; import vue from "@vitejs/plugin-vue"; import path from "path"; +import { visualizer } from "rollup-plugin-visualizer"; + +const buildReportplugin = + process.env.npm_lifecycle_event === "build:report" + ? visualizer({ + open: true, + brotliSize: true, + gzipSize: true, + filename: "dist/report.html", + }) + : null; // https://vitejs.dev/config/ export default defineConfig({ - plugins: [vue(), vuetify(), splitVendorChunkPlugin()], + plugins: [vue(), vuetify(), splitVendorChunkPlugin(), buildReportplugin], server: { host: "0.0.0.0", port: 3000, diff --git a/garage-ui/yarn.lock b/garage-ui/yarn.lock index 077ec87..b49c438 100644 --- a/garage-ui/yarn.lock +++ b/garage-ui/yarn.lock @@ -342,19 +342,17 @@ dependencies: upath "^2.0.1" -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -axios@^1.6.7: - version "1.6.7" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7" - integrity sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA== +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: - follow-redirects "^1.15.4" - form-data "^4.0.0" - proxy-from-env "^1.1.0" + color-convert "^2.0.1" balanced-match@^1.0.0: version "1.0.2" @@ -368,12 +366,26 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== dependencies: - delayed-stream "~1.0.0" + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== computeds@^0.0.1: version "0.0.1" @@ -404,10 +416,15 @@ debug@^4.3.3: dependencies: ms "2.1.2" -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== entities@^4.5.0: version "4.5.0" @@ -450,30 +467,26 @@ esbuild@^0.19.3: "@esbuild/win32-ia32" "0.19.12" "@esbuild/win32-x64" "0.19.12" +escalade@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + estree-walker@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== -follow-redirects@^1.15.4: - version "1.15.5" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" - integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== - -form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - fsevents@~2.3.2, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + graceful-fs@^4.1.2: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" @@ -496,11 +509,33 @@ image-size@~0.5.0: resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" integrity sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ== +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + is-what@^3.14.1: version "3.14.1" resolved "https://registry.yarnpkg.com/is-what/-/is-what-3.14.1.tgz#e1222f46ddda85dead0fd1c9df131760e77755c1" integrity sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA== +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +ky@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ky/-/ky-1.2.0.tgz#698be7d2c48cf875d2d4721fc4c69b681877a112" + integrity sha512-dnPW+T78MuJ9tLAiF/apJV7bP7RRRCARXQxsCmsWiKLXqGtMBOgDVOFRYzCAfNe/OrRyFyor5ESgvvC+QWEqOA== + less@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/less/-/less-4.2.0.tgz#cbefbfaa14a4cd388e2099b2b51f956e1465c450" @@ -540,18 +575,6 @@ make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@^2.1.12: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - mime@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -587,6 +610,15 @@ needle@^3.1.0: iconv-lite "^0.6.3" sax "^1.2.4" +open@^8.4.0: + version "8.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + parse-node-version@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" @@ -602,6 +634,11 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + pify@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" @@ -624,16 +661,26 @@ postcss@^8.4.33, postcss@^8.4.35: picocolors "^1.0.0" source-map-js "^1.0.2" -proxy-from-env@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== - prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +rollup-plugin-visualizer@^5.12.0: + version "5.12.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.12.0.tgz#661542191ce78ee4f378995297260d0c1efb1302" + integrity sha512-8/NU9jXcHRs7Nnj07PF2o4gjxmm9lXIrZ8r175bT9dK8qoLlvKTwRMArRCMgpMGlq8CTLugRvEmyMeMXIU2pNQ== + dependencies: + open "^8.4.0" + picomatch "^2.3.1" + source-map "^0.7.4" + yargs "^17.5.1" + rollup@^4.2.0: version "4.12.0" resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.12.0.tgz#0b6d1e5f3d46bbcf244deec41a7421dc54cc45b5" @@ -683,11 +730,32 @@ source-map-js@^1.0.2: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== +source-map@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== + source-map@~0.6.0: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + tslib@^2.3.0: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" @@ -773,7 +841,39 @@ vuetify@^3.5.4: resolved "https://registry.yarnpkg.com/vuetify/-/vuetify-3.5.4.tgz#f919c5194995a123815c277a95812bc230e33464" integrity sha512-fHgfWMI7+z/UtbVPOezX+O1MNBOOMBW9HnKejcBIyQQ7jFRnTHbDQmbINf25FK0wrg/zkjfzyOmWWREKW39eXg== +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.5.1: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" diff --git a/garage_cmd/crawl_jav.go b/garage_cmd/crawl_jav.go index 430b1f0..e81ebe1 100644 --- a/garage_cmd/crawl_jav.go +++ b/garage_cmd/crawl_jav.go @@ -2,7 +2,6 @@ package main import ( "github.com/gsxhnd/garage/garage_jav" - "github.com/gsxhnd/garage/utils" "github.com/urfave/cli/v2" ) @@ -35,9 +34,6 @@ var javCodeCmd = &cli.Command{ javOutputFlag, }, Action: func(ctx *cli.Context) error { - var ( - logger = utils.GetLogger() - ) opt := &garage_jav.JavCrawlConfig{ Code: ctx.Args().Get(0), DownloadMagent: ctx.Bool("magnet"), @@ -47,12 +43,12 @@ var javCodeCmd = &cli.Command{ c, err := garage_jav.NewJavbusCrawl(logger, opt) if err != nil { - logger.Panic("client init error: " + err.Error()) + logger.Panicw("client init error: " + err.Error()) return err } if err := c.StartCrawlJavbusMovie(); err != nil { - logger.Panic("crawl error: " + err.Error()) + logger.Panicw("crawl error: " + err.Error()) return err } @@ -75,7 +71,6 @@ var javPrefixCmd = &cli.Command{ &cli.IntFlag{Name: "prefix-max", Value: 5, Usage: "番号结束编号"}, }, Action: func(ctx *cli.Context) error { - var logger = utils.GetLogger() opt := &garage_jav.JavCrawlConfig{ Proxy: ctx.String("proxy"), @@ -88,12 +83,12 @@ var javPrefixCmd = &cli.Command{ c, err := garage_jav.NewJavbusCrawl(logger, opt) if err != nil { - logger.Panic("client init error: " + err.Error()) + logger.Panicw("client init error: " + err.Error()) return err } if err := c.StartCrawlJavbusMovieByPrefix(); err != nil { - logger.Panic("crawl error: " + err.Error()) + logger.Panicw("crawl error: " + err.Error()) return err } return nil @@ -111,9 +106,6 @@ var javStarCodeCmd = &cli.Command{ &cli.StringFlag{Name: "star-code", Value: "vfn", Usage: "演员番号"}, }, Action: func(ctx *cli.Context) error { - var ( - logger = utils.GetLogger() - ) opt := &garage_jav.JavCrawlConfig{ DownloadMagent: ctx.Bool("magnet"), @@ -124,12 +116,12 @@ var javStarCodeCmd = &cli.Command{ c, err := garage_jav.NewJavbusCrawl(logger, opt) if err != nil { - logger.Panic("client init error: " + err.Error()) + logger.Panicw("client init error: " + err.Error()) return err } if err := c.StartCrawlJavbusMovieByStar(); err != nil { - logger.Panic("crawl error: " + err.Error()) + logger.Panicw("crawl error: " + err.Error()) return err } return nil @@ -146,8 +138,6 @@ var javStarCodeFromDirCmd = &cli.Command{ &cli.StringFlag{Name: "input", Required: true}, }, Action: func(ctx *cli.Context) error { - var logger = utils.GetLogger() - opt := &garage_jav.JavCrawlConfig{ DownloadMagent: ctx.Bool("magnet"), Proxy: ctx.String("proxy"), @@ -156,12 +146,12 @@ var javStarCodeFromDirCmd = &cli.Command{ c, err := garage_jav.NewJavbusCrawl(logger, opt) if err != nil { - logger.Panic("client init error: " + err.Error()) + logger.Panicw("client init error: " + err.Error()) return err } if err := c.StartCrawlJavbusMovieByFilepath(ctx.String("input")); err != nil { - logger.Panic("crawl error: " + err.Error()) + logger.Panicw("crawl error: " + err.Error()) return err } return nil diff --git a/garage_cmd/ffmpeg.go b/garage_cmd/ffmpeg.go index 403e9ab..395290a 100644 --- a/garage_cmd/ffmpeg.go +++ b/garage_cmd/ffmpeg.go @@ -1,77 +1,66 @@ package main import ( + "context" + "github.com/gsxhnd/garage/garage_ffmpeg" - "github.com/gsxhnd/garage/utils" "github.com/urfave/cli/v2" "go.uber.org/zap" ) -var ( - ffmpegBatchInputPathFlag = &cli.StringFlag{ - Name: "input-path", - Value: "./", - Usage: "源视频路径", - } - ffmpegBatchInputTypeFlag = &cli.StringFlag{ - Name: "input-type", - Value: ".mkv", - Usage: "源视频后缀", - } - ffmpegBatchInputSubSuffixFlag = &cli.StringFlag{ - Name: "input-sub-suffix", - Value: ".ass", - Usage: "添加的字幕后缀", - } - ffmpegBatchInputSubNoFlag = &cli.IntFlag{ - Name: "input-sub-no", - Value: 0, - Usage: "添加的字幕所处流的位置", - } - ffmpegBatchInputSubLangFlag = &cli.StringFlag{ - Name: "input-sub-lang", - Value: "chi", - Usage: "添加的字幕语言缩写其他语言请参考ffmpeg", - } - ffmpegBatchInputSubTitleFlag = &cli.StringFlag{ - Name: "input-sub-title", - Value: "Chinese", - Usage: "添加的字幕标题", - } - ffmpegBatchInputFontsPathFlag = &cli.StringFlag{ - Name: "input-fonts-path", - Usage: "添加的字体文件夹", - } - ffmpegBatchOutputDestPathFlag = &cli.StringFlag{ - Name: "output-path", - Value: "./result/", - Usage: "转换后文件存储位置", - } - ffmpegBatchOutputTypeFlag = &cli.StringFlag{ - Name: "output-type", - Value: ".mkv", - Usage: "转换后的视频后缀", - } - ffmpegBatchAdvanceFlag = &cli.StringFlag{ - Name: "advance", - Value: "", - Usage: "高级自定义参数", - } - ffmpegBatchExecFlag = &cli.BoolFlag{ - Name: "exec", - Value: false, - Usage: "是否执行批处理命令False时仅打印命令", - } -) +type garage_ffmpeg_option string var ffmpegBatchCmd = &cli.Command{ - Name: "ffmpeg-batch", + Name: "ffmpeg_batch", Description: "ffmpeg视频批处理工具,支持视频格式转换、字幕添加和字体添加", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "input_path", + Value: "./", + Usage: "源视频路径", + }, &cli.StringFlag{ + Name: "input_format", + Value: ".mkv", + Usage: "源视频后缀", + }, + &cli.StringFlag{ + Name: "output_path", + Value: "./result/", + Usage: "转换后文件存储位置", + }, + &cli.StringFlag{ + Name: "output_format", + Value: ".mkv", + Usage: "转换后的视频后缀", + }, + &cli.StringFlag{ + Name: "advance", + Value: "", + Usage: "高级自定义参数", + }, + &cli.BoolFlag{ + Name: "exec", + Value: false, + Usage: "是否执行批处理命令False时仅打印命令", + }, + }, Subcommands: []*cli.Command{ ffmpegBatchConvertCmd, ffmpegBatchAddSubCmd, ffmpegBatchAddFontCmd, }, + Before: func(ctx *cli.Context) error { + var opt = garage_ffmpeg.VideoBatchOption{ + InputPath: ctx.String("input_path"), + InputFormat: ctx.String("input_format"), + OutputPath: ctx.String("output_path"), + OutputFormat: ctx.String("output_format"), + Advance: ctx.String("advance"), + Exec: ctx.Bool("exec"), + } + ctx.Context = context.WithValue(ctx.Context, garage_ffmpeg_option("opt"), opt) + return nil + }, } var ffmpegBatchConvertCmd = &cli.Command{ @@ -79,27 +68,13 @@ var ffmpegBatchConvertCmd = &cli.Command{ Aliases: nil, Usage: "视频转换批处理", UsageText: "", - Flags: []cli.Flag{ - ffmpegBatchInputPathFlag, - ffmpegBatchInputTypeFlag, - ffmpegBatchOutputDestPathFlag, - ffmpegBatchOutputTypeFlag, - ffmpegBatchAdvanceFlag, - ffmpegBatchExecFlag, - }, Action: func(c *cli.Context) error { var ( - logger = utils.GetLogger() - // inputPath = c.String("input-path") - // inputType = c.String("input-type") - // outputPath = c.String("output-path") - // outputType = c.String("output-type") - // advance = c.String("advance") + opt = c.Context.Value(garage_ffmpeg_option("opt")).(garage_ffmpeg.VideoBatchOption) ) - - vb, err := garage_ffmpeg.NewVideoBatch(logger, nil) + vb, err := garage_ffmpeg.NewVideoBatch(&opt) if err != nil { - logger.Panic("Create dest path error", zap.Error(err)) + logger.Panicf("Create dest path error", zap.Error(err)) return err } return vb.StartConvertBatch() @@ -107,87 +82,68 @@ var ffmpegBatchConvertCmd = &cli.Command{ } var ffmpegBatchAddSubCmd = &cli.Command{ - Name: "add-sub", + Name: "add_sub", Aliases: nil, Usage: "视频添加字幕批处理", UsageText: "", Flags: []cli.Flag{ - ffmpegBatchInputPathFlag, - ffmpegBatchInputTypeFlag, - ffmpegBatchInputSubSuffixFlag, - ffmpegBatchInputSubNoFlag, - ffmpegBatchInputSubTitleFlag, - ffmpegBatchInputSubLangFlag, - ffmpegBatchInputFontsPathFlag, - ffmpegBatchOutputDestPathFlag, - ffmpegBatchExecFlag, + &cli.StringFlag{ + Name: "input_fonts_path", + Usage: "添加的字体文件夹", + }, + &cli.StringFlag{ + Name: "input_sub_suffix", + Value: ".ass", + Usage: "添加的字幕后缀", + }, + &cli.IntFlag{ + Name: "input_sub_no", + Value: 0, + Usage: "添加的字幕所处流的位置", + }, + &cli.StringFlag{ + Name: "input_sub_lang", + Value: "chi", + Usage: "添加的字幕语言缩写其他语言请参考ffmpeg", + }, + &cli.StringFlag{ + Name: "input_sub_title", + Value: "Chinese", + Usage: "添加的字幕标题", + }, }, - Action: func(c *cli.Context) error { - var ( - logger = utils.GetLogger() - // inputPath = c.String("input-path") - // inputType = c.String("input-type") - // inputSubNo = c.Int("input-sub-no") - // inputSubSuffix = c.String("input-sub-suffix") - // inputSubLang = c.String("input-sub-lang") - // inputSubTitle = c.String("input-sub-title") - // inputFontsPath = c.String("input-fonts-path") - // outputPath = c.String("output-path") - ) - vb, err := garage_ffmpeg.NewVideoBatch(logger, nil) + Action: func(ctx *cli.Context) error { + var opt = ctx.Context.Value(garage_ffmpeg_option("opt")).(garage_ffmpeg.VideoBatchOption) + opt.FontsPath = ctx.String("input_fonts_path") + vb, err := garage_ffmpeg.NewVideoBatch(&opt) if err != nil { - logger.Panic("Create dest path error", zap.Error(err)) + logger.Panicf("Create dest path error", zap.Error(err)) return err } return vb.StartAddSubtittleBatch() - // if err != nil { - // logger.Panic("Get add subtitle batch error", zap.Error(err)) - // } - // logger.Info("Get all videos, starting add subtitle") - // for _, cmd := range batch { - // if !c.Bool("exec") { - // logger.Sugar().Infof("cmd: %v", cmd) - // } else { - // startTime := time.Now() - // logger.Sugar().Infof("Start add subtitle into video, cmd: %v", cmd) - // cmd := exec.Command("powershell", cmd) - // cmd.Stdout = os.Stdout - // cmd.Stderr = os.Stderr - // err := cmd.Run() - // if err != nil { - // logger.Sugar().Errorf("cmd errror: %v", err) - // } - // logger.Sugar().Infof("Finished add subtitle into video") - // logger.Sugar().Infof("Finished convert video, spent time: %v sec", time.Since(startTime).Seconds()) - // } - // } }, } var ffmpegBatchAddFontCmd = &cli.Command{ - Name: "add-fonts", + Name: "add_fonts", Aliases: nil, Usage: "视频添加字体批处理", UsageText: "", Flags: []cli.Flag{ - ffmpegBatchInputPathFlag, - ffmpegBatchInputTypeFlag, - ffmpegBatchInputFontsPathFlag, - ffmpegBatchOutputDestPathFlag, - ffmpegBatchExecFlag, + &cli.StringFlag{ + Name: "input_fonts_path", + Usage: "添加的字体文件夹", + Required: true, + }, }, - Action: func(c *cli.Context) error { - var ( - logger = utils.GetLogger() - // inputPath = c.String("input-path") - // inputType = c.String("input-type") - // inputFontsPath = c.String("input-fonts-path") - // outputPath = c.String("output-path") - ) - vb, err := garage_ffmpeg.NewVideoBatch(logger, nil) + Action: func(ctx *cli.Context) error { + var opt = ctx.Context.Value(garage_ffmpeg_option("opt")).(garage_ffmpeg.VideoBatchOption) + opt.FontsPath = ctx.String("input_fonts_path") + vb, err := garage_ffmpeg.NewVideoBatch(&opt) + if err != nil { - logger.Panic("Create dest path error", zap.Error(err)) + logger.Panicf("Create dest path error", zap.Error(err)) return err } diff --git a/garage_cmd/main.go b/garage_cmd/main.go index 81e0dbe..7285c2e 100644 --- a/garage_cmd/main.go +++ b/garage_cmd/main.go @@ -6,12 +6,20 @@ import ( "log" "os" + "github.com/gsxhnd/garage/utils" "github.com/urfave/cli/v2" _ "github.com/glebarez/go-sqlite" ) -var RootCmd = cli.NewApp() +var ( + RootCmd = cli.NewApp() + logger = utils.NewLogger(&utils.Config{ + Log: utils.LogConfig{ + Level: "debug", + }, + }) +) func init() { RootCmd.HideVersion = true diff --git a/garage_di/app.go b/garage_di/app.go index 7418d24..d5ad74f 100644 --- a/garage_di/app.go +++ b/garage_di/app.go @@ -1,9 +1,12 @@ package garage_di import ( + "fmt" "io/fs" "log" "net/http" + "os/exec" + "runtime" "github.com/gin-gonic/gin" garage_ui "github.com/gsxhnd/garage/garage-ui" @@ -15,6 +18,23 @@ type Application struct { router *routes.Routes } +// Open calls the OS default program for uri +func Open(uri string) error { + var commands = map[string]string{ + "windows": "cmd /c start", + "darwin": "open", + "linux": "xdg-open", + } + + run, ok := commands[runtime.GOOS] + if !ok { + return fmt.Errorf("don't know how to open things on %s platform", runtime.GOOS) + } + + cmd := exec.Command(run, uri) + return cmd.Start() +} + func NewApplication(r *routes.Routes) *Application { r.Init() return &Application{ @@ -41,6 +61,8 @@ func (a *Application) Run() error { return a.router.Engine.Run("0.0.0.0:8080") }) + Open("http://localhost:8081") + if err := g.Wait(); err != nil { return err } else { diff --git a/garage_ffmpeg/ffmpeg.go b/garage_ffmpeg/ffmpeg.go index d78198a..477745e 100644 --- a/garage_ffmpeg/ffmpeg.go +++ b/garage_ffmpeg/ffmpeg.go @@ -1,16 +1,12 @@ package garage_ffmpeg import ( + "errors" "fmt" "os" "os/exec" "path" "path/filepath" - "strconv" - "strings" - "time" - - "go.uber.org/zap" ) type VideoBatchOption struct { @@ -28,139 +24,234 @@ type VideoBatchOption struct { } type VideoBatcher interface { - createDestDir() error // 创建输出后的文件夹 - getVideosList() error // 获取视频列表 - getFontsList() error // 获取字体列表 - getFontsParams() (string, error) // 获取字体列表 - StartAddSubtittleBatch() error // 添加字幕 - StartAddFontsBatch() error // 添加字体 - StartConvertBatch() error // 转换视频 - GetConvertBatch() error // 转换视频 + createDestDir() error // 创建输出后的文件夹 + GetVideosList() ([]string, error) // 获取视频列表s + GetFontsList() ([]string, error) // 获取字体列表 + GetFontsParams() (string, error) // 获取字体列表 + GetConvertBatch() ([]string, error) // 获取转换视频命令 + StartConvertBatch() error // 转换视频 + GetAddFontsBatch() ([]string, error) // 获取添加字体命令 + StartAddFontsBatch() error // 添加字体 + GetAddSubtittleBatch() ([]string, error) // + StartAddSubtittleBatch() error // 添加字幕 executeBatch() error } type videoBatch struct { - option *VideoBatchOption - videosList []string - fontsList []string - fontsParams string - cmdBatch []string - logger *zap.Logger + option *VideoBatchOption + cmdBatch []string } +var FONT_EXT = []string{".ttf", ".otf", ".ttc"} + const CONVERT_TEMPLATE = `ffmpeg.exe -i "%v" %v "%v"` const ADD_SUB_TEMPLATE = `ffmpeg.exe -i "%s" -sub_charenc UTF-8 -i "%s" -map 0 -map 1 -metadata:s:s:%v language=%v -metadata:s:s:%v title="%v" -c copy %s "%v"` const ADD_FONT_TEMPLATE = `ffmpeg.exe -i "%s" -c copy %s "%v"` const FONT_TEMPLATE = `-attach "%s" -metadata:s:t:%v mimetype=application/x-truetype-font ` -func NewVideoBatch(l *zap.Logger, opt *VideoBatchOption) (VideoBatcher, error) { - client := &videoBatch{ - logger: l, - option: opt, - videosList: make([]string, 0), - fontsList: make([]string, 0), - fontsParams: "", - cmdBatch: make([]string, 0), +func NewVideoBatch(opt *VideoBatchOption) (VideoBatcher, error) { + // if err := client.createDestDir(); err != nil { + // return nil, err + // } + return &videoBatch{ + option: opt, + cmdBatch: make([]string, 0), + }, nil +} + +func (vb *videoBatch) GetVideosList() ([]string, error) { + var videosList []string = make([]string, 0) + if err := filepath.Walk(vb.option.InputPath, func(path string, fi os.FileInfo, err error) error { + if err != nil { + return err + } + + if fi.IsDir() { + return nil + } + + filename := fi.Name() + fileExt := filepath.Ext(filename) + // vb.logger.Debug("get video filename: " + filename) + + if fileExt == "."+vb.option.InputFormat { + videosList = append(videosList, path) + return nil + } + return nil + }); err != nil { + return nil, err + } else { + return videosList, nil } +} - if err := client.createDestDir(); err != nil { +func (vb *videoBatch) GetFontsList() ([]string, error) { + var fontsList []string = make([]string, 0) + if err := filepath.Walk(vb.option.FontsPath, func(path string, fi os.FileInfo, err error) error { + if err != nil { + return err + } + + if fi.IsDir() { + return nil + } + + filename := fi.Name() + fileExt := filepath.Ext(filename) + + for _, b := range FONT_EXT { + if fileExt == b { + fontsList = append(fontsList, filename) + } + } + return nil + }); err != nil { return nil, err + } else { + return fontsList, nil } - return client, nil } -func (vb *videoBatch) GetConvertBatch() error { - if err := vb.getVideosList(); err != nil { - return err +func (vb *videoBatch) GetFontsParams() (string, error) { + var fontsParams = "" + fontsList, err := vb.GetFontsList() + if err != nil { + return "", nil } - for _, v := range vb.videosList { + for i, v := range fontsList { + fontPath := filepath.Join(vb.option.FontsPath, v) + fontsParams += fmt.Sprintf(FONT_TEMPLATE, fontPath, i) + } + + return fontsParams, nil +} + +func (vb *videoBatch) GetConvertBatch() ([]string, error) { + videosList, err := vb.GetVideosList() + if err != nil { + return nil, err + } + + for _, v := range videosList { inputVideo := filepath.Join(vb.option.InputPath, v+vb.option.InputFormat) outputVideo := filepath.Join(vb.option.OutputPath, v+vb.option.OutputFormat) s := fmt.Sprintf(CONVERT_TEMPLATE, inputVideo, vb.option.Advance, outputVideo) vb.cmdBatch = append(vb.cmdBatch, s) } - return nil + return vb.cmdBatch, nil } func (vb *videoBatch) StartConvertBatch() error { - vb.GetConvertBatch() + _, err := vb.GetConvertBatch() + if err != nil { + return err + } return vb.executeBatch() } -func (vb *videoBatch) StartAddSubtittleBatch() error { - if err := vb.getVideosList(); err != nil { - return err +func (vb *videoBatch) GetAddFontsBatch() ([]string, error) { + videosList, err := vb.GetVideosList() + if err != nil { + return nil, err } - vb.logger.Debug("Source videos directory: " + vb.option.InputPath) - vb.logger.Debug("Get matching video count: " + strconv.Itoa(len(vb.videosList))) - vb.logger.Debug("Target video's subtitle stream number: " + strconv.Itoa(vb.option.InputSubNo)) - vb.logger.Debug("Target video's subtitle language: " + vb.option.InputSubLang) - vb.logger.Debug("Target video's subtitle title: " + vb.option.InputSubTitle) - - if vb.option.FontsPath != "" { - if err := vb.getFontsList(); err != nil { - return err - } - vb.logger.Info("Target video's font paths: " + vb.option.FontsPath) - vb.logger.Info(fmt.Sprintf("Attach fonts parameters: %v", vb.fontsParams)) - } else { - vb.logger.Info("Target video's font paths not set, skip.") + fontsParams, err := vb.GetFontsParams() + if err != nil { + return nil, err } - vb.logger.Info("Dest video directory: " + vb.option.OutputPath) - template := `ffmpeg.exe -i "%s" -sub_charenc UTF-8 -i "%s" -map 0 -map 1 -metadata:s:s:%v language=%v -metadata:s:s:%v title="%v" -c copy %s "%v"` - for _, v := range vb.videosList { + for _, v := range videosList { sourceVideo := filepath.Join(vb.option.InputPath, v+vb.option.InputFormat) - sourceSubtitle := filepath.Join(vb.option.InputPath, v+vb.option.InputSubSuffix) destVideo := filepath.Join(vb.option.OutputPath, v+vb.option.InputFormat) - s := fmt.Sprintf(template, - sourceVideo, sourceSubtitle, vb.option.InputSubNo, - vb.option.InputSubLang, vb.option.InputSubNo, vb.option.InputSubTitle, - vb.fontsParams, destVideo) + s := fmt.Sprintf(ADD_FONT_TEMPLATE, sourceVideo, fontsParams, destVideo) vb.cmdBatch = append(vb.cmdBatch, s) } - vb.logger.Info("Get all videos, starting convert") - if vb.option.Exec { + return vb.cmdBatch, nil +} + +func (vb *videoBatch) StartAddFontsBatch() error { + // if fontsList, err := vb.GetFontsList(); err != nil { + // return err + // } + + // vb.logger.Info("Source videos directory: " + vb.option.InputPath) + // vb.logger.Info("Get matching video count: " + strconv.Itoa(len(vb.videosList))) + // vb.logger.Info("Target video's font paths: " + vb.option.FontsPath) + // vb.logger.Info(fmt.Sprintf("Attach fonts parameters: %v", vb.fontsParams)) + // vb.logger.Info("Dest video directory: " + vb.option.OutputPath) + + if !vb.option.Exec { return nil + } else { + _, err := vb.GetConvertBatch() + if err != nil { + return err + } + return vb.executeBatch() } - return nil } -func (vb *videoBatch) StartAddFontsBatch() error { - if err := vb.getVideosList(); err != nil { - return err +func (vb *videoBatch) GetAddSubtittleBatch() ([]string, error) { + videosList, err := vb.GetVideosList() + if err != nil { + return nil, err } - if err := vb.getFontsList(); err != nil { - return err + // if vb.option.FontsPath != "" { + // } + + fontsParams, err := vb.GetFontsParams() + if err != nil { + return nil, err } - vb.logger.Info("Source videos directory: " + vb.option.InputPath) - vb.logger.Info("Get matching video count: " + strconv.Itoa(len(vb.videosList))) - vb.logger.Info("Target video's font paths: " + vb.option.FontsPath) - vb.logger.Info(fmt.Sprintf("Attach fonts parameters: %v", vb.fontsParams)) - vb.logger.Info("Dest video directory: " + vb.option.OutputPath) - template := `ffmpeg.exe -i "%s" -c copy %s "%v"` - for _, v := range vb.videosList { + for _, v := range videosList { sourceVideo := filepath.Join(vb.option.InputPath, v+vb.option.InputFormat) + sourceSubtitle := filepath.Join(vb.option.InputPath, v+vb.option.InputSubSuffix) destVideo := filepath.Join(vb.option.OutputPath, v+vb.option.InputFormat) - s := fmt.Sprintf(template, sourceVideo, vb.fontsParams, destVideo) + s := fmt.Sprintf(ADD_SUB_TEMPLATE, + sourceVideo, sourceSubtitle, vb.option.InputSubNo, + vb.option.InputSubLang, vb.option.InputSubNo, vb.option.InputSubTitle, + fontsParams, destVideo) vb.cmdBatch = append(vb.cmdBatch, s) } - if vb.option.Exec { + return vb.cmdBatch, nil +} + +func (vb *videoBatch) StartAddSubtittleBatch() error { + + // vb.logger.Debug("Source videos directory: " + vb.option.InputPath) + // vb.logger.Debug("Get matching video count: " + strconv.Itoa(len(vb.videosList))) + // vb.logger.Debug("Target video's subtitle stream number: " + strconv.Itoa(vb.option.InputSubNo)) + // vb.logger.Debug("Target video's subtitle language: " + vb.option.InputSubLang) + // vb.logger.Debug("Target video's subtitle title: " + vb.option.InputSubTitle) + + // vb.logger.Info("Target video's font paths: " + vb.option.FontsPath) + // vb.logger.Info(fmt.Sprintf("Attach fonts parameters: %v", vb.fontsParams)) + // vb.logger.Info("Target video's font paths not set, skip.") + // vb.logger.Info("Dest video directory: " + vb.option.OutputPath) + + // vb.logger.Info("Get all videos, starting convert") + + if !vb.option.Exec { return nil + } else { + _, err := vb.GetAddSubtittleBatch() + if err != nil { + return err + } + return vb.executeBatch() } - return nil } func (vb *videoBatch) createDestDir() error { destDir := path.Join(vb.option.OutputPath) - vb.logger.Info("Start creating destination directory: " + destDir) + // vb.logger.Info("Start creating destination directory: " + destDir) if fi, err := os.Stat(destDir); err != nil { if os.IsNotExist(err) { os.MkdirAll(destDir, os.ModePerm) @@ -169,94 +260,32 @@ func (vb *videoBatch) createDestDir() error { } } else { if fi.IsDir() { - vb.logger.Info("Destination directory already exists") + return errors.New("destination directory already exists") + // vb.logger.Info("Destination directory already exists") } } - vb.logger.Info("Destination directory created") + // vb.logger.Info("Destination directory created") return nil } -func (vb *videoBatch) getVideosList() error { - err := filepath.Walk(vb.option.InputPath, func(path string, fi os.FileInfo, err error) error { - if fi == nil { - if err != nil { - return err - } - return nil - } - if fi.IsDir() { - return nil - } - filename := fi.Name() - vb.logger.Debug("get video filename: " + filename) - fileExt := filepath.Ext(filename) - if fileExt == vb.option.InputFormat { - fileName := strings.TrimSuffix(filename, fileExt) - vb.videosList = append(vb.videosList, fileName) - return nil - } - return nil - }) - return err -} - -func (vb *videoBatch) getFontsList() error { - fontExts := []string{".ttf", ".otf", ".ttc"} - - err := filepath.Walk(vb.option.FontsPath, func(path string, fi os.FileInfo, err error) error { - if fi == nil { - if err != nil { - return err - } - return nil - } - if fi.IsDir() { - return nil - } - filename := fi.Name() - fileExt := filepath.Ext(filename) - - for _, b := range fontExts { - if fileExt == b { - vb.fontsList = append(vb.fontsList, filename) - } - } - return nil - }) - return err -} - -func (vb *videoBatch) getFontsParams() (string, error) { - if err := vb.getFontsList(); err != nil { - return "", nil - } - var fontsParams = "" - - for i, v := range vb.fontsList { - fontPath := filepath.Join(vb.option.FontsPath, v) - fontsParams += fmt.Sprintf(FONT_TEMPLATE, fontPath, i) - } - return fontsParams, nil -} - func (vb *videoBatch) executeBatch() error { for _, cmd := range vb.cmdBatch { if vb.option.Exec { - vb.logger.Sugar().Infof("cmd: %v", cmd) + // vb.logger.Sugar().Infof("cmd: %v", cmd) return nil } - startTime := time.Now() - vb.logger.Sugar().Infof("Start convert video cmd: %v", cmd) + // startTime := time.Now() + // vb.logger.Sugar().Infof("Start convert video cmd: %v", cmd) cmd := exec.Command("powershell", cmd) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err := cmd.Run() if err != nil { - vb.logger.Sugar().Errorf("cmd error: %v", err) + // vb.logger.Sugar().Errorf("cmd error: %v", err) return err } - vb.logger.Sugar().Infof("Finished convert video, spent time: %v sec", time.Since(startTime).Seconds()) + // vb.logger.Sugar().Infof("Finished convert video, spent time: %v sec", time.Since(startTime).Seconds()) } return nil } diff --git a/garage_ffmpeg/ffmpeg_test.go b/garage_ffmpeg/ffmpeg_test.go index 98f2963..97b64d0 100644 --- a/garage_ffmpeg/ffmpeg_test.go +++ b/garage_ffmpeg/ffmpeg_test.go @@ -1,41 +1,61 @@ package garage_ffmpeg import ( + "strconv" "testing" - "github.com/gsxhnd/garage/utils" "github.com/stretchr/testify/assert" ) -var logger = utils.GetLogger() +func createCorrectVideos(inputPath, formart string) []string { + var a = make([]string, 0) + for i := 1; i < 5; i++ { + for j := 1; j < 6; j++ { + a = append(a, inputPath+"/"+strconv.Itoa(i)+"/"+strconv.Itoa(j)+"."+formart) + } + } + return a +} func Test_videoBatch_getVideosList(t *testing.T) { type args struct { - inputPath string + InputPath string InputFormat string } + tests := []struct { name string args args want []string wantErr bool }{ - {"test_mkv", args{inputPath: "../testdata", InputFormat: ".mkv"}, []string{"1", "2", "3", "4", "5"}, false}, - {"test_mp4", args{inputPath: "../testdata", InputFormat: ".mp4"}, []string{"1", "2", "3", "4", "5"}, false}, - // {"test_err", args{sourceRootPath: "../../testdata", sourceVideoType: ".mp4"}, []string{}, false}, + {"test_mkv", args{InputPath: "../testdata", InputFormat: "mkv"}, []string{}, false}, + {"test_mp4", args{InputPath: "../testdata", InputFormat: "mp4"}, []string{}, false}, + {"test_err", args{InputPath: "../111", InputFormat: "mp4"}, []string{}, true}, + {"test_err", args{InputPath: "../111", InputFormat: "mp4"}, []string{}, true}, } + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { vb := &videoBatch{ - logger: logger, option: &VideoBatchOption{ - InputPath: tt.args.inputPath, + InputPath: tt.args.InputPath, InputFormat: tt.args.InputFormat, }, } - err := vb.getVideosList() - t.Log(vb.videosList) - assert.Nil(t, err) + + videosList, err := vb.GetVideosList() + t.Log(videosList) + + if tt.wantErr { + assert.NotNil(t, err) + } else { + var cList = createCorrectVideos(tt.args.InputPath, tt.args.InputFormat) + t.Log(cList) + + assert.Nil(t, err) + assert.Equal(t, videosList, cList) + } }) } } @@ -52,14 +72,14 @@ func Test_videoBatch_getFontsParams(t *testing.T) { }{ // TODO: Add test cases. } + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { vb := &videoBatch{ option: tt.fields.option, - - logger: logger, } - got, err := vb.getFontsParams() + + got, err := vb.GetFontsParams() if (err != nil) != tt.wantErr { t.Errorf("videoBatch.getFontsParams() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/garage_jav/javbus.go b/garage_jav/javbus.go index 5243430..b9d8133 100644 --- a/garage_jav/javbus.go +++ b/garage_jav/javbus.go @@ -16,52 +16,41 @@ import ( "github.com/antchfx/htmlquery" "github.com/go-gota/gota/dataframe" + "github.com/go-resty/resty/v2" "github.com/gocolly/colly/v2" "github.com/gocolly/colly/v2/queue" + "github.com/gsxhnd/garage/utils" "github.com/inhies/go-bytesize" - "go.uber.org/zap" ) -type JavbusCrawl interface { // 通过番号爬取对应的电影信息 - StartCrawlJavbusMovie() error - // 通过番号前缀爬取对应的电影信息 - StartCrawlJavbusMovieByPrefix() error - // 通过演员ID爬取对应的电影信息 - StartCrawlJavbusMovieByStar() error - // 访问文件夹下的视频列表爬取电影信息 - StartCrawlJavbusMovieByFilepath(inputPath string) error - // 创建输出目录 - mkAllDir() error - getJavMovieInfoByJavbus(element *colly.HTMLElement) +type JavbusCrawl interface { + StartCrawlJavbusMovie() error // 通过番号爬取对应的电影信息 + GetJavbusMovie(code string) (*JavMovie, error) // 通过番号爬取对应的电影信息 + StartCrawlJavbusMovieByPrefix() error // 通过番号前缀爬取对应的电影信息 + StartCrawlJavbusMovieByStar() error // 通过演员ID爬取对应的电影信息 + StartCrawlJavbusMovieByFilepath(inputPath string) error // 访问文件夹下的视频列表爬取电影信息 + mkAllDir() error // 创建输出目录 + savejavInfos() error // 保存CSV格式的电影信息 + saveCovers(coverPath, name string) error // 下载电影封面 + saveMagents() error // 下载磁力列表 + Save() error + getJavMovieInfowByJavbus(element *colly.HTMLElement) getJavMovieMagnetByJavbus(e *colly.HTMLElement) getJavStarMovieByJavbus(e *colly.HTMLElement) - - // 保存CSV格式的电影信息 - saveJavInfos() error - // 下载电影封面 - saveCovers(coverPath, name string) error - // 下载磁力列表 - saveMagents() error } type javbusCrawl struct { - logger *zap.Logger + logger utils.Logger option *JavCrawlConfig collector *colly.Collector + collectorQueue *queue.Queue pageCollector *colly.Collector httpClient *http.Client + requestClient *resty.Client maxDepth int javbusUrl string javInfos []JavMovie javMagnets []string - downloadMagent bool - destPath string - starCode string - starPage int - prefixCode string - prefixMinNo int - prefixMaxNo int - javQueue *queue.Queue } type CrawlOptions struct { @@ -74,7 +63,7 @@ type CrawlOptions struct { PrefixMaxNo int } -func NewJavbusCrawl(logger *zap.Logger, option *JavCrawlConfig) (JavbusCrawl, error) { +func NewJavbusCrawl(logger utils.Logger, option *JavCrawlConfig) (JavbusCrawl, error) { collector := colly.NewCollector() collector.ParseHTTPErrorResponse = true collector.SetRedirectHandler(func(req *http.Request, via []*http.Request) error { @@ -85,7 +74,7 @@ func NewJavbusCrawl(logger *zap.Logger, option *JavCrawlConfig) (JavbusCrawl, er RandomDelay: 5 * time.Second, }) collector.OnRequest(func(r *colly.Request) { - logger.Info("Visiting: " + r.URL.String()) + logger.Infow("Visiting: " + r.URL.String()) }) q, _ := queue.New(1, &queue.InMemoryQueueStorage{MaxSize: 10000}) @@ -107,22 +96,60 @@ func NewJavbusCrawl(logger *zap.Logger, option *JavCrawlConfig) (JavbusCrawl, er } return &javbusCrawl{ - logger: logger, - option: option, - collector: collector, - pageCollector: nil, - httpClient: httpClient, - maxDepth: 100, - javbusUrl: "https://www.javbus.com", - javInfos: make([]JavMovie, 0), - javMagnets: make([]string, 0), - starPage: 2, - javQueue: q, + logger: logger, + option: option, + collector: collector, + collectorQueue: q, + pageCollector: nil, + httpClient: httpClient, + requestClient: resty.New(), + maxDepth: 100, + javbusUrl: "https://www.javbus.com", + javInfos: make([]JavMovie, 0), + javMagnets: make([]string, 0), }, nil } +func (cc *javbusCrawl) StartCrawlJavbusMovie() error { + cc.logger.Infow("Download Infow: " + cc.option.Code) + + if cc.option.DownloadMagent { + cc.collector.OnHTML("body", cc.getJavMovieMagnetByJavbus) + } + cc.collector.OnHTML(".container", cc.getJavMovieInfowByJavbus) + + if err := cc.collector.Visit(cc.javbusUrl + "/" + cc.option.Code); err != nil { + return err + } + cc.collector.Wait() + + if len(cc.javInfos) == 0 { + return nil + } + + if err := cc.savejavInfos(); err != nil { + return err + } + for _, v := range cc.javInfos { + err := cc.saveCovers(v.Cover, v.Code) + if err != nil { + return err + } + } + cc.saveMagents() + return nil +} + +func (cc *javbusCrawl) GetJavbusMovie(code string) (*JavMovie, error) { + return nil, nil +} + +func (cc *javbusCrawl) Save() error { + return nil +} + func (cc *javbusCrawl) mkAllDir() error { - fullPath := filepath.Join(cc.destPath, "cover") + fullPath := filepath.Join(cc.option.DestPath, "cover") _, err := os.Stat(fullPath) if err != nil { if os.IsNotExist(err) { @@ -139,39 +166,39 @@ func (cc *javbusCrawl) mkAllDir() error { return nil } -func (cc *javbusCrawl) getJavMovieInfoByJavbus(e *colly.HTMLElement) { - var info = &JavMovie{} - info.Title = e.ChildText("h3") - info.Cover = e.ChildAttr(".screencap img", "src") - e.ForEach(".info p", func(i int, element *colly.HTMLElement) { +func (cc *javbusCrawl) getJavMovieInfowByJavbus(e *colly.HTMLElement) { + var Infow = &JavMovie{} + Infow.Title = e.ChildText("h3") + Infow.Cover = e.ChildAttr(".screencap img", "src") + e.ForEach(".Infow p", func(i int, element *colly.HTMLElement) { key := element.ChildText("span") switch i { case 0: - info.Code = element.ChildTexts("span")[1] + Infow.Code = element.ChildTexts("span")[1] } switch key { case "發行日期:": pd := element.Text - info.PublishDate = strings.Split(pd, " ")[1] + Infow.PublishDate = strings.Split(pd, " ")[1] case "長度:": pd := element.Text p := strings.Split(pd, " ")[1] - info.Length = strings.Split(p, "分鐘")[0] + Infow.Length = strings.Split(p, "分鐘")[0] case "導演:": - info.Director = element.ChildText("a") + Infow.Director = element.ChildText("a") case "製作商:": - info.ProduceCompany = element.ChildText("a") + Infow.ProduceCompany = element.ChildText("a") case "發行商:": - info.PublishCompany = element.ChildText("a") + Infow.PublishCompany = element.ChildText("a") case "系列:": - info.Series = element.ChildText("a") + Infow.Series = element.ChildText("a") } }) e.ForEach("ul li .star-name a", func(i int, element *colly.HTMLElement) { star := element.Attr("title") - info.Stars += star + ";" + Infow.Stars += star + ";" }) - cc.javInfos = append(cc.javInfos, *info) + cc.javInfos = append(cc.javInfos, *Infow) } func (cc *javbusCrawl) getJavMovieMagnetByJavbus(e *colly.HTMLElement) { @@ -188,33 +215,33 @@ func (cc *javbusCrawl) getJavMovieMagnetByJavbus(e *colly.HTMLElement) { param = strings.Replace(param, ";", "&", -1) param = strings.Replace(param, "'", "", -1) urlS := "https://www.javbus.com/ajax/uncledatoolsbyajax.php?" + param + "lang=zh&floor=442" - cc.logger.Info("Get magnet url: " + urlS) + cc.logger.Infow("Get magnet url: " + urlS) r, _ := http.NewRequest("GET", urlS, nil) r.Header.Add("Referer", e.Request.URL.Scheme+"://"+e.Request.URL.Host+e.Request.URL.Path) res, err := cc.httpClient.Do(r) if err != nil { - cc.logger.Error("http client error: " + err.Error()) + cc.logger.Errorw("http client error: " + err.Error()) return } body, err := io.ReadAll(res.Body) if err != nil { - cc.logger.Error("http read response error: " + err.Error()) + cc.logger.Errorw("http read response error: " + err.Error()) return } defer res.Body.Close() doc, err := htmlquery.Parse(strings.NewReader("" + string(body) + "
")) if err != nil { - cc.logger.Error("html query error: " + err.Error()) + cc.logger.Errorw("html query error: " + err.Error()) } list, err := htmlquery.QueryAll(doc, "//tr") if err != nil { - cc.logger.Error("html query tr error: " + err.Error()) + cc.logger.Errorw("html query tr error: " + err.Error()) } if len(list) == 0 { - cc.logger.Info("当前无磁力连接") + cc.logger.Infow("当前无磁力连接") return } @@ -270,7 +297,7 @@ func (cc *javbusCrawl) getJavMovieMagnetByJavbus(e *colly.HTMLElement) { func (cc *javbusCrawl) getJavStarMovieByJavbus(e *colly.HTMLElement) { e.ForEach("#waterfall > div", func(i int, element *colly.HTMLElement) { - cc.javQueue.AddURL(element.ChildAttr("a", "href")) + cc.collectorQueue.AddURL(element.ChildAttr("a", "href")) }) e.ForEach("div.text-center.hidden-xs > ul", func(i int, element *colly.HTMLElement) { page := element.ChildAttr("a#next", "href") @@ -280,54 +307,24 @@ func (cc *javbusCrawl) getJavStarMovieByJavbus(e *colly.HTMLElement) { }) } -func (cc *javbusCrawl) StartCrawlJavbusMovie() error { - cc.logger.Info("Download info: " + cc.option.Code) - - if cc.downloadMagent { - cc.collector.OnHTML("body", cc.getJavMovieMagnetByJavbus) - } - cc.collector.OnHTML(".container", cc.getJavMovieInfoByJavbus) - - if err := cc.collector.Visit(cc.javbusUrl + "/" + cc.option.Code); err != nil { - return err - } - cc.collector.Wait() - - if len(cc.javInfos) == 0 { - return nil - } - - if err := cc.saveJavInfos(); err != nil { - return err - } - for _, v := range cc.javInfos { - err := cc.saveCovers(v.Cover, v.Code) - if err != nil { - return err - } - } - cc.saveMagents() - return nil -} - func (cc *javbusCrawl) StartCrawlJavbusMovieByPrefix() error { q, _ := queue.New(1, &queue.InMemoryQueueStorage{MaxSize: 10000}) - for i := cc.prefixMinNo; i <= cc.prefixMaxNo; i++ { - code := fmt.Sprintf("%s-%03d", cc.prefixCode, i) + for i := cc.option.PrefixMinNo; i <= cc.option.PrefixMaxNo; i++ { + code := fmt.Sprintf("%s-%03d", cc.option.PrefixCode, i) q.AddURL(cc.javbusUrl + "/" + code) } - if cc.downloadMagent { + if cc.option.DownloadMagent { cc.collector.OnHTML("body", cc.getJavMovieMagnetByJavbus) } - cc.collector.OnHTML(".container", cc.getJavMovieInfoByJavbus) + cc.collector.OnHTML(".container", cc.getJavMovieInfowByJavbus) q.Run(cc.collector) cc.collector.Wait() - cc.saveJavInfos() + cc.savejavInfos() cc.saveMagents() for _, v := range cc.javInfos { err := cc.saveCovers(v.Cover, v.Code) @@ -340,7 +337,7 @@ func (cc *javbusCrawl) StartCrawlJavbusMovieByPrefix() error { func (cc *javbusCrawl) StartCrawlJavbusMovieByStar() error { starCode := cc.option.StarCode - cc.logger.Debug("Getting star code: " + starCode) + cc.logger.Debugw("Getting star code: " + starCode) cc.pageCollector = cc.collector.Clone() cc.pageCollector.OnHTML("body", cc.getJavStarMovieByJavbus) @@ -349,23 +346,23 @@ func (cc *javbusCrawl) StartCrawlJavbusMovieByStar() error { return err } cc.pageCollector.Wait() - if cc.javQueue.IsEmpty() { + if cc.collectorQueue.IsEmpty() { return nil } - infoCrawlClient := cc.collector.Clone() - infoCrawlClient.OnRequest(func(r *colly.Request) { - cc.logger.Info("Visiting: " + r.URL.String()) + InfowCrawlClient := cc.collector.Clone() + InfowCrawlClient.OnRequest(func(r *colly.Request) { + cc.logger.Infow("Visiting: " + r.URL.String()) }) - if cc.downloadMagent { - infoCrawlClient.OnHTML("body", cc.getJavMovieMagnetByJavbus) + if cc.option.DownloadMagent { + InfowCrawlClient.OnHTML("body", cc.getJavMovieMagnetByJavbus) } - infoCrawlClient.OnHTML(".container", cc.getJavMovieInfoByJavbus) - cc.javQueue.Run(infoCrawlClient) + InfowCrawlClient.OnHTML(".container", cc.getJavMovieInfowByJavbus) + cc.collectorQueue.Run(InfowCrawlClient) cc.collector.Wait() - cc.saveJavInfos() + cc.savejavInfos() cc.saveMagents() for _, v := range cc.javInfos { err := cc.saveCovers(v.Cover, v.Code) @@ -402,12 +399,12 @@ func (cc *javbusCrawl) StartCrawlJavbusMovieByFilepath(inputPath string) error { return err } - cc.collector.OnHTML(".container", cc.getJavMovieInfoByJavbus) + cc.collector.OnHTML(".container", cc.getJavMovieInfowByJavbus) q.Run(cc.collector) cc.collector.Wait() - cc.saveJavInfos() + cc.savejavInfos() for _, v := range cc.javInfos { err := cc.saveCovers(v.Cover, v.Code) if err != nil { @@ -418,13 +415,13 @@ func (cc *javbusCrawl) StartCrawlJavbusMovieByFilepath(inputPath string) error { return nil } -func (cc *javbusCrawl) save() {} +// func (cc *javbusCrawl) save() {} -func (cc *javbusCrawl) saveJavInfos() error { +func (cc *javbusCrawl) savejavInfos() error { df := dataframe.LoadStructs(cc.javInfos) - f, err := os.OpenFile(path.Join(cc.destPath, time.Now().Local().Format("2006-01-02-15-04-05")+"-jav_info.csv"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm) + f, err := os.OpenFile(path.Join(cc.option.DestPath, time.Now().Local().Format("2006-01-02-15-04-05")+"-jav_Infow.csv"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm) if err != nil { - cc.logger.Error("Save jav info file failed error: %s" + err.Error()) + cc.logger.Errorw("Save jav Infow file failed error: %s" + err.Error()) return err } defer f.Close() @@ -436,9 +433,9 @@ func (cc *javbusCrawl) saveMagents() error { return nil } - f, err := os.OpenFile(path.Join(cc.destPath, time.Now().Local().Format("2006-01-02-15-04-05")+"-jav_magnet.text"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm) + f, err := os.OpenFile(path.Join(cc.option.DestPath, time.Now().Local().Format("2006-01-02-15-04-05")+"-jav_magnet.text"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm) if err != nil { - cc.logger.Error("Save jav info file failed error: %s" + err.Error()) + cc.logger.Errorw("Save jav Infow file failed error: %s" + err.Error()) return err } defer f.Close() @@ -453,7 +450,7 @@ func (cc *javbusCrawl) saveCovers(coverPath, code string) error { var urlImg = "" u, err := url.ParseRequestURI(coverPath) if err != nil { - cc.logger.Error("parse cover path failed error: %s" + err.Error()) + cc.logger.Errorw("parse cover path failed error: %s" + err.Error()) return err } if u.Host == "" { @@ -462,16 +459,16 @@ func (cc *javbusCrawl) saveCovers(coverPath, code string) error { urlImg = coverPath } - cc.logger.Info("downloading coverage url: " + urlImg) + cc.logger.Infow("downloading coverage url: " + urlImg) ext := path.Ext(urlImg) resp, err := cc.httpClient.Get(urlImg) if err != nil { - cc.logger.Error("downloading coverage error: " + err.Error()) + cc.logger.Errorw("downloading coverage error: " + err.Error()) return err } body, _ := ioutil.ReadAll(resp.Body) - f, err := os.OpenFile(path.Join(cc.destPath, "cover", code+ext), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) + f, err := os.OpenFile(path.Join(cc.option.DestPath, "cover", code+ext), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) if err != nil { return err } diff --git a/garage_jav/model.go b/garage_jav/model.go index 9585be9..d8fe647 100644 --- a/garage_jav/model.go +++ b/garage_jav/model.go @@ -28,6 +28,7 @@ type JavMovie struct { PublishCompany string `json:"publish_company"` Series string `json:"series"` Stars string `json:"stars"` + Magnets []JavMovieMagnet } type JavMovieMagnet struct { diff --git a/go.mod b/go.mod index b0d6dd0..216d785 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/gin-gonic/gin v1.9.1 github.com/glebarez/go-sqlite v1.22.0 github.com/go-gota/gota v0.12.0 + github.com/go-resty/resty/v2 v2.11.0 github.com/gocolly/colly/v2 v2.1.0 github.com/google/wire v0.6.0 github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf @@ -14,6 +15,7 @@ require ( github.com/urfave/cli/v2 v2.27.1 go.uber.org/zap v1.26.0 golang.org/x/sync v0.6.0 + gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) require ( diff --git a/go.sum b/go.sum index acd021e..c58a829 100644 --- a/go.sum +++ b/go.sum @@ -66,6 +66,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U= github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8= +github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= @@ -178,6 +180,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= @@ -224,6 +227,7 @@ golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= @@ -252,6 +256,7 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -261,6 +266,7 @@ golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -274,6 +280,8 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -323,6 +331,8 @@ google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7 google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/taskfile.yaml b/taskfile.yaml index 2c45970..bb76f03 100644 --- a/taskfile.yaml +++ b/taskfile.yaml @@ -7,6 +7,8 @@ tasks: - rm -rvf testdata/ - mkdir -p testdata/{1,2,3,4} - touch ./testdata/{1,2,3,4}/{1,2,3,4,5}.{mkv,mp4,ass,ttf} + - mkdir ./garage-ui/dist + - touch ./garage-ui/dist/index.html - ls -al ./testdata clean: cmds: diff --git a/utils/config.go b/utils/config.go index 5aad48e..23cac90 100644 --- a/utils/config.go +++ b/utils/config.go @@ -1,9 +1,18 @@ package utils type Config struct { - Dev bool `env:"DEV" envDefault:"true"` - Debug bool `env:"DEBUG" envDefault:"true"` - LogLevel string `env:"LOG_LEVEL" envDefault:"info"` + Dev bool `env:"DEV" envDefault:"true"` + Debug bool `env:"DEBUG" envDefault:"true"` + Log LogConfig `json:"log,omitempty"` + LogLevel string `env:"LOG_LEVEL" envDefault:"info"` +} + +type LogConfig struct { + Level string + Filename string + MaxSize int + MaxAge int + MaxBackups int TraceEnable bool `env:"TRACE_ENABLE" envDefault:"false"` TraceUrl string `env:"TRACE_URL"` } diff --git a/utils/logger.go b/utils/logger.go index c310e74..a904d8f 100644 --- a/utils/logger.go +++ b/utils/logger.go @@ -6,6 +6,7 @@ import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" + "gopkg.in/natefinch/lumberjack.v2" ) type Logger interface { @@ -17,6 +18,8 @@ type Logger interface { Warnw(template string, keysAndValues ...interface{}) Errorf(template string, args ...interface{}) Errorw(template string, keysAndValues ...interface{}) + Panicf(template string, args ...interface{}) + Panicw(template string, keysAndValues ...interface{}) } type logger struct { @@ -56,30 +59,33 @@ var ( ) func NewLogger(cfg *Config) Logger { - var encode zapcore.Encoder - var level zapcore.Level + var ( + level zapcore.Level + core zapcore.Core + ) - if cfg.Dev { - encode = devEncoder - } else { - encode = prodEncoder - } - if cfg.LogLevel == "debug" { - level = zap.DebugLevel - } else if cfg.LogLevel == "info" { + if cfg.Log.Level == "info" { level = zap.InfoLevel - } else { + } else if cfg.Log.Level == "warn" { level = zap.WarnLevel + } else { + level = zap.DebugLevel } - var core = zapcore.NewTee( - zapcore.NewCore(encode, os.Stdout, level), - ) - zapLogger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1)) + if !cfg.Dev { + core = zapcore.NewCore(prodEncoder, zapcore.AddSync(&lumberjack.Logger{ + Filename: cfg.Log.Filename, + MaxSize: cfg.Log.MaxSize, + MaxBackups: cfg.Log.MaxBackups, + MaxAge: cfg.Log.MaxAge, + }, + ), level) + } else { + core = zapcore.NewCore(devEncoder, os.Stdout, level) + } return &logger{ - // Logger: zapLogger, - Suger: zapLogger.Sugar(), + Suger: zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1)).Sugar(), } } @@ -119,22 +125,11 @@ func (l *logger) Errorw(template string, keysAndValues ...interface{}) { l.Suger.Errorw(template, keysAndValues...) } -func GetLogger() *zap.Logger { - debugEncoder := zapcore.NewConsoleEncoder(zapcore.EncoderConfig{ - MessageKey: "msg", - LevelKey: "level", - TimeKey: "ts", - EncodeLevel: zapcore.CapitalColorLevelEncoder, - EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { - enc.AppendString(t.UTC().Format("2006-01-02T15:04:05.000000-07:00")) - }, - EncodeCaller: zapcore.ShortCallerEncoder, - EncodeDuration: func(d time.Duration, enc zapcore.PrimitiveArrayEncoder) { - enc.AppendInt64(int64(d) / 1000000) - }, - }) - var core = zapcore.NewTee( - zapcore.NewCore(debugEncoder, os.Stdout, zap.DebugLevel), - ) - return zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1)) +func (l *logger) Panicf(template string, args ...interface{}) { + defer l.Suger.Sync() + l.Suger.Panicf(template, args...) +} +func (l *logger) Panicw(template string, keysAndValues ...interface{}) { + defer l.Suger.Sync() + l.Suger.Panicw(template, keysAndValues...) }