Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] support processing of (many) more media types #3090

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
2d0a4d0
initial work replacing our media decoding / encoding pipeline with ff…
NyaaaWhatsUpDoc Jul 9, 2024
49f30df
specify the video codec to use when generating static image from emoji
NyaaaWhatsUpDoc Jul 9, 2024
c6efd4e
update go-storage library (fixes incompatibility after updating go-io…
NyaaaWhatsUpDoc Jul 9, 2024
5edebe4
maintain image aspect ratio when generating a thumbnail for it
NyaaaWhatsUpDoc Jul 9, 2024
1222f1b
update readme to show go-ffmpreg
NyaaaWhatsUpDoc Jul 9, 2024
a91dd7d
fix a bunch of media tests, move filesize checking to callers of medi…
NyaaaWhatsUpDoc Jul 10, 2024
c6a4fcf
remove extra debug from error message
NyaaaWhatsUpDoc Jul 10, 2024
105b099
fix up incorrect function signatures
NyaaaWhatsUpDoc Jul 11, 2024
fa12109
update PutFile to just use regular file copy, as changes are file is …
NyaaaWhatsUpDoc Jul 11, 2024
f8b4a6a
fix remaining tests, remove some unneeded tests now we're working wit…
NyaaaWhatsUpDoc Jul 11, 2024
f8bf305
update more tests, add more code comments
NyaaaWhatsUpDoc Jul 11, 2024
1c06cc5
add utilities to generate processed emoji / media outputs
NyaaaWhatsUpDoc Jul 11, 2024
dbab56a
fix remaining tests
NyaaaWhatsUpDoc Jul 11, 2024
b9042ba
add test for opus media file, add license header to utility cmds
NyaaaWhatsUpDoc Jul 11, 2024
3670c64
limit the number of concurrently available ffmpeg / ffprobe instances
NyaaaWhatsUpDoc Jul 11, 2024
6bbf007
reduce number of instances
NyaaaWhatsUpDoc Jul 11, 2024
1f40ee7
further reduce number of instances
NyaaaWhatsUpDoc Jul 12, 2024
d456d85
fix envparsing test with configuration variables
NyaaaWhatsUpDoc Jul 12, 2024
651c8c9
update docs and configuration with new media-{local,remote}-max-size …
NyaaaWhatsUpDoc Jul 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ The following open source libraries, frameworks, and tools are used by GoToSocia
- [gruf/go-debug](https://codeberg.org/gruf/go-debug); debug build tag. [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-errors](https://codeberg.org/gruf/go-errors); context-like error w/ value wrapping [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-fastcopy](https://codeberg.org/gruf/go-fastcopy); performant (buffer pooled) I/O copying [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-ffmpreg](https://codeberg.org/gruf/go-ffmpreg); embedded ffmpeg / ffprobe WASM binaries [GPL-3.0 License](https://spdx.org/licenses/GPL-3.0-only.html).
- [gruf/go-kv](https://codeberg.org/gruf/go-kv); log field formatting. [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-list](https://codeberg.org/gruf/go-list); generic doubly linked list. [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-mutexes](https://codeberg.org/gruf/go-mutexes); safemutex & mutex map. [MIT License](https://spdx.org/licenses/MIT.html).
Expand Down
47 changes: 39 additions & 8 deletions cmd/gotosocial/action/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,22 @@ import (
"net/http"
"os"
"os/signal"
"runtime"
"strings"
"syscall"
"time"

"github.com/KimMachineGun/automemlimit/memlimit"
"github.com/gin-gonic/gin"
"github.com/ncruces/go-sqlite3"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
"github.com/superseriousbusiness/gotosocial/internal/api"
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/cleaner"
"github.com/superseriousbusiness/gotosocial/internal/filter/spam"
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/media/ffmpeg"
"github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/metrics"
"github.com/superseriousbusiness/gotosocial/internal/middleware"
Expand Down Expand Up @@ -66,14 +69,15 @@ import (

// Start creates and starts a gotosocial server
var Start action.GTSAction = func(ctx context.Context) error {
if _, err := maxprocs.Set(maxprocs.Logger(nil)); err != nil {
log.Warnf(ctx, "could not set CPU limits from cgroup: %s", err)
}

if _, err := memlimit.SetGoMemLimitWithOpts(); err != nil {
if !strings.Contains(err.Error(), "cgroup mountpoint does not exist") {
log.Warnf(ctx, "could not set Memory limits from cgroup: %s", err)
}
// Set GOMAXPROCS / GOMEMLIMIT
// to match container limits.
setLimits(ctx)

// Compile WASM modules ahead of first use
// to prevent unexpected initial slowdowns.
log.Info(ctx, "precompiling WebAssembly")
if err := precompileWASM(ctx); err != nil {
return err
}

var (
Expand Down Expand Up @@ -429,3 +433,30 @@ var Start action.GTSAction = func(ctx context.Context) error {

return nil
}

func setLimits(ctx context.Context) {
if _, err := maxprocs.Set(maxprocs.Logger(nil)); err != nil {
log.Warnf(ctx, "could not set CPU limits from cgroup: %s", err)
}

if _, err := memlimit.SetGoMemLimitWithOpts(); err != nil {
if !strings.Contains(err.Error(), "cgroup mountpoint does not exist") {
log.Warnf(ctx, "could not set Memory limits from cgroup: %s", err)
}
}
}

func precompileWASM(ctx context.Context) error {
// TODO: make max number instances configurable
maxprocs := runtime.GOMAXPROCS(0)
if err := sqlite3.Initialize(); err != nil {
return gtserror.Newf("error compiling sqlite3: %w", err)
}
if err := ffmpeg.InitFfmpeg(ctx, maxprocs); err != nil {
return gtserror.Newf("error compiling ffmpeg: %w", err)
}
if err := ffmpeg.InitFfprobe(ctx, maxprocs); err != nil {
return gtserror.Newf("error compiling ffprobe: %w", err)
}
return nil
}
122 changes: 122 additions & 0 deletions cmd/process-emoji/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// GoToSocial
// Copyright (C) GoToSocial Authors [email protected]
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package main

import (
"context"
"io"
"os"
"os/signal"
"syscall"

"codeberg.org/gruf/go-storage/memory"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/media"
"github.com/superseriousbusiness/gotosocial/internal/state"
"github.com/superseriousbusiness/gotosocial/internal/storage"
"github.com/superseriousbusiness/gotosocial/internal/util"
)

func main() {
ctx := context.Background()
ctx, cncl := signal.NotifyContext(ctx, syscall.SIGTERM, syscall.SIGINT)
defer cncl()

if len(os.Args) != 3 {
log.Panic(ctx, "Usage: go run ./cmd/process-emoji <input-file> <output-static>")
}

var st storage.Driver
st.Storage = memory.Open(10, true)

var state state.State
state.Storage = &st

state.Caches.Init()

var err error

config.SetHost("example.com")
config.SetStorageBackend("disk")
config.SetStorageLocalBasePath("/tmp/gotosocial")
config.SetDbType("sqlite")
config.SetDbAddress(":memory:")

state.DB, err = bundb.NewBunDBService(ctx, &state)
if err != nil {
log.Panic(ctx, err)
}

if err := state.DB.CreateInstanceAccount(ctx); err != nil {
log.Panicf(ctx, "error creating instance account: %s", err)
}

if err := state.DB.CreateInstanceInstance(ctx); err != nil {
log.Panicf(ctx, "error creating instance instance: %s", err)
}

if err := state.DB.CreateInstanceApplication(ctx); err != nil {
log.Panicf(ctx, "error creating instance application: %s", err)
}

mgr := media.NewManager(&state)

processing, err := mgr.CreateEmoji(ctx,
"emoji",
"example.com",
func(ctx context.Context) (reader io.ReadCloser, err error) {
return os.Open(os.Args[1])
},
media.AdditionalEmojiInfo{
URI: util.Ptr("example.com/emoji"),
},
)
if err != nil {
log.Panic(ctx, err)
}

emoji, err := processing.Load(ctx)
if err != nil {
log.Panic(ctx, err)
}

copyFile(ctx, &st, emoji.ImageStaticPath, os.Args[2])
}

func copyFile(ctx context.Context, st *storage.Driver, key string, path string) {
rc, err := st.GetStream(ctx, key)
if err != nil {
log.Panic(ctx, err)
}
defer rc.Close()

_ = os.Remove(path)

output, err := os.Create(path)
if err != nil {
log.Panic(ctx, err)
}
defer output.Close()

_, err = io.Copy(output, rc)
if err != nil {
log.Panic(ctx, err)
}
}
124 changes: 124 additions & 0 deletions cmd/process-media/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// GoToSocial
// Copyright (C) GoToSocial Authors [email protected]
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package main

import (
"context"
"io"
"os"
"os/signal"
"syscall"

"codeberg.org/gruf/go-storage/memory"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/media"
"github.com/superseriousbusiness/gotosocial/internal/state"
"github.com/superseriousbusiness/gotosocial/internal/storage"
)

func main() {
ctx := context.Background()
ctx, cncl := signal.NotifyContext(ctx, syscall.SIGTERM, syscall.SIGINT)
defer cncl()

if len(os.Args) != 4 {
log.Panic(ctx, "Usage: go run ./cmd/process-media <input-file> <output-processed> <output-thumbnail>")
}

var st storage.Driver
st.Storage = memory.Open(10, true)

var state state.State
state.Storage = &st

state.Caches.Init()

var err error

config.SetHost("example.com")
config.SetStorageBackend("disk")
config.SetStorageLocalBasePath("/tmp/gotosocial")
config.SetDbType("sqlite")
config.SetDbAddress(":memory:")

state.DB, err = bundb.NewBunDBService(ctx, &state)
if err != nil {
log.Panic(ctx, err)
}

if err := state.DB.CreateInstanceAccount(ctx); err != nil {
log.Panicf(ctx, "error creating instance account: %s", err)
}

if err := state.DB.CreateInstanceInstance(ctx); err != nil {
log.Panicf(ctx, "error creating instance instance: %s", err)
}

if err := state.DB.CreateInstanceApplication(ctx); err != nil {
log.Panicf(ctx, "error creating instance application: %s", err)
}

account, err := state.DB.GetInstanceAccount(ctx, "")
if err != nil {
log.Panic(ctx, err)
}

mgr := media.NewManager(&state)

processing, err := mgr.CreateMedia(ctx,
account.ID,
func(ctx context.Context) (reader io.ReadCloser, err error) {
return os.Open(os.Args[1])
},
media.AdditionalMediaInfo{},
)
if err != nil {
log.Panic(ctx, err)
}

media, err := processing.Load(ctx)
if err != nil {
log.Panic(ctx, err)
}

copyFile(ctx, &st, media.File.Path, os.Args[2])
copyFile(ctx, &st, media.Thumbnail.Path, os.Args[3])
}

func copyFile(ctx context.Context, st *storage.Driver, key string, path string) {
rc, err := st.GetStream(ctx, key)
if err != nil {
log.Panic(ctx, err)
}
defer rc.Close()

_ = os.Remove(path)

output, err := os.Create(path)
if err != nil {
log.Panic(ctx, err)
}
defer output.Close()

_, err = io.Copy(output, rc)
if err != nil {
log.Panic(ctx, err)
}
}
17 changes: 8 additions & 9 deletions docs/configuration/media.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,24 @@
##### MEDIA CONFIG #####
########################

# Config pertaining to media uploads (videos, image, image descriptions, emoji).
# Config pertaining to media uploads (media, image descriptions, emoji).

# Size. Maximum allowed image upload size in bytes.
# Size. Max size in bytes of media uploads via API.
#
# Raising this limit may cause other servers to not fetch media
# attached to a post.
#
# Examples: [2097152, 10485760, 10MB, 10MiB]
# Default: 10MiB (10485760 bytes)
media-image-max-size: 10MiB
# Examples: [2097152, 10485760, 40MB, 40MiB]
# Default: 40MiB (41943040 bytes)
media-local-max-size: 40MiB

# Size. Maximum allowed video upload size in bytes.
# Size. Max size in bytes of media to download from other instances.
#
# Raising this limit may cause other servers to not fetch media
# attached to a post.
# Lowering this limit may cause your instance not to fetch post media.
#
# Examples: [2097152, 10485760, 40MB, 40MiB]
# Default: 40MiB (41943040 bytes)
media-video-max-size: 40MiB
media-remote-max-size: 40MiB

# Int. Minimum amount of characters required as an image or video description.
# Examples: [500, 1000, 1500]
Expand Down
Loading