thread-master/old/backend/internal/library/placement/resolve_media_exec.go

144 lines
4.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package placement
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
)
type resolveMediaInput struct {
Permalink string `json:"permalink"`
StorageState string `json:"storage_state,omitempty"`
}
type resolveMediaOutput struct {
MediaID string `json:"mediaId"`
}
// RunResolveMediaFromPermalink opens a Threads post page via Playwright and extracts numeric media id.
func RunResolveMediaFromPermalink(ctx context.Context, permalink, storageState string) (string, error) {
permalink = strings.TrimSpace(permalink)
if permalink == "" {
return "", fmt.Errorf("permalink is required")
}
if endpoint := strings.TrimSpace(os.Getenv("HAIXUN_RESOLVE_MEDIA_URL")); endpoint != "" {
return runHTTPResolveMedia(ctx, endpoint, permalink, storageState)
}
repoRoot, cliPath, err := resolveMediaCLI()
if err != nil {
return "", err
}
payload, err := json.Marshal(resolveMediaInput{
Permalink: permalink,
StorageState: strings.TrimSpace(storageState),
})
if err != nil {
return "", err
}
runCtx, cancel := context.WithTimeout(ctx, 90*time.Second)
defer cancel()
cmd := exec.CommandContext(runCtx, "npx", "tsx", cliPath)
cmd.Dir = repoRoot
cmd.Stdin = bytes.NewReader(payload)
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
msg := strings.TrimSpace(stderr.String())
if msg == "" {
msg = err.Error()
}
return "", fmt.Errorf("permalink media resolve failed: %s", msg)
}
var out resolveMediaOutput
if err := json.Unmarshal(stdout.Bytes(), &out); err != nil {
return "", fmt.Errorf("permalink media resolve output parse failed: %w", err)
}
mediaID := strings.TrimSpace(out.MediaID)
if mediaID == "" {
return "", fmt.Errorf("permalink media resolve returned empty media id")
}
return mediaID, nil
}
func runHTTPResolveMedia(ctx context.Context, endpoint, permalink, storageState string) (string, error) {
payload, err := json.Marshal(resolveMediaInput{Permalink: permalink, StorageState: strings.TrimSpace(storageState)})
if err != nil {
return "", err
}
runCtx, cancel := context.WithTimeout(ctx, 90*time.Second)
defer cancel()
url := strings.TrimRight(endpoint, "/") + "/resolve"
req, err := http.NewRequestWithContext(runCtx, http.MethodPost, url, bytes.NewReader(payload))
if err != nil {
return "", err
}
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
return "", fmt.Errorf("permalink media resolve service failed: %w", err)
}
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
if res.StatusCode < 200 || res.StatusCode >= 300 {
return "", fmt.Errorf("permalink media resolve service returned HTTP %d: %s", res.StatusCode, strings.TrimSpace(string(body)))
}
var out resolveMediaOutput
if err := json.Unmarshal(body, &out); err != nil {
return "", fmt.Errorf("permalink media resolve service output parse failed: %w", err)
}
mediaID := strings.TrimSpace(out.MediaID)
if mediaID == "" {
return "", fmt.Errorf("permalink media resolve service returned empty media id")
}
return mediaID, nil
}
func resolveMediaCLI() (repoRoot, cliPath string, err error) {
if root := strings.TrimSpace(os.Getenv("HAIXUN_REPO_ROOT")); root != "" {
cli := filepath.Join(root, "haixun-backend", "worker", "threads-resolve-media-cli.ts")
if fileExists(cli) {
return root, cli, nil
}
cli = filepath.Join(root, "worker", "threads-resolve-media-cli.ts")
if fileExists(cli) {
return root, cli, nil
}
}
cwd, err := os.Getwd()
if err != nil {
return "", "", fmt.Errorf("resolve media cli: %w", err)
}
dir := cwd
for i := 0; i < 6; i++ {
cli := filepath.Join(dir, "haixun-backend", "worker", "threads-resolve-media-cli.ts")
if fileExists(cli) {
return dir, cli, nil
}
cli = filepath.Join(dir, "worker", "threads-resolve-media-cli.ts")
if fileExists(cli) {
return dir, cli, nil
}
parent := filepath.Dir(dir)
if parent == dir {
break
}
dir = parent
}
return "", "", fmt.Errorf("找不到 threads-resolve-media-cli.ts請設定 HAIXUN_REPO_ROOT")
}