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

server: new kara webhook #97

Merged
merged 16 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions server/karaenv.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ type KaraberusConfig struct {
Dakara KaraberusDakaraConfig `env_prefix:"DAKARA"`
Mugen KaraberusMugenConfig `env_prefix:"MUGEN"`
UIDistDir string `envkey:"UI_DIST_DIR" default:"/usr/share/karaberus/ui_dist"`
Webhooks []string `envkey:"WEBHOOKS" separator:" " example:"discord=<url1> discord=<url2> json=<url3>"`
}

func getEnvDefault(name string, defaultValue string) string {
Expand Down Expand Up @@ -143,9 +144,13 @@ func setConfigValue(config_value reflect.Value, config_type reflect.Type, prefix
switch field_type.Type {
case reflect.TypeOf([]string{}):
value := getFieldValue(field_type, prefix)
sep := field_type.Tag.Get("separator")
arrval := strings.Split(value, sep)
field.Set(reflect.ValueOf(arrval))
if value == "" {
field.Set(reflect.ValueOf([]string{}))
} else {
sep := field_type.Tag.Get("separator")
arrval := strings.Split(value, sep)
field.Set(reflect.ValueOf(arrval))
}
case reflect.TypeOf(""):
field.SetString(getFieldValue(field_type, prefix))
case reflect.TypeOf(0):
Expand Down
1 change: 1 addition & 0 deletions server/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ karaberus_server_files = files(
'token.go',
'upload.go',
'user.go',
'webhooks.go',
)

karaberus_server_tests = files('karaberus_test.go')
Expand Down
47 changes: 33 additions & 14 deletions server/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,16 @@ func CurrentKaras(tx *gorm.DB) *gorm.DB {
return tx.Where("current_kara_info_id IS NULL")
}

type NewKaraUpdate struct{}

func WithNewKaraUpdate(tx *gorm.DB) *gorm.DB {
return tx.WithContext(context.WithValue(tx.Statement.Context, NewKaraUpdate{}, true))
}

func isNewKaraUpdate(tx *gorm.DB) bool {
return tx.Statement.Context.Value(NewKaraUpdate{}) != nil
}

type UpdateAssociations struct{}

func WithAssociationsUpdate(tx *gorm.DB) *gorm.DB {
Expand Down Expand Up @@ -417,13 +427,29 @@ func (ki *KaraInfoDB) AfterUpdate(tx *gorm.DB) error {
return err
}

if ki.CurrentKaraInfoID == nil && CONFIG.Dakara.BaseURL != "" && ki.UploadInfo.VideoUploaded && ki.UploadInfo.SubtitlesUploaded {
SyncDakaraNotify()
}
if !ki.KaraokeCreationTime.IsZero() {
err = UploadHookGitlab(tx, ki)
if err != nil {
return err
if ki.CurrentKaraInfoID == nil {
if CONFIG.Dakara.BaseURL != "" && ki.UploadInfo.VideoUploaded && ki.UploadInfo.SubtitlesUploaded {
SyncDakaraNotify()
}

if isNewKaraUpdate(tx) {
err = UploadHookGitlab(tx, ki)
if err != nil {
return err
}

// ignore imported karas
mugen_import := &MugenImport{}
err := tx.Where(&MugenImport{KaraID: ki.ID}).First(mugen_import).Error
if err == nil {
// kara was imported
return nil
}
if !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}

go PostWebhooks(*ki)
}
}
return nil
Expand All @@ -439,13 +465,6 @@ func (ki *KaraInfoDB) BeforeUpdate(tx *gorm.DB) error {
return err
}

// check for unix time 0 is for older karaokes, because we also used
// that at some point
if ki.VideoUploaded && ki.SubtitlesUploaded &&
ki.KaraokeCreationTime.IsZero() || ki.KaraokeCreationTime.Unix() == 0 {
ki.KaraokeCreationTime = time.Now().UTC()
}

// create historic entry with the current value
orig_kara_info.ID = 0
orig_kara_info.CurrentKaraInfo = ki
Expand Down
27 changes: 24 additions & 3 deletions server/s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,30 @@ func SaveFileToS3WithMetadata(ctx context.Context, tx *gorm.DB, fd io.Reader, ka
return nil, err
}

err = updateKaraokeAfterUpload(tx, kara, type_directory, filesize, crc32)
if err != nil {
return nil, err
currentTime := time.Now().UTC()
switch type_directory {
case "video":
kara.VideoUploaded = true
kara.VideoModTime = currentTime
kara.VideoSize = filesize
kara.VideoCRC32 = crc32
case "inst":
kara.InstrumentalUploaded = true
kara.InstrumentalModTime = currentTime
kara.InstrumentalSize = filesize
kara.InstrumentalCRC32 = crc32
case "sub":
kara.SubtitlesUploaded = true
kara.SubtitlesModTime = currentTime
kara.SubtitlesSize = filesize
kara.SubtitlesCRC32 = crc32
}
// check for unix time 0 is for older karaokes, because we also used
// that at some point
if kara.VideoUploaded && kara.SubtitlesUploaded &&
kara.KaraokeCreationTime.IsZero() || kara.KaraokeCreationTime.Unix() == 0 {
kara.KaraokeCreationTime = currentTime
tx = WithNewKaraUpdate(tx)
}

res, err := CheckKara(ctx, *kara)
Expand Down
26 changes: 0 additions & 26 deletions server/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
"os"
"strconv"
"strings"
"time"

"github.com/danielgtaylor/huma/v2"
"github.com/gofiber/fiber/v2"
Expand Down Expand Up @@ -151,31 +150,6 @@ type UploadOutput struct {
}
}

func updateKaraokeAfterUpload(tx *gorm.DB, kara *KaraInfoDB, filetype string, filesize int64, crc32 uint32) error {
currentTime := time.Now().UTC()
switch filetype {
case "video":
kara.VideoUploaded = true
kara.VideoModTime = currentTime
kara.VideoSize = filesize
kara.VideoCRC32 = crc32
return nil
case "inst":
kara.InstrumentalUploaded = true
kara.InstrumentalModTime = currentTime
kara.InstrumentalSize = filesize
kara.InstrumentalCRC32 = crc32
return nil
case "sub":
kara.SubtitlesUploaded = true
kara.SubtitlesModTime = currentTime
kara.SubtitlesSize = filesize
kara.SubtitlesCRC32 = crc32
return nil
}
return errors.New("Unknown file type " + filetype)
}

func UploadKaraFile(ctx context.Context, input *UploadInput) (*UploadOutput, error) {
db := GetDB(ctx)
var err error
Expand Down
123 changes: 123 additions & 0 deletions server/webhooks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package server

import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"strings"
)

type Webhook struct {
Type string
URL string
}

type WebhookTemplateContext struct {
Kara KaraInfoDB
Server string
Title string
Description string
Resource string
}

func parseWebhooksConfig() []Webhook {
var webhooks []Webhook
for _, kv := range CONFIG.Webhooks {
typ, url, found := strings.Cut(kv, "=")
if !found {
getLogger().Printf("invalid webhook value: %s", kv)
continue
}
webhooks = append(webhooks, Webhook{typ, url})
}
return webhooks
}

func PostWebhooks(kara KaraInfoDB) {
title := kara.FriendlyName()
desc, err := karaDescription(kara)
if err != nil {
getLogger().Printf("error generating description for webhooks: %s", err)
return
}

tmplCtx := WebhookTemplateContext{
Kara: kara,
Server: CONFIG.Listen.Addr(),
Title: title,
Description: desc,
Resource: fmt.Sprintf("%s/karaoke/browse/%d", CONFIG.Listen.Addr(), kara.ID),
}

for _, webhook := range parseWebhooksConfig() {
var err error
switch webhook.Type {
case "json":
err = postJsonWebhook(webhook.URL, tmplCtx)
case "discord":
err = postDiscordWebhook(webhook.URL, tmplCtx)
default:
err = fmt.Errorf("unknown webhook type %s", webhook.Type)
}
if err != nil {
getLogger().Printf("error during %s webhook: %s", webhook, err)
}
}
}

func postJsonWebhook(url string, tmplCtx WebhookTemplateContext) error {
b, err := json.Marshal(tmplCtx)
if err != nil {
return err
}
resp, err := http.Post(url, "application/json", bytes.NewReader(b))
if err != nil {
return err
}
defer Closer(resp.Body)
return nil
}

type DiscordEmbedAuthor struct {
Name string `json:"name"`
IconURL string `json:"icon_url"`
}

type DiscordEmbed struct {
Author DiscordEmbedAuthor `json:"author,omitempty"`
Title string `json:"title"`
URL string `json:"url"`
Description string `json:"description"`
Color uint `json:"color"`
}

type DiscordWebhook struct {
Embeds []DiscordEmbed `json:"embeds"`
}

func postDiscordWebhook(url string, tmplCtx WebhookTemplateContext) error {
webhook_data := DiscordWebhook{
Embeds: []DiscordEmbed{DiscordEmbed{
Author: DiscordEmbedAuthor{
Name: "New Karaoke!",
IconURL: fmt.Sprintf("%s/vite.svg", tmplCtx.Server),
},
Title: tmplCtx.Title,
URL: tmplCtx.Resource,
Description: tmplCtx.Description,
Color: 10053324,
}},
}

body, err := json.Marshal(webhook_data)
if err != nil {
return err
}
resp, err := http.Post(url, "application/json", bytes.NewBuffer(body))
if err != nil {
return err
}
defer Closer(resp.Body)
return nil
}
Loading