图生视频
This commit is contained in:
commit
69bdad7090
|
@ -0,0 +1,40 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type ANS string
|
||||||
|
|
||||||
|
const (
|
||||||
|
reset ANS = "\033[0m"
|
||||||
|
red ANS = "\033[31m"
|
||||||
|
green ANS = "\033[32m"
|
||||||
|
yellow ANS = "\033[33m"
|
||||||
|
blue ANS = "\033[34m"
|
||||||
|
purple ANS = "\033[35m"
|
||||||
|
cyan ANS = "\033[36m"
|
||||||
|
white ANS = "\033[37m"
|
||||||
|
)
|
||||||
|
|
||||||
|
func success(content string, arg ...interface{}) {
|
||||||
|
fmt.Printf("%s%s%s\n", green, fmt.Sprintf(content, arg...), reset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fatal(content string, arg ...interface{}) {
|
||||||
|
fmt.Printf("%s%s%s\n", red, fmt.Sprintf(content, arg...), reset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func warning(content string, arg ...interface{}) {
|
||||||
|
fmt.Printf("%s%s%s\n", yellow, fmt.Sprintf(content, arg...), reset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func log(content string, arg ...interface{}) {
|
||||||
|
fmt.Printf("%s\n", fmt.Sprintf(content, arg...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func input(content string, arg ...interface{}) {
|
||||||
|
fmt.Printf("\n%s%s%s", cyan, fmt.Sprintf(content, arg...), reset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func tip(content string, arg ...interface{}) {
|
||||||
|
fmt.Printf("\n*%s%s%s", purple, fmt.Sprintf(content, arg...), reset)
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
func padToLength(s string, length int, padChar rune) string {
|
||||||
|
if len(s) >= length {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
// 计算需要填充的字符数
|
||||||
|
padding := make([]rune, length-len(s))
|
||||||
|
for i := range padding {
|
||||||
|
padding[i] = padChar
|
||||||
|
}
|
||||||
|
// 将原字符串和填充字符组合起来
|
||||||
|
return s + string(padding)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加密函数
|
||||||
|
func encrypt(key []byte, text string) (string, error) {
|
||||||
|
plaintext := []byte(text)
|
||||||
|
|
||||||
|
// 创建一个新的cipher.Block
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建一个新的GCM模式的加密器
|
||||||
|
gcm, err := cipher.NewGCM(block)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建一个nonce(随机数)
|
||||||
|
nonce := make([]byte, gcm.NonceSize())
|
||||||
|
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加密数据
|
||||||
|
ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
|
||||||
|
|
||||||
|
// 返回Base64编码的加密字符串
|
||||||
|
return base64.StdEncoding.EncodeToString(ciphertext), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解密函数
|
||||||
|
func decrypt(key []byte, encryptedText string) (string, error) {
|
||||||
|
// 解码Base64字符串
|
||||||
|
ciphertext, err := base64.StdEncoding.DecodeString(encryptedText)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建一个新的cipher.Block
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建一个新的GCM模式的解密器
|
||||||
|
gcm, err := cipher.NewGCM(block)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分离nonce和实际加密数据
|
||||||
|
nonceSize := gcm.NonceSize()
|
||||||
|
if len(ciphertext) < nonceSize {
|
||||||
|
return "", errors.New("ciphertext too short")
|
||||||
|
}
|
||||||
|
|
||||||
|
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
|
||||||
|
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(plaintext), nil
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func isImage(contentType string) bool {
|
||||||
|
imageTypes := []string{
|
||||||
|
"image/jpeg",
|
||||||
|
"image/png",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, imageType := range imageTypes {
|
||||||
|
if strings.Contains(contentType, imageType) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isURL(s string) bool {
|
||||||
|
// 检查字符串是否为空
|
||||||
|
if len(s) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用url.Parse解析URL
|
||||||
|
u, err := url.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查URL的Scheme是否为空(即是否有协议,如http, https等)
|
||||||
|
if u.Scheme == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查URL的Host是否为空(即是否有主机名)
|
||||||
|
if u.Host == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果需要,可以进一步验证Scheme是否为允许的协议(如http, https)
|
||||||
|
// 例如:
|
||||||
|
// allowedSchemes := map[string]bool{"http": true, "https": true}
|
||||||
|
// if !allowedSchemes[u.Scheme] {
|
||||||
|
// return false
|
||||||
|
// }
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkURL checks if the URL is accessible and if the content is an image.
|
||||||
|
func checkURL(url string) (bool, error) {
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return false, fmt.Errorf("failed to fetch URL, status code: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
contentType := resp.Header.Get("Content-Type")
|
||||||
|
if contentType == "" {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return isImage(contentType), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isLocalPath(s string) bool {
|
||||||
|
return strings.HasPrefix(s, "/") || (len(s) >= 2 && s[1] == ':')
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
module ex_pic_to_video
|
||||||
|
|
||||||
|
go 1.23.7
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be
|
||||||
|
github.com/manifoldco/promptui v0.9.0
|
||||||
|
github.com/volcengine/volcengine-go-sdk v1.1.8
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
|
||||||
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
|
github.com/volcengine/volc-sdk-golang v1.0.23 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20200918174421-af09f7315aff // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.2.8 // indirect
|
||||||
|
)
|
|
@ -0,0 +1,239 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"github.com/manifoldco/promptui"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/common-nighthawk/go-figure"
|
||||||
|
"github.com/volcengine/volcengine-go-sdk/service/arkruntime"
|
||||||
|
"github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
|
||||||
|
"github.com/volcengine/volcengine-go-sdk/volcengine"
|
||||||
|
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
reader = bufio.NewReader(os.Stdin)
|
||||||
|
)
|
||||||
|
|
||||||
|
var statusMap = map[string]string{
|
||||||
|
"queued": "排队中",
|
||||||
|
"running": "任务运行中",
|
||||||
|
"cancelled": "取消任务",
|
||||||
|
"succeeded": "任务成功",
|
||||||
|
"failed": "任务失败",
|
||||||
|
}
|
||||||
|
|
||||||
|
type Conf struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Model string `json:"model"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Video struct {
|
||||||
|
Url string `json:"url"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func setVideoInfo() *Video {
|
||||||
|
var video = new(Video)
|
||||||
|
img(video)
|
||||||
|
desc(video)
|
||||||
|
return video
|
||||||
|
}
|
||||||
|
|
||||||
|
var c *Conf
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
log("正在读取配置。。。")
|
||||||
|
c = getConf()
|
||||||
|
success("读取配置成功!请务必保证网络通畅")
|
||||||
|
client := arkruntime.NewClientWithApiKey(c.Key)
|
||||||
|
video := setVideoInfo()
|
||||||
|
//finish(client, video)
|
||||||
|
do(client, c, video)
|
||||||
|
}
|
||||||
|
|
||||||
|
func do(client *arkruntime.Client, c *Conf, video *Video) {
|
||||||
|
ctx := context.Background()
|
||||||
|
createReq := model.CreateContentGenerationTaskRequest{
|
||||||
|
Model: c.Model,
|
||||||
|
Content: []*model.CreateContentGenerationContentItem{
|
||||||
|
{
|
||||||
|
// 文本提示词与参数组合
|
||||||
|
Type: model.ContentGenerationContentItemTypeText,
|
||||||
|
Text: volcengine.String(video.Text),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 图片URL
|
||||||
|
Type: model.ContentGenerationContentItemTypeImage,
|
||||||
|
ImageURL: &model.ImageURL{
|
||||||
|
URL: video.Url, //请上传可以访问的图片URL
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
createResponse, err := client.CreateContentGenerationTask(ctx, createReq)
|
||||||
|
if err != nil {
|
||||||
|
warning("创建视频失败: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
success("视频正在制作中,请稍后,taskId: %s", createResponse.ID)
|
||||||
|
listenTask(ctx, client, createResponse.ID, video)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listenTask(ctx context.Context, client *arkruntime.Client, taskId string, video *Video) {
|
||||||
|
var (
|
||||||
|
req = model.GetContentGenerationTaskRequest{taskId}
|
||||||
|
)
|
||||||
|
for {
|
||||||
|
time.Sleep(time.Second * 1)
|
||||||
|
resp, err := client.GetContentGenerationTask(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
warning("获取视频信息失败: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log(statusMap[resp.Status])
|
||||||
|
if resp.Status == "succeeded" {
|
||||||
|
tip("本次消耗token: %d\n", resp.Usage.TotalTokens)
|
||||||
|
success("请复制下面地址到浏览器进行下载:\n")
|
||||||
|
fmt.Println(resp.Content.VideoURL)
|
||||||
|
finish(client, video)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func finish(client *arkruntime.Client, video *Video) {
|
||||||
|
prompt := promptui.Select{
|
||||||
|
Label: "",
|
||||||
|
Items: []string{"退出", "重新生成", "修改设置重新生成"},
|
||||||
|
}
|
||||||
|
_, r, err := prompt.Run()
|
||||||
|
if err != nil {
|
||||||
|
warning("Prompt failed %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch r {
|
||||||
|
case "退出":
|
||||||
|
prompt1 := promptui.Select{
|
||||||
|
Label: "是否退出",
|
||||||
|
Items: []string{"否", "是"},
|
||||||
|
}
|
||||||
|
_, q, _err := prompt1.Run()
|
||||||
|
if _err != nil {
|
||||||
|
warning("Prompt failed %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if q == "是" {
|
||||||
|
tip("最后祝您身体健康,再见")
|
||||||
|
exit()
|
||||||
|
} else {
|
||||||
|
finish(client, video)
|
||||||
|
}
|
||||||
|
|
||||||
|
case "重新生成":
|
||||||
|
do(client, c, video)
|
||||||
|
case "修改设置重新生成":
|
||||||
|
video = setVideoInfo()
|
||||||
|
do(client, c, video)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConf() *Conf {
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
fp := fmt.Sprintf("%s/%s", wd, "ex_pic_to_video_conf")
|
||||||
|
if err != nil {
|
||||||
|
fatal("获取当前路径失败")
|
||||||
|
exit()
|
||||||
|
}
|
||||||
|
file, err := os.Open(fp)
|
||||||
|
if err != nil {
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
os.Remove(fp)
|
||||||
|
}
|
||||||
|
return cConf(fp)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
content, err := os.ReadFile(fp)
|
||||||
|
if err != nil {
|
||||||
|
fatal("获取配置信息失败")
|
||||||
|
exit()
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
pwd string
|
||||||
|
conf = new(Conf)
|
||||||
|
)
|
||||||
|
for {
|
||||||
|
input("请输入访问密码: ")
|
||||||
|
pwd, _ = reader.ReadString('\n')
|
||||||
|
cjson, err := decrypt([]byte(padToLength(strings.TrimSpace(pwd), 16, ' ')), string(content))
|
||||||
|
if err != nil {
|
||||||
|
warning("密码错误")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = json.Unmarshal([]byte(cjson), &conf)
|
||||||
|
if err != nil {
|
||||||
|
warning("配置读取失败")
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return conf
|
||||||
|
}
|
||||||
|
|
||||||
|
func cConf(fp string) *Conf {
|
||||||
|
conf := new(Conf)
|
||||||
|
log("进入初始化配置向导")
|
||||||
|
log("----------------------")
|
||||||
|
|
||||||
|
input("请设置key: ")
|
||||||
|
k, _ := reader.ReadString('\n')
|
||||||
|
conf.Key = strings.TrimSpace(k)
|
||||||
|
if conf.Key == "lsxd2025" {
|
||||||
|
conf.Key = ""
|
||||||
|
conf.Model = ""
|
||||||
|
myFigure := figure.NewFigure("LSXD", "", true)
|
||||||
|
myFigure.Print()
|
||||||
|
success("欢迎进入调试模式!")
|
||||||
|
return conf
|
||||||
|
}
|
||||||
|
|
||||||
|
input("请设置model: ")
|
||||||
|
m, _ := reader.ReadString('\n')
|
||||||
|
conf.Model = strings.TrimSpace(m)
|
||||||
|
|
||||||
|
var (
|
||||||
|
pwd string
|
||||||
|
)
|
||||||
|
for {
|
||||||
|
input("请设置访问密码(密码长度请小于16位): ")
|
||||||
|
pwd, _ = reader.ReadString('\n')
|
||||||
|
if len(strings.TrimSpace(pwd)) > 16 {
|
||||||
|
warning("密码长度请小于16位")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
cjson, _ := json.Marshal(conf)
|
||||||
|
pad := padToLength(strings.TrimSpace(pwd), 16, ' ')
|
||||||
|
en, e := encrypt([]byte(pad), string(cjson))
|
||||||
|
if e != nil {
|
||||||
|
fatal("设置失败,程序退出:%v", e)
|
||||||
|
exit()
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(fp, []byte(en), 0666); err != nil {
|
||||||
|
fatal("写入失败,程序退出")
|
||||||
|
exit()
|
||||||
|
}
|
||||||
|
return conf
|
||||||
|
}
|
||||||
|
|
||||||
|
func exit() {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
func test() {
|
||||||
|
//key = "236ba4b6-9daa-4755-b22f-2fd274cd223a"
|
||||||
|
//modelEp = "doubao-seedance-1-0-lite-i2v-250428"
|
||||||
|
//desc = "女孩抱着狐狸,女孩睁开眼,温柔地看向镜头,狐狸友善地抱着,镜头缓缓拉出,女孩的头发被风吹动 --resolution 480p --dur 5 --camerafixed false --watermark false"
|
||||||
|
//img = "https://ark-project.tos-cn-beijing.volces.com/doc_image/i2v_foxrgirl.png"
|
||||||
|
}
|
|
@ -0,0 +1,144 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"github.com/manifoldco/promptui"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func img(video *Video) {
|
||||||
|
tip("图片信息,可以是图片URL或图片在此电脑上的本地图片。")
|
||||||
|
tip("图片URL:请确保图片URL可被访问。eg:https://ark-project.tos-cn-beijing.volces.com/doc_image/i2v_foxrgirl.png")
|
||||||
|
tip("本地图片:请确保图片存在的jpg/jpge/png图片,路径名称请用英文,避免不必要的错误。eg:C:\\Users\\Administrator\\Desktop\\sucai\\fox.png")
|
||||||
|
for {
|
||||||
|
input("请设置图片信息: ")
|
||||||
|
m, _ := reader.ReadString('\n')
|
||||||
|
video.Url = strings.TrimSpace(m)
|
||||||
|
if isURL(video.Url) { //判断是否为URL
|
||||||
|
isImg, err := checkURL(video.Url)
|
||||||
|
if !isImg {
|
||||||
|
warning("URL内容不是图片,请检查URL是否正确!")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
warning("URL不可访问,请检查网络或URL是否正确!")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if isLocalPath(video.Url) {
|
||||||
|
fileExt := strings.ToLower(filepath.Ext(video.Url))
|
||||||
|
var mimeType string
|
||||||
|
switch fileExt {
|
||||||
|
case ".jpg", ".jpeg":
|
||||||
|
mimeType = "jpeg"
|
||||||
|
case ".png":
|
||||||
|
mimeType = "png"
|
||||||
|
default:
|
||||||
|
warning("不支持的图片格式: %s", fileExt)
|
||||||
|
}
|
||||||
|
content, err := os.ReadFile(video.Url)
|
||||||
|
if err != nil {
|
||||||
|
warning("无法读取文件: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// 将文件内容编码为Base64字符串
|
||||||
|
dataURL := fmt.Sprintf("data:image/%s;base64,%s", mimeType, base64.StdEncoding.EncodeToString(content))
|
||||||
|
video.Url = dataURL
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func desc(video *Video) {
|
||||||
|
var d strings.Builder
|
||||||
|
tip("描述信息,请输入一段描述文字,描述文字越多,生成视频越精彩。")
|
||||||
|
input("请设置描述信息: ")
|
||||||
|
text, _ := reader.ReadString('\n')
|
||||||
|
d.WriteString(fmt.Sprintf("%s ", strings.TrimSpace(text)))
|
||||||
|
d.WriteString("--watermark ")
|
||||||
|
d.WriteString("false ")
|
||||||
|
for {
|
||||||
|
prompt := promptui.Select{
|
||||||
|
Label: "分辨率: ",
|
||||||
|
Items: []string{"480p", "720p"},
|
||||||
|
}
|
||||||
|
_, r, err := prompt.Run()
|
||||||
|
if err != nil {
|
||||||
|
warning("Prompt failed %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
d.WriteString("--resolution ")
|
||||||
|
d.WriteString(r)
|
||||||
|
d.WriteString(" ")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
prompt := promptui.Select{
|
||||||
|
Label: "视频时长(秒): ",
|
||||||
|
Items: []string{"5", "10"},
|
||||||
|
}
|
||||||
|
_, r, err := prompt.Run()
|
||||||
|
if err != nil {
|
||||||
|
warning("Prompt failed %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
d.WriteString("--dur ")
|
||||||
|
d.WriteString(r)
|
||||||
|
d.WriteString(" ")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
tip("adaptive:根据所上传图片的比例,自动选择最合适的宽高比。")
|
||||||
|
for {
|
||||||
|
prompt := promptui.Select{
|
||||||
|
Label: "视频的宽高比例: ",
|
||||||
|
Items: []string{"adaptive", "16:9", "9:16", "4:3", "3:4", "21:9", "9:21", "1:1"},
|
||||||
|
}
|
||||||
|
_, r, err := prompt.Run()
|
||||||
|
if err != nil {
|
||||||
|
warning("Prompt failed %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
d.WriteString("--ratio ")
|
||||||
|
d.WriteString(r)
|
||||||
|
d.WriteString(" ")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
prompt := promptui.Select{
|
||||||
|
Label: "帧率: ",
|
||||||
|
Items: []string{"16", "24"},
|
||||||
|
}
|
||||||
|
_, r, err := prompt.Run()
|
||||||
|
if err != nil {
|
||||||
|
warning("Prompt failed %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
d.WriteString("--framepersecond ")
|
||||||
|
d.WriteString(r)
|
||||||
|
d.WriteString(" ")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
prompt := promptui.Select{
|
||||||
|
Label: "是否固定摄像头: ",
|
||||||
|
Items: []string{"false", "true"},
|
||||||
|
}
|
||||||
|
_, r, err := prompt.Run()
|
||||||
|
if err != nil {
|
||||||
|
warning("Prompt failed %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
d.WriteString("--camerafixed ")
|
||||||
|
d.WriteString(r)
|
||||||
|
d.WriteString(" ")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
video.Text = d.String()
|
||||||
|
}
|
Loading…
Reference in New Issue