feat: migrate serevices
Some checks failed
build quyun / Build (push) Failing after 2m50s

This commit is contained in:
2025-12-19 19:05:12 +08:00
parent 005585c53b
commit 557a641f41
71 changed files with 5626 additions and 280 deletions

View File

@@ -0,0 +1,69 @@
package utils
import (
"bufio"
"context"
"os/exec"
"github.com/go-pay/errgroup"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
// ExecCommand executes a command and streams its output in real-time
func ExecCommand(name string, args ...string) error {
log.Infof("Executing command: %s %v", name, args)
cmd := exec.Command(name, args...)
stdout, err := cmd.StdoutPipe()
if err != nil {
return errors.Wrap(err, "stdout pipe error")
}
stderr, err := cmd.StderrPipe()
if err != nil {
return errors.Wrap(err, "stderr pipe error")
}
if err := cmd.Start(); err != nil {
return errors.Wrap(err, "command start error")
}
var eg errgroup.Group
eg.Go(func(ctx context.Context) error {
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
log.Info(scanner.Text())
}
return nil
})
eg.Go(func(ctx context.Context) error {
scanner := bufio.NewScanner(stderr)
for scanner.Scan() {
log.Error(scanner.Text())
}
return nil
})
if err := cmd.Wait(); err != nil {
return errors.Wrap(err, "command wait error")
}
if err := eg.Wait(); err != nil {
return errors.Wrap(err, "command wait error")
}
return nil
}
// ExecCommandOutput executes a command and returns its output
func ExecCommandOutput(name string, args ...string) ([]byte, error) {
log.Infof("Executing command: %s %v", name, args)
cmd := exec.Command(name, args...)
output, err := cmd.Output()
if err != nil {
return nil, errors.Wrapf(err, "failed to execute command: %s", name)
}
return output, nil
}

View File

@@ -0,0 +1,62 @@
package utils
import (
"strconv"
"strings"
"github.com/pkg/errors"
)
func GetMediaDuration(path string) (int64, error) {
args := []string{
"-v", "error",
"-show_entries", "format=duration",
"-of", "default=noprint_wrappers=1:nokey=1",
path,
}
output, err := ExecCommandOutput("ffprobe", args...)
if err != nil {
return 0, errors.Wrap(err, "ffprobe error")
}
duration := strings.TrimSpace(string(output))
durationFloat, err := strconv.ParseFloat(duration, 64)
if err != nil {
return 0, errors.Wrap(err, "duration conversion error")
}
return int64(durationFloat), nil
}
func CutMedia(input, output string, start, end int64) error {
args := []string{
"-y",
"-hide_banner",
"-nostats",
"-v", "error",
"-ss", strconv.FormatInt(start, 10),
"-i", input,
"-t", strconv.FormatInt(end, 10),
"-c", "copy",
output,
}
return ExecCommand("ffmpeg", args...)
}
// GetFrameImageFromVideo extracts target time frame from a video file and saves it as an image.
func GetFrameImageFromVideo(input, output string, time int64) error {
args := []string{
"-y",
"-hide_banner",
"-nostats",
"-v", "error",
"-i", input,
"-ss", strconv.FormatInt(time, 10),
"-vframes", "1",
output,
}
return ExecCommand("ffmpeg", args...)
}

View File

@@ -0,0 +1,49 @@
package utils
import (
"path/filepath"
"testing"
"github.com/rogeecn/fabfile"
. "github.com/smartystreets/goconvey/convey"
)
func Test_GetMediaDuration(t *testing.T) {
Convey("test_get_media_duration", t, func() {
target := fabfile.MustFind("fixtures/input.mp4")
duration, err := GetMediaDuration(target)
So(err, ShouldBeNil)
t.Logf("duration is: %d", duration)
})
}
func Test_CutMedia(t *testing.T) {
Convey("test_cut_media", t, func() {
input := fabfile.MustFind("fixtures/input.mp4")
output := fabfile.MustFind("fixtures/output.mp4")
t.Logf("INPUT: %s", input)
t.Logf("OUTPUT: %s", output)
err := CutMedia(input, output, 0, 10)
So(err, ShouldBeNil)
// check output duration
duration, err := GetMediaDuration(output)
So(err, ShouldBeNil)
t.Logf("output duration is: %d", duration)
})
}
func Test_GetFrameImageFromVideo(t *testing.T) {
Convey("test_get_frame_image_from_video", t, func() {
input := fabfile.MustFind("fixtures/input.mp4")
output := filepath.Join(filepath.Dir(input), "output.jpg")
t.Logf("INPUT: %s", input)
t.Logf("OUTPUT: %s", output)
err := GetFrameImageFromVideo(input, output, 1)
So(err, ShouldBeNil)
})
}

View File

@@ -0,0 +1,19 @@
package utils
import (
"net/url"
"github.com/gofiber/fiber/v3"
)
func FullURI(ctx fiber.Ctx) string {
fullURL := string(ctx.Request().URI().FullURI())
u, err := url.Parse(fullURL)
if err != nil {
return ""
}
u.Scheme = ctx.Scheme()
u.Host = ctx.Host()
return u.String()
}

View File

@@ -0,0 +1,42 @@
package utils
import (
"crypto/md5"
"fmt"
"io"
"os"
)
// Compare file md5
func CompareFileMd5(file, md5 string) (bool, error) {
fileMd5, err := GetFileMd5(file)
if err != nil {
return false, err
}
return fileMd5 == md5, nil
}
// GetFileMd5
func GetFileMd5(file string) (string, error) {
f, err := os.Open(file)
if err != nil {
return "", err
}
defer f.Close()
h := md5.New()
if _, err := io.Copy(h, f); err != nil {
return "", err
}
return fmt.Sprintf("%x", h.Sum(nil)), nil
}
// GetFileSize
func GetFileSize(file string) (int64, error) {
fi, err := os.Stat(file)
if err != nil {
return 0, err
}
return fi.Size(), nil
}

View File

@@ -0,0 +1,45 @@
package utils
import "strings"
// FormatTitle
// Format the title of a media file by replacing spaces with underscores and removing special characters
func FormatTitle(title string) string {
// remove file ext from title
if strings.Contains(title, ".") {
title = strings.Split(title, ".")[0]
}
// replace all spaces with underscores
replacements := []string{
" ", "",
"!", "",
"@", "",
"#", "",
"$", "",
"%", "",
"^", "",
"&", "",
"*", "",
"(", "",
")", "",
"[", "【",
"]", "】",
"{", "《",
"}", "》",
":", "",
";", "",
"'", "",
"\"", "",
"<", "",
">", "",
",", "",
".", "",
"?", "",
}
replacer := strings.NewReplacer(replacements...)
title = replacer.Replace(title)
return title
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,11 @@
package utils
import "testing"
func TestNames_Random(t *testing.T) {
for i := 0; i < 10; i++ {
name := RandomNickname()
t.Logf("name: %s", name)
}
}