├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── release.yml │ └── test.yml ├── .gitignore ├── .goreleaser.yaml ├── .swaggo ├── .vscode └── launch.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bin └── main.go ├── cmd └── crawal_javbus.go ├── garage_cmd ├── crawl_javbus.go ├── crawl_javdb.go ├── main.go └── version.go ├── garage_jav ├── javbus.go ├── javdb.go └── model.go ├── go.mod ├── go.sum └── utils ├── config.go ├── file.go ├── file_test.go ├── logger.go └── logger_test.go /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | jobs: 9 | build: 10 | name: Release 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Check out code into the Go module directory 14 | uses: actions/checkout@v4 15 | - name: Set up Go 1.x 16 | uses: actions/setup-go@v4 17 | with: 18 | go-version: ^1.21 19 | id: go 20 | - run: go version 21 | 22 | - name: Get dependencies 23 | run: go get -v -t -d ./... 24 | 25 | - uses: goreleaser/goreleaser-action@v4 26 | with: 27 | # either 'goreleaser' (default) or 'goreleaser-pro': 28 | distribution: goreleaser 29 | version: latest 30 | args: release --clean 31 | env: 32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 33 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - "docs/**" 7 | - "README.md" 8 | - "CHANGELOG.md" 9 | - "LICENSE" 10 | tags-ignore: 11 | - "v*" 12 | 13 | jobs: 14 | Test: 15 | name: Test 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Check out code into the Go module directory 19 | uses: actions/checkout@v4 20 | - name: Set up Go 1.x 21 | uses: actions/setup-go@v4 22 | with: 23 | go-version: ^1.21 24 | id: go 25 | 26 | - name: Get dependencies 27 | run: go get -v -t -d ./... 28 | 29 | # - name: Install Task 30 | # uses: arduino/setup-task@v1 31 | # with: 32 | # version: 3.x 33 | 34 | # - name: Mock Test Data 35 | # run: task gen_test 36 | 37 | - name: Test 38 | run: go test -v -coverprofile=coverage.out -covermode=atomic ./... 39 | 40 | - name: Upload coverage reports to Codecov 41 | uses: codecov/codecov-action@v4 42 | with: 43 | files: ./coverage.out 44 | env: 45 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # System Files 2 | .DS_Store 3 | Thumbs.db 4 | *.iml 5 | .idea/ 6 | 7 | build/ 8 | javbus/ 9 | dist/ 10 | target 11 | testdata/ 12 | testdb/ 13 | data/ 14 | log/ 15 | .dev.env -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://goreleaser.com/static/schema.json 2 | # This is an example .goreleaser.yml file with some sensible defaults. 3 | # Make sure to check the documentation at https://goreleaser.com 4 | project_name: garage 5 | git: 6 | tag_sort: -version:creatordate 7 | report_sizes: true 8 | 9 | before: 10 | hooks: 11 | - go mod tidy 12 | 13 | builds: 14 | - id: garage 15 | main: ./garage_cmd 16 | env: 17 | - CGO_ENABLED=0 18 | flags: 19 | - -v 20 | - -trimpath 21 | ldflags: 22 | - -s -w 23 | - -X "{{.ModulePath}}/src/cmd.tag={{.Tag}}" 24 | - -X "{{.ModulePath}}/src/cmd.buildDate={{.Now}}" 25 | - -X "{{.ModulePath}}/src/cmd.gitCommit={{.FullCommit}}" 26 | goos: 27 | - linux 28 | - freebsd 29 | - windows 30 | - darwin 31 | goarch: 32 | - amd64 33 | - "386" 34 | - arm 35 | - arm64 36 | goarm: 37 | - "6" 38 | - "7" 39 | 40 | archives: 41 | - id: garage 42 | builds: [garage] 43 | format: tar.gz 44 | name_template: >- 45 | {{ .ProjectName }}_ 46 | {{- title .Os }}_ 47 | {{- if eq .Arch "amd64" }}x86_64 48 | {{- else if eq .Arch "386" }}i386 49 | {{- else }}{{ .Arch }}{{ end }} 50 | {{- if .Arm }}v{{ .Arm }}{{ end }} 51 | # use zip for windows archives 52 | format_overrides: 53 | - goos: windows 54 | format: zip 55 | files: 56 | - "LICENSE" 57 | - "CHANGELOG.md" 58 | 59 | checksum: 60 | name_template: 'checksums.txt' 61 | snapshot: 62 | name_template: "{{ incpatch .Version }}-next" 63 | 64 | changelog: 65 | sort: asc 66 | filters: 67 | exclude: 68 | - '^docs:' 69 | - '^test:' -------------------------------------------------------------------------------- /.swaggo: -------------------------------------------------------------------------------- 1 | replace database/sql.NullInt64 int 2 | replace database/sql.NullString string 3 | replace database/sql.NullTime time.Time -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Command Crawl", 9 | "type": "go", 10 | "request": "launch", 11 | "mode": "auto", 12 | "showLog": true, 13 | "debugAdapter": "dlv-dap", 14 | "program": "${workspaceRoot}/garage_cmd", 15 | "args": ["crawl", "jav-code", "--magnet", "TKD-048"], 16 | "output": "${workspaceRoot}/build/garage", 17 | "trace": "verbose", 18 | "cwd": "${workspaceRoot}" 19 | }, 20 | { 21 | "name": "Command Javbus Star Crawl", 22 | "type": "go", 23 | "request": "launch", 24 | "mode": "auto", 25 | "showLog": true, 26 | "debugAdapter": "dlv-dap", 27 | "program": "${workspaceRoot}/garage_cmd", 28 | "args": ["crawl", "jav-star-code", "--magnet"], 29 | "output": "${workspaceRoot}/build/garage", 30 | "trace": "verbose", 31 | "cwd": "${workspaceRoot}" 32 | }, 33 | { 34 | "name": "Command Server", 35 | "type": "go", 36 | "request": "launch", 37 | "mode": "auto", 38 | "showLog": true, 39 | "debugAdapter": "dlv-dap", 40 | "program": "${workspaceRoot}/garage_server", 41 | "args": [], 42 | "output": "${workspaceRoot}/build/garage_server", 43 | "envFile": "${workspaceFolder}/.dev.env", 44 | "trace": "verbose", 45 | "cwd": "${workspaceRoot}" 46 | } 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 0.0.12 4 | 5 | ## 0.0.11 6 | 7 | - FIX: javbus redirect error 8 | 9 | ## 0.0.10 10 | 11 | - FIX: magnet not download 12 | 13 | ## 0.0.9 14 | 15 | ### bate-01 16 | 17 | - FEAT: add crawl info by jav star code 18 | 19 | ## 0.0.8 20 | 21 | - DOC: 文档更新 22 | - REFACTOR: `ffmpeg`子命令添加自动执行参数 23 | - REFACTOR: 目录变更 24 | - REFACTOR: 子命令修改 25 | - FEAT: 爬虫添加前缀爬取 26 | - FEAT: 磁力链接下载 27 | - FEAT: 根据视频名称爬取 28 | 29 | ## 0.0.7 30 | 31 | - FIX: 爬虫部分没有提前创建下载数据 32 | 33 | ## 0.0.6 34 | 35 | - CHORE: 编译去除路径 36 | - REFACTOR: `ffmpeg`部分子命令不再自动执行命令而是生成脚本文件 37 | - REFACTOR: 修改单个字体文件,使用字体文件夹代替,自动识别 TTF,OTF 文件并添加 38 | - REFACTOR: 修改添加字幕子命令 `video_import_subtitle` 39 | - REFACTOR: 修改导出字幕子命令 `video_export_subtitle` 40 | 41 | ## 0.0.5 42 | 43 | - FIX: 参数字符处理 44 | - FIX: 批量命令参数缺少导致异常 45 | 46 | ## 0.0.4 47 | 48 | - FIX: 修改视频输出文件未知错误 49 | - FEAT: 视频命令行功能参数添加 50 | - FEAT: 视频命令行功能日志输出内容增加 51 | 52 | ## 0.0.3 53 | 54 | - FEAT: 添加了视频处理的相关命令 55 | 56 | ## 0.0.2 57 | 58 | - FEAT: 添加根据演员`ID`爬取数据 59 | - FEAT: 添加根据番号前缀爬取数据 60 | - REFACTOR: 爬指定番号的命令删除了`code`参数 61 | 62 | ## 0.0.1 63 | 64 | - FEAT: 爬取指定番号信息数据和封面信息并保存在当前文件夹下 65 | - FEAT: 全局代理设置 66 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 伍轻鸣 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Alt](https://repobeats.axiom.co/api/embed/a600b32115176854e9ab824307882a9350d5d9de.svg "Repobeats analytics image") 2 | 3 | [![codecov](https://codecov.io/gh/gsxhnd/garage/branch/master/graph/badge.svg?token=6EAEPP8LT7)](https://codecov.io/gh/gsxhnd/garage) 4 | 5 | # Garage 6 | 7 | `garage`是一个命令行工具,提供爬虫功能。 8 | 9 | [更新日志](./CHANGELOG.md) 10 | 11 | ## 下载命令行工具 🔧 12 | 13 | 命令行工具提供`Windows`、`macOS`、`Linux`平台预编译的二进制。 14 | 15 | 最新下载地址: 16 | 17 | ```bash 18 | garage --help # 获取帮助 19 | ``` 20 | 21 | ## 命令行文档 22 | 23 | 帮助你爬取指定番号,演员和番号系列数据,磁力链接和封面。 24 | 25 | 支持网站: `javbus` 26 | 27 | 支持功能: 28 | 29 | - [x] 常规番号爬取 30 | - [x] 番号前缀批量爬取 31 | - [x] 根据演员 ID 批量爬取 32 | - [x] 遍历本地文件视频爬取对应信息 33 | - [x] Cover 图片下载 34 | - [x] 数据基础信息下载 35 | - [x] 磁力连接保存 36 | 37 | ```shell 38 | # 抓取所有影片封面和信息,保存到当前目录下的 javs 文件夹下以番号命名的子文件夹中 39 | $ garage crawl jav-code --help 40 | $ garage crawl jav-path-code --help 41 | $ garage crawl jav-prefix-code --help 42 | $ garage crawl jav-star-code --help 43 | ``` 44 | 45 | ## Thanks 46 | 47 | Thank for JetBrains Open Source License 48 | 49 | [![JetBrains logo](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.png)](https://jb.gg/OpenSourceSupport) 50 | -------------------------------------------------------------------------------- /bin/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/urfave/cli/v2" 7 | ) 8 | 9 | func main() { 10 | rootCmd := cli.NewApp() 11 | 12 | err := rootCmd.Run(os.Args) 13 | if err != nil { 14 | panic(err) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /cmd/crawal_javbus.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import "github.com/urfave/cli/v2" 4 | 5 | var CrawlJavbusCmd = &cli.Command{ 6 | Name: "crawl_javbus", 7 | Description: "", 8 | Flags: []cli.Flag{ 9 | &cli.StringFlag{Name: "proxy", Usage: "代理配置,如: http://127.0.0.1:1080"}, 10 | &cli.StringFlag{Name: "dest", Usage: "设置下载目录", Value: "./javbus"}, 11 | &cli.BoolFlag{ 12 | Name: "magnet", 13 | Usage: "保存磁力链接,开启参数 --magnet", 14 | Value: false, 15 | }, 16 | }, 17 | } 18 | -------------------------------------------------------------------------------- /garage_cmd/crawl_javbus.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "path/filepath" 5 | 6 | "github.com/gsxhnd/garage/garage_jav" 7 | "github.com/gsxhnd/garage/utils" 8 | "github.com/urfave/cli/v2" 9 | ) 10 | 11 | var crawlJavbusCmd = &cli.Command{ 12 | Name: "crawl_javbus", 13 | Description: "", 14 | Flags: []cli.Flag{ 15 | &cli.StringFlag{Name: "proxy", Usage: "代理配置,如: http://127.0.0.1:1080"}, 16 | &cli.StringFlag{Name: "output", Usage: "设置下载目录", Value: "./javbus"}, 17 | &cli.BoolFlag{ 18 | Name: "magnet", 19 | Usage: "保存磁力链接,开启参数 --magnet", 20 | Value: false, 21 | }, 22 | }, 23 | Before: func(ctx *cli.Context) error { 24 | return utils.MakeDir(filepath.Join(ctx.String("output"), "cover")) 25 | }, 26 | Subcommands: []*cli.Command{ 27 | javbusCodeCmd, 28 | javbusPrefixCmd, 29 | javbusStarCodeCmd, 30 | javbusCodeFromDirCmd, 31 | }, 32 | } 33 | 34 | var javbusCodeCmd = &cli.Command{ 35 | Name: "javbus_code", 36 | Aliases: nil, 37 | Usage: "根据指定番号爬取数据", 38 | UsageText: "jav_code --site [javbus/javlibrary] XXX-001", 39 | Flags: []cli.Flag{}, 40 | Action: func(ctx *cli.Context) error { 41 | config := &garage_jav.CrawlConfig{ 42 | Proxy: ctx.String("proxy"), 43 | } 44 | opt := &garage_jav.JavbusOption{ 45 | Code: []string{ctx.Args().Get(0)}, 46 | DownloadMagent: ctx.Bool("magnet"), 47 | OutPath: ctx.String("output"), 48 | } 49 | 50 | c, err := garage_jav.NewJavbusCrawl(logger, opt, config) 51 | if err != nil { 52 | logger.Panicw("client init error: " + err.Error()) 53 | return err 54 | } 55 | _, err = c.GetJavbusMovie() 56 | if err != nil { 57 | return err 58 | } 59 | return c.SaveLocal(nil) 60 | }, 61 | } 62 | 63 | var javbusPrefixCmd = &cli.Command{ 64 | Name: "javbus_prefix_code", 65 | Usage: "根据番号前缀爬取数据", 66 | Flags: []cli.Flag{ 67 | &cli.StringFlag{Name: "prefix_code", Value: "EKDV", Usage: "番号前缀"}, 68 | &cli.Uint64Flag{Name: "prefix_min", Value: 1, Usage: "番号开始编号"}, 69 | &cli.Uint64Flag{Name: "prefix_max", Value: 5, Usage: "番号结束编号"}, 70 | &cli.Uint64Flag{Name: "prefix_zero", Value: 3, Usage: "番号结束编号"}, 71 | }, 72 | Action: func(ctx *cli.Context) error { 73 | config := &garage_jav.CrawlConfig{ 74 | Proxy: ctx.String("proxy"), 75 | } 76 | opt := &garage_jav.JavbusOption{ 77 | DownloadMagent: ctx.Bool("magnet"), 78 | PrefixCode: []string{ctx.String("prefix_code")}, 79 | PrefixMinNo: ctx.Uint64("prefix_min"), 80 | PrefixMaxNo: ctx.Uint64("prefix_max"), 81 | PrefixZero: ctx.Uint64("prefix_zero"), 82 | OutPath: ctx.String("output"), 83 | } 84 | 85 | c, err := garage_jav.NewJavbusCrawl(logger, opt, config) 86 | if err != nil { 87 | logger.Panicw("client init error: " + err.Error()) 88 | return err 89 | } 90 | if _, err := c.GetJavbusMovieByPrefix(); err != nil { 91 | return err 92 | } 93 | return c.SaveLocal(nil) 94 | }, 95 | } 96 | 97 | var javbusStarCodeCmd = &cli.Command{ 98 | Name: "javbus_star_code", 99 | Usage: "根据演员ID爬取数据", 100 | Flags: []cli.Flag{ 101 | &cli.StringFlag{Name: "star_code", Value: "vfn", Usage: "演员番号"}, 102 | }, 103 | Action: func(ctx *cli.Context) error { 104 | config := &garage_jav.CrawlConfig{ 105 | Proxy: ctx.String("proxy"), 106 | } 107 | opt := &garage_jav.JavbusOption{ 108 | DownloadMagent: ctx.Bool("magnet"), 109 | StarCode: []string{ctx.String("star_code")}, 110 | OutPath: ctx.String("output"), 111 | } 112 | 113 | c, err := garage_jav.NewJavbusCrawl(logger, opt, config) 114 | if err != nil { 115 | logger.Panicw("client init error: " + err.Error()) 116 | return err 117 | } 118 | if _, err := c.GetJavbusMovieByStar(); err != nil { 119 | return err 120 | } 121 | return c.SaveLocal(nil) 122 | }, 123 | } 124 | 125 | var javbusCodeFromDirCmd = &cli.Command{ 126 | Name: "code_from_dir", 127 | Usage: "根据演员ID爬取数据", 128 | Flags: []cli.Flag{ 129 | &cli.StringFlag{Name: "input_path", Required: true}, 130 | }, 131 | Action: func(ctx *cli.Context) error { 132 | config := &garage_jav.CrawlConfig{ 133 | Proxy: ctx.String("proxy"), 134 | } 135 | opt := &garage_jav.JavbusOption{ 136 | DownloadMagent: ctx.Bool("magnet"), 137 | VideosPath: ctx.String("input_path"), 138 | OutPath: ctx.String("output"), 139 | } 140 | 141 | c, err := garage_jav.NewJavbusCrawl(logger, opt, config) 142 | if err != nil { 143 | logger.Panicw("client init error: " + err.Error()) 144 | return err 145 | } 146 | 147 | if _, err := c.GetJavbusMovieByFilepath(); err != nil { 148 | return err 149 | } 150 | 151 | return nil 152 | }, 153 | } 154 | -------------------------------------------------------------------------------- /garage_cmd/crawl_javdb.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/urfave/cli/v2" 4 | 5 | var crawlJavDBCmd = &cli.Command{ 6 | Name: "crawl_javdb", 7 | Usage: "", 8 | Action: func(c *cli.Context) error { 9 | return nil 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /garage_cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/gsxhnd/garage/utils" 7 | "github.com/urfave/cli/v2" 8 | ) 9 | 10 | var ( 11 | RootCmd = cli.NewApp() 12 | logger = utils.NewLogger(nil) 13 | ) 14 | 15 | func init() { 16 | RootCmd.HideVersion = true 17 | RootCmd.Usage = "Set of crwal tool" 18 | RootCmd.Flags = []cli.Flag{} 19 | RootCmd.Commands = []*cli.Command{ 20 | crawlJavbusCmd, 21 | crawlJavDBCmd, 22 | versionCmd, 23 | } 24 | } 25 | 26 | func main() { 27 | err := RootCmd.Run(os.Args) 28 | if err != nil { 29 | panic(err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /garage_cmd/version.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | 7 | "github.com/urfave/cli/v2" 8 | ) 9 | 10 | var ( 11 | tag string 12 | buildDate string 13 | gitCommit string 14 | ) 15 | 16 | var versionCmd = &cli.Command{ 17 | Name: "version", 18 | Usage: "show version information", 19 | Action: func(context *cli.Context) error { 20 | fmt.Println("Version: ", tag) 21 | fmt.Println("BuildDate:", buildDate) 22 | fmt.Println("GitCommit:", gitCommit) 23 | fmt.Println("GoVersion:", runtime.Version()) 24 | fmt.Println("Compiler:", runtime.Compiler) 25 | fmt.Println("Paltform:", fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)) 26 | return nil 27 | }, 28 | } 29 | -------------------------------------------------------------------------------- /garage_jav/javbus.go: -------------------------------------------------------------------------------- 1 | package garage_jav 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "net/url" 8 | "os" 9 | "path" 10 | "path/filepath" 11 | "strconv" 12 | "strings" 13 | "time" 14 | 15 | "github.com/antchfx/htmlquery" 16 | "github.com/go-gota/gota/dataframe" 17 | "github.com/go-resty/resty/v2" 18 | "github.com/gocolly/colly/v2" 19 | "github.com/gocolly/colly/v2/queue" 20 | "github.com/gsxhnd/garage/utils" 21 | "github.com/inhies/go-bytesize" 22 | ) 23 | 24 | const JAVBUS_URL = "https://www.javbus.com" 25 | 26 | type JavbusCrawl interface { 27 | GetJavbusMovieByHomePage() ([]JavMovie, error) // 通过首页爬取对应的电影信息 28 | GetJavbusMovie() ([]JavMovie, error) // 通过番号爬取对应的电影信息 29 | GetJavbusMovieByPrefix() ([]JavMovie, error) // 通过番号前缀爬取对应的电影信息 30 | GetJavbusMovieByStar() ([]JavMovie, error) // 通过演员ID爬取对应的电影信息 31 | GetJavbusMovieByFilepath() ([]JavMovie, error) // 访问文件夹下的视频列表爬取电影信息 32 | SaveLocal(infos []JavMovie) error 33 | } 34 | 35 | type javbusCrawl struct { 36 | logger utils.Logger 37 | opt *JavbusOption 38 | collector *colly.Collector 39 | httpClient *resty.Client 40 | } 41 | 42 | func NewJavbusCrawl(logger utils.Logger, opt *JavbusOption, config *CrawlConfig) (JavbusCrawl, error) { 43 | collector := colly.NewCollector() 44 | collector.ParseHTTPErrorResponse = true 45 | collector.SetRedirectHandler(func(req *http.Request, via []*http.Request) error { 46 | return http.ErrUseLastResponse 47 | }) 48 | collector.Limit(&colly.LimitRule{ 49 | Parallelism: 1, 50 | RandomDelay: 5 * time.Second, 51 | }) 52 | collector.OnRequest(func(r *colly.Request) { 53 | logger.Infow("Visiting: " + r.URL.String()) 54 | }) 55 | http := resty.New() 56 | 57 | if config.Proxy != "" { 58 | _, err := url.Parse(config.Proxy) 59 | if err != nil { 60 | return nil, err 61 | } 62 | 63 | if err := collector.SetProxy(config.Proxy); err != nil { 64 | return nil, err 65 | } 66 | 67 | http.SetProxy(config.Proxy) 68 | } 69 | 70 | return &javbusCrawl{ 71 | logger: logger, 72 | opt: opt, 73 | collector: collector, 74 | httpClient: http, 75 | }, nil 76 | } 77 | 78 | // TODO: need query function 79 | func (jc *javbusCrawl) GetJavbusMovieByHomePage() ([]JavMovie, error) { 80 | return nil, nil 81 | } 82 | 83 | func (jc *javbusCrawl) GetJavbusMovie() ([]JavMovie, error) { 84 | queue, _ := queue.New(1, &queue.InMemoryQueueStorage{MaxSize: 10000}) 85 | 86 | for _, code := range jc.opt.Code { 87 | queue.AddURL(JAVBUS_URL + "/" + code) 88 | } 89 | 90 | jc.collector.OnHTML(".container", jc.javbusMovieInfoCrawl) 91 | jc.collector.OnHTML("body", jc.javbusMovieMagnetCrawl) 92 | queue.Run(jc.collector) 93 | return nil, nil 94 | } 95 | 96 | func (jc *javbusCrawl) GetJavbusMovieByPrefix() ([]JavMovie, error) { 97 | codes := jc.getCodeByPrefix() 98 | queue, _ := queue.New(1, &queue.InMemoryQueueStorage{MaxSize: 10000}) 99 | for _, code := range codes { 100 | queue.AddURL(JAVBUS_URL + "/" + code) 101 | } 102 | 103 | jc.collector.OnHTML(".container", jc.javbusMovieInfoCrawl) 104 | jc.collector.OnHTML("body", jc.javbusMovieMagnetCrawl) 105 | queue.Run(jc.collector) 106 | return nil, nil 107 | } 108 | 109 | func (jc *javbusCrawl) GetJavbusMovieByStar() ([]JavMovie, error) { 110 | collector := jc.collector.Clone() 111 | pageCollector := jc.collector.Clone() 112 | queue, _ := queue.New(1, &queue.InMemoryQueueStorage{MaxSize: 10000}) 113 | 114 | pageCollector.OnHTML("body", func(h *colly.HTMLElement) { 115 | h.ForEach("#waterfall > div", func(i int, element *colly.HTMLElement) { 116 | queue.AddURL(element.ChildAttr("a", "href")) 117 | }) 118 | h.ForEach("div.text-center.hidden-xs > ul", func(i int, element *colly.HTMLElement) { 119 | page := element.ChildAttr("a#next", "href") 120 | if page != "" { 121 | pageCollector.Visit(JAVBUS_URL + element.ChildAttr("a#next", "href")) 122 | } 123 | }) 124 | }) 125 | 126 | for _, starCode := range jc.opt.StarCode { 127 | err := pageCollector.Visit(JAVBUS_URL + "/star/" + starCode) 128 | if err != nil { 129 | return nil, err 130 | } 131 | } 132 | pageCollector.Wait() 133 | 134 | if queue.IsEmpty() { 135 | return nil, nil 136 | } 137 | 138 | collector.OnHTML(".container", jc.javbusMovieInfoCrawl) 139 | collector.OnHTML("body", jc.javbusMovieMagnetCrawl) 140 | queue.Run(collector) 141 | collector.Wait() 142 | 143 | return nil, nil 144 | } 145 | 146 | func (jc *javbusCrawl) GetJavbusMovieByFilepath() ([]JavMovie, error) { 147 | var videoExt = []string{".avi", ".mp4", ".mkv"} 148 | var codes []string = make([]string, 0) 149 | if err := filepath.Walk(jc.opt.VideosPath, func(path string, fi os.FileInfo, err error) error { 150 | if fi == nil { 151 | if err != nil { 152 | return err 153 | } 154 | return nil 155 | } 156 | if fi.IsDir() { 157 | return nil 158 | } 159 | filename := fi.Name() 160 | fileExt := filepath.Ext(filename) 161 | for _, b := range videoExt { 162 | if fileExt == b { 163 | code := strings.Replace(filename, b, "", -1) 164 | codes = append(codes, code) 165 | } 166 | } 167 | return nil 168 | }); err != nil { 169 | return nil, err 170 | } 171 | 172 | fmt.Println(codes) 173 | 174 | return nil, nil 175 | } 176 | 177 | func (jc *javbusCrawl) SaveLocal(infos []JavMovie) error { 178 | df := dataframe.LoadStructs(infos) 179 | f, err := os.OpenFile(path.Join(jc.opt.OutPath, time.Now().Local().Format("2006-01-02-15-04-05")+"-jav_Infow.csv"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm) 180 | if err != nil { 181 | jc.logger.Errorw("Save jav Infow file failed error: %s" + err.Error()) 182 | return err 183 | } 184 | defer f.Close() 185 | if err := df.WriteCSV(f); err != nil { 186 | return err 187 | } 188 | 189 | // if len(jc.javMagnets) == 0 { 190 | // return nil 191 | // } 192 | 193 | // f, err := os.OpenFile(path.Join(jc.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) 194 | // if err != nil { 195 | // jc.logger.Errorw("Save jav Infow file failed error: %s" + err.Error()) 196 | // return err 197 | // } 198 | // defer f.Close() 199 | 200 | // for _, v := range jc.javMagnets { 201 | // f.WriteString(v + "\n") 202 | // } 203 | 204 | // var urlImg = "" 205 | // u, err := url.ParseRequestURI(coverPath) 206 | // if err != nil { 207 | // jc.logger.Errorw("parse cover path failed error: %s" + err.Error()) 208 | // return err 209 | // } 210 | // if u.Host == "" { 211 | // urlImg = jc.javbusUrl + coverPath 212 | // } else { 213 | // urlImg = coverPath 214 | // } 215 | 216 | // jc.logger.Infow("downloading coverage url: " + urlImg) 217 | // ext := path.Ext(urlImg) 218 | // resp, err := jc.httpClient.Get(urlImg) 219 | // if err != nil { 220 | // jc.logger.Errorw("downloading coverage error: " + err.Error()) 221 | // return err 222 | // } 223 | // body, _ := ioutil.ReadAll(resp.Body) 224 | 225 | // f, err := os.OpenFile(path.Join(jc.option.DestPath, "cover", code+ext), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) 226 | // if err != nil { 227 | // return err 228 | // } 229 | // defer f.Close() 230 | // if _, err := io.Copy(f, bytes.NewReader(body)); err != nil { 231 | // return err 232 | // } 233 | return nil 234 | } 235 | 236 | func (jc *javbusCrawl) javbusMovieInfoCrawl(e *colly.HTMLElement) { 237 | var Info = &JavMovie{} 238 | Info.Title = e.ChildText("h3") 239 | Info.Cover = e.ChildAttr(".screencap img", "src") 240 | e.ForEach(".Info p", func(i int, element *colly.HTMLElement) { 241 | key := element.ChildText("span") 242 | switch i { 243 | case 0: 244 | Info.Code = element.ChildTexts("span")[1] 245 | } 246 | switch key { 247 | case "發行日期:": 248 | pd := element.Text 249 | Info.PublishDate = strings.Split(pd, " ")[1] 250 | case "長度:": 251 | pd := element.Text 252 | p := strings.Split(pd, " ")[1] 253 | Info.Length = strings.Split(p, "分鐘")[0] 254 | case "導演:": 255 | Info.Director = element.ChildText("a") 256 | case "製作商:": 257 | Info.ProduceCompany = element.ChildText("a") 258 | case "發行商:": 259 | Info.PublishCompany = element.ChildText("a") 260 | case "系列:": 261 | Info.Series = element.ChildText("a") 262 | } 263 | }) 264 | 265 | e.ForEach("ul li .star-name a", func(i int, element *colly.HTMLElement) { 266 | star := element.Attr("title") 267 | Info.Stars += star + ";" 268 | }) 269 | 270 | // jc. = append(t.javMovies, *Info) 271 | } 272 | 273 | func (jc *javbusCrawl) javbusMovieMagnetCrawl(e *colly.HTMLElement) { 274 | if !jc.opt.DownloadMagent { 275 | return 276 | } 277 | 278 | e.ForEach("script", func(i int, element *colly.HTMLElement) { 279 | if i != 2 { 280 | return 281 | } 282 | param := element.Text 283 | param = strings.Replace(param, " ", "", -1) 284 | param = strings.Replace(param, "\tvar", "", -1) 285 | param = strings.Replace(param, "\r", "", -1) 286 | param = strings.Replace(param, "\r\n", "", -1) 287 | param = strings.Replace(param, "\n", "", -1) 288 | param = strings.Replace(param, ";", "&", -1) 289 | param = strings.Replace(param, "'", "", -1) 290 | urlS := "https://www.javbus.com/ajax/uncledatoolsbyajax.php?" + param + "lang=zh&floor=442" 291 | // t.logger.Infow("Get magnet url: " + urlS) 292 | 293 | res, err := jc.httpClient.R().SetHeaders(map[string]string{ 294 | "Referer": e.Request.URL.Scheme + "://" + e.Request.URL.Host + e.Request.URL.Path, 295 | }).Get(urlS) 296 | if err != nil { 297 | // cc.logger.Errorw("http client error: " + err.Error()) 298 | return 299 | } 300 | 301 | body, err := io.ReadAll(res.RawResponse.Body) 302 | if err != nil { 303 | // cc.logger.Errorw("http read response error: " + err.Error()) 304 | return 305 | } 306 | // defer res.Body.Close() 307 | 308 | doc, err := htmlquery.Parse(strings.NewReader("" + string(body) + "
")) 309 | if err != nil { 310 | // cc.logger.Errorw("html query error: " + err.Error()) 311 | } 312 | list, err := htmlquery.QueryAll(doc, "//tr") 313 | if err != nil { 314 | // cc.logger.Errorw("html query tr error: " + err.Error()) 315 | } 316 | if len(list) == 0 { 317 | // cc.logger.Infow("当前无磁力连接") 318 | return 319 | } 320 | 321 | var mList = make([]JavMovieMagnet, 0) 322 | for _, n := range list { 323 | tdList, _ := htmlquery.QueryAll(n, "//td/a") 324 | var m = JavMovieMagnet{ 325 | HD: false, 326 | Subtitle: false, 327 | } 328 | for tdIndex, tdValue := range tdList { 329 | switch tdIndex { 330 | case 0: 331 | m.Link = htmlquery.SelectAttr(tdValue, "href") 332 | m.Name = htmlquery.InnerText(tdValue) 333 | default: 334 | if htmlquery.InnerText(tdValue) == "高清" { 335 | m.HD = true 336 | } else if htmlquery.InnerText(tdValue) == "字幕" { 337 | m.Subtitle = true 338 | } else { 339 | var sizeStr string = htmlquery.InnerText(tdValue) 340 | sizeStr = strings.Replace(sizeStr, " ", "", -1) 341 | sizeStr = strings.Replace(sizeStr, "\n", "", -1) 342 | sizeStr = strings.Replace(sizeStr, "\x09", "", -1) 343 | _, err := time.Parse("2006-01-02", sizeStr) 344 | if err != nil { 345 | b, err := bytesize.Parse(sizeStr) 346 | if err != nil { 347 | return 348 | } 349 | sizeStr = strings.Replace(b.Format("%.2f", "MB", false), "MB", "", -1) 350 | size, _ := strconv.ParseFloat(sizeStr, 64) 351 | m.Size = size 352 | } 353 | } 354 | } 355 | } 356 | mList = append(mList, m) 357 | } 358 | 359 | var maxSize float64 = 0 360 | // var bestMagnet string = "" 361 | for _, m := range mList { 362 | if m.Size > maxSize { 363 | maxSize = m.Size 364 | // bestMagnet = m.Link 365 | } 366 | } 367 | 368 | // TODO: jav code need 369 | // t.javMovies[0].Magnets = append(t.javMovies[0].Magnets, mList...) 370 | }) 371 | } 372 | 373 | func (jc *javbusCrawl) getCodeByPrefix() []string { 374 | var codes []string = make([]string, 0) 375 | for _, code := range jc.opt.PrefixCode { 376 | for i := jc.opt.PrefixMinNo; i < jc.opt.PrefixMaxNo; i++ { 377 | strNum := strconv.FormatUint(i, 10) 378 | if len(strNum) >= int(jc.opt.PrefixZero) { 379 | codes = append(codes, code+strNum) 380 | } else { 381 | zerosStr := make([]byte, int(jc.opt.PrefixZero)-len(strNum)) 382 | for i := range zerosStr { 383 | zerosStr[i] = '0' 384 | } 385 | codes = append(codes, code+string(append(zerosStr, []byte(strNum)...))) 386 | } 387 | } 388 | } 389 | return codes 390 | } 391 | -------------------------------------------------------------------------------- /garage_jav/javdb.go: -------------------------------------------------------------------------------- 1 | package garage_jav 2 | 3 | const JAVDB_URL = "https://www.javdb.com" 4 | 5 | type JavdbCrawl interface { 6 | GetJavbusMovieByHomePage() ([]JavMovie, error) // 通过首页爬取对应的电影信息 7 | GetJavbusMovie() ([]JavMovie, error) // 通过番号爬取对应的电影信息 8 | GetJavbusMovieByPrefix() ([]JavMovie, error) // 通过番号前缀爬取对应的电影信息 9 | GetJavbusMovieByStar() ([]JavMovie, error) // 通过演员ID爬取对应的电影信息 10 | GetJavbusMovieByFilepath() ([]JavMovie, error) // 访问文件夹下的视频列表爬取电影信息 11 | SaveLocal(infos []JavMovie) error 12 | } 13 | -------------------------------------------------------------------------------- /garage_jav/model.go: -------------------------------------------------------------------------------- 1 | package garage_jav 2 | 3 | import "time" 4 | 5 | type CrawlConfig struct { 6 | Proxy string `json:"proxy"` 7 | DestPath string `json:"dest_path"` 8 | RandomDelay time.Duration `json:"random_delay"` 9 | Parallelism int `json:"parallelism"` 10 | } 11 | 12 | type JavbusOption struct { 13 | Code []string `json:"code"` 14 | StarCode []string `json:"star_code"` 15 | DownloadMagent bool `json:"download_magent"` 16 | DownloadCover bool `json:"download_cover"` 17 | PrefixCode []string `json:"prefix_code"` 18 | PrefixMinNo uint64 `json:"prefix_min_no"` 19 | PrefixMaxNo uint64 `json:"prefix_max_no"` 20 | PrefixZero uint64 `json:"prefix_zero"` 21 | VideosPath string `json:"videos_path"` 22 | PageStartNo uint `json:"page_start_no"` 23 | OutPath string `json:"out_path"` 24 | } 25 | 26 | type JavMovie struct { 27 | Code string `json:"code"` 28 | Title string `json:"title"` 29 | Cover string `json:"cover"` 30 | PublishDate string `json:"publish_date"` 31 | Length string `json:"length"` 32 | Director string `json:"director"` 33 | ProduceCompany string `json:"produce_company"` 34 | PublishCompany string `json:"publish_company"` 35 | Series string `json:"series"` 36 | Stars string `json:"stars"` 37 | Magnets []JavMovieMagnet `json:"magnets" dataframe:"-"` 38 | } 39 | 40 | type JavMovieMagnet struct { 41 | Name string `json:"name"` 42 | Link string `json:"link"` 43 | Size float64 `json:"size"` 44 | Subtitle bool `json:"subtitle"` 45 | HD bool `json:"hd"` 46 | } 47 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/gsxhnd/garage 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.1 6 | 7 | require ( 8 | github.com/antchfx/htmlquery v1.3.3 9 | github.com/caarlos0/env/v11 v11.2.2 10 | github.com/go-gota/gota v0.12.0 11 | github.com/go-resty/resty/v2 v2.15.3 12 | github.com/gocolly/colly/v2 v2.1.0 13 | github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf 14 | github.com/stretchr/testify v1.9.0 15 | github.com/urfave/cli/v2 v2.27.4 16 | go.uber.org/zap v1.27.0 17 | gopkg.in/natefinch/lumberjack.v2 v2.2.1 18 | ) 19 | 20 | require ( 21 | github.com/PuerkitoBio/goquery v1.5.1 // indirect 22 | github.com/andybalholm/cascadia v1.2.0 // indirect 23 | github.com/antchfx/xmlquery v1.2.4 // indirect 24 | github.com/antchfx/xpath v1.3.2 // indirect 25 | github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect 26 | github.com/davecgh/go-spew v1.1.1 // indirect 27 | github.com/gobwas/glob v0.2.3 // indirect 28 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 29 | github.com/golang/protobuf v1.5.4 // indirect 30 | github.com/google/go-cmp v0.6.0 // indirect 31 | github.com/kennygrant/sanitize v1.2.4 // indirect 32 | github.com/kr/pretty v0.1.0 // indirect 33 | github.com/pmezard/go-difflib v1.0.0 // indirect 34 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 35 | github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect 36 | github.com/temoto/robotstxt v1.1.1 // indirect 37 | github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect 38 | go.uber.org/multierr v1.10.0 // indirect 39 | golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 // indirect 40 | golang.org/x/net v0.29.0 // indirect 41 | golang.org/x/text v0.18.0 // indirect 42 | gonum.org/v1/gonum v0.9.1 // indirect 43 | google.golang.org/appengine v1.6.8 // indirect 44 | google.golang.org/protobuf v1.34.2 // indirect 45 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect 46 | gopkg.in/yaml.v3 v3.0.1 // indirect 47 | ) 48 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 3 | gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= 4 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 5 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 6 | github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE= 7 | github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= 8 | github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= 9 | github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= 10 | github.com/andybalholm/cascadia v1.2.0 h1:vuRCkM5Ozh/BfmsaTm26kbjm0mIOM3yS5Ek/F5h18aE= 11 | github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY= 12 | github.com/antchfx/htmlquery v1.2.3/go.mod h1:B0ABL+F5irhhMWg54ymEZinzMSi0Kt3I2if0BLYa3V0= 13 | github.com/antchfx/htmlquery v1.3.3 h1:x6tVzrRhVNfECDaVxnZi1mEGrQg3mjE/rxbH2Pe6dNE= 14 | github.com/antchfx/htmlquery v1.3.3/go.mod h1:WeU3N7/rL6mb6dCwtE30dURBnBieKDC/fR8t6X+cKjU= 15 | github.com/antchfx/xmlquery v1.2.4 h1:T/SH1bYdzdjTMoz2RgsfVKbM5uWh3gjDYYepFqQmFv4= 16 | github.com/antchfx/xmlquery v1.2.4/go.mod h1:KQQuESaxSlqugE2ZBcM/qn+ebIpt+d+4Xx7YcSGAIrM= 17 | github.com/antchfx/xpath v1.1.6/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk= 18 | github.com/antchfx/xpath v1.1.8/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk= 19 | github.com/antchfx/xpath v1.3.2 h1:LNjzlsSjinu3bQpw9hWMY9ocB80oLOWuQqFvO6xt51U= 20 | github.com/antchfx/xpath v1.3.2/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= 21 | github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= 22 | github.com/caarlos0/env/v11 v11.2.2 h1:95fApNrUyueipoZN/EhA8mMxiNxrBwDa+oAZrMWl3Kg= 23 | github.com/caarlos0/env/v11 v11.2.2/go.mod h1:JBfcdeQiBoI3Zh1QRAWfe+tpiNTmDtcCj/hHHHMx0vc= 24 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 25 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 26 | github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= 27 | github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 28 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 29 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 30 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 31 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 32 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 33 | github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= 34 | github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= 35 | github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= 36 | github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= 37 | github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= 38 | github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= 39 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 40 | github.com/go-gota/gota v0.12.0 h1:T5BDg1hTf5fZ/CO+T/N0E+DDqUhvoKBl+UVckgcAAQg= 41 | github.com/go-gota/gota v0.12.0/go.mod h1:UT+NsWpZC/FhaOyWb9Hui0jXg0Iq8e/YugZHTbyW/34= 42 | github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= 43 | github.com/go-resty/resty/v2 v2.15.3 h1:bqff+hcqAflpiF591hhJzNdkRsFhlB96CYfBwSFvql8= 44 | github.com/go-resty/resty/v2 v2.15.3/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU= 45 | github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= 46 | github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= 47 | github.com/gocolly/colly v1.2.0/go.mod h1:Hof5T3ZswNVsOHYmba1u03W65HDWgpV5HifSuueE0EA= 48 | github.com/gocolly/colly/v2 v2.1.0 h1:k0DuZkDoCsx51bKpRJNEmcxcp+W5N8ziuwGaSDuFoGs= 49 | github.com/gocolly/colly/v2 v2.1.0/go.mod h1:I2MuhsLjQ+Ex+IzK3afNS8/1qP3AedHOusRPcRdC5o0= 50 | github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= 51 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 52 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 53 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= 54 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 55 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 56 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 57 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 58 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 59 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 60 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 61 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 62 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 63 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 64 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 65 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 66 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 67 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 68 | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 69 | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 70 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 71 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 72 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 73 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 74 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 75 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 76 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 77 | github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf h1:FtEj8sfIcaaBfAKrE1Cwb61YDtYq9JxChK1c7AKce7s= 78 | github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf/go.mod h1:yrqSXGoD/4EKfF26AOGzscPOgTTJcyAwM2rpixWT+t4= 79 | github.com/jawher/mow.cli v1.1.0/go.mod h1:aNaQlc7ozF3vw6IJ2dHjp2ZFiA4ozMIYY6PyuRJwlUg= 80 | github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= 81 | github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= 82 | github.com/kennygrant/sanitize v1.2.4 h1:gN25/otpP5vAsO2djbMhF/LQX6R7+O1TB4yv8NzpJ3o= 83 | github.com/kennygrant/sanitize v1.2.4/go.mod h1:LGsjYYtgxbetdg5owWB2mpgUL6e2nfw2eObZ0u0qvak= 84 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 85 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 86 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 87 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 88 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 89 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 90 | github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= 91 | github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= 92 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 93 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 94 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 95 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 96 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 97 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 98 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 99 | github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= 100 | github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI= 101 | github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= 102 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 103 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 104 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 105 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 106 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 107 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 108 | github.com/temoto/robotstxt v1.1.1 h1:Gh8RCs8ouX3hRSxxK7B1mO5RFByQ4CmJZDwgom++JaA= 109 | github.com/temoto/robotstxt v1.1.1/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo= 110 | github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8= 111 | github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ= 112 | github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= 113 | github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= 114 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 115 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 116 | go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 117 | go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= 118 | go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 119 | go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= 120 | go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= 121 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 122 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 123 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 124 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 125 | golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 126 | golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 127 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 128 | golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 129 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 130 | golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= 131 | golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 h1:pVgRXcIictcr+lBQIFeiwuwtDIs4eL21OuM9nyAADmo= 132 | golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= 133 | golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= 134 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 135 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 136 | golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 137 | golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 138 | golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 139 | golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 140 | golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 141 | golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 142 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 143 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 144 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 145 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 146 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 147 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 148 | golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 149 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 150 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 151 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 152 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 153 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 154 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 155 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 156 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 157 | golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 158 | golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 159 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 160 | golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= 161 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 162 | golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 163 | golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= 164 | golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= 165 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 166 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 167 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 168 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 169 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 170 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 171 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 172 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 173 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 174 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 175 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 176 | golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 177 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 178 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 179 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 180 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 181 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 182 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 183 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 184 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 185 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 186 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 187 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 188 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 189 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 190 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 191 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 192 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 193 | golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= 194 | golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 195 | golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= 196 | golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 197 | golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 198 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 199 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 200 | golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 201 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 202 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 203 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 204 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 205 | golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 206 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 207 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 208 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 209 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 210 | gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= 211 | gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= 212 | gonum.org/v1/gonum v0.9.1 h1:HCWmqqNoELL0RAQeKBXWtkp04mGk8koafcB4He6+uhc= 213 | gonum.org/v1/gonum v0.9.1/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= 214 | gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= 215 | gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= 216 | gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= 217 | gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= 218 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 219 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 220 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 221 | google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= 222 | google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= 223 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 224 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 225 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 226 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 227 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 228 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 229 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 230 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 231 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 232 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 233 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 234 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 235 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 236 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 237 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 238 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 239 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 240 | google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= 241 | google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= 242 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 243 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 244 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 245 | gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= 246 | gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= 247 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 248 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 249 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 250 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 251 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 252 | -------------------------------------------------------------------------------- /utils/config.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/caarlos0/env/v11" 5 | ) 6 | 7 | type Config struct { 8 | Mode string `env:"MODE" envDefault:"dev"` 9 | DatabasePath string `env:"DB_PATH" envDefault:"./data/garage.db"` 10 | Listen string `env:"LISTEN" envDefault:":8080"` 11 | Log LogConfig 12 | Storage StorageConfig 13 | } 14 | 15 | type LogConfig struct { 16 | Level string `env:"LOG_LEVEL" envDefault:"debug"` 17 | FileName string `env:"LOG_FILE_NAME" envDefault:"./data/log/garage.log"` 18 | MaxBackups int `env:"LOG_MAX_BACKUPS" envDefault:"10"` 19 | MaxAge int `env:"LOG_MAX_AGE" envDefault:"7"` 20 | } 21 | 22 | type StorageConfig struct { 23 | Type string `env:"STORAGE_TYPE" envDefault:"local"` 24 | Path string `env:"STORAGE_PATH" envDefault:"./data/cover"` 25 | Endpoint string `env:"STORAGE_ENDPOINT" envDefault:"localhost:9000"` 26 | BucketName string `env:"STORAGE_BUCKET_NAME" envDefault:"jav-cover"` 27 | AccessKey string `env:"STORAGE_ACCESS_KEY"` 28 | SecretKey string `env:"STORAGE_SECRET_KEY"` 29 | } 30 | 31 | func NewConfig() (*Config, error) { 32 | var c Config 33 | 34 | opts := env.Options{ 35 | Prefix: "GARAGE_", 36 | } 37 | 38 | if err := env.ParseWithOptions(&c, opts); err != nil { 39 | return nil, err 40 | } 41 | 42 | return &c, nil 43 | } 44 | -------------------------------------------------------------------------------- /utils/file.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | ) 7 | 8 | func MakeDir(fullPath string) error { 9 | fs, err := os.Stat(fullPath) 10 | 11 | if os.IsNotExist(err) { 12 | err = os.MkdirAll(fullPath, os.ModePerm) 13 | return err 14 | } 15 | 16 | if !fs.IsDir() { 17 | return errors.New("file name exist, but not dir") 18 | } 19 | 20 | if err != nil { 21 | return err 22 | } 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /utils/file_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestMakeDir(t *testing.T) { 10 | tests := []struct { 11 | name string 12 | path string 13 | wantErr bool 14 | }{ 15 | {"test", "../javbus/cover", false}, 16 | {"test_repeat", "../javbus/cover", false}, 17 | {"test_repeat", "../testdata/1/1.ass", true}, 18 | } 19 | for _, tt := range tests { 20 | t.Run(tt.name, func(t *testing.T) { 21 | err := MakeDir(tt.path) 22 | if tt.wantErr { 23 | assert.NotNil(t, err) 24 | } else { 25 | assert.Nil(t, err) 26 | } 27 | }) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /utils/logger.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "os" 5 | "time" 6 | 7 | "go.uber.org/zap" 8 | "go.uber.org/zap/zapcore" 9 | "gopkg.in/natefinch/lumberjack.v2" 10 | ) 11 | 12 | type Logger interface { 13 | Debugf(template string, args ...interface{}) 14 | Debugw(template string, keysAndValues ...interface{}) 15 | Infof(template string, args ...interface{}) 16 | Infow(template string, keysAndValues ...interface{}) 17 | Warnf(template string, args ...interface{}) 18 | Warnw(template string, keysAndValues ...interface{}) 19 | Errorf(template string, args ...interface{}) 20 | Errorw(template string, keysAndValues ...interface{}) 21 | Panicf(template string, args ...interface{}) 22 | Panicw(template string, keysAndValues ...interface{}) 23 | } 24 | 25 | type logger struct { 26 | // Logger *zap.Logger 27 | Suger *zap.SugaredLogger 28 | } 29 | 30 | var ( 31 | devEncoder = zapcore.NewConsoleEncoder(zapcore.EncoderConfig{ 32 | MessageKey: "msg", 33 | LevelKey: "level", 34 | TimeKey: "time", 35 | CallerKey: "caller", 36 | EncodeLevel: zapcore.CapitalColorLevelEncoder, 37 | EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { 38 | enc.AppendString(t.UTC().Format("2006-01-02T15:04:05.000000-07:00")) 39 | }, 40 | EncodeCaller: zapcore.ShortCallerEncoder, 41 | EncodeDuration: func(d time.Duration, enc zapcore.PrimitiveArrayEncoder) { 42 | enc.AppendInt64(int64(d) / 1000000) 43 | }, 44 | }) 45 | prodEncoder = zapcore.NewJSONEncoder(zapcore.EncoderConfig{ 46 | MessageKey: "msg", 47 | LevelKey: "level", 48 | TimeKey: "time", 49 | CallerKey: "caller", 50 | EncodeLevel: zapcore.CapitalLevelEncoder, 51 | EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { 52 | enc.AppendString(t.UTC().Format("2006-01-02T15:04:05.000000-07:00")) 53 | }, 54 | EncodeCaller: zapcore.ShortCallerEncoder, 55 | EncodeDuration: func(d time.Duration, enc zapcore.PrimitiveArrayEncoder) { 56 | enc.AppendInt64(int64(d) / 1000000) 57 | }, 58 | }) 59 | ) 60 | 61 | func NewLogger(cfg *Config) Logger { 62 | var ( 63 | level zapcore.Level 64 | core zapcore.Core 65 | ) 66 | 67 | level = zap.InfoLevel 68 | switch cfg.Log.Level { 69 | case "debug": 70 | level = zap.DebugLevel 71 | case "info": 72 | level = zap.InfoLevel 73 | case "warn": 74 | level = zap.WarnLevel 75 | default: 76 | level = zap.InfoLevel 77 | } 78 | 79 | if !(cfg.Mode == "dev") { 80 | core = zapcore.NewCore(prodEncoder, zapcore.AddSync(&lumberjack.Logger{ 81 | Filename: cfg.Log.FileName, 82 | MaxBackups: cfg.Log.MaxBackups, 83 | MaxAge: cfg.Log.MaxAge, 84 | }, 85 | ), level) 86 | } else { 87 | core = zapcore.NewCore(devEncoder, os.Stdout, level) 88 | } 89 | 90 | return &logger{ 91 | Suger: zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1)).Sugar(), 92 | } 93 | } 94 | 95 | func (l *logger) Debugf(template string, args ...interface{}) { 96 | defer l.Suger.Sync() 97 | l.Suger.Debugf(template, args...) 98 | } 99 | func (l *logger) Debugw(template string, keysAndValues ...interface{}) { 100 | defer l.Suger.Sync() 101 | l.Suger.Debugw(template, keysAndValues...) 102 | } 103 | 104 | func (l *logger) Infof(template string, args ...interface{}) { 105 | defer l.Suger.Sync() 106 | l.Suger.Infof(template, args...) 107 | } 108 | func (l *logger) Infow(template string, keysAndValues ...interface{}) { 109 | defer l.Suger.Sync() 110 | l.Suger.Infow(template, keysAndValues...) 111 | } 112 | 113 | func (l *logger) Warnf(template string, args ...interface{}) { 114 | defer l.Suger.Sync() 115 | l.Suger.Warnf(template, args...) 116 | } 117 | func (l *logger) Warnw(template string, keysAndValues ...interface{}) { 118 | defer l.Suger.Sync() 119 | l.Suger.Warnw(template, keysAndValues...) 120 | } 121 | 122 | func (l *logger) Errorf(template string, args ...interface{}) { 123 | defer l.Suger.Sync() 124 | l.Suger.Errorf(template, args...) 125 | } 126 | func (l *logger) Errorw(template string, keysAndValues ...interface{}) { 127 | defer l.Suger.Sync() 128 | l.Suger.Errorw(template, keysAndValues...) 129 | } 130 | 131 | func (l *logger) Panicf(template string, args ...interface{}) { 132 | defer l.Suger.Sync() 133 | l.Suger.Panicf(template, args...) 134 | } 135 | func (l *logger) Panicw(template string, keysAndValues ...interface{}) { 136 | defer l.Suger.Sync() 137 | l.Suger.Panicw(template, keysAndValues...) 138 | } 139 | -------------------------------------------------------------------------------- /utils/logger_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | --------------------------------------------------------------------------------