tag.mx raw

   1  package protocol
   2  
   3  import "runtime"
   4  // Tag names. Single-letter tags are the only ones relays index, which is why
   5  // the spec mirrors multi-letter tags with a single-letter one wherever a query
   6  // has to work: `x` mirrors `audio`, `a` mirrors `song`.
   7  const (
   8  	TagD          = "d"
   9  	TagA          = "a"
  10  	TagE          = "e"
  11  	TagP          = "p"
  12  	TagX          = "x"
  13  	TagK          = "k"
  14  	TagI          = "i"
  15  	TagT          = "t"
  16  	TagG          = "g"
  17  	TagR          = "r"
  18  	TagL          = "L"
  19  	TagLValue     = "l"
  20  	TagARoot      = "A"
  21  	TagKRoot      = "K"
  22  	TagPRoot      = "P"
  23  	TagDay        = "D"
  24  	TagN          = "n"
  25  	TagTitle      = "title"
  26  	TagSummary    = "summary"
  27  	TagDescription = "description"
  28  	TagImage      = "image"
  29  	TagContent    = "content"
  30  	TagPublishedAt = "published_at"
  31  	TagStatus     = "status"
  32  	TagLocation   = "location"
  33  	TagPrice      = "price"
  34  	TagStart      = "start"
  35  	TagEnd        = "end"
  36  	TagStartTzid  = "start_tzid"
  37  	TagFb         = "fb"
  38  	TagRole       = "role"
  39  	TagArtist     = "artist"
  40  	TagAlbum      = "album"
  41  	TagTrack      = "track"
  42  	TagDisc       = "disc"
  43  	TagDuration   = "duration"
  44  	TagGenre      = "genre"
  45  	TagReleaseDate = "release-date"
  46  	TagISRC       = "isrc"
  47  	TagLanguage   = "language"
  48  	TagBPM        = "bpm"
  49  	TagKey        = "key"
  50  	TagCopyright  = "copyright"
  51  	TagAudio      = "audio"
  52  	TagMaster     = "master"
  53  	TagCodec      = "codec"
  54  	TagServer     = "server"
  55  	TagMint       = "mint"
  56  	TagRetailer   = "retailer"
  57  	TagExpiration = "expiration"
  58  	TagFilter     = "filter"
  59  	TagFp         = "fp"
  60  	TagCatalog    = "catalog"
  61  	TagTier       = "tier"
  62  	TagPayment    = "payment"
  63  	TagToken      = "token"
  64  	TagChannel    = "channel"
  65  	TagPeriod     = "period"
  66  	TagMode       = "mode"
  67  	TagBytes      = "bytes"
  68  	TagAmountSats = "amount_sats"
  69  	TagPaid       = "paid"
  70  	TagSats       = "sats"
  71  	TagPlays      = "plays"
  72  	TagDownloads  = "downloads"
  73  	TagListeners  = "listeners"
  74  	TagReceipts   = "receipts"
  75  	TagBlind      = "blind"
  76  	TagHost       = "host"
  77  	TagStatement  = "statement"
  78  	TagDleq       = "dleq"
  79  	TagSong       = "song"
  80  	TagEncore     = "encore"
  81  	TagFeat       = "feat"
  82  	TagCover      = "cover"
  83  	TagCapacity   = "capacity"
  84  	TagPosition   = "position"
  85  	TagJoinedAt   = "joined_at"
  86  	TagStudio     = "studio"
  87  	TagFormat     = "format"
  88  	TagFormatDetail = "format-detail"
  89  	TagConditionMedia  = "condition-media"
  90  	TagConditionSleeve = "condition-sleeve"
  91  	TagPressing   = "pressing"
  92  	TagIncludes   = "includes"
  93  	TagQuantity   = "quantity"
  94  	TagTerritory  = "territory"
  95  	TagBarcode    = "barcode"
  96  	TagSizes      = "sizes"
  97  	TagColor      = "color"
  98  	TagMaterial   = "material"
  99  	TagPrintMethod = "print-method"
 100  	TagDesigner   = "designer"
 101  	TagWeight     = "weight"
 102  	TagSleeve     = "sleeve"
 103  	TagRelay      = "relay"
 104  	TagChallenge  = "challenge"
 105  	TagT0         = "t0"
 106  	TagT1         = "t1"
 107  	TagAmount     = "amount"
 108  	TagUnit       = "unit"
 109  )
 110  
 111  // Controlled vocabularies. The documents call these conventions rather than
 112  // closed registries - "new role IDs emerge as the community uses them" - so
 113  // none of them is enforced on parse. They exist to spell values once.
 114  const (
 115  	// Role namespaces for NIP-32 label events (confirmations and attestations).
 116  	NamespaceCredit = "musiquay.credit"
 117  	NamespaceAttest = "musiquay.attest"
 118  
 119  	// Label values on a confirmation (NamespaceCredit).
 120  	LabelConfirmed = "confirmed"
 121  	LabelDenied    = "denied"
 122  	LabelCorrected = "corrected"
 123  
 124  	// Label value on an attestation (NamespaceAttest).
 125  	LabelAttests = "attests"
 126  
 127  	// Access filter tiers. The d tag is "<tier>:<asset-address>", so these
 128  	// values are the head of the d value, not a tag of their own.
 129  	TierFan    = "fan"
 130  	TierRetail = "retail"
 131  	TierSub    = "sub"
 132  
 133  	// Filter structure declaration. Only one value is defined; an unknown
 134  	// value or version is a fail-closed condition on the host.
 135  	FilterBloom = "bloom"
 136  
 137  	// Payment legs accepted on an access filter and declared on an order.
 138  	PaymentCashu     = "cashu"
 139  	PaymentLightning = "lightning"
 140  
 141  	// Delivery receipt modes.
 142  	ModeDownload = "download"
 143  	ModeStream   = "stream"
 144  
 145  	// Counter provenance: listeners is always an estimate.
 146  	Approx = "approx"
 147  
 148  	// RSVP statuses (NIP-52 plus the waitlist convention).
 149  	StatusAccepted  = "accepted"
 150  	StatusDeclined  = "declined"
 151  	StatusTentative = "tentative"
 152  	StatusWaitlist  = "waitlist"
 153  
 154  	// Free/busy indicator beside an RSVP status.
 155  	FbFree = "free"
 156  	FbBusy = "busy"
 157  
 158  	// Classified listing status.
 159  	StatusActive = "active"
 160  
 161  	// Edition format family.
 162  	FormatVinyl    = "vinyl"
 163  	FormatCD       = "CD"
 164  	FormatCassette = "cassette"
 165  
 166  	// Setlist song annotations.
 167  	SongFeat  = "feat"
 168  	SongCover = "cover"
 169  )
 170  
 171  // AccessTier reports whether s is one of the three defined filter tiers. The
 172  // d tag of an access filter is "<tier>:<asset-address>".
 173  func AccessTier(s string) (ok bool) {
 174  	switch s {
 175  	case TierFan, TierRetail, TierSub:
 176  		return true
 177  	}
 178  	return false
 179  }
 180  
 181  // RosterRoles is the event-roster vocabulary, grouped as the spec groups it.
 182  // Actors include companies: a rental house is as much a participant as a
 183  // drummer, and the protocol does not distinguish them.
 184  var RosterRoles []string
 185  
 186  // CreditRoles is the release and track credit vocabulary. It extends the roster
 187  // vocabulary with the composition, production and studio side. Instruments are
 188  // carried inside the role token ("performer-cello"), which is why the performer
 189  // family is open-ended rather than a list.
 190  var CreditRoles []string
 191  
 192  var rolesReady bool
 193  
 194  // initRoles fills the two tables on first use. Lazy rather than an init()
 195  // or an initializer, for two reasons: the test harness does not run a package's
 196  // init (moxie test compiles the package under test as the build root and its
 197  // init never reaches initAll), and every allocation stored into a package
 198  // global must outlive this call - the frame arena is released on return, so the
 199  // tables are built in the root arena. nostr/pkg/kind does the same.
 200  func initRoles() {
 201  	if rolesReady {
 202  		return
 203  	}
 204  	rolesReady = true
 205  	runtime.SovereignSetArena(runtime.RootArena())
 206  	RosterRoles = []string{
 207  		// Organization
 208  		"promoter", "booking-agent", "venue", "label", "sponsor",
 209  		// Sound
 210  		"sound-foh", "sound-monitors", "sound-broadcast", "sound-recording",
 211  		// Technical
 212  		"lighting", "stage-manager", "backline", "av-tech", "rigger",
 213  		// Performance. "performer" is the generic form the music, calendar and
 214  		// setlist examples use; the roster vocabulary refines it per billing.
 215  		"performer", "performer-headliner", "performer-support", "performer-session",
 216  		"performer-guest", "performer-dj", "performer-mc",
 217  		// Crew
 218  		"road-crew", "tour-manager", "merch", "van-driver",
 219  		// Venue staff
 220  		"door", "bar", "security", "venue-manager", "venue-tech", "catering",
 221  		// Documentation
 222  		"photographer", "filmmaker", "live-stream", "journalist", "reviewer",
 223  		// Creative
 224  		"designer", "vj", "visual-artist",
 225  		// Service companies
 226  		"van-rental", "backline-rental", "pa-rental", "lighting-rental",
 227  		"catering-company", "production-company", "studio",
 228  	}
 229  	CreditRoles = []string{
 230  		// Composition
 231  		"songwriter", "lyricist", "composer", "arranger", "orchestrator",
 232  		// Performance
 233  		"performer-vocals", "performer-guitar", "performer-bass", "performer-drums",
 234  		"performer-keys", "featured-artist",
 235  		// Production
 236  		"producer", "co-producer", "executive-producer",
 237  		// Recording
 238  		"recording-engineer", "assistant-engineer",
 239  		// Mixing
 240  		"mix-engineer", "mix-assistant",
 241  		// Mastering
 242  		"mastering-engineer",
 243  		// Label and business
 244  		"a-and-r", "manager", "distributor",
 245  		// Visual
 246  		"artwork-designer", "illustrator", "layout-designer", "typography",
 247  		// Video
 248  		"video-director", "cinematographer", "video-editor", "colorist", "vfx",
 249  		// Written
 250  		"liner-notes", "translator",
 251  		// Manufacturing
 252  		"pressing-plant", "replication-plant", "duplication-house", "print-house",
 253  	}
 254  }
 255  
 256  // KnownRole reports whether role appears in either vocabulary. Unknown roles
 257  // are legal - the vocabularies are conventions - so this is for display and
 258  // validation warnings, never a gate.
 259  func KnownRole(role string) (ok bool) {
 260  	initRoles()
 261  	for _, r := range RosterRoles {
 262  		if r == role {
 263  			return true
 264  		}
 265  	}
 266  	for _, r := range CreditRoles {
 267  		if r == role {
 268  			return true
 269  		}
 270  	}
 271  	// performer-<instrument> is open-ended by design.
 272  	if len(role) > 10 && role[:10] == "performer-" {
 273  		return true
 274  	}
 275  	return false
 276  }
 277