diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..180b75e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea +.vscode +build +.DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a521c7f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +FROM golang:1.20 as builder + +WORKDIR /app + +COPY go.mod go.sum ./ + +RUN go mod download + +COPY . . + +RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o violet . + +FROM alpine + +RUN apk update \ + && apk upgrade \ + && apk add --no-cache ca-certificates tzdata \ + && update-ca-certificates 2>/dev/null || true + +RUN apk add --no-cache ffmpeg + +COPY --from=builder /app/violet /violet + +EXPOSE 8080 + +CMD ["/violet"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..457c4d4 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +## violet + +A Video translated text example in Go \ No newline at end of file diff --git a/api/douyin.go b/api/douyin.go new file mode 100644 index 0000000..019852d --- /dev/null +++ b/api/douyin.go @@ -0,0 +1,152 @@ +package api + +import ( + "encoding/json" + "fmt" + "io" + "log" + "math/rand" + "net/http" + "regexp" + "strconv" + "strings" + "time" + "violet/config" + "violet/model" +) + +// randomUserAgent 从配置文件中随机返回一个 User-Agent 字符串 +func randomUserAgent() string { + rand.Seed(time.Now().UnixNano()) + return config.Config.App.UserAgents[rand.Intn(len(config.Config.App.UserAgents))] +} + +// ProcessUserInput 正则表达式处理输入的字符 +func ProcessUserInput(input string) string { + linkRegex := regexp.MustCompile(`v\.douyin\.com\/[a-zA-Z0-9]+`) + idRegex := regexp.MustCompile(`\d{19}`) + + if linkRegex.MatchString(input) { + return linkRegex.FindString(input) + } else if idRegex.MatchString(input) { + return idRegex.FindString(input) + } + + return "" +} + +// ExtractVideoId 提取视频的 id +func ExtractVideoId(link string) string { + // 先判断链接是否包含协议,如果缺失则补充 + if !strings.HasPrefix(link, "http://") && !strings.HasPrefix(link, "https://") { + link = "https://" + link + } + + // 发送请求,获取重定向后的 url + resp, err := http.Get(link) + if err != nil { + log.Println("[Error] - 请求失败:", err) + + return "" + } + + defer resp.Body.Close() + + finalURL := resp.Request.URL.String() + log.Print("[Info] - finalURL: " + finalURL) + finalURL = resp.Request.URL.String() + + idRegex := regexp.MustCompile(`/video/(\d+)`) + matches := idRegex.FindStringSubmatch(finalURL) + + if len(matches) > 1 { + log.Println("[Info] - 已获取到视频 ID:" + matches[1]) + + return matches[1] + } + return "" +} + +func IsNumber(s string) bool { + _, err := strconv.Atoi(s) + + return err == nil +} + +// GetDouYinVideoInfo 获取抖音视频的信息 +func GetDouYinVideoInfo(videoId string) (string, string, error) { + url := fmt.Sprintf("https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids=%s&a_bogus=64745b2b5bdc4e75b720a9a85b19867a", videoId) + method := "GET" + + client := &http.Client{} + request, err := http.NewRequest(method, url, nil) + if err != nil { + log.Println("[Error] - ", err) + + return "", "", err + } + + request.Header.Add("User-Agent", randomUserAgent()) + response, err := client.Do(request) + if err != nil { + log.Println("[Error] - ", err) + + return "", "", err + } + + defer response.Body.Close() + + body, err := io.ReadAll(response.Body) + if err != nil { + log.Println("[Error] - ", err) + + return "", "", err + } + + if response.StatusCode != 200 { + log.Println("[Error] - ", string(body)) + + return "", "", fmt.Errorf("[Error] - 响应失败") + } + + var videoInfo model.DYVideoInfo + err = json.Unmarshal(body, &videoInfo) + if err != nil { + log.Println("[Error] - JSON 解析失败", err) + + return "", "", err + } + + if len(videoInfo.ItemList) > 0 && videoInfo.ItemList[0].Video.PlayAddr.Uri != "" { + uri := videoInfo.ItemList[0].Video.PlayAddr.Uri + desc := videoInfo.ItemList[0].Desc + finalURL := fmt.Sprintf("https://www.iesdouyin.com/aweme/v1/play/?video_id=%s&ratio=1080p&line=0", uri) + + return finalURL, desc, nil + } + + return "", "", fmt.Errorf("[Error] - 找不到视频") +} + +func GetDouYinInfo(link string) (string, string, error) { + videoIdOrLink := ProcessUserInput(link) + var videoId string + if videoIdOrLink != "" { + if IsNumber(videoIdOrLink) { + videoId = videoIdOrLink + } else { + videoId = ExtractVideoId(videoIdOrLink) + } + } + + if len(videoId) == 0 { + return "", "", fmt.Errorf("[Error] - 找不到视频") + } + + finalURL, title, err := GetDouYinVideoInfo(videoId) + if err != nil { + return "", "", err + } + + return finalURL, title, nil +} diff --git a/api/gemini.go b/api/gemini.go new file mode 100644 index 0000000..745b24a --- /dev/null +++ b/api/gemini.go @@ -0,0 +1,195 @@ +package api + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "math/rand" + "net/http" + "net/url" + "time" + "violet/config" + "violet/model" +) + +func RandKey(t []string) string { + rand.Seed(time.Now().UnixNano()) + + return t[rand.Intn(len(t))] +} + +func NewClient() *http.Client { + // 开启代理 + if config.Config.Proxy.Protocol != "" { + proxyURL, err := url.Parse(config.Config.Proxy.Protocol) + if err != nil { + log.Println("[Error] - 设置代理出错:", err) + log.Println("用默认直连") + client := &http.Client{} + + return client + } + + log.Println("[Info] - 正在代理请求 Gemini\n代理地址:", proxyURL) + + // 创建一个自定义的 Transport + transport := &http.Transport{ + Proxy: http.ProxyURL(proxyURL), + } + + // 使用自定义的 Transport 创建一个 http.Client + client := &http.Client{ + Transport: transport, + } + + return client + } else { + // 全部直连,不走代理 + client := &http.Client{} + + return client + } +} + +func SetGeminiV(data model.FrameInfo) (error, string) { + if data.Base64Data == "" { + return fmt.Errorf("[Error] - Base64 数据为空"), "" + } + + _url := fmt.Sprintf(config.Config.App.GeminiUrl+"/v1beta/models/gemini-pro-vision:generateContent?key=%s", RandKey(config.Config.App.GeminiKey)) + method := "POST" + payload := model.GeminiData{ + Contents: []model.Contents{ + { + Parts: []model.Parts{ + { + Text: fmt.Sprintf("这个图片是一段视频中第%d段的第%d帧,他的详细内容内容是什么?比如有什么人物,他们在做什么动作,说什么话。这个时候你就是一个视频脚本分析大师,你应该剖析他们原本的剧情或者画面呈现的东西,你应该直接输出告诉我视频这一帧呈现的内容,现在请开始你分析:", data.SegmentIndex, data.FrameIndex), + }, + { + InlineData: &model.InlineData{ + MimeType: "image/jpeg", + Data: data.Base64Data, + }, + }, + }, + }, + }, + } + + payloadBytes, err := json.Marshal(payload) + if err != nil { + return fmt.Errorf("[Error] - error marshaling payload: %v", err), "" + } + + client := NewClient() + + request, err := http.NewRequest(method, _url, bytes.NewReader(payloadBytes)) + if err != nil { + return fmt.Errorf("[Error] - 创建请求失败:%v", err), "" + } + + request.Header.Add("Content-Type", "application/json") + response, err := client.Do(request) + if err != nil { + return fmt.Errorf("[Error] - 发送请求失败:%v", err), "" + } + + defer response.Body.Close() + + body, err := ioutil.ReadAll(response.Body) + if err != nil { + return fmt.Errorf("[Error] - 读取响应失败:%v", err), "" + } + + if response.StatusCode != 200 { + return fmt.Errorf("[Error] - status code error: %d %s", response.StatusCode, string(body)), "" + } + + // 解析响应 + var geminiResponse model.GeminiResponse + err = json.Unmarshal(body, &geminiResponse) + if err != nil { + return fmt.Errorf("[Error] - JSON 解析失败 %v", err), "" + } + + // 检查 Candidates 切片是否为空 + if len(geminiResponse.Candidates) == 0 { + return fmt.Errorf("[Error] - Candidates 为空"), "" + } + + // 检查 Parts 切片是否为空 + if len(geminiResponse.Candidates[0].Content.Parts) == 0 { + return fmt.Errorf("[Error] - Parts 为空"), "" + } + + frameDescription := fmt.Sprintf("片段 %d 中的第 %d 帧的内容是%s", + data.SegmentIndex, + data.FrameIndex, + geminiResponse.Candidates[0].Content.Parts[0].Text) + + return nil, frameDescription +} + +func SetGemini(content string) (error, string) { + _url := fmt.Sprintf(config.Config.App.GeminiUrl+"/v1beta/models/gemini-pro:generateContent?key=%s", RandKey(config.Config.App.GeminiKey)) + method := "POST" + + payload := model.GeminiPro{ + Contents: []model.GeminiProContent{ + { + Role: "USER", + Parts: []model.GeminiProPart{ + { + Text: fmt.Sprintf("你现在是一个视频脚本整合大师。你的任务是将一系列乱序的视频片段整合成一个完整的故事。每个片段都包含了一系列帧的详细内容描述。由于这些片段是乱序的,你需要先找到第 0 片段的第 0 帧,这是视频的开头。从那里开始,确定每个片段及其帧的正确顺序,然后按照这个顺序来分析整个视频的内容。你的最终目标是输出一个连贯的视频内容脚本,该脚本详细地叙述了视频的全部故事线,包括所有关键的对话、场景和情感转变。请注意,你不需要输出处理的过程,只需要提供视频的完整内容概要。现在开始,请查看以下视频片段及其内容描述,并根据这些信息,从第 0 片段的第 0 帧开始,创建一个完整的视频内容脚本:'%s'", content), + }, + }, + }, + }, + } + payloadBytes, err := json.Marshal(payload) + if err != nil { + return fmt.Errorf("[Error] - error marshaling payload: %v", err), "" + } + + client := NewClient() + + request, err := http.NewRequest(method, _url, bytes.NewReader(payloadBytes)) + if err != nil { + return fmt.Errorf("[Error] - 创建请求失败:%v", err), "" + } + + request.Header.Add("Content-Type", "application/json") + + response, err := client.Do(request) + if err != nil { + return fmt.Errorf("[Error] - 发送请求失败: %v", err), "" + } + + defer response.Body.Close() + + body, err := ioutil.ReadAll(response.Body) + if err != nil { + return fmt.Errorf("[Error] - error reading response body: %v", err), "" + } + + if response.StatusCode != 200 { + return fmt.Errorf("[Error] - status code error: %d %s", response.StatusCode, string(body)), "" + } + + var geminiResponse model.GeminiResponse + if err = json.Unmarshal(body, &geminiResponse); err != nil { + return fmt.Errorf("[Error] - JSON 解析失败 %v", err), "" + } + + if len(geminiResponse.Candidates) == 0 { + return fmt.Errorf("[Error] - Candidates 为空"), "" + } + + if len(geminiResponse.Candidates[0].Content.Parts) == 0 { + return fmt.Errorf("[Error] - Parts 为空"), "" + } + + return nil, geminiResponse.Candidates[0].Content.Parts[0].Text +} diff --git a/api/video.go b/api/video.go new file mode 100644 index 0000000..949de63 --- /dev/null +++ b/api/video.go @@ -0,0 +1,159 @@ +package api + +import ( + "bytes" + "encoding/base64" + "fmt" + "io" + "log" + "os/exec" + "strconv" + "strings" + "sync" + "time" + "violet/model" +) + +// getVideoDuration 获取视频时长 +func getVideoDuration(videoURL string) (float64, error) { + // 使用 ffmpeg 获取视频时长 + cmd := exec.Command("ffmpeg", "-i", videoURL) + output, err := cmd.CombinedOutput() + if err == nil { + return 0, fmt.Errorf("ffmpeg didn't retrun an error, but it should have") + } + + // 解析 ffmpeg 输出,寻找时长 + outputStr := string(output) + if strings.Contains(outputStr, "Duration") { + start := strings.Index(outputStr, "Duration: ") + if start != -1 { + end := strings.Index(outputStr[start:], ",") + if end != -1 { + durationStr := outputStr[start+10 : start+end] + t, err := time.Parse("15:04:05.00", durationStr) + if err == nil { + return float64(t.Hour()*3600+t.Minute()*60+t.Second()) + float64(t.Nanosecond())/1e9, nil + } + } + } + } + return 0, fmt.Errorf("unable to parse duration from ffmpeg output") +} + +// VideoSlice 视频截取 +func VideoSlice(videoURL string) (error, string) { + segments := 6 // 分成6段 + intercept := "1/5" // 每5帧截取一帧 + duration, err := getVideoDuration(videoURL) + if err != nil { + return fmt.Errorf("Error getting video duration: %s\n", err), "" + } + if duration < 60 && duration > 10 { + segments = 1 + intercept = "1/5" + } else if duration <= 10 { + segments = 5 + intercept = "1" + } + // 每段的时长 + segmentDuration := duration / float64(segments) + var wg sync.WaitGroup + var FrameDescriptions []string + for i := 0; i < segments; i++ { + wg.Add(1) + go func(segmentIndex int) { + defer wg.Done() + startTime := float64(segmentIndex) * segmentDuration + + // 构建ffmpeg命令,使用管道输出 + cmd := exec.Command( + "ffmpeg", + "-i", videoURL, + "-ss", strconv.FormatFloat(startTime, 'f', -1, 64), + "-t", strconv.FormatFloat(segmentDuration, 'f', -1, 64), + "-vf", fmt.Sprintf("fps=%s,scale=iw/5:-1", intercept), // 降低帧率和分辨率 + "-f", "image2pipe", + "-vcodec", "mjpeg", + "pipe:1", + ) + + // 创建管道 + stdoutPipe, err := cmd.StdoutPipe() + if err != nil { + log.Printf("Error creating stdout pipe for segment %d: %s\n", segmentIndex, err) + return + } + + // 启动命令 + if err := cmd.Start(); err != nil { + log.Printf("Error starting ffmpeg for segment %d: %s\n", segmentIndex, err) + return + } + + // 读取数据并处理 + buffer := make([]byte, 4096) // 用于存储从管道读取的数据 + imageBuffer := new(bytes.Buffer) + var mu sync.Mutex + frameIndex := 0 + for { + n, err := stdoutPipe.Read(buffer) + //log.Print(len(buffer)) + if err != nil { + if err == io.EOF { + break // 管道关闭,没有更多的数据 + } + log.Printf("Error reading from stdout pipe: %s\n", err) + return + } + if n > 0 { + imageBuffer.Write(buffer[:n]) + // 检查imageBuffer中是否存在JPEG结束标记 + if idx := bytes.Index(imageBuffer.Bytes(), []byte("\xff\xd9")); idx != -1 { + // 截取到结束标记的部分作为一张完整的JPEG图像 + jpegData := imageBuffer.Bytes()[:idx+2] // 包含结束标记 + base64Data := base64.StdEncoding.EncodeToString(jpegData) + imageBuffer.Next(idx + 2) // 移除已处理的JPEG图像数据 + + // 输出带有标记的Base64字符串 + frameInfo := model.FrameInfo{ + SegmentIndex: segmentIndex, + FrameIndex: frameIndex, + Base64Data: base64Data, + } + frameIndex++ + var frameDescription string + + err, frameDescription = SetGeminiV(frameInfo) + if err != nil { + log.Printf("Error creating stdout pipe for segment %d: %s", segmentIndex, err) + continue + } + if len(frameDescription) != 0 { + mu.Lock() + FrameDescriptions = append(FrameDescriptions, frameDescription) + mu.Unlock() + } + } + } + } + + // 等待命令完成 + if err := cmd.Wait(); err != nil { + log.Printf("Error waiting for ffmpeg command to finish for segment %d: %s\n", segmentIndex, err) + return + } + }(i) + } + + wg.Wait() // 等待所有 goroutine 完成 + fullDescription := strings.Join(FrameDescriptions, " ") + d := "" + + err, d = SetGemini(fullDescription) + if err != nil { + return err, "" + } + + return nil, d +} diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..06f8337 --- /dev/null +++ b/build.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o build/violet-linux-amd64 ./ +CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o build/violet-linux-arm64 ./ +CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o build/violet-darwin-amd64 ./ +CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o build/violet-darwin-arm64 ./ +CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o build/violet-windows-amd64.exe ./ \ No newline at end of file diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..943c2ed --- /dev/null +++ b/config.yaml @@ -0,0 +1,17 @@ +App: + # 支持添加多个 Key + GeminiKey: + - YOUR-GEMINI-KEY + GeminiUrl: https://generativelanguage.googleapis.com + UserAgents: + - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Safari/605.2.15 + - Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.104 Safari/537.66 + +# 本地服务启动配置项 +Server: + Port: 8080 + Host: localhost + +# 代理配置(http|https|sock5://ip:port),为空则不使用代理 +Proxy: + Protocol: http://192.168.1.100:7890 \ No newline at end of file diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..754fa0e --- /dev/null +++ b/config/config.go @@ -0,0 +1,25 @@ +package config + +import ( + "github.com/spf13/viper" + "log" + "violet/model" +) + +var Config model.Config + +func ConfigInit() { + viper.AddConfigPath(".") + viper.SetConfigName("config") + viper.SetConfigType("yaml") + + err := viper.ReadInConfig() + if err != nil { + log.Fatalf("[Error] - 找不到配置文件,%s", err) + } + + err = viper.Unmarshal(&Config) + if err != nil { + log.Fatalf("[Error] - 配置文件读取错误,%s", err) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..74f8025 --- /dev/null +++ b/go.mod @@ -0,0 +1,52 @@ +module violet + +go 1.21.3 + +require ( + github.com/gin-contrib/cors v1.5.0 + github.com/gin-gonic/gin v1.9.1 + github.com/spf13/viper v1.18.2 +) + +require ( + github.com/bytedance/sonic v1.10.1 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect + github.com/chenzhuoyu/iasm v0.9.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gin-contrib/sse v0.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.15.5 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/leodido/go-urn v1.2.4 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mitchellh/mapstructure v1.5.0 // 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.1.0 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/arch v0.5.0 // indirect + golang.org/x/crypto v0.16.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b9d4dd2 --- /dev/null +++ b/go.sum @@ -0,0 +1,137 @@ +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= +github.com/bytedance/sonic v1.10.1 h1:7a1wuFXL1cMy7a3f7/VFcEtriuXQnUBhtoVfOZiaysc= +github.com/bytedance/sonic v1.10.1/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= +github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= +github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gin-contrib/cors v1.5.0 h1:DgGKV7DDoOn36DFkNtbHrjoRiT5ExCe+PC9/xp7aKvk= +github.com/gin-contrib/cors v1.5.0/go.mod h1:TvU7MAZ3EwrPLI2ztzTt3tqgvBCq+wn8WpZmfADjupI= +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.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +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.15.5 h1:LEBecTWb/1j5TNY1YYG2RcOUN3R7NLylN+x8TTueE24= +github.com/go-playground/validator/v10 v10.15.5/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +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/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +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/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +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.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +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/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/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +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= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.5.0 h1:jpGode6huXQxcskEIpOCvrU+tzo81b6+oFLUYXWtH/Y= +golang.org/x/arch v0.5.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +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.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +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= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/main.go b/main.go new file mode 100644 index 0000000..35aeac8 --- /dev/null +++ b/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "github.com/gin-contrib/cors" + "github.com/gin-gonic/gin" + "violet/config" + "violet/server" + "violet/util" +) + +func init() { + config.ConfigInit() +} + +func main() { + gin.SetMode(gin.ReleaseMode) + router := gin.New() + router.Use(cors.Default()) + + util.P(config.Config.Server.Host, config.Config.Server.Port, Version) + + router.GET("/", server.Test) // test router + router.POST("/video2text", server.Video2Text) + router.POST("/removeWatermark", server.RemoveWatermark) + + err := router.Run(":" + config.Config.Server.Port) + if err != nil { + panic("server start fail") + + return + } +} diff --git a/model/config.go b/model/config.go new file mode 100644 index 0000000..2f8356e --- /dev/null +++ b/model/config.go @@ -0,0 +1,17 @@ +package model + +type Config struct { + App struct { + GeminiKey []string `yaml:"GeminiKey"` + GeminiUrl string `yaml:"GeminiUrl"` + UserAgents []string `yaml:"UserAgents"` + } `yaml:"App"` + Server struct { + Host string `yaml:"Host"` + Port string `yaml:"Port"` + MaxFileSize int `yaml:"MaxFileSize"` + } `yaml:"Server"` + Proxy struct { + Protocol string `yaml:"Protocol"` + } `yaml:"Proxy"` +} diff --git a/model/gemini.go b/model/gemini.go new file mode 100644 index 0000000..b11d5f5 --- /dev/null +++ b/model/gemini.go @@ -0,0 +1,55 @@ +package model + +type GeminiData struct { + Contents []Contents `json:"contents"` +} + +type InlineData struct { + MimeType string `json:"mime_type"` + Data string `json:"data"` +} + +type Parts struct { + Text string `json:"text,omitempty"` + InlineData *InlineData `json:"inline_data,omitempty"` +} + +type Contents struct { + Parts []Parts `json:"parts"` +} + +type GeminiResponse struct { + Candidates []struct { + Content struct { + Parts []struct { + Text string `json:"text"` + } `json:"parts"` + Role string `json:"role"` + } `json:"content"` + FinishReason string `json:"finishReason"` + Index int `json:"index"` + SafetyRatings []struct { + Category string `json:"category"` + Probability string `json:"probability"` + } `json:"safetyRatings"` + } `json:"candidates"` + PromptFeedback struct { + SafetyRatings []struct { + Category string `json:"category"` + Probability string `json:"probability"` + } `json:"safetyRatings"` + } `json:"promptFeedback"` +} + +type GeminiProPart struct { + Text string `json:"text"` +} + +type GeminiProContent struct { + Role string `json:"role"` + Parts []GeminiProPart `json:"parts"` +} + +type GeminiPro struct { + Contents []GeminiProContent `json:"contents"` +} diff --git a/model/video.go b/model/video.go new file mode 100644 index 0000000..c46c7ff --- /dev/null +++ b/model/video.go @@ -0,0 +1,26 @@ +package model + +// FrameInfo 帧信息和base64编码 +type FrameInfo struct { + SegmentIndex int `json:"segment_index"` + FrameIndex int `json:"frame_index"` + Base64Data string `json:"base64_data"` +} + +// VideoInfo 视频的基本信息 +type VideoInfo struct { + Format struct { + Duration string `json:"duration"` // 视频时长 + } `json:"format"` +} + +type DYVideoInfo struct { + ItemList []struct { + Video struct { + PlayAddr struct { + Uri string `json:"uri"` + } `json:"play_addr"` + } `json:"video"` + Desc string `json:"desc"` + } `json:"item_list"` +} diff --git a/server/server.go b/server/server.go new file mode 100644 index 0000000..b638216 --- /dev/null +++ b/server/server.go @@ -0,0 +1,134 @@ +package server + +import ( + "github.com/gin-gonic/gin" + "log" + "net/http" + "strings" + "violet/api" +) + +func Test(ctx *gin.Context) { + ctx.JSON(http.StatusOK, gin.H{ + "message": "OK", + }) +} + +func Video2Text(ctx *gin.Context) { + var data map[string]string + + err := ctx.ShouldBind(&data) + if err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{ + "message": "error parsing JSON", + "error": err.Error(), + }) + + return + } + if len(data["url"]) == 0 { + ctx.JSON(http.StatusBadRequest, gin.H{ + "message": "参数为空", + "error": "error parsing JSON", + }) + + return + } + + var finalUrl, title string + if strings.Contains(data["url"], "douyin.com") { + f, t, err := api.GetDouYinInfo(data["url"]) + if err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{ + "message": "视频链接解析失败,请检查链接是否合法", + "error": err.Error(), + }) + + return + } + finalUrl = f + title = t + } else { + ctx.JSON(http.StatusBadRequest, gin.H{ + "message": "未知的视频链接,请检查链接是否合法", + "error": "The URL is wrong, please check", + }) + + return + } + + err, d := api.VideoSlice(finalUrl) + if err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{ + "message": "未知错误,请重试", + "error": err.Error(), + }) + + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "message": "success", + "data": gin.H{ + "url": finalUrl, + "title": title, + "content": d, + }, + }) + + log.Println("[Info] - done") +} + +func RemoveWatermark(ctx *gin.Context) { + var data map[string]string + + err := ctx.ShouldBind(&data) + if err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{ + "message": "error parsing JSON", + "error": err.Error(), + }) + + return + } + if len(data["url"]) == 0 { + ctx.JSON(http.StatusBadRequest, gin.H{ + "message": "参数为空", + "error": "error parsing JSON", + }) + + return + } + + var finalUrl, title string + if strings.Contains(data["url"], "douyin.com") { + f, t, err := api.GetDouYinInfo(data["url"]) + if err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{ + "message": "视频链接解析失败,请检查链接是否合法", + "error": err.Error(), + }) + + return + } + finalUrl = f + title = t + } else { + ctx.JSON(http.StatusBadRequest, gin.H{ + "message": "未知的视频链接,请检查链接是否合法", + "error": "The URL is wrong, please check", + }) + + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "message": "success", + "data": gin.H{ + "url": finalUrl, + "title": title, + }, + }) + + log.Println("[Info] - done") +} diff --git a/util/print.go b/util/print.go new file mode 100644 index 0000000..924db62 --- /dev/null +++ b/util/print.go @@ -0,0 +1,17 @@ +package util + +import "fmt" + +func P(h, p, v string) { + fmt.Printf(` + _ _ _ + (_) | | | | +__ ___ ___ | | ___| |_ +\ \ / / |/ _ \| |/ _ \ __| + \ V /| | (_) | | __/ |_ + \_/ |_|\___/|_|\___|\__| + +v%s +server started on %s:%s +`, v, h, p) +} diff --git a/version.go b/version.go new file mode 100644 index 0000000..b0e11c0 --- /dev/null +++ b/version.go @@ -0,0 +1,3 @@ +package main + +var Version = "1.0.0"