mirror of
https://github.com/ProudMuBai/GoFilm.git
synced 2026-04-18 22:37:36 +08:00
Storage optimization
This commit is contained in:
@@ -23,8 +23,8 @@
|
||||
"typescript": "^4.9.3",
|
||||
"unplugin-auto-import": "^0.15.1",
|
||||
"unplugin-vue-components": "^0.24.1",
|
||||
"vite": "^4.2.0",
|
||||
"vite-plugin-ssr": "^0.4.109",
|
||||
"vite": "^7.3.1",
|
||||
"vite-plugin-ssr": "^0.4.85",
|
||||
"vue-router": "^4.1.6",
|
||||
"vue-tsc": "^1.2.0"
|
||||
}
|
||||
|
||||
@@ -86,13 +86,15 @@ const (
|
||||
// SearchTableName 存放检索信息的数据表名
|
||||
SearchTableName = "search"
|
||||
UserTableName = "users"
|
||||
MovieDetailName = "movie_details"
|
||||
SlaveMovieInfo = "slave_infos"
|
||||
UserIdInitialVal = 10000
|
||||
FileTableName = "files"
|
||||
FailureRecordTableName = "failure_records"
|
||||
|
||||
//mysql服务配置信息 root:root 设置mysql账户的用户名和密码
|
||||
|
||||
MysqlDsn = "root:root@(192.168.20.5:3306)/FilmSite?charset=utf8mb4&parseTime=True&loc=Local"
|
||||
MysqlDsn = "root:root@(192.168.20.5:3601)/FilmSite?charset=utf8mb4&parseTime=True&loc=Local"
|
||||
//MysqlDsn = "root:MuBai0916$@(113.44.5.201:3610)/FilmSite?charset=utf8mb4&parseTime=True&loc=Local"
|
||||
|
||||
// MysqlDsn docker compose 环境下的链接信息 mysql:3306 为 docker compose 中 mysql服务对应的网络名称和端口
|
||||
@@ -108,7 +110,7 @@ const (
|
||||
//RedisPassword = `MuBai0916$`
|
||||
//RedisDBNo = 0
|
||||
|
||||
RedisAddr = `192.168.20.5:6379`
|
||||
RedisAddr = `192.168.20.5:3602`
|
||||
RedisPassword = `root`
|
||||
RedisDBNo = 0
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"server/logic"
|
||||
"server/model/system"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -40,10 +41,10 @@ func FilmDetail(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
// 获取影片详情信息
|
||||
detail := logic.IL.GetFilmDetail(id)
|
||||
detail := logic.IL.GetFilmDetail(int64(id))
|
||||
// 获取相关推荐影片数据
|
||||
page := system.Page{Current: 0, PageSize: 14}
|
||||
relateMovie := logic.IL.RelateMovie(detail.MovieDetail, &page)
|
||||
relateMovie := logic.IL.RelateMovie(detail, &page)
|
||||
system.Success(gin.H{
|
||||
"detail": detail,
|
||||
"relate": relateMovie,
|
||||
@@ -61,23 +62,22 @@ func FilmPlayInfo(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
// 获取影片详情信息
|
||||
detail := logic.IL.GetFilmDetail(id)
|
||||
detail := logic.IL.GetFilmDetail(int64(id))
|
||||
// 如果 playFrom 为空, 则设置默认播放源和默认影片数据
|
||||
if len(playFrom) <= 1 && len(detail.List) > 0 {
|
||||
playFrom = detail.List[0].Id
|
||||
|
||||
}
|
||||
// 获取当前影片播放信息
|
||||
var currentPlay system.MovieUrlInfo
|
||||
var currentPlay system.PlayItem
|
||||
for _, v := range detail.List {
|
||||
if v.Id == playFrom {
|
||||
currentPlay = v.LinkList[episode]
|
||||
}
|
||||
}
|
||||
|
||||
// 推荐影片信息
|
||||
page := system.Page{Current: 0, PageSize: 14}
|
||||
relateMovie := logic.IL.RelateMovie(detail.MovieDetail, &page)
|
||||
relateMovie := logic.IL.RelateMovie(detail, &page)
|
||||
system.Success(gin.H{
|
||||
"detail": detail,
|
||||
"current": currentPlay,
|
||||
|
||||
@@ -1,56 +1,66 @@
|
||||
module server
|
||||
|
||||
go 1.20
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/gin-gonic/gin v1.9.0
|
||||
github.com/gocolly/colly/v2 v2.1.0
|
||||
github.com/golang-jwt/jwt/v5 v5.1.0
|
||||
github.com/redis/go-redis/v9 v9.0.2
|
||||
github.com/robfig/cron/v3 v3.0.0
|
||||
gorm.io/driver/mysql v1.4.7
|
||||
gorm.io/gorm v1.25.5
|
||||
github.com/gin-gonic/gin v1.12.0
|
||||
github.com/gocolly/colly/v2 v2.3.0
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||
github.com/redis/go-redis/v9 v9.18.0
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
gorm.io/driver/mysql v1.6.0
|
||||
gorm.io/gorm v1.31.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/PuerkitoBio/goquery v1.5.1 // indirect
|
||||
github.com/andybalholm/cascadia v1.2.0 // indirect
|
||||
github.com/antchfx/htmlquery v1.2.3 // indirect
|
||||
github.com/antchfx/xmlquery v1.2.4 // indirect
|
||||
github.com/antchfx/xpath v1.1.8 // indirect
|
||||
github.com/bytedance/sonic v1.8.5 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/PuerkitoBio/goquery v1.11.0 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/antchfx/htmlquery v1.3.5 // indirect
|
||||
github.com/antchfx/xmlquery v1.5.0 // indirect
|
||||
github.com/antchfx/xpath v1.3.5 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.24.4 // indirect
|
||||
github.com/bytedance/gopkg v0.1.3 // indirect
|
||||
github.com/bytedance/sonic v1.15.0 // indirect
|
||||
github.com/bytedance/sonic/loader v0.5.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.12.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.30.1 // indirect
|
||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
||||
github.com/golang/protobuf v1.5.0 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/goccy/go-yaml v1.19.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kennygrant/sanitize v1.2.4 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||
github.com/leodido/go-urn v1.2.2 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.7 // indirect
|
||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect
|
||||
github.com/temoto/robotstxt v1.1.1 // indirect
|
||||
github.com/nlnwa/whatwg-url v0.6.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/quic-go/quic-go v0.59.0 // indirect
|
||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect
|
||||
github.com/temoto/robotstxt v1.1.2 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.7.0 // indirect
|
||||
golang.org/x/net v0.8.0 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
golang.org/x/text v0.8.0 // indirect
|
||||
google.golang.org/appengine v1.6.6 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
github.com/ugorji/go/codec v1.3.1 // indirect
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
golang.org/x/arch v0.22.0 // indirect
|
||||
golang.org/x/crypto v0.48.0 // indirect
|
||||
golang.org/x/net v0.51.0 // indirect
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
golang.org/x/text v0.34.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
)
|
||||
|
||||
338
server/go.sum
338
server/go.sum
@@ -1,211 +1,227 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
|
||||
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
github.com/andybalholm/cascadia v1.2.0 h1:vuRCkM5Ozh/BfmsaTm26kbjm0mIOM3yS5Ek/F5h18aE=
|
||||
github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY=
|
||||
github.com/antchfx/htmlquery v1.2.3 h1:sP3NFDneHx2stfNXCKbhHFo8XgNjCACnU/4AO5gWz6M=
|
||||
github.com/antchfx/htmlquery v1.2.3/go.mod h1:B0ABL+F5irhhMWg54ymEZinzMSi0Kt3I2if0BLYa3V0=
|
||||
github.com/antchfx/xmlquery v1.2.4 h1:T/SH1bYdzdjTMoz2RgsfVKbM5uWh3gjDYYepFqQmFv4=
|
||||
github.com/antchfx/xmlquery v1.2.4/go.mod h1:KQQuESaxSlqugE2ZBcM/qn+ebIpt+d+4Xx7YcSGAIrM=
|
||||
github.com/antchfx/xpath v1.1.6/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=
|
||||
github.com/antchfx/xpath v1.1.8 h1:PcL6bIX42Px5usSx6xRYw/wjB3wYGkj0MJ9MBzEKVgk=
|
||||
github.com/antchfx/xpath v1.1.8/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=
|
||||
github.com/bsm/ginkgo/v2 v2.5.0 h1:aOAnND1T40wEdAtkGSkvSICWeQ8L3UASX7YVCqQx+eQ=
|
||||
github.com/bsm/gomega v1.20.0 h1:JhAwLmtRzXFTx2AkALSLa8ijZafntmhSoU63Ok18Uq8=
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.8.5 h1:kjX0/vo5acEQ/sinD/18SkA/lDDUk23F0RcaHvI7omc=
|
||||
github.com/bytedance/sonic v1.8.5/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw=
|
||||
github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ=
|
||||
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
|
||||
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
|
||||
github.com/antchfx/htmlquery v1.3.5 h1:aYthDDClnG2a2xePf6tys/UyyM/kRcsFRm+ifhFKoU0=
|
||||
github.com/antchfx/htmlquery v1.3.5/go.mod h1:5oyIPIa3ovYGtLqMPNjBF2Uf25NPCKsMjCnQ8lvjaoA=
|
||||
github.com/antchfx/xmlquery v1.5.0 h1:uAi+mO40ZWfyU6mlUBxRVvL6uBNZ6LMU4M3+mQIBV4c=
|
||||
github.com/antchfx/xmlquery v1.5.0/go.mod h1:lJfWRXzYMK1ss32zm1GQV3gMIW/HFey3xDZmkP1SuNc=
|
||||
github.com/antchfx/xpath v1.3.5 h1:PqbXLC3TkfeZyakF5eeh3NTWEbYl4VHNVeufANzDbKQ=
|
||||
github.com/antchfx/xpath v1.3.5/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
|
||||
github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/bits-and-blooms/bitset v1.24.4 h1:95H15Og1clikBrKr/DuzMXkQzECs1M6hhoGXLwLQOZE=
|
||||
github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
|
||||
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
|
||||
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
|
||||
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
|
||||
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
|
||||
github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8=
|
||||
github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k=
|
||||
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
|
||||
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||
github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
|
||||
github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI=
|
||||
github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA=
|
||||
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
|
||||
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
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=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/gocolly/colly v1.2.0/go.mod h1:Hof5T3ZswNVsOHYmba1u03W65HDWgpV5HifSuueE0EA=
|
||||
github.com/gocolly/colly/v2 v2.1.0 h1:k0DuZkDoCsx51bKpRJNEmcxcp+W5N8ziuwGaSDuFoGs=
|
||||
github.com/gocolly/colly/v2 v2.1.0/go.mod h1:I2MuhsLjQ+Ex+IzK3afNS8/1qP3AedHOusRPcRdC5o0=
|
||||
github.com/golang-jwt/jwt/v5 v5.1.0 h1:UGKbA/IPjtS6zLcdB7i5TyACMgSbOTiR8qzXgw8HWQU=
|
||||
github.com/golang-jwt/jwt/v5 v5.1.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
|
||||
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/gocolly/colly/v2 v2.3.0 h1:HSFh0ckbgVd2CSGRE+Y/iA4goUhGROJwyQDCMXGFBWM=
|
||||
github.com/gocolly/colly/v2 v2.3.0/go.mod h1:Qp54s/kQbwCQvFVx8KzKCSTXVJ1wWT4QeAKEu33x1q8=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/jawher/mow.cli v1.1.0/go.mod h1:aNaQlc7ozF3vw6IJ2dHjp2ZFiA4ozMIYY6PyuRJwlUg=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kennygrant/sanitize v1.2.4 h1:gN25/otpP5vAsO2djbMhF/LQX6R7+O1TB4yv8NzpJ3o=
|
||||
github.com/kennygrant/sanitize v1.2.4/go.mod h1:LGsjYYtgxbetdg5owWB2mpgUL6e2nfw2eObZ0u0qvak=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/leodido/go-urn v1.2.2 h1:7z68G0FCGvDk646jz1AelTYNYWrTNm0bEcFAo147wt4=
|
||||
github.com/leodido/go-urn v1.2.2/go.mod h1:kUaIbLZWttglzwNuG0pgsh5vuV6u2YcGBYz1hIPjtOQ=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us=
|
||||
github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
|
||||
github.com/nlnwa/whatwg-url v0.6.2 h1:jU61lU2ig4LANydbEJmA2nPrtCGiKdtgT0rmMd2VZ/Q=
|
||||
github.com/nlnwa/whatwg-url v0.6.2/go.mod h1:x0FPXJzzOEieQtsBT/AKvbiBbQ46YlL6Xa7m02M1ECk=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/redis/go-redis/v9 v9.0.2 h1:BA426Zqe/7r56kCcvxYLWe1mkaz71LKF77GwgFzSxfE=
|
||||
github.com/redis/go-redis/v9 v9.0.2/go.mod h1:/xDTe9EF1LM61hek62Poq2nzQSGj0xSrEtEHbBQevps=
|
||||
github.com/robfig/cron/v3 v3.0.0 h1:kQ6Cb7aHOHTSzNVNEhmp8EcWKLb4CbiMW9h9VyIhO4E=
|
||||
github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ=
|
||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
|
||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
|
||||
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
|
||||
github.com/redis/go-redis/v9 v9.18.0 h1:pMkxYPkEbMPwRdenAzUNyFNrDgHx9U+DrBabWNfSRQs=
|
||||
github.com/redis/go-redis/v9 v9.18.0/go.mod h1:k3ufPphLU5YXwNTUcCRXGxUoF1fqxnhFQmscfkCoDA0=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA=
|
||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/temoto/robotstxt v1.1.1 h1:Gh8RCs8ouX3hRSxxK7B1mO5RFByQ4CmJZDwgom++JaA=
|
||||
github.com/temoto/robotstxt v1.1.1/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/temoto/robotstxt v1.1.2 h1:W2pOjSJ6SWvldyEuiFXNxz3xZ8aiWX5LbfDiOFd7Fxg=
|
||||
github.com/temoto/robotstxt v1.1.2/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
|
||||
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
|
||||
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI=
|
||||
golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
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.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
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.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
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.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||
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=
|
||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
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/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||
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=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
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=
|
||||
gorm.io/driver/mysql v1.4.7 h1:rY46lkCspzGHn7+IYsNpSfEv9tA+SU4SkkB+GFX125Y=
|
||||
gorm.io/driver/mysql v1.4.7/go.mod h1:SxzItlnT1cb6e1e4ZRpgJN2VYtcqJgqnHxWr4wsP8oc=
|
||||
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
|
||||
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
|
||||
gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
|
||||
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
|
||||
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"regexp"
|
||||
"server/config"
|
||||
"server/model/system"
|
||||
"server/plugin/db"
|
||||
"server/plugin/common/util"
|
||||
"server/plugin/spider"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -82,16 +81,13 @@ func (i *IndexLogic) ClearIndexCache() {
|
||||
}
|
||||
|
||||
// GetFilmDetail 影片详情信息页面处理
|
||||
func (i *IndexLogic) GetFilmDetail(id int) system.MovieDetailVo {
|
||||
// 通过Id 获取影片search信息
|
||||
search := system.SearchInfo{}
|
||||
db.Mdb.Where("mid", id).First(&search)
|
||||
// 获取redis中的完整影视信息 MovieDetail:Cid11:Id24676
|
||||
movieDetail := system.GetDetailByKey(fmt.Sprintf(config.MovieDetailKey, search.Cid, search.Mid))
|
||||
var res = system.MovieDetailVo{MovieDetail: movieDetail}
|
||||
func (i *IndexLogic) GetFilmDetail(id int64) system.MovieDetailVo {
|
||||
// 通过mid获取影片的详情信息
|
||||
detail := system.GetDetailByMid(id)
|
||||
//查找其他站点是否存在影片对应的播放源
|
||||
res.List = multipleSource(&movieDetail)
|
||||
return res
|
||||
ml := multipleSource(&detail)
|
||||
// 转换组合主次站点信息
|
||||
return system.ConvertMovieDetailVo(detail, ml)
|
||||
}
|
||||
|
||||
// GetCategoryInfo 分类信息获取, 组装导航栏需要的信息
|
||||
@@ -135,13 +131,9 @@ func (i *IndexLogic) GetNavCategory() []*system.Category {
|
||||
// SearchFilmInfo 获取关键字匹配的影片信息
|
||||
func (i *IndexLogic) SearchFilmInfo(key string, page *system.Page) []system.MovieBasicInfo {
|
||||
// 1. 从mysql中获取满足条件的数据, 每页10条
|
||||
sl := system.SearchFilmKeyword(key, page)
|
||||
// 2. 获取redis中的basicMovieInfo信息
|
||||
var bl []system.MovieBasicInfo
|
||||
for _, s := range sl {
|
||||
bl = append(bl, system.GetBasicInfoByKey(fmt.Sprintf(config.MovieBasicInfoKey, s.Cid, s.Mid)))
|
||||
}
|
||||
return bl
|
||||
ids := system.SearchFilmKeyword(key, page)
|
||||
// 2. 通过ids获取对应的影片信息
|
||||
return system.GetBasicInfoByIds(ids)
|
||||
}
|
||||
|
||||
// GetFilmCategory 根据Pid或Cid获取指定的分页数据
|
||||
@@ -169,7 +161,7 @@ func (i *IndexLogic) GetPidCategory(pid int64) *system.CategoryTree {
|
||||
}
|
||||
|
||||
// RelateMovie 根据当前影片信息匹配相关的影片
|
||||
func (i *IndexLogic) RelateMovie(detail system.MovieDetail, page *system.Page) []system.MovieBasicInfo {
|
||||
func (i *IndexLogic) RelateMovie(detail system.MovieDetailVo, page *system.Page) []system.MovieBasicInfo {
|
||||
/*
|
||||
根据当前影片信息匹配相关的影片
|
||||
1. 分类Cid,
|
||||
@@ -203,53 +195,40 @@ func (i *IndexLogic) SearchTags(pid int64) map[string]interface{} {
|
||||
func multipleSource(detail *system.MovieDetail) []system.PlayLinkVo {
|
||||
// 生成多站点的播放源信息
|
||||
master := system.GetCollectSourceListByGrade(system.MasterCollect)
|
||||
var playList = []system.PlayLinkVo{{master[0].Id, master[0].Name, detail.PlayList[0]}}
|
||||
|
||||
// 整合多播放源, 初始化存储key map
|
||||
names := make(map[string]int)
|
||||
// 1. 判断detail的dbId是否存在, 存在则添加到names中作为匹配条件
|
||||
if detail.DbId > 0 {
|
||||
names[system.GenerateHashKey(detail.DbId)] = 0
|
||||
}
|
||||
// 2. 对name进行去除特殊格式处理
|
||||
names[system.GenerateHashKey(detail.Name)] = 0
|
||||
// 3. 对包含第一季的name进行处理
|
||||
names[system.GenerateHashKey(regexp.MustCompile(`第一季$`).ReplaceAllString(detail.Name, ""))] = 0
|
||||
|
||||
// 4. 将subtitle进行切分,放入names中
|
||||
if len(detail.SubTitle) > 0 && strings.Contains(detail.SubTitle, ",") {
|
||||
for _, v := range strings.Split(detail.SubTitle, ",") {
|
||||
names[system.GenerateHashKey(v)] = 0
|
||||
var l = []system.PlayLinkVo{{master[0].Id, master[0].Name, detail.PlayList[0]}}
|
||||
// 通过 name 以及 subTitle 生成 hash id 和 dbID 匹配次级站点播放信息
|
||||
// 使用map 防止清洗后的id重复
|
||||
idMap := make(map[string]int)
|
||||
idMap[system.GenerateHashKey(detail.Mid)] = 0
|
||||
// 将subTitle进行切割
|
||||
if len(detail.SubTitle) > 0 {
|
||||
for _, s := range strings.Split(util.FormatSpecialChar(detail.SubTitle), ",") {
|
||||
idMap[system.GenerateHashKey(s)] = 0
|
||||
}
|
||||
}
|
||||
if len(detail.SubTitle) > 0 && strings.Contains(detail.SubTitle, "/") {
|
||||
for _, v := range strings.Split(detail.SubTitle, "/") {
|
||||
names[system.GenerateHashKey(v)] = 0
|
||||
}
|
||||
// 遍历idMqp整合ids
|
||||
var ids []string
|
||||
for id, _ := range idMap {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
// 遍历所有附属站点列表
|
||||
sc := system.GetCollectSourceListByGrade(system.SlaveCollect)
|
||||
for _, s := range sc {
|
||||
for k, _ := range names {
|
||||
pl := system.GetMultiplePlay(s.Id, k)
|
||||
if len(pl) > 0 {
|
||||
// 如果当前站点已经匹配到数据则直接退出当前循环
|
||||
//detail.PlayList = append(detail.PlayList, pl)
|
||||
playList = append(playList, system.PlayLinkVo{Id: s.Id, Name: s.Name, LinkList: pl})
|
||||
break
|
||||
}
|
||||
}
|
||||
// 获取附属站点的基本信息
|
||||
sMap := make(map[string]system.FilmSource)
|
||||
for _, c := range system.GetCollectSourceListByGrade(system.SlaveCollect) {
|
||||
sMap[c.Id] = c
|
||||
}
|
||||
|
||||
return playList
|
||||
// 获取满足条件的次级站点播放数据
|
||||
for _, s := range system.GetMultiplePlay(ids, detail.DbId) {
|
||||
l = append(l, system.PlayLinkVo{Id: s.Mid, Name: sMap[s.Mid].Name, LinkList: s.PlayList[0]})
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// GetFilmsByTags 通过searchTag 返回满足条件的分页影片信息
|
||||
func (i *IndexLogic) GetFilmsByTags(st system.SearchTagsVO, page *system.Page) []system.MovieBasicInfo {
|
||||
// 获取满足条件的影片id 列表
|
||||
sl := system.GetSearchInfosByTags(st, page)
|
||||
ids := system.GetSearchInfosByTags(st, page)
|
||||
// 通过key 获取对应影片的基本信息
|
||||
return system.GetBasicInfoBySearchInfos(sl...)
|
||||
return system.GetBasicInfoByIds(ids)
|
||||
}
|
||||
|
||||
// GetFilmClassify 通过Pid返回当前所属分类下的首页展示数据
|
||||
|
||||
@@ -7,12 +7,11 @@ import (
|
||||
"server/plugin/SystemInit"
|
||||
"server/plugin/db"
|
||||
"server/router"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// 执行初始化前等待20s , 让mysql服务完成初始化指令
|
||||
time.Sleep(time.Second * 20)
|
||||
//time.Sleep(time.Second * 20)
|
||||
//初始化redis客户端
|
||||
err := db.InitRedisConn()
|
||||
if err != nil {
|
||||
|
||||
@@ -3,8 +3,6 @@ package system
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"gorm.io/gorm"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
@@ -12,6 +10,9 @@ import (
|
||||
"server/plugin/common/util"
|
||||
"server/plugin/db"
|
||||
"strings"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// FileInfo 图片信息对象
|
||||
@@ -173,8 +174,10 @@ func SyncFilmPicture() {
|
||||
|
||||
// ReplaceDetailPic 将影片详情中的图片地址替换为自己的
|
||||
func ReplaceDetailPic(d *MovieDetail) {
|
||||
// 查询影片对应的本地图片信息
|
||||
if ExistFileInfoByRid(d.Id) {
|
||||
// 获取主站点信息, 如果未开启图片同步功能则直接返回
|
||||
s := GetCollectSourceListByGrade(MasterCollect)[0]
|
||||
// 如果开启了图片同步 且存在对应图片信息 则查询影片对应的本地图片信息,
|
||||
if s.SyncPictures && ExistFileInfoByRid(d.Id) {
|
||||
// 如果存在关联的本地图片, 则查询对应的图片信息
|
||||
f := GetFileInfoByRid(d.Id)
|
||||
// 替换采集站的图片链接为本地链接
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"hash/fnv"
|
||||
"log"
|
||||
"regexp"
|
||||
"server/config"
|
||||
"server/plugin/common/util"
|
||||
"server/plugin/db"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Movie 影片基本信息
|
||||
@@ -25,31 +30,6 @@ type Movie struct {
|
||||
PlayFrom string `json:"playFrom"` // 播放来源
|
||||
}
|
||||
|
||||
// MovieDescriptor 影片详情介绍信息
|
||||
type MovieDescriptor struct {
|
||||
SubTitle string `json:"subTitle"` //子标题
|
||||
CName string `json:"cName"` //分类名称
|
||||
EnName string `json:"enName"` //英文名
|
||||
Initial string `json:"initial"` //首字母
|
||||
ClassTag string `json:"classTag"` //分类标签
|
||||
Actor string `json:"actor"` //主演
|
||||
Director string `json:"director"` //导演
|
||||
Writer string `json:"writer"` //作者
|
||||
Blurb string `json:"blurb"` //简介, 残缺,不建议使用
|
||||
Remarks string `json:"remarks"` // 更新情况
|
||||
ReleaseDate string `json:"releaseDate"` //上映时间
|
||||
Area string `json:"area"` // 地区
|
||||
Language string `json:"language"` //语言
|
||||
Year string `json:"year"` //年份
|
||||
State string `json:"state"` //影片状态 正片|预告...
|
||||
UpdateTime string `json:"updateTime"` //更新时间
|
||||
AddTime int64 `json:"addTime"` //资源添加时间戳
|
||||
DbId int64 `json:"dbId"` //豆瓣id
|
||||
DbScore string `json:"dbScore"` // 豆瓣评分
|
||||
Hits int64 `json:"hits"` //影片热度
|
||||
Content string `json:"content"` //内容简介
|
||||
}
|
||||
|
||||
// MovieBasicInfo 影片基本信息
|
||||
type MovieBasicInfo struct {
|
||||
Id int64 `json:"id"` //影片Id
|
||||
@@ -68,203 +48,420 @@ type MovieBasicInfo struct {
|
||||
Year string `json:"year"` //年份
|
||||
}
|
||||
|
||||
// MovieUrlInfo 影视资源url信息
|
||||
type MovieUrlInfo struct {
|
||||
// PlayItem 影视资源url信息
|
||||
type PlayItem struct {
|
||||
Episode string `json:"episode"` // 集数
|
||||
Link string `json:"link"` // 播放地址
|
||||
}
|
||||
|
||||
// MoviePlayList 播放列表信息, 二维切片
|
||||
type MoviePlayList [][]PlayItem
|
||||
|
||||
// FromList 播放来源切片
|
||||
type FromList []string
|
||||
|
||||
// MovieDetail 影片详情信息
|
||||
type MovieDetail struct {
|
||||
Id int64 `json:"id"` //影片Id
|
||||
Cid int64 `json:"cid"` //分类ID
|
||||
Pid int64 `json:"pid"` //一级分类ID
|
||||
Name string `json:"name"` //片名
|
||||
Picture string `json:"picture"` //简介图片
|
||||
PlayFrom []string `json:"playFrom"` // 播放来源
|
||||
DownFrom string `json:"DownFrom"` //下载来源 例: http
|
||||
Id int64 `json:"id" gorm:"primaryKey"` //影片Id
|
||||
Mid int64 `json:"mid"` //影片Id
|
||||
Cid int64 `json:"cid"` //分类ID
|
||||
Pid int64 `json:"pid"` //一级分类ID
|
||||
Name string `json:"name"` //片名
|
||||
Picture string `json:"picture"` //简介图片
|
||||
SubTitle string `json:"subTitle"` //子标题
|
||||
CName string `json:"cName"` //分类名称
|
||||
EnName string `json:"enName"` //英文名
|
||||
Initial string `json:"initial"` //首字母
|
||||
ClassTag string `json:"classTag"` //分类标签
|
||||
Actor string `json:"actor"` //主演
|
||||
Director string `json:"director"` //导演
|
||||
Writer string `json:"writer"` //作者
|
||||
Blurb string `json:"blurb"` //简介, 残缺,不建议使用
|
||||
Remarks string `json:"remarks"` // 更新情况
|
||||
ReleaseDate string `json:"releaseDate"` //上映时间
|
||||
Area string `json:"area"` // 地区
|
||||
Language string `json:"language"` //语言
|
||||
Year string `json:"year"` //年份
|
||||
State string `json:"state"` //影片状态 正片|预告...
|
||||
UpdateTime string `json:"updateTime"` //更新时间
|
||||
AddTime int64 `json:"addTime"` //资源添加时间戳
|
||||
DbId int64 `json:"dbId"` //豆瓣id
|
||||
DbScore string `json:"dbScore"` // 豆瓣评分
|
||||
Hits int64 `json:"hits"` //影片热度
|
||||
Content string `json:"content"` //内容简介
|
||||
PlayFrom FromList `json:"playFrom" gorm:"type:json"` // 播放来源
|
||||
DownFrom string `json:"DownFrom"` //下载来源 例: http
|
||||
//PlaySeparator string `json:"playSeparator"` // 播放信息分隔符
|
||||
PlayList [][]MovieUrlInfo `json:"playList"` //播放地址url
|
||||
DownloadList [][]MovieUrlInfo `json:"downloadList"` // 下载url地址
|
||||
MovieDescriptor `json:"descriptor"` //影片描述信息
|
||||
PlayList MoviePlayList `json:"playList" gorm:"type:json"` //播放地址url
|
||||
DownloadList MoviePlayList `json:"downloadList" gorm:"type:json"` // 下载url地址
|
||||
}
|
||||
|
||||
// ===================================Redis数据交互========================================================
|
||||
type SlaveMovieInfo struct {
|
||||
Id int64 `json:"id" gorm:"primaryKey"` // 自增ID
|
||||
Sid string `json:"sid"` // 采集站标识ID
|
||||
//Name string `json:"name"` // 影片名称
|
||||
Mid string `json:"mid"` // 归一匹配ID
|
||||
DbId int64 `json:"dbId"` //豆瓣ID 可能为空
|
||||
PlayList MoviePlayList `json:"playList" gorm:"type:json"`
|
||||
}
|
||||
|
||||
// TableName 设置MovieDetail表的表名
|
||||
func (m *MovieDetail) TableName() string {
|
||||
return config.MovieDetailName
|
||||
}
|
||||
|
||||
// TableName 设置slaveMovieInfo 表名
|
||||
func (m *SlaveMovieInfo) TableName() string {
|
||||
return config.SlaveMovieInfo
|
||||
}
|
||||
|
||||
// CreateMovieDetailTable 创建存储检索信息的数据表
|
||||
func CreateMovieDetailTable() {
|
||||
// 如果不存在则创建表 并设置自增ID初始值为10000
|
||||
if !ExistMovieDetailTable() {
|
||||
err := db.Mdb.AutoMigrate(&MovieDetail{})
|
||||
if err != nil {
|
||||
log.Println("Create Table MovieDetailsTable Failed: ", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ExistMovieDetailTable 检测是否存在 MovieDetails表
|
||||
func ExistMovieDetailTable() bool {
|
||||
return db.Mdb.Migrator().HasTable(&MovieDetail{})
|
||||
}
|
||||
|
||||
// CreateSlaveMovieInfoTable 创建存储检索信息的数据表
|
||||
func CreateSlaveMovieInfoTable() {
|
||||
// 如果不存在则创建表 并设置自增ID初始值为10000
|
||||
if !ExistSlaveMovieInfoTable() {
|
||||
err := db.Mdb.AutoMigrate(&SlaveMovieInfo{})
|
||||
if err != nil {
|
||||
log.Println("Create Table SlaveMovieInfoTable Failed: ", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ExistSlaveMovieInfoTable 检测是否存在 MovieDetails表
|
||||
func ExistSlaveMovieInfoTable() bool {
|
||||
return db.Mdb.Migrator().HasTable(&SlaveMovieInfo{})
|
||||
}
|
||||
|
||||
// =================================== column序列化 接口========================================================
|
||||
|
||||
func (m *MoviePlayList) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
*m = nil
|
||||
return nil
|
||||
}
|
||||
b, ok := value.([]byte)
|
||||
if !ok {
|
||||
return errors.New("MoviePlayList serialization failed, value is not []byte")
|
||||
}
|
||||
return json.Unmarshal(b, m)
|
||||
}
|
||||
|
||||
func (m MoviePlayList) Value() (driver.Value, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
func (fl *FromList) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
*fl = nil
|
||||
return nil
|
||||
}
|
||||
b, ok := value.([]byte)
|
||||
if !ok {
|
||||
return errors.New("FromList serialization failed, value is not []byte")
|
||||
}
|
||||
return json.Unmarshal(b, fl)
|
||||
}
|
||||
|
||||
func (fl FromList) Value() (driver.Value, error) {
|
||||
if fl == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return json.Marshal(fl)
|
||||
}
|
||||
|
||||
// =================================== Spider数据处理 ========================================================
|
||||
|
||||
// SaveDetails 保存影片详情信息到redis中 格式: MovieDetail:Cid?:Id?
|
||||
func SaveDetails(list []MovieDetail) (err error) {
|
||||
// 遍历list中的信息
|
||||
for _, detail := range list {
|
||||
// 序列化影片详情信息
|
||||
data, _ := json.Marshal(detail)
|
||||
// 1. 原使用Zset存储, 但是不便于单个检索 db.Rdb.ZAdd(db.Cxt, fmt.Sprintf("%s:Cid%d", config.MovieDetailKey, detail.Cid), redis.Z{Score: float64(detail.Id), Member: member}).Err()
|
||||
// 改为普通 k v 存储, k-> id关键字, v json序列化的结果
|
||||
err = db.Rdb.Set(db.Cxt, fmt.Sprintf(config.MovieDetailKey, detail.Cid, detail.Id), data, config.FilmExpired).Err()
|
||||
// 2. 同步保存简略信息到redis中
|
||||
SaveMovieBasicInfo(detail)
|
||||
// 3. 保存 Search tag redis中
|
||||
if err == nil {
|
||||
// 转换 detail信息
|
||||
searchInfo := ConvertSearchInfo(detail)
|
||||
// 只存储用于检索对应影片的关键字信息
|
||||
SaveSearchTag(searchInfo)
|
||||
}
|
||||
|
||||
func SaveDetails(ml []MovieDetail) (err error) {
|
||||
// 1. 先将详情信息存入 MovieDetail表中
|
||||
if err = db.Mdb.Create(&ml).Error; err != nil {
|
||||
log.Println("影片详情信息保存失败: ", err)
|
||||
}
|
||||
// 保存一份search信息到mysql, 批量存储
|
||||
BatchSaveSearchInfo(list)
|
||||
// 2. 将详情信息转化为SearchInfo并保存
|
||||
BatchSaveSearchInfo(ml)
|
||||
return err
|
||||
}
|
||||
|
||||
// SaveDetail 保存单部影片信息
|
||||
func SaveDetail(detail MovieDetail) (err error) {
|
||||
// 序列化影片详情信息
|
||||
data, _ := json.Marshal(detail)
|
||||
// 保存影片信息到Redis
|
||||
err = db.Rdb.Set(db.Cxt, fmt.Sprintf(config.MovieDetailKey, detail.Cid, detail.Id), data, config.FilmExpired).Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 2. 同步保存简略信息到redis中
|
||||
SaveMovieBasicInfo(detail)
|
||||
// 转换 detail信息
|
||||
searchInfo := ConvertSearchInfo(detail)
|
||||
// 3. 保存 Search tag redis中
|
||||
// 只存储用于检索对应影片的关键字信息
|
||||
func SaveDetail(m MovieDetail) (err error) {
|
||||
// 1. 转换 detail信息 searchInfo
|
||||
searchInfo := ConvertSearchInfo(m)
|
||||
// 2. 保存 Search tag 到 redis中 只存储用于检索对应影片的关键字信息
|
||||
SaveSearchTag(searchInfo)
|
||||
// 3. 将影片详情信息保存到 MovieDetails表中
|
||||
|
||||
// 4. 先查询数据库中是否存在对应记录 ,如果不存在对应记录则 保存当前记录
|
||||
tx := db.Mdb.Begin()
|
||||
if !ExistMovieDetailByMid(m.Mid) {
|
||||
// 执行插入操作
|
||||
if err := tx.Create(&m).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// 只对会变化的字段进行更新
|
||||
err := tx.Model(&MovieDetail{}).Where("mid", m.Mid).Updates(MovieDetail{PlayList: m.PlayList, DownloadList: m.DownloadList,
|
||||
Remarks: m.Remarks, State: m.State, UpdateTime: m.UpdateTime, AddTime: m.AddTime, DbScore: m.DbScore, Hits: m.Hits}).Error
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
// 提交事务
|
||||
tx.Commit()
|
||||
|
||||
// 保存影片检索信息到searchTable
|
||||
err = SaveSearchInfo(searchInfo)
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
// SaveMovieBasicInfo 摘取影片的详情部分信息转存为影视基本信息
|
||||
func SaveMovieBasicInfo(detail MovieDetail) {
|
||||
basicInfo := MovieBasicInfo{
|
||||
Id: detail.Id,
|
||||
Cid: detail.Cid,
|
||||
Pid: detail.Pid,
|
||||
Name: detail.Name,
|
||||
SubTitle: detail.SubTitle,
|
||||
CName: detail.CName,
|
||||
State: detail.State,
|
||||
Picture: detail.Picture,
|
||||
Actor: detail.Actor,
|
||||
Director: detail.Director,
|
||||
Blurb: detail.Blurb,
|
||||
Remarks: detail.Remarks,
|
||||
Area: detail.Area,
|
||||
Year: detail.Year,
|
||||
}
|
||||
data, _ := json.Marshal(basicInfo)
|
||||
_ = db.Rdb.Set(db.Cxt, fmt.Sprintf(config.MovieBasicInfoKey, detail.Cid, detail.Id), data, config.FilmExpired).Err()
|
||||
// ExistMovieDetailByMid 通过mid判断是否存在对应信息
|
||||
func ExistMovieDetailByMid(mid int64) bool {
|
||||
var count int64
|
||||
db.Mdb.Model(&SearchInfo{}).Where("mid", mid).Count(&count)
|
||||
return count > 0
|
||||
}
|
||||
|
||||
// SaveSitePlayList 仅保存播放url列表信息到当前站点
|
||||
func SaveSitePlayList(id string, list []MovieDetail) (err error) {
|
||||
// 如果list 为空则直接返回
|
||||
if len(list) <= 0 {
|
||||
func SaveSitePlayList(id string, ml []MovieDetail) (err error) {
|
||||
// 如果ml 为空则直接返回
|
||||
if len(ml) <= 0 {
|
||||
return nil
|
||||
}
|
||||
res := make(map[string]string)
|
||||
for _, d := range list {
|
||||
if len(d.PlayList) > 0 {
|
||||
data, _ := json.Marshal(d.PlayList[0])
|
||||
// 不保存电影解说类
|
||||
if strings.Contains(d.CName, "解说") {
|
||||
continue
|
||||
var sl []SlaveMovieInfo
|
||||
for _, m := range ml {
|
||||
s := SlaveMovieInfo{Sid: id, Mid: GenerateHashKey(m.Name), DbId: m.DbId, PlayList: m.PlayList}
|
||||
// 查询表中是否已经存在对应的数据记录, 如果有则更新, 无则追加到切片中统一处理
|
||||
if id := ExistSlaveMovieInfo(s); id <= 0 {
|
||||
if err = db.Mdb.Model(&s).Where("id", id).Updates(s).Error; err != nil {
|
||||
log.Println("附属站点影片信息更新失败: ", err)
|
||||
}
|
||||
// 如果DbId不为0, 则以dbID作为key进行hash额外存储一次
|
||||
if d.DbId != 0 {
|
||||
res[GenerateHashKey(d.DbId)] = string(data)
|
||||
}
|
||||
res[GenerateHashKey(d.Name)] = string(data)
|
||||
continue
|
||||
}
|
||||
sl = append(sl, s)
|
||||
}
|
||||
// 如果结果不为空,则将数据保存到redis中
|
||||
if len(res) > 0 {
|
||||
// 保存形式 key: MultipleSource:siteName Hash[hash(movieName)]list
|
||||
err = db.Rdb.HMSet(db.Cxt, fmt.Sprintf(config.MultipleSiteDetail, id), res).Err()
|
||||
// 将处理后的结果存储到 SalveMovieInfo表中
|
||||
if err = db.Mdb.Create(&sl).Error; err != nil {
|
||||
log.Println("附属站点影片信息保存失败: ", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// BatchSaveSearchInfo 批量保存Search信息
|
||||
func BatchSaveSearchInfo(list []MovieDetail) {
|
||||
var infoList []SearchInfo
|
||||
for _, v := range list {
|
||||
infoList = append(infoList, ConvertSearchInfo(v))
|
||||
// ExistSlaveMovieInfo 查询对应记录, 如果存在则返还id, 不存在则返还 -1
|
||||
func ExistSlaveMovieInfo(s SlaveMovieInfo) int64 {
|
||||
var id int64
|
||||
if err := db.Mdb.Model(&SlaveMovieInfo{}).Select("id").Where("sid = ? AND (mid = ? OR db_id = ?)", s.Sid, s.Mid, s.DbId).First(&id).Error; err != nil {
|
||||
// 如果错误类型为gorm.ErrRecordNotFound, 直接返回 0
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return 0
|
||||
}
|
||||
// 如果是其他异常则输出异常信息并返回 -1
|
||||
log.Println("Find SlaveMovieInfo Failed: ", err)
|
||||
return -1
|
||||
}
|
||||
// 将检索信息存入redis中做一次转存
|
||||
RdbSaveSearchInfo(infoList)
|
||||
return id
|
||||
}
|
||||
|
||||
// ============================ APi接口 ==================================================
|
||||
|
||||
// GetDetailByMid 获取影片对应的详情信息
|
||||
func GetDetailByMid(mid int64) MovieDetail {
|
||||
var d MovieDetail
|
||||
// 查询mid对应的影片详情信息, 只查询部分字段
|
||||
if err := db.Mdb.Model(&MovieDetail{}).Where("mid = ?", mid).First(&d).Error; err != nil {
|
||||
log.Println("Find MovieDetail Failed: ", err)
|
||||
return d
|
||||
}
|
||||
// 执行本地图片匹配
|
||||
ReplaceDetailPic(&d)
|
||||
return d
|
||||
}
|
||||
|
||||
// GetBasicInfoByMid 获取Id对应的影片基本信息
|
||||
func GetBasicInfoByMid(mid int64) MovieBasicInfo {
|
||||
// 通过id查询满足条件的影片基本信息
|
||||
var basic MovieBasicInfo
|
||||
var d MovieDetail
|
||||
// 查询mid对应的影片详情信息, 只查询部分字段
|
||||
if err := db.Mdb.Model(&MovieDetail{}).Select("id, mid, cid, pid, name, sub_title, c_name, state, picture, actor, director,"+
|
||||
" content, remarks, area, year").Where("mid = ?", mid).First(&d).Error; err != nil {
|
||||
log.Println("Find MovieDetail Failed: ", err)
|
||||
return basic
|
||||
}
|
||||
// 匹配本地图片
|
||||
ReplaceDetailPic(&d)
|
||||
// 将 MovieDetail转化为 BasicInfo
|
||||
basic = ConvertBasicInfo(d)
|
||||
return basic
|
||||
}
|
||||
|
||||
// GetBasicInfoByIds 通过searchInfo 获取影片的基本信息
|
||||
func GetBasicInfoByIds(ids []int64) []MovieBasicInfo {
|
||||
var ml []MovieDetail
|
||||
var l []MovieBasicInfo
|
||||
// 使用in查询, 一次性拿到满足条件的数据
|
||||
if err := db.Mdb.Model(&MovieDetail{}).Select("id, mid, cid, pid, name, sub_title, c_name, state, picture, actor, director,"+
|
||||
" content, remarks, area, year").Where("mid IN (?)", ids).Find(&ml).Error; err != nil {
|
||||
log.Println("BatchFind BasicInfo Failed: ", err)
|
||||
return nil
|
||||
}
|
||||
// 将查询到的结果批量转化为BasicInfo
|
||||
for _, m := range ml {
|
||||
// 执行本地图片匹配
|
||||
ReplaceDetailPic(&m)
|
||||
l = append(l, ConvertBasicInfo(m))
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// GetMovieListByPid 通过Pid 分类ID 获取对应影片的数据信息
|
||||
func GetMovieListByPid(pid int64, page *Page) []MovieBasicInfo {
|
||||
// 返回分页参数
|
||||
var count int64
|
||||
db.Mdb.Model(&SearchInfo{}).Where("pid", pid).Count(&count)
|
||||
page.Total = int(count)
|
||||
page.PageCount = int((page.Total + page.PageSize - 1) / page.PageSize)
|
||||
// 通过Search表查询
|
||||
var ids []int64
|
||||
if err := db.Mdb.Limit(page.PageSize).Offset((page.Current-1)*page.PageSize).Select("mid").Where("pid", pid).Order("update_stamp DESC").Find(&ids).Error; err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
// 通过ids查询影片基本信息并返回
|
||||
return GetBasicInfoByIds(ids)
|
||||
}
|
||||
|
||||
// GetMovieListByCid 通过Cid查找对应的影片分页数据, 不适合GetMovieListByPid 糅合
|
||||
func GetMovieListByCid(cid int64, page *Page) []MovieBasicInfo {
|
||||
// 返回分页参数
|
||||
var count int64
|
||||
db.Mdb.Model(&SearchInfo{}).Where("cid", cid).Count(&count)
|
||||
page.Total = int(count)
|
||||
page.PageCount = int((page.Total + page.PageSize - 1) / page.PageSize)
|
||||
// 进行具体的信息查询
|
||||
var ids []int64
|
||||
if err := db.Mdb.Limit(page.PageSize).Offset((page.Current-1)*page.PageSize).Select("mid").Where("cid", cid).Order("update_stamp DESC").Find(&ids).Error; err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
// 通过影片ID去redis中获取id对应数据信息
|
||||
return GetBasicInfoByIds(ids)
|
||||
}
|
||||
|
||||
// GetRelateMovieBasicInfo GetRelateMovie 根据 name, cid, pid, classtag 获取相关影片
|
||||
func GetRelateMovieBasicInfo(search SearchInfo, page *Page) []MovieBasicInfo {
|
||||
/*
|
||||
根据当前影片信息匹配相关的影片
|
||||
1. 分类Cid,
|
||||
2. 如果影片名称含有第x季 则根据影片名进行模糊匹配
|
||||
3. class_tag 剧情内容匹配, 切分后使用 or 进行匹配
|
||||
4. area 地区
|
||||
5. 语言 Language
|
||||
*/
|
||||
// sql 拼接查询条件
|
||||
sql := ""
|
||||
|
||||
// 优先进行名称相似匹配, 先对影片名称进行精简, 只保留主体用于匹配同系列影片
|
||||
name := util.CleanFilmName(search.Name)
|
||||
sql = fmt.Sprintf(`select mid from %s where (name LIKE "%%%s%%" or sub_title LIKE "%%%[2]s%%") AND cid=%d AND search.deleted_at IS NULL union`, search.TableName(), name, search.Cid)
|
||||
|
||||
// 添加其他相似匹配规则 同属二级分类
|
||||
sql = fmt.Sprintf(`%s (select * from %s where cid=%d AND `, sql, search.TableName(), search.Cid)
|
||||
// 根据剧情标签查找相似影片, classTag 使用的分隔符为 , | /首先去除 classTag 中包含的所有空格
|
||||
search.ClassTag = strings.ReplaceAll(search.ClassTag, " ", "")
|
||||
// 如果 classTag 中包含分割符则进行拆分匹配
|
||||
cl := strings.Split(util.FormatSpecialChar(search.ClassTag), ",")
|
||||
if len(cl) > 0 {
|
||||
s := "("
|
||||
for _, c := range cl {
|
||||
s = fmt.Sprintf(`%s class_tag like "%%%s%%" OR`, s, c)
|
||||
}
|
||||
sql = fmt.Sprintf("%s %s)", sql, strings.TrimSuffix(s, "OR"))
|
||||
} else {
|
||||
sql = fmt.Sprintf(`%s class_tag like "%%%s%%"`, sql, search.ClassTag)
|
||||
}
|
||||
// 除名称外的相似影片使用随机排序
|
||||
//sql = fmt.Sprintf("%s ORDER BY RAND() limit %d,%d)", sql, page.Current, page.PageSize)
|
||||
sql = fmt.Sprintf("%s AND search.deleted_at IS NULL limit %d,%d)", sql, page.Current, page.PageSize)
|
||||
// 条件拼接完成后加上limit参数
|
||||
sql = fmt.Sprintf("(%s) limit %d,%d", sql, page.Current, page.PageSize)
|
||||
// 执行sql, 获取满足条件的影片mid切片
|
||||
var ids []int64
|
||||
db.Mdb.Raw(sql).Scan(&ids)
|
||||
// 通过 ids 获取影片基本信息,并返回
|
||||
return GetBasicInfoByIds(ids)
|
||||
}
|
||||
|
||||
// GetMultiplePlay 通过影片名的ID值匹配播放源, 不区分站点
|
||||
func GetMultiplePlay(mIds []string, dbIds int64) []SlaveMovieInfo {
|
||||
// 初始化返回值
|
||||
var l []SlaveMovieInfo
|
||||
// 通过siteId, mIds, dbIds 检索满足条件的数据
|
||||
if err := db.Mdb.Model(&SlaveMovieInfo{}).Select("play_list").Where("mid IN (?) OR db_id = ?", mIds, dbIds).Find(&l).Error; err != nil {
|
||||
log.Println("GetMultiplePlay Failed: ", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
// ============================ 数据处理 ==================================================
|
||||
|
||||
// ConvertSearchInfo 将detail信息处理成 searchInfo
|
||||
func ConvertSearchInfo(detail MovieDetail) SearchInfo {
|
||||
score, _ := strconv.ParseFloat(detail.DbScore, 64)
|
||||
stamp, _ := time.ParseInLocation(time.DateTime, detail.UpdateTime, time.Local)
|
||||
func ConvertSearchInfo(m MovieDetail) SearchInfo {
|
||||
score, _ := strconv.ParseFloat(m.DbScore, 64)
|
||||
stamp, _ := time.ParseInLocation(time.DateTime, m.UpdateTime, time.Local)
|
||||
// detail中的年份信息并不准确, 因此采用 ReleaseDate中的年份
|
||||
year, err := strconv.ParseInt(regexp.MustCompile(`[1-9][0-9]{3}`).FindString(detail.ReleaseDate), 10, 64)
|
||||
year, err := strconv.ParseInt(regexp.MustCompile(`[1-9][0-9]{3}`).FindString(m.ReleaseDate), 10, 64)
|
||||
if err != nil {
|
||||
year = 0
|
||||
}
|
||||
return SearchInfo{
|
||||
Mid: detail.Id,
|
||||
Cid: detail.Cid,
|
||||
Pid: detail.Pid,
|
||||
Name: detail.Name,
|
||||
SubTitle: detail.SubTitle,
|
||||
CName: detail.CName,
|
||||
ClassTag: detail.ClassTag,
|
||||
Area: detail.Area,
|
||||
Language: detail.Language,
|
||||
Mid: m.Id,
|
||||
Cid: m.Cid,
|
||||
Pid: m.Pid,
|
||||
Name: m.Name,
|
||||
SubTitle: m.SubTitle,
|
||||
CName: m.CName,
|
||||
ClassTag: m.ClassTag,
|
||||
Area: m.Area,
|
||||
Language: m.Language,
|
||||
Year: year,
|
||||
Initial: detail.Initial,
|
||||
Initial: m.Initial,
|
||||
Score: score,
|
||||
Hits: detail.Hits,
|
||||
Hits: m.Hits,
|
||||
UpdateStamp: stamp.Unix(),
|
||||
State: detail.State,
|
||||
Remarks: detail.Remarks,
|
||||
State: m.State,
|
||||
Remarks: m.Remarks,
|
||||
// ReleaseDate 部分影片缺失该参数, 所以使用添加时间作为上映时间排序
|
||||
ReleaseStamp: detail.AddTime,
|
||||
ReleaseStamp: m.AddTime,
|
||||
}
|
||||
}
|
||||
|
||||
// GetBasicInfoByKey 获取Id对应的影片基本信息
|
||||
func GetBasicInfoByKey(key string) MovieBasicInfo {
|
||||
// 反序列化得到的结果
|
||||
data := []byte(db.Rdb.Get(db.Cxt, key).Val())
|
||||
basic := MovieBasicInfo{}
|
||||
_ = json.Unmarshal(data, &basic)
|
||||
// 执行本地图片匹配
|
||||
ReplaceBasicDetailPic(&basic)
|
||||
return basic
|
||||
}
|
||||
|
||||
// GetDetailByKey 获取影片对应的详情信息
|
||||
func GetDetailByKey(key string) MovieDetail {
|
||||
// 反序列化得到的结果
|
||||
data := []byte(db.Rdb.Get(db.Cxt, key).Val())
|
||||
detail := MovieDetail{}
|
||||
_ = json.Unmarshal(data, &detail)
|
||||
|
||||
// 执行本地图片匹配
|
||||
ReplaceDetailPic(&detail)
|
||||
return detail
|
||||
}
|
||||
|
||||
// GetBasicInfoBySearchInfos 通过searchInfo 获取影片的基本信息
|
||||
func GetBasicInfoBySearchInfos(infos ...SearchInfo) []MovieBasicInfo {
|
||||
var list []MovieBasicInfo
|
||||
for _, s := range infos {
|
||||
data := []byte(db.Rdb.Get(db.Cxt, fmt.Sprintf(config.MovieBasicInfoKey, s.Cid, s.Mid)).Val())
|
||||
basic := MovieBasicInfo{}
|
||||
_ = json.Unmarshal(data, &basic)
|
||||
|
||||
// 执行本地图片匹配
|
||||
ReplaceBasicDetailPic(&basic)
|
||||
list = append(list, basic)
|
||||
}
|
||||
return list
|
||||
// ConvertBasicInfo 将Detail信息转化为basic信息
|
||||
func ConvertBasicInfo(m MovieDetail) MovieBasicInfo {
|
||||
return MovieBasicInfo{Id: m.Mid, Cid: m.Cid, Pid: m.Pid, Name: m.Name, SubTitle: m.SubTitle,
|
||||
CName: m.CName, State: m.State, Picture: m.Picture, Actor: m.Actor, Director: m.Director, Blurb: m.Content,
|
||||
Remarks: m.Remarks, Area: m.Area, Year: m.Year}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -274,18 +471,45 @@ func GetBasicInfoBySearchInfos(infos ...SearchInfo) []MovieBasicInfo {
|
||||
3. 去除name首尾的标点符号
|
||||
4. 将处理完成后的name转化为hash值作为存储时的key
|
||||
*/
|
||||
// GenerateHashKey 存储播放源信息时对影片名称进行处理, 提高各站点间同一影片的匹配度
|
||||
// GenerateHashKey 存储播放源信息时对影片名称进行处理-生成id, 提高各站点间同一影片的匹配度
|
||||
func GenerateHashKey[K string | ~int | int64](key K) string {
|
||||
mName := fmt.Sprint(key)
|
||||
//1. 去除name中的所有空格
|
||||
mName = regexp.MustCompile(`\s`).ReplaceAllString(mName, "")
|
||||
//2. 去除name中含有的别名~.*~
|
||||
mName = regexp.MustCompile(`~.*~$`).ReplaceAllString(mName, "")
|
||||
//2. 添加常用的名称标准化替换规则
|
||||
rules := []string{
|
||||
// 中文季数标签统一
|
||||
"season", "s", "第", "s", "季", "", "期", "", "画", "",
|
||||
// --- 3. 剧场版标准化 ---
|
||||
"剧场版", "ovo", "映画", "ovo", "电影版", "ovo", "The Movie", "ovo", "Movie", "ovo", "(Movie)", "ovo", "〔映画〕", "ovo",
|
||||
// 特殊数学符号 (用户常用来代替数字,如 ∬ 代表 2)
|
||||
"Ⅰ", "1", "Ⅱ", "2", "Ⅲ",
|
||||
"∫", "1", "∬", "2", "∮", "3", "Ⅳ", "4", "Ⅴ", "5", "Ⅵ", "6", "Ⅶ", "7", "Ⅷ", "8", "Ⅸ", "9", "Ⅹ", "10", // 用户可能用积分号代表季数
|
||||
"一", "1", "二", "2", "三", "3", "四", "4", "五", "5", "六", "6", "七", "7", "八", "8", "九", "9",
|
||||
// 移除或替换无意义的装饰符号,这些符号在搜索中通常不仅无用还会阻碍匹配
|
||||
"★", "", "☆", "", "◆", "", "◇", "", "●", "", "○", "",
|
||||
"【", "", "】", "", "〖", "", "〗", "", "〔", "", "〕", "",
|
||||
"「", "", "」", "", "『", "", "』", "",
|
||||
"|", "", "|", "", // 竖线分隔符
|
||||
"~", "", "~", "", // 波浪号
|
||||
"...", "", "……", "", // 省略号
|
||||
"!", "", "!", "", "?", "", "?", "",
|
||||
"(", "", ")", "", "(", "", ")", "",
|
||||
"[", "", "]", "", "[", "", "]", "",
|
||||
"{", "", "}", "", "{", "", "}", "",
|
||||
"&", "&", "+", "+",
|
||||
"-", "", "-", "", "—", "", "–", "", // 策略:通常移除所有标点,让 "A-B" 变成 "AB"
|
||||
"_", "", "_", "",
|
||||
".", "", ".", "", "。", "",
|
||||
",", "", ",", "",
|
||||
":", "", ":", "", ":", "",
|
||||
";", "", ";", "",
|
||||
"'", "", "’", "", "\"", "", "“", "", "”", "",
|
||||
"`", "", "`", "",
|
||||
}
|
||||
mName = strings.NewReplacer(rules...).Replace(mName)
|
||||
//3. 去除name首尾的标点符号
|
||||
mName = regexp.MustCompile(`^[[:punct:]]+|[[:punct:]]+$`).ReplaceAllString(mName, "")
|
||||
// 部分站点包含 动画版, 特殊别名 等字符, 需进行删除
|
||||
//mName = regexp.MustCompile(`动画版`).ReplaceAllString(mName, "")
|
||||
mName = regexp.MustCompile(`季.*`).ReplaceAllString(mName, "季")
|
||||
//4. 将处理完成后的name转化为hash值作为存储时的key
|
||||
h := fnv.New32a()
|
||||
_, err := h.Write([]byte(mName))
|
||||
@@ -294,27 +518,3 @@ func GenerateHashKey[K string | ~int | int64](key K) string {
|
||||
}
|
||||
return fmt.Sprint(h.Sum32())
|
||||
}
|
||||
|
||||
// ============================采集方案.v1 遗留==================================================
|
||||
|
||||
// SaveMoves 保存影片分页请求list
|
||||
func SaveMoves(list []Movie) (err error) {
|
||||
// 整合数据
|
||||
for _, m := range list {
|
||||
//score, _ := time.ParseInLocation(time.DateTime, m.Time, time.Local)
|
||||
movie, _ := json.Marshal(m)
|
||||
// 以Cid为目录为集合进行存储, 便于后续搜索, 以影片id为分值进行存储 例 MovieList:Cid%d
|
||||
err = db.Rdb.ZAdd(db.Cxt, fmt.Sprintf(config.MovieListInfoKey, m.Cid), redis.Z{Score: float64(m.Id), Member: movie}).Err()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// AllMovieInfoKey 获取redis中所有的影视列表信息key MovieList:Cid
|
||||
func AllMovieInfoKey() []string {
|
||||
return db.Rdb.Keys(db.Cxt, fmt.Sprint("MovieList:Cid*")).Val()
|
||||
}
|
||||
|
||||
// GetMovieListByKey 获取指定分类的影片列表数据
|
||||
func GetMovieListByKey(key string) []string {
|
||||
return db.Rdb.ZRange(db.Cxt, key, 0, -1).Val()
|
||||
}
|
||||
|
||||
@@ -3,10 +3,7 @@ package system
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"gorm.io/gorm"
|
||||
"log"
|
||||
"math"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"server/config"
|
||||
@@ -15,6 +12,9 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// SearchInfo 存储用于检索的信息
|
||||
@@ -51,18 +51,6 @@ func (s *SearchInfo) TableName() string {
|
||||
|
||||
// ================================= Spider 数据处理(redis) =================================
|
||||
|
||||
// RdbSaveSearchInfo 批量保存检索信息到redis
|
||||
func RdbSaveSearchInfo(list []SearchInfo) {
|
||||
// 1.整合一下zset数据集
|
||||
var members []redis.Z
|
||||
for _, s := range list {
|
||||
member, _ := json.Marshal(s)
|
||||
members = append(members, redis.Z{Score: float64(s.Mid), Member: member})
|
||||
}
|
||||
// 2.批量保存到zset集合中
|
||||
db.Rdb.ZAdd(db.Cxt, config.SearchInfoTemp, members...)
|
||||
}
|
||||
|
||||
// FilmZero 删除所有库存数据
|
||||
func FilmZero() {
|
||||
// 删除redis中当前库存储的所有数据
|
||||
@@ -95,6 +83,15 @@ func DelMtPlay(keys []string) {
|
||||
db.Rdb.Del(db.Cxt, keys...)
|
||||
}
|
||||
|
||||
// TunCateSearchTable 截断SearchInfo数据表
|
||||
func TunCateSearchTable() {
|
||||
var searchInfo SearchInfo
|
||||
err := db.Mdb.Exec(fmt.Sprintf("TRUNCATE TABLE %s", searchInfo.TableName())).Error
|
||||
if err != nil {
|
||||
log.Println("TRUNCATE TABLE Error: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
SearchKeyword 设置search关键字集合(影片分类检索类型数据)
|
||||
类型, 剧情 , 地区, 语言, 年份, 首字母, 排序
|
||||
@@ -251,21 +248,13 @@ func AddSearchIndex() {
|
||||
}
|
||||
|
||||
// BatchSave 批量保存影片search信息
|
||||
func BatchSave(list []SearchInfo) {
|
||||
tx := db.Mdb.Begin()
|
||||
// 防止程序异常终止
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
tx.Rollback()
|
||||
}
|
||||
}()
|
||||
if err := tx.CreateInBatches(list, len(list)).Error; err != nil {
|
||||
// 插入失败则回滚事务, 重新进行插入
|
||||
tx.Rollback()
|
||||
func BatchSave(sl []SearchInfo) {
|
||||
if err := db.Mdb.Model(&SearchInfo{}).Create(&sl).Error; err != nil {
|
||||
log.Println("BatchSaveSearchInfo Failed: ", err)
|
||||
return
|
||||
}
|
||||
// 保存成功后将相应tag数据缓存到redis中
|
||||
BatchHandleSearchTag(list...)
|
||||
tx.Commit()
|
||||
BatchHandleSearchTag(sl...)
|
||||
}
|
||||
|
||||
// BatchSaveOrUpdate 判断数据库中是否存在对应mid的数据, 如果存在则更新, 否则插入
|
||||
@@ -330,12 +319,21 @@ func ExistSearchInfo(mid int64) bool {
|
||||
return count > 0
|
||||
}
|
||||
|
||||
// TunCateSearchTable 截断SearchInfo数据表
|
||||
func TunCateSearchTable() {
|
||||
var searchInfo SearchInfo
|
||||
err := db.Mdb.Exec(fmt.Sprintf("TRUNCATE TABLE %s", searchInfo.TableName())).Error
|
||||
if err != nil {
|
||||
log.Println("TRUNCATE TABLE Error: ", err)
|
||||
// 777777777
|
||||
|
||||
// BatchSaveSearchInfo 批量保存Search信息
|
||||
func BatchSaveSearchInfo(ml []MovieDetail) {
|
||||
var sl []SearchInfo
|
||||
for _, m := range ml {
|
||||
s := ConvertSearchInfo(m)
|
||||
// 保存一份tag信息到redis
|
||||
SaveSearchTag(s)
|
||||
// 追加数据到转化后的切片中
|
||||
sl = append(sl, s)
|
||||
}
|
||||
// 批量保存影片检索信息
|
||||
if err := db.Mdb.Create(&sl).Error; err != nil {
|
||||
log.Println("影片详情信息保存失败: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,50 +389,6 @@ func SearchInfoToMdb(model int) {
|
||||
|
||||
// ================================= API 数据接口信息处理 =================================
|
||||
|
||||
// GetMovieListByPid 通过Pid 分类ID 获取对应影片的数据信息
|
||||
func GetMovieListByPid(pid int64, page *Page) []MovieBasicInfo {
|
||||
// 返回分页参数
|
||||
var count int64
|
||||
db.Mdb.Model(&SearchInfo{}).Where("pid", pid).Count(&count)
|
||||
page.Total = int(count)
|
||||
page.PageCount = int((page.Total + page.PageSize - 1) / page.PageSize)
|
||||
// 进行具体的信息查询
|
||||
var s []SearchInfo
|
||||
if err := db.Mdb.Limit(page.PageSize).Offset((page.Current-1)*page.PageSize).Where("pid", pid).Order("update_stamp DESC").Find(&s).Error; err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
// 通过影片ID去redis中获取id对应数据信息
|
||||
var list []MovieBasicInfo
|
||||
for _, v := range s {
|
||||
// 通过key搜索指定的影片信息 , MovieDetail:Cid6:Id15441
|
||||
list = append(list, GetBasicInfoByKey(fmt.Sprintf(config.MovieBasicInfoKey, v.Cid, v.Mid)))
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// GetMovieListByCid 通过Cid查找对应的影片分页数据, 不适合GetMovieListByPid 糅合
|
||||
func GetMovieListByCid(cid int64, page *Page) []MovieBasicInfo {
|
||||
// 返回分页参数
|
||||
var count int64
|
||||
db.Mdb.Model(&SearchInfo{}).Where("cid", cid).Count(&count)
|
||||
page.Total = int(count)
|
||||
page.PageCount = int((page.Total + page.PageSize - 1) / page.PageSize)
|
||||
// 进行具体的信息查询
|
||||
var s []SearchInfo
|
||||
if err := db.Mdb.Limit(page.PageSize).Offset((page.Current-1)*page.PageSize).Where("cid", cid).Order("update_stamp DESC").Find(&s).Error; err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
// 通过影片ID去redis中获取id对应数据信息
|
||||
var list []MovieBasicInfo
|
||||
for _, v := range s {
|
||||
// 通过key搜索指定的影片信息 , MovieDetail:Cid6:Id15441
|
||||
list = append(list, GetBasicInfoByKey(fmt.Sprintf(config.MovieBasicInfoKey, v.Cid, v.Mid)))
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// GetHotMovieByPid 获取Pid指定类别的热门影片
|
||||
func GetHotMovieByPid(pid int64, page *Page) []SearchInfo {
|
||||
// 返回分页参数
|
||||
@@ -472,89 +426,17 @@ func GetHotMovieByCid(cid int64, page *Page) []SearchInfo {
|
||||
}
|
||||
|
||||
// SearchFilmKeyword 通过关键字搜索库存中满足条件的影片名
|
||||
func SearchFilmKeyword(keyword string, page *Page) []SearchInfo {
|
||||
var searchList []SearchInfo
|
||||
func SearchFilmKeyword(keyword string, page *Page) []int64 {
|
||||
var ids []int64
|
||||
// 1. 先统计搜索满足条件的数据量
|
||||
var count int64
|
||||
db.Mdb.Model(&SearchInfo{}).Where("name LIKE ?", fmt.Sprint(`%`, keyword, `%`)).Or("sub_title LIKE ?", fmt.Sprint(`%`, keyword, `%`)).Count(&count)
|
||||
page.Total = int(count)
|
||||
page.PageCount = int((page.Total + page.PageSize - 1) / page.PageSize)
|
||||
// 2. 获取满足条件的数据
|
||||
db.Mdb.Limit(page.PageSize).Offset((page.Current-1)*page.PageSize).
|
||||
Where("name LIKE ?", fmt.Sprintf(`%%%s%%`, keyword)).Or("sub_title LIKE ?", fmt.Sprintf(`%%%s%%`, keyword)).Order("year DESC, update_stamp DESC").Find(&searchList)
|
||||
return searchList
|
||||
}
|
||||
|
||||
// GetRelateMovieBasicInfo GetRelateMovie 根据SearchInfo获取相关影片
|
||||
func GetRelateMovieBasicInfo(search SearchInfo, page *Page) []MovieBasicInfo {
|
||||
/*
|
||||
根据当前影片信息匹配相关的影片
|
||||
1. 分类Cid,
|
||||
2. 如果影片名称含有第x季 则根据影片名进行模糊匹配
|
||||
3. class_tag 剧情内容匹配, 切分后使用 or 进行匹配
|
||||
4. area 地区
|
||||
5. 语言 Language
|
||||
*/
|
||||
// sql 拼接查询条件
|
||||
sql := ""
|
||||
|
||||
// 优先进行名称相似匹配
|
||||
//search.Name = regexp.MustCompile("第.{1,3}季").ReplaceAllString(search.Name, "")
|
||||
name := regexp.MustCompile(`(第.{1,3}季.*)|([0-9]{1,3})|(剧场版)|(\s\S*$)|(之.*)|([\p{P}\p{S}].*)`).ReplaceAllString(search.Name, "")
|
||||
// 如果处理后的影片名称依旧没有改变 且具有一定长度 则截取部分内容作为搜索条件
|
||||
if len(name) == len(search.Name) && len(name) > 10 {
|
||||
// 中文字符需截取3的倍数,否则可能乱码
|
||||
name = name[:int(math.Ceil(float64(len(name))/5)*3)]
|
||||
}
|
||||
sql = fmt.Sprintf(`select * from %s where (name LIKE "%%%s%%" or sub_title LIKE "%%%[2]s%%") AND cid=%d AND search.deleted_at IS NULL union`, search.TableName(), name, search.Cid)
|
||||
// 执行后续匹配内容, 匹配结果过少,减少过滤条件
|
||||
//sql = fmt.Sprintf(`%s select * from %s where cid=%d AND area="%s" AND language="%s" AND`, sql, search.TableName(), search.Cid, search.Area, search.Language)
|
||||
|
||||
// 添加其他相似匹配规则
|
||||
sql = fmt.Sprintf(`%s (select * from %s where cid=%d AND `, sql, search.TableName(), search.Cid)
|
||||
// 根据剧情标签查找相似影片, classTag 使用的分隔符为 , | /
|
||||
// 首先去除 classTag 中包含的所有空格
|
||||
search.ClassTag = strings.ReplaceAll(search.ClassTag, " ", "")
|
||||
// 如果 classTag 中包含分割符则进行拆分匹配
|
||||
if strings.Contains(search.ClassTag, ",") {
|
||||
s := "("
|
||||
for _, t := range strings.Split(search.ClassTag, ",") {
|
||||
s = fmt.Sprintf(`%s class_tag like "%%%s%%" OR`, s, t)
|
||||
}
|
||||
sql = fmt.Sprintf("%s %s)", sql, strings.TrimSuffix(s, "OR"))
|
||||
} else if strings.Contains(search.ClassTag, "/") {
|
||||
s := "("
|
||||
for _, t := range strings.Split(search.ClassTag, "/") {
|
||||
s = fmt.Sprintf(`%s class_tag like "%%%s%%" OR`, s, t)
|
||||
}
|
||||
sql = fmt.Sprintf("%s %s)", sql, strings.TrimSuffix(s, "OR"))
|
||||
} else {
|
||||
sql = fmt.Sprintf(`%s class_tag like "%%%s%%"`, sql, search.ClassTag)
|
||||
}
|
||||
// 除名称外的相似影片使用随机排序
|
||||
//sql = fmt.Sprintf("%s ORDER BY RAND() limit %d,%d)", sql, page.Current, page.PageSize)
|
||||
sql = fmt.Sprintf("%s AND search.deleted_at IS NULL limit %d,%d)", sql, page.Current, page.PageSize)
|
||||
// 条件拼接完成后加上limit参数
|
||||
sql = fmt.Sprintf("(%s) limit %d,%d", sql, page.Current, page.PageSize)
|
||||
// 执行sql
|
||||
var list []SearchInfo
|
||||
db.Mdb.Raw(sql).Scan(&list)
|
||||
// 根据list 获取对应的BasicInfo
|
||||
var basicList []MovieBasicInfo
|
||||
for _, s := range list {
|
||||
// 通过key获取对应的影片基本数据
|
||||
basicList = append(basicList, GetBasicInfoByKey(fmt.Sprintf(config.MovieBasicInfoKey, s.Cid, s.Mid)))
|
||||
}
|
||||
|
||||
return basicList
|
||||
}
|
||||
|
||||
// GetMultiplePlay 通过影片名hash值匹配播放源
|
||||
func GetMultiplePlay(siteId, key string) []MovieUrlInfo {
|
||||
data := db.Rdb.HGet(db.Cxt, fmt.Sprintf(config.MultipleSiteDetail, siteId), key).Val()
|
||||
var playList []MovieUrlInfo
|
||||
_ = json.Unmarshal([]byte(data), &playList)
|
||||
return playList
|
||||
db.Mdb.Limit(page.PageSize).Offset((page.Current-1)*page.PageSize).Select("mid").
|
||||
Where("name LIKE ?", fmt.Sprintf(`%%%s%%`, keyword)).Or("sub_title LIKE ?", fmt.Sprintf(`%%%s%%`, keyword)).Order("year DESC, update_stamp DESC").Find(&ids)
|
||||
return ids
|
||||
}
|
||||
|
||||
// GetSearchTag 通过影片分类 Pid 返回对应分类的tag信息
|
||||
@@ -629,7 +511,7 @@ func HandleTagStr(title string, tags ...string) []map[string]string {
|
||||
}
|
||||
|
||||
// GetSearchInfosByTags 查询满足searchTag条件的影片分页数据
|
||||
func GetSearchInfosByTags(st SearchTagsVO, page *Page) []SearchInfo {
|
||||
func GetSearchInfosByTags(st SearchTagsVO, page *Page) []int64 {
|
||||
// 准备查询语句的条件
|
||||
qw := db.Mdb.Model(&SearchInfo{})
|
||||
// 通过searchTags的非空属性值, 拼接对应的查询条件
|
||||
@@ -679,19 +561,19 @@ func GetSearchInfosByTags(st SearchTagsVO, page *Page) []SearchInfo {
|
||||
// 返回分页参数
|
||||
GetPage(qw, page)
|
||||
// 查询具体的searchInfo 分页数据
|
||||
var sl []SearchInfo
|
||||
if err := qw.Limit(page.PageSize).Offset((page.Current - 1) * page.PageSize).Find(&sl).Error; err != nil {
|
||||
var ids []int64
|
||||
if err := qw.Select("mid").Limit(page.PageSize).Offset((page.Current - 1) * page.PageSize).Find(&ids).Error; err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
return sl
|
||||
return ids
|
||||
|
||||
}
|
||||
|
||||
// GetMovieListBySort 通过排序类型返回对应的影片基本信息
|
||||
func GetMovieListBySort(t int, pid int64, page *Page) []MovieBasicInfo {
|
||||
var sl []SearchInfo
|
||||
qw := db.Mdb.Model(&SearchInfo{}).Where("pid", pid).Limit(page.PageSize).Offset((page.Current) - 10*page.PageSize)
|
||||
var ids []int64
|
||||
qw := db.Mdb.Model(&SearchInfo{}).Select("mid").Where("pid", pid).Limit(page.PageSize).Offset((page.Current) - 10*page.PageSize)
|
||||
// 针对不同排序类型返回对应的分页数据
|
||||
switch t {
|
||||
case 0:
|
||||
@@ -704,11 +586,11 @@ func GetMovieListBySort(t int, pid int64, page *Page) []MovieBasicInfo {
|
||||
// 最近更新 (更新时间)
|
||||
qw.Order("update_stamp DESC")
|
||||
}
|
||||
if err := qw.Find(&sl).Error; err != nil {
|
||||
if err := qw.Find(&ids).Error; err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
return GetBasicInfoBySearchInfos(sl...)
|
||||
return GetBasicInfoByIds(ids)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -112,15 +112,46 @@ type UserInfoVo struct {
|
||||
|
||||
// PlayLinkVo 多站点播放链接数据列表
|
||||
type PlayLinkVo struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
LinkList []MovieUrlInfo `json:"linkList"`
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
LinkList []PlayItem `json:"linkList"`
|
||||
}
|
||||
|
||||
// MovieDetailVo 影片详情数据, 播放源合并版
|
||||
type MovieDetailVo struct {
|
||||
MovieDetail
|
||||
List []PlayLinkVo `json:"list"`
|
||||
Id int64 `json:"id" gorm:"primaryKey"` //影片Id
|
||||
Mid int64 `json:"mid"` //影片Id
|
||||
Cid int64 `json:"cid"` //分类ID
|
||||
Pid int64 `json:"pid"` //一级分类ID
|
||||
Name string `json:"name"` //片名
|
||||
Picture string `json:"picture"` //简介图片
|
||||
SubTitle string `json:"subTitle"` //子标题
|
||||
CName string `json:"cName"` //分类名称
|
||||
EnName string `json:"enName"` //英文名
|
||||
Initial string `json:"initial"` //首字母
|
||||
ClassTag string `json:"classTag"` //分类标签
|
||||
Actor string `json:"actor"` //主演
|
||||
Director string `json:"director"` //导演
|
||||
Writer string `json:"writer"` //作者
|
||||
Blurb string `json:"blurb"` //简介, 残缺,不建议使用
|
||||
Remarks string `json:"remarks"` // 更新情况
|
||||
ReleaseDate string `json:"releaseDate"` //上映时间
|
||||
Area string `json:"area"` // 地区
|
||||
Language string `json:"language"` //语言
|
||||
Year string `json:"year"` //年份
|
||||
State string `json:"state"` //影片状态 正片|预告...
|
||||
UpdateTime string `json:"updateTime"` //更新时间
|
||||
AddTime int64 `json:"addTime"` //资源添加时间戳
|
||||
DbId int64 `json:"dbId"` //豆瓣id
|
||||
DbScore string `json:"dbScore"` // 豆瓣评分
|
||||
Hits int64 `json:"hits"` //影片热度
|
||||
Content string `json:"content"` //内容简介
|
||||
PlayFrom FromList `json:"playFrom" gorm:"type:json"` // 播放来源
|
||||
DownFrom string `json:"DownFrom"` //下载来源 例: http
|
||||
List []PlayLinkVo `json:"list"` // 播放信息切片组
|
||||
//PlaySeparator string `json:"playSeparator"` // 播放信息分隔符
|
||||
//PlayList MoviePlayList `json:"playList" gorm:"type:json"` //播放地址url
|
||||
DownloadList MoviePlayList `json:"downloadList" gorm:"type:json"` // 下载url地址
|
||||
}
|
||||
|
||||
type RecordRequestVo struct {
|
||||
@@ -132,3 +163,41 @@ type RecordRequestVo struct {
|
||||
EndTime time.Time `json:"endTime"` // 结束时间
|
||||
Paging *Page `json:"paging"` // 分页参数
|
||||
}
|
||||
|
||||
// ----------------------------------- vo type convert --------------------------------------------------------------
|
||||
|
||||
// ConvertMovieDetailVo 整合详情信息
|
||||
func ConvertMovieDetailVo(d MovieDetail, l []PlayLinkVo) MovieDetailVo {
|
||||
return MovieDetailVo{
|
||||
Id: d.Id,
|
||||
Mid: d.Mid,
|
||||
Cid: d.Cid,
|
||||
Pid: d.Pid,
|
||||
Name: d.Name,
|
||||
Picture: d.Picture,
|
||||
SubTitle: d.SubTitle,
|
||||
CName: d.CName,
|
||||
EnName: d.EnName,
|
||||
Initial: d.Initial,
|
||||
ClassTag: d.ClassTag,
|
||||
Actor: d.Actor,
|
||||
Director: d.Director,
|
||||
Writer: d.Writer,
|
||||
Blurb: "", // blurb 和 content 内容重复度过高, 且内存占用过高, 所以舍弃简介字段
|
||||
Remarks: d.Remarks,
|
||||
ReleaseDate: d.ReleaseDate,
|
||||
Area: d.Area,
|
||||
Language: d.Language,
|
||||
Year: d.Year,
|
||||
State: d.State,
|
||||
UpdateTime: d.UpdateTime,
|
||||
AddTime: d.AddTime,
|
||||
DbId: d.DbId,
|
||||
DbScore: d.DbScore,
|
||||
Hits: d.Hits,
|
||||
Content: d.Content,
|
||||
PlayFrom: d.PlayFrom,
|
||||
DownFrom: d.DownFrom,
|
||||
List: l,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@ func TableInIt() {
|
||||
system.InitAdminAccount()
|
||||
// 创建 Search Table
|
||||
system.CreateSearchTable()
|
||||
// 创建 MovieDetails Table
|
||||
system.CreateMovieDetailTable()
|
||||
// 创建 SlaveMovieInfo Table
|
||||
system.CreateSlaveMovieInfoTable()
|
||||
// 创建图片信息管理表
|
||||
system.CreateFileTable()
|
||||
// 创建采集失效记录表
|
||||
|
||||
@@ -30,12 +30,6 @@ func FilmSourceInit() {
|
||||
{Id: util.GenerateSalt(), Name: "HD(SN)", Uri: `https://suoniapi.com/api.php/provide/vod/from/snm3u8/`, ResultModel: system.JsonResult, Grade: system.SlaveCollect, SyncPictures: false, CollectType: system.CollectVideo, State: false, Interval: 2000},
|
||||
{Id: util.GenerateSalt(), Name: "HD(DB)", Uri: `https://caiji.dbzy.tv/api.php/provide/vod/from/dbm3u8/at/josn/`, ResultModel: system.JsonResult, Grade: system.SlaveCollect, SyncPictures: false, CollectType: system.CollectVideo, State: false},
|
||||
{Id: util.GenerateSalt(), Name: "HD(IK)", Uri: `https://ikunzyapi.com/api.php/provide/vod/at/json`, ResultModel: system.JsonResult, Grade: system.SlaveCollect, SyncPictures: false, CollectType: system.CollectVideo, State: false},
|
||||
//{Id: util.GenerateSalt(), Name: "WX(T2)", Uri: `https://api.wuxianzy.net/api.php/provide/vod/`, ResultModel: system.JsonResult, Grade: system.SlaveCollect, SyncPictures: false, CollectType: system.CollectVideo, State: false},
|
||||
//{Id: util.GenerateSalt(), Name: "OK(BK)", Uri: `https://api.okzy.org/api.php/provide/vod/`, ResultModel: system.JsonResult, Grade: system.SlaveCollect, SyncPictures: false, CollectType: system.CollectVideo, State: false},
|
||||
//{Id: util.GenerateSalt(), Name: "HD(HW)", Uri: `https://cjhwba.com/api.php/provide/vod/`, ResultModel: system.JsonResult, Grade: system.SlaveCollect, SyncPictures: false, CollectType: system.CollectVideo, State: false, Interval: 3000},
|
||||
//{Id: util.GenerateSalt(), Name: "HD(lzBk)", Uri: `https://cj.lzcaiji.com/api.php/provide/vod/`, ResultModel: system.JsonResult, Grade: system.SlaveCollect, SyncPictures: false, CollectType: system.CollectVideo, State: false},
|
||||
//{Id: util.GenerateSalt(), Name: "HD(fs)", Uri: `https://www.feisuzyapi.com/api.php/provide/vod/`, ResultModel: system.JsonResult, Grade: system.SlaveCollect, SyncPictures: false, CollectType: system.CollectVideo, State: false},
|
||||
//{Id: util.GenerateSalt(), Name: "HD(bfBk)", Uri: `http://app.bfzyapi.com/api.php/provide/vod/`, ResultModel: system.JsonResult, Grade: system.SlaveCollect, SyncPictures: false, CollectType: system.CollectVideo, State: false},
|
||||
}
|
||||
err := system.SaveCollectSourceList(l)
|
||||
if err != nil {
|
||||
|
||||
@@ -3,9 +3,11 @@ package conver
|
||||
import (
|
||||
"encoding/xml"
|
||||
"log"
|
||||
"regexp"
|
||||
"server/config"
|
||||
"server/model/collect"
|
||||
"server/model/system"
|
||||
"server/plugin/common/util"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -70,36 +72,42 @@ func ConvertFilmDetails(details []collect.FilmDetail) []system.MovieDetail {
|
||||
|
||||
// ConvertFilmDetail 将影片详情数据处理转化为 system.MovieDetail
|
||||
func ConvertFilmDetail(detail collect.FilmDetail) system.MovieDetail {
|
||||
/*
|
||||
对需数据进行相应的简化处理
|
||||
1.对常见分割符进行统一化处理
|
||||
2.如果演员和导演名单过长,则进行截断, 最多只保留3个
|
||||
*/
|
||||
detail.VodActor = regexp.MustCompile(`[$&#%]`).ReplaceAllString(detail.VodActor, ",")
|
||||
|
||||
md := system.MovieDetail{
|
||||
Id: detail.VodID,
|
||||
Mid: detail.VodID,
|
||||
Cid: detail.TypeID,
|
||||
Pid: detail.TypeID1,
|
||||
Name: detail.VodName,
|
||||
Picture: detail.VodPic,
|
||||
DownFrom: detail.VodDownFrom,
|
||||
MovieDescriptor: system.MovieDescriptor{
|
||||
SubTitle: detail.VodSub,
|
||||
CName: detail.TypeName,
|
||||
EnName: detail.VodEn,
|
||||
Initial: detail.VodLetter,
|
||||
ClassTag: detail.VodClass,
|
||||
Actor: detail.VodActor,
|
||||
Director: detail.VodDirector,
|
||||
Writer: detail.VodWriter,
|
||||
Blurb: detail.VodBlurb,
|
||||
Remarks: detail.VodRemarks,
|
||||
ReleaseDate: detail.VodPubDate,
|
||||
Area: detail.VodArea,
|
||||
Language: detail.VodLang,
|
||||
Year: detail.VodYear,
|
||||
State: detail.VodState,
|
||||
UpdateTime: detail.VodTime,
|
||||
AddTime: detail.VodTimeAdd,
|
||||
DbId: detail.VodDouBanID,
|
||||
DbScore: detail.VodDouBanScore,
|
||||
Hits: detail.VodHits,
|
||||
Content: detail.VodContent,
|
||||
},
|
||||
SubTitle: detail.VodSub,
|
||||
CName: detail.TypeName,
|
||||
EnName: detail.VodEn,
|
||||
Initial: detail.VodLetter,
|
||||
ClassTag: detail.VodClass,
|
||||
Actor: util.TruncateBySep(detail.VodActor, 3),
|
||||
Director: util.TruncateBySep(detail.VodDirector, 2),
|
||||
Writer: util.TruncateBySep(detail.VodWriter, 2),
|
||||
//Blurb: detail.VodBlurb,
|
||||
Blurb: "", // blurb 和 content 内容重复度过高, 且内存占用过高, 所以舍弃简介字段
|
||||
Remarks: detail.VodRemarks,
|
||||
ReleaseDate: detail.VodPubDate,
|
||||
Area: detail.VodArea,
|
||||
Language: detail.VodLang,
|
||||
Year: detail.VodYear,
|
||||
State: detail.VodState,
|
||||
UpdateTime: detail.VodTime,
|
||||
AddTime: detail.VodTimeAdd,
|
||||
DbId: detail.VodDouBanID,
|
||||
DbScore: detail.VodDouBanScore,
|
||||
Hits: detail.VodHits,
|
||||
Content: detail.VodContent,
|
||||
}
|
||||
// 通过分割符切分播放源信息 PlaySeparator $$$
|
||||
md.PlayFrom = strings.Split(detail.VodPlayFrom, detail.VodPlayNote)
|
||||
@@ -111,8 +119,8 @@ func ConvertFilmDetail(detail collect.FilmDetail) system.MovieDetail {
|
||||
}
|
||||
|
||||
// GenFilmPlayList 处理影片播放地址数据, 只保留m3u8与mp4格式的链接,生成playList
|
||||
func GenFilmPlayList(playUrl, separator string) [][]system.MovieUrlInfo {
|
||||
var res [][]system.MovieUrlInfo
|
||||
func GenFilmPlayList(playUrl, separator string) system.MoviePlayList {
|
||||
var res system.MoviePlayList
|
||||
if separator != "" {
|
||||
// 1. 通过分隔符切分播放源地址
|
||||
for _, l := range strings.Split(playUrl, separator) {
|
||||
@@ -120,7 +128,6 @@ func GenFilmPlayList(playUrl, separator string) [][]system.MovieUrlInfo {
|
||||
if strings.Contains(l, ".m3u8") || strings.Contains(l, ".mp4") {
|
||||
// 2. 将每组播放源对应的播放列表信息存储到列表中
|
||||
res = append(res, ConvertPlayUrl(l))
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -134,8 +141,8 @@ func GenFilmPlayList(playUrl, separator string) [][]system.MovieUrlInfo {
|
||||
}
|
||||
|
||||
// GenAllFilmPlayList 处理影片播放地址数据, 保留全部播放链接,生成playList
|
||||
func GenAllFilmPlayList(playUrl, separator string) [][]system.MovieUrlInfo {
|
||||
var res [][]system.MovieUrlInfo
|
||||
func GenAllFilmPlayList(playUrl, separator string) system.MoviePlayList {
|
||||
var res system.MoviePlayList
|
||||
if separator != "" {
|
||||
// 1. 通过分隔符切分播放源地址
|
||||
for _, l := range strings.Split(playUrl, separator) {
|
||||
@@ -150,18 +157,18 @@ func GenAllFilmPlayList(playUrl, separator string) [][]system.MovieUrlInfo {
|
||||
}
|
||||
|
||||
// ConvertPlayUrl 将单个playFrom的播放地址字符串处理成列表形式
|
||||
func ConvertPlayUrl(playUrl string) []system.MovieUrlInfo {
|
||||
func ConvertPlayUrl(playUrl string) []system.PlayItem {
|
||||
// 对每个片源的集数和播放地址进行分割 Episode$Link#Episode$Link
|
||||
var l []system.MovieUrlInfo
|
||||
var l []system.PlayItem
|
||||
for _, p := range strings.Split(playUrl, "#") {
|
||||
// 处理 Episode$Link 形式的播放信息
|
||||
if strings.Contains(p, "$") {
|
||||
l = append(l, system.MovieUrlInfo{
|
||||
l = append(l, system.PlayItem{
|
||||
Episode: strings.Split(p, "$")[0],
|
||||
Link: strings.Split(p, "$")[1],
|
||||
})
|
||||
} else {
|
||||
l = append(l, system.MovieUrlInfo{
|
||||
l = append(l, system.PlayItem{
|
||||
Episode: "(`・ω・´)",
|
||||
Link: p,
|
||||
})
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"log"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GenerateUUID 生成UUID
|
||||
@@ -101,6 +102,7 @@ func ValidURL(s string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// ValidPwd 校验密码
|
||||
func ValidPwd(s string) error {
|
||||
if len(s) < 8 || len(s) > 12 {
|
||||
return fmt.Errorf("密码长度不符合规范, 必须为8-10位")
|
||||
@@ -124,3 +126,69 @@ func ValidPwd(s string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TruncateBySep 截断字符串,保留指定数量的结果
|
||||
func TruncateBySep(s string, limit int) string {
|
||||
// 如果保留数量小于等于0则返回空值
|
||||
if limit <= 0 {
|
||||
return ""
|
||||
}
|
||||
// 先强制对不同的分割符进行统一替换为 ,
|
||||
s = regexp.MustCompile(`[$&#%]`).ReplaceAllString(s, ",")
|
||||
// 使用 strings.Split 分割字符串
|
||||
// Split 会在分隔符连续出现或出现在首尾时产生空字符串,这通常符合预期
|
||||
parts := strings.Split(s, ",")
|
||||
// 片段数量小于或等于限制,直接返回原字符串
|
||||
// 返回原字符串是为了保留原始的格式(比如末尾是否有分隔符)
|
||||
// 即使不截断也重新 Join 一遍(去除多余的空片段等)
|
||||
return strings.Join(parts[:limit], ",")
|
||||
}
|
||||
|
||||
// CleanFilmName 清洗影片名称,只保留主体
|
||||
func CleanFilmName(name string) string {
|
||||
if name == "" {
|
||||
return ""
|
||||
}
|
||||
// 1. 去除常见的前缀 (方括号、圆括号内的内容) 匹配 [xxx], 【xxx】, (xxx), (xxx)
|
||||
//rePrefix := regexp.MustCompile(`^\s*[\[【\((][^\]】\))]*[\]】\))]\s*`)
|
||||
//for rePrefix.MatchString(name) {
|
||||
// name = rePrefix.ReplaceAllString(name, "")
|
||||
//}
|
||||
// 2.定义需要清洗的特殊标识关键字集合
|
||||
var noisePatterns = []string{
|
||||
`第 [零一二三四五六七八九十\d]+ 季`, `第 [零一二三四五六七八九十\d]+ 话`, `第 [零一二三四五六七八九十\d]+ 集`,
|
||||
`Season\s*\d+`, `S\d+`, `Ep\d+`, `\d{1,3}\s*(话 | 集)`,
|
||||
`\s+(II|III|IV|V|VI|VII|VIII|IX|X)\s*$`,
|
||||
`剧场版`, `电影版`, `OVA`, `OAD`, `SP`, `特别篇`, `总集篇`, `外传`, `序`, `破`, `急`, `终章`,
|
||||
`\d{3,4}[Pp]`, `HD`, `FHD`, `UHD`, `4K`, `BD`, `BluRay`, `BDRip`, `HEVC`, `H264`, `H265`,
|
||||
`GB`, `MB`, `MP4`, `MKV`, `AVI`, `RMVB`,
|
||||
`字幕组`, `动漫`, `动画`, `新版`, `重制版`, `连载`, `更新`, `全集`, `合集`,
|
||||
`Uncensored`, `NoCen`, `Dubbed`, `Subbed`, `Raw`, `生肉`, `熟肉`,
|
||||
}
|
||||
// 3. 处理拼接完整的正则表达式
|
||||
fullPattern := `(?i)(?:\s+|\.+|_+|-+) (` + strings.Join(noisePatterns, "|") + `).*$`
|
||||
cutRegex := regexp.MustCompile(fullPattern)
|
||||
// 去除满足匹配集的子串
|
||||
name = cutRegex.ReplaceAllString(name, "")
|
||||
// 特殊处理 "之" 字结构 (仅当 "之" 后紧跟噪音词时切除) 之\s*(噪音词)
|
||||
reRegex := regexp.MustCompile(`(?i) 之\s* (` + strings.Join(noisePatterns, "|") + `).*$`)
|
||||
name = reRegex.ReplaceAllString(name, "")
|
||||
|
||||
// 修剪 - 去除末尾残留的符号和空白
|
||||
name = strings.TrimRight(name, " \t\n\r._-])::")
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
// FormatSpecialChar 格式化特殊字符, 统一替换为逗号
|
||||
func FormatSpecialChar(src string) string {
|
||||
// 执行替换
|
||||
return strings.Map(func(r rune) rune {
|
||||
switch r {
|
||||
case '#', '/', '$', '&', '%', '^', '*', '-':
|
||||
return ','
|
||||
default:
|
||||
return r
|
||||
}
|
||||
}, src)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"server/config"
|
||||
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
"gorm.io/gorm/schema"
|
||||
"server/config"
|
||||
)
|
||||
|
||||
var Mdb *gorm.DB
|
||||
@@ -25,7 +26,8 @@ func InitMysql() (err error) {
|
||||
SingularTable: true, //是否使用 结构体名称作为表名 (关闭自动变复数)
|
||||
//NameReplacer: strings.NewReplacer("spider_", ""), // 替表名和字段中的 Me 为 空
|
||||
},
|
||||
Logger: logger.Default.LogMode(logger.Info), //设置日志级别为Info
|
||||
Logger: logger.Default.LogMode(logger.Warn), //设置日志级别为Info
|
||||
//Logger: logger.Default.LogMode(logger.Info), //设置日志级别为Info
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user