backend-ci.yml's `test -z "$(gofmt -l .)"` strict gate (added in
13c21ac11) failed on a backlog of unformatted files. None of the
85 files in this commit had been edited since the gate was added
because no push touched veza-backend-api/** in between, so the
gate never fired until today's CI fixes triggered it.
The diff is exclusively whitespace alignment in struct literals
and trailing-space comments. `go build ./...` and the full test
suite (with VEZA_SKIP_INTEGRATION=1 -short) pass identically.
181 lines
7 KiB
Go
181 lines
7 KiB
Go
package distribution
|
|
|
|
import (
|
|
"encoding/json"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// DistributionStatus represents the overall status of a distribution submission
|
|
type DistributionStatus string
|
|
|
|
const (
|
|
StatusSubmitted DistributionStatus = "submitted"
|
|
StatusProcessing DistributionStatus = "processing"
|
|
StatusLive DistributionStatus = "live"
|
|
StatusRejected DistributionStatus = "rejected"
|
|
StatusRemoved DistributionStatus = "removed"
|
|
StatusFailed DistributionStatus = "failed"
|
|
)
|
|
|
|
// Platform represents a streaming platform
|
|
type Platform string
|
|
|
|
const (
|
|
PlatformSpotify Platform = "spotify"
|
|
PlatformAppleMusic Platform = "apple_music"
|
|
PlatformDeezer Platform = "deezer"
|
|
)
|
|
|
|
// AllPlatforms returns the list of supported platforms
|
|
func AllPlatforms() []Platform {
|
|
return []Platform{PlatformSpotify, PlatformAppleMusic, PlatformDeezer}
|
|
}
|
|
|
|
// ImportStatus represents the status of a royalty import
|
|
type ImportStatus string
|
|
|
|
const (
|
|
ImportPending ImportStatus = "pending"
|
|
ImportCompleted ImportStatus = "completed"
|
|
ImportFailed ImportStatus = "failed"
|
|
ImportPartial ImportStatus = "partial"
|
|
)
|
|
|
|
// PlatformStatus represents the distribution status on a single platform
|
|
type PlatformStatus struct {
|
|
Status string `json:"status"`
|
|
URL string `json:"url,omitempty"`
|
|
LiveDate *string `json:"live_date,omitempty"`
|
|
Error *string `json:"error,omitempty"`
|
|
}
|
|
|
|
// DistributionMetadata holds the metadata snapshot for a distribution submission
|
|
type DistributionMetadata struct {
|
|
ArtistName string `json:"artist_name"`
|
|
AlbumName string `json:"album_name"`
|
|
Genre string `json:"genre"`
|
|
ReleaseDate string `json:"release_date"`
|
|
FeaturingArtists []string `json:"featuring_artists,omitempty"`
|
|
TrackTitle string `json:"track_title"`
|
|
UPC string `json:"upc,omitempty"`
|
|
ISRC string `json:"isrc,omitempty"`
|
|
}
|
|
|
|
// TrackDistribution represents a distribution submission for a track
|
|
type TrackDistribution struct {
|
|
ID uuid.UUID `gorm:"type:uuid;primaryKey" json:"id"`
|
|
TrackID uuid.UUID `gorm:"type:uuid;not null" json:"track_id"`
|
|
CreatorID uuid.UUID `gorm:"type:uuid;not null" json:"creator_id"`
|
|
Distributor string `gorm:"not null;size:50;default:'distrokid'" json:"distributor"`
|
|
SubmissionID string `gorm:"not null;size:255;uniqueIndex" json:"submission_id"`
|
|
SubmissionUPC string `gorm:"size:50" json:"submission_upc,omitempty"`
|
|
SubmissionISRC string `gorm:"size:50" json:"submission_isrc,omitempty"`
|
|
Metadata json.RawMessage `gorm:"type:jsonb;not null;default:'{}'" json:"metadata"`
|
|
CoverFilePath string `gorm:"size:512" json:"cover_file_path,omitempty"`
|
|
OverallStatus DistributionStatus `gorm:"not null;size:50;default:'submitted'" json:"overall_status"`
|
|
PlatformStatuses json.RawMessage `gorm:"type:jsonb;not null;default:'{}'" json:"platform_statuses"`
|
|
|
|
SubmittedAt time.Time `gorm:"not null" json:"submitted_at"`
|
|
FirstLiveAt *time.Time `json:"first_live_at,omitempty"`
|
|
LastStatusCheckAt *time.Time `json:"last_status_check_at,omitempty"`
|
|
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
|
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
|
}
|
|
|
|
func (TrackDistribution) TableName() string {
|
|
return "track_distributions"
|
|
}
|
|
|
|
func (d *TrackDistribution) BeforeCreate(tx *gorm.DB) error {
|
|
if d.ID == uuid.Nil {
|
|
d.ID = uuid.New()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetPlatformStatuses unmarshals and returns the platform statuses map
|
|
func (d *TrackDistribution) GetPlatformStatuses() (map[Platform]PlatformStatus, error) {
|
|
result := make(map[Platform]PlatformStatus)
|
|
if len(d.PlatformStatuses) == 0 {
|
|
return result, nil
|
|
}
|
|
err := json.Unmarshal(d.PlatformStatuses, &result)
|
|
return result, err
|
|
}
|
|
|
|
// SetPlatformStatuses marshals and stores the platform statuses map
|
|
func (d *TrackDistribution) SetPlatformStatuses(statuses map[Platform]PlatformStatus) error {
|
|
data, err := json.Marshal(statuses)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
d.PlatformStatuses = data
|
|
return nil
|
|
}
|
|
|
|
// GetMetadata unmarshals the metadata field
|
|
func (d *TrackDistribution) GetMetadata() (*DistributionMetadata, error) {
|
|
var meta DistributionMetadata
|
|
if len(d.Metadata) == 0 {
|
|
return &meta, nil
|
|
}
|
|
err := json.Unmarshal(d.Metadata, &meta)
|
|
return &meta, err
|
|
}
|
|
|
|
// StatusHistoryEntry represents a status change event
|
|
type StatusHistoryEntry struct {
|
|
ID uuid.UUID `gorm:"type:uuid;primaryKey" json:"id"`
|
|
DistributionID uuid.UUID `gorm:"type:uuid;not null" json:"distribution_id"`
|
|
Platform string `gorm:"not null;size:50" json:"platform"`
|
|
OldStatus string `gorm:"size:50" json:"old_status,omitempty"`
|
|
NewStatus string `gorm:"not null;size:50" json:"new_status"`
|
|
Note string `gorm:"type:text" json:"note,omitempty"`
|
|
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
|
}
|
|
|
|
func (StatusHistoryEntry) TableName() string {
|
|
return "track_distribution_status_history"
|
|
}
|
|
|
|
func (e *StatusHistoryEntry) BeforeCreate(tx *gorm.DB) error {
|
|
if e.ID == uuid.Nil {
|
|
e.ID = uuid.New()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ExternalStreamingRoyalty represents monthly royalty data from an external platform
|
|
type ExternalStreamingRoyalty struct {
|
|
ID uuid.UUID `gorm:"type:uuid;primaryKey" json:"id"`
|
|
TrackID uuid.UUID `gorm:"type:uuid;not null" json:"track_id"`
|
|
CreatorID uuid.UUID `gorm:"type:uuid;not null" json:"creator_id"`
|
|
ReportingPeriodStart time.Time `gorm:"type:date;not null" json:"reporting_period_start"`
|
|
ReportingPeriodEnd time.Time `gorm:"type:date;not null" json:"reporting_period_end"`
|
|
Platform string `gorm:"not null;size:50" json:"platform"`
|
|
TotalStreams int64 `gorm:"not null;default:0" json:"total_streams"`
|
|
TotalRevenueCents int64 `gorm:"not null;default:0" json:"total_revenue_cents"`
|
|
Currency string `gorm:"size:3;default:'USD'" json:"currency"`
|
|
StreamsBreakdown json.RawMessage `gorm:"type:jsonb" json:"streams_breakdown,omitempty"`
|
|
Distributor string `gorm:"not null;size:50;default:'distrokid'" json:"distributor"`
|
|
DistributorReportID string `gorm:"size:255" json:"distributor_report_id,omitempty"`
|
|
ImportStatus ImportStatus `gorm:"not null;size:50;default:'pending'" json:"import_status"`
|
|
ImportError string `gorm:"type:text" json:"import_error,omitempty"`
|
|
ImportedAt *time.Time `json:"imported_at,omitempty"`
|
|
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
|
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
|
}
|
|
|
|
func (ExternalStreamingRoyalty) TableName() string {
|
|
return "external_streaming_royalties"
|
|
}
|
|
|
|
func (r *ExternalStreamingRoyalty) BeforeCreate(tx *gorm.DB) error {
|
|
if r.ID == uuid.Nil {
|
|
r.ID = uuid.New()
|
|
}
|
|
return nil
|
|
}
|