Files
llink/go/internal/media/helpers.go
T
Arjun Patel d262f734f0 Mobile notifications for iOS (#210)
* mobile: wire notification registration and listener

* implement backend components for push notifications

* refactor: agentic comment cleanup

* docs: use proper module name for particle processor

* set required env variables for push notifications

* bump version

* fix: always upsert push token on mobile start

* Revert "fix: always upsert push token on mobile start"

This reverts commit 90ff18a788.

* send push notifications regardless of online status
2026-05-18 12:44:31 -07:00

104 lines
2.4 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 media
import (
"bytes"
"context"
"errors"
"log/slog"
"os"
"os/exec"
"strings"
)
func IsIOSPlayableMime(mime string) bool {
switch mime {
case "video/mp4", "video/quicktime", "audio/mp4", "audio/aac", "audio/x-m4a", "audio/mpeg":
return true
}
return false
}
func IsAudio(mime string) bool {
return strings.HasPrefix(mime, "audio/")
}
type TranscodeInput struct {
SourceURL string
MimeType string
}
type TranscodeOutput struct {
TempLocalFilePath string
OutputMimeType string
OutputExt string // e.g. ".m4a" or ".mp4"
}
var (
ErrInvalidInput error = errors.New("invalid input")
)
// TranscodeToMp4 writes the result to a temp file; caller is responsible
// for deleting TempLocalFilePath.
func TranscodeToMp4(ctx context.Context, input TranscodeInput) (*TranscodeOutput, error) {
if input.SourceURL == "" || input.MimeType == "" {
return nil, ErrInvalidInput
}
isAudio := IsAudio(input.MimeType)
var outputExt, outputMime string
if isAudio {
outputExt = ".m4a"
outputMime = "audio/mp4"
} else {
outputExt = ".mp4"
outputMime = "video/mp4"
}
tmp, err := os.CreateTemp("", "transcode-*"+outputExt)
if err != nil {
slog.Error("transcode: failed to create temp file", "error", err)
return nil, err
}
tmpPath := tmp.Name()
tmp.Close()
var args []string
if isAudio {
args = []string{
"-y", "-i", input.SourceURL,
"-vn",
"-c:a", "aac", "-b:a", "128k",
"-movflags", "+faststart",
tmpPath,
}
} else {
// 1440p4K screen recordings + libx264's lookahead buffers can OOM the
// worker. Bound parallelism/lookahead and downscale to 1080p; the
// original WebM stays in GCS untouched.
args = []string{
"-y", "-i", input.SourceURL,
"-vf", "scale='min(1920,iw)':-2:flags=lanczos",
"-c:v", "libx264", "-preset", "veryfast", "-crf", "23",
"-pix_fmt", "yuv420p", "-profile:v", "baseline", "-level", "3.1",
"-x264-params", "rc-lookahead=20:ref=2",
"-threads", "2", "-filter_threads", "2",
"-c:a", "aac", "-b:a", "128k",
"-movflags", "+faststart",
tmpPath,
}
}
cmd := exec.CommandContext(ctx, "ffmpeg", args...)
var stderr bytes.Buffer
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
return nil, errors.Join(err, errors.New(stderr.String()))
}
return &TranscodeOutput{
TempLocalFilePath: tmpPath,
OutputMimeType: outputMime,
OutputExt: outputExt,
}, nil
}