package logic import ( "context" "strings" "time" "go.uber.org/zap" infmedia "spark-server/internal/infrastructure/media" squareentity "spark-server/internal/module/square/entity" uploadentity "spark-server/internal/module/upload/entity" "spark-server/internal/pkg/reqctx" ) // ObjectStore writes compressed bot assets before square publish. type ObjectStore interface { Put(ctx context.Context, key string, body []byte, contentType string) error PutLarge(ctx context.Context, key string, body []byte, contentType string, maxBytes int) error } // BindMedia attaches optional compressor + object store for RSS enclosure images/videos. func (s *Service) BindMedia(comp *MediaCompressor, store ObjectStore, botCompress bool) { if s == nil { return } s.comp = comp s.store = store s.botCompress = botCompress } // attachMedia downloads/compresses optional enclosure and returns square Image refs (best-effort). func (s *Service) attachMedia(ctx context.Context, item FeedItem) []*squareentity.Image { if s == nil || !s.botCompress || s.comp == nil || s.store == nil { return nil } imgURL := strings.TrimSpace(item.ImageURL) if imgURL == "" { return nil } asset, err := s.comp.FetchImage(ctx, imgURL) if err != nil { s.log.Warn("squarebot compress image", zap.String("url", imgURL), zap.Error(err)) return nil } ext := extForMIME(asset.MIME) key := uploadentity.BuildObjectKey( uploadentity.PurposeSquareImage, 0, reqctx.NewObjectName(), time.Now().UTC().Format("20060102"), ext, ) maxBytes := int(asset.Size) + 1 if maxBytes < 1<<20 { maxBytes = 1 << 20 } if err := s.store.PutLarge(ctx, key, asset.Data, asset.MIME, maxBytes); err != nil { s.log.Warn("squarebot put image", zap.String("key", key), zap.Error(err)) return nil } return []*squareentity.Image{{ Key: key, Mime: asset.MIME, Width: asset.Width, Height: asset.Height, Size: asset.Size, }} } // PrepareVideoAsset compresses a remote video and uploads it (bot video paths; requires ffmpeg). func (s *Service) PrepareVideoAsset(ctx context.Context, videoURL string) (*infmedia.BotAsset, string, error) { if s == nil || !s.botCompress || s.comp == nil || s.store == nil { return nil, "", nil } asset, err := s.comp.FetchVideo(ctx, videoURL) if err != nil { return nil, "", err } key := uploadentity.BuildObjectKey( uploadentity.PurposeSquareVideo, 0, reqctx.NewObjectName(), time.Now().UTC().Format("20060102"), "mp4", ) if err := s.store.PutLarge(ctx, key, asset.Data, asset.MIME, int(asset.Size)+1); err != nil { return nil, "", err } return asset, key, nil } func extForMIME(mime string) string { switch strings.ToLower(strings.TrimSpace(mime)) { case "image/png": return "png" case "image/gif": return "gif" case "image/webp": return "webp" default: return "jpg" } }