github.com/readium/readium-lcp-server@v0.0.0-20240101192032-6e95190e99f1/rwpm/metadata.go (about) 1 // Copyright 2020 Readium Foundation. All rights reserved. 2 // Use of this source code is governed by a BSD-style license 3 // that can be found in the LICENSE file exposed on Github (readium) in the project repository. 4 5 package rwpm 6 7 import ( 8 "encoding/json" 9 "strings" 10 "time" 11 ) 12 13 // Metadata for the default context in WebPub 14 type Metadata struct { 15 Type string `json:"@type,omitempty"` 16 ConformsTo string `json:"conformsTo,omitempty"` 17 Identifier string `json:"identifier,omitempty"` 18 Title MultiLanguage `json:"title"` 19 Subtitle MultiLanguage `json:"subtitle,omitempty"` 20 SortAs string `json:"sortAs,omitempty"` 21 Description string `json:"description,omitempty"` 22 Language MultiString `json:"language,omitempty"` 23 ReadingProgression string `json:"readingProgression,omitempty"` 24 // 25 Modified *time.Time `json:"modified,omitempty"` 26 Published *Date `json:"published,omitempty"` 27 // contributors 28 Publisher Contributors `json:"publisher,omitempty"` 29 Artist Contributors `json:"artist,omitempty"` 30 Author Contributors `json:"author,omitempty"` 31 Colorist Contributors `json:"colorist,omitempty"` 32 Contributor Contributors `json:"contributor,omitempty"` 33 Editor Contributors `json:"editor,omitempty"` 34 Illustrator Contributors `json:"illustrator,omitempty"` 35 Imprint Contributors `json:"imprint,omitempty"` 36 Inker Contributors `json:"inker,omitempty"` 37 Letterer Contributors `json:"letterer,omitempty"` 38 Narrator Contributors `json:"narrator,omitempty"` 39 Penciler Contributors `json:"penciler,omitempty"` 40 Translator Contributors `json:"translator,omitempty"` 41 // other descriptive metadata 42 Subject Subjects `json:"subject,omitempty"` 43 Duration float32 `json:"duration,omitempty"` 44 NumberOfPages int `json:"numberOfPages,omitempty"` 45 Abridged bool `json:"abridged,omitempty"` 46 // collections & series 47 BelongsTo *BelongsTo `json:"belongsTo,omitempty"` 48 49 OtherMetadata []Meta `json:"-"` //Extension point for other metadata 50 } 51 52 // DateOrDatetime struct 53 type DateOrDatetime time.Time 54 55 // UnmarshalJSON unmarshalls DateOrDatetime 56 func (d *DateOrDatetime) UnmarshalJSON(b []byte) error { 57 58 s := strings.Trim(string(b), "\"") 59 // process a date 60 if len(s) == 11 && strings.Index(s, "Z") == 10 { // a date may end with a 'Z' 61 s = strings.TrimRight(s, "Z") 62 } 63 if len(s) == 10 { 64 s = s + "T00:00:00Z" 65 } 66 67 // process a date-time, RFC 3999 compliant 68 date, err := time.Parse(time.RFC3339, s) 69 *d = DateOrDatetime(date) 70 return err 71 } 72 73 // MarshalJSON marshalls DateOrDatetime 74 func (d DateOrDatetime) MarshalJSON() ([]byte, error) { 75 return json.Marshal(time.Time(d)) 76 } 77 78 // Date struct 79 type Date time.Time 80 81 // UnmarshalJSON unmarshalls Date 82 func (d *Date) UnmarshalJSON(b []byte) error { 83 84 // trim the quotes around the value 85 s := string(b[1 : len(b)-1]) 86 // process a date 87 if len(s) == 11 && strings.Index(s, "Z") == 10 { // a date may end with a 'Z' 88 s = strings.TrimRight(s, "Z") 89 } 90 if len(s) == 10 { 91 s = s + "T00:00:00Z" 92 } 93 94 // process a date-time, RFC 3999 compliant 95 date, err := time.Parse(time.RFC3339, s) 96 if err != nil { 97 return err 98 } 99 *d = Date(date) 100 return nil 101 } 102 103 // MarshalJSON marshalls Date 104 func (d Date) MarshalJSON() ([]byte, error) { 105 106 date := time.Time(d) 107 return []byte(date.Format("\"2006-01-02\"")), nil 108 } 109 110 // Meta is a generic structure for other metadata 111 type Meta struct { 112 Property string 113 Value interface{} 114 Children []Meta 115 } 116 117 // Properties object used to link properties 118 // Used also in Rendition for fxl 119 type Properties struct { 120 Contains []string `json:"contains,omitempty"` 121 Layout string `json:"layout,omitempty"` 122 MediaOverlay string `json:"mediaOverlay,omitempty"` 123 Orientation string `json:"orientation,omitempty"` 124 Overflow string `json:"overflow,omitempty"` 125 Page string `json:"page,omitempty"` 126 Spread string `json:"spread,omitempty"` 127 Encrypted *Encrypted `json:"encrypted,omitempty"` 128 } 129 130 // Encrypted contains metadata from encryption xml 131 type Encrypted struct { 132 Scheme string `json:"scheme,omitempty"` 133 Profile string `json:"profile,omitempty"` 134 Algorithm string `json:"algorithm,omitempty"` 135 Compression string `json:"compression,omitempty"` 136 OriginalLength int `json:"originalLength,omitempty"` 137 } 138 139 // Subjects is an array of subjects 140 type Subjects []Subject 141 142 // Subject of a publication 143 type Subject struct { 144 Name string `json:"name"` 145 SortAs string `json:"sortAs,omitempty"` 146 Scheme string `json:"scheme,omitempty"` 147 Code string `json:"code,omitempty"` 148 } 149 150 // UnmarshalJSON unmarshals Subjects 151 func (s *Subjects) UnmarshalJSON(b []byte) error { 152 153 var sbjs []Subject 154 sbjs = make([]Subject, 1) 155 var sbj Subject 156 157 // literal value 158 var literal string 159 var err error 160 if err = json.Unmarshal(b, &literal); err == nil { 161 sbjs[0].Name = literal 162 163 // object value 164 } else if err = json.Unmarshal(b, &sbj); err == nil { 165 sbjs[0] = sbj 166 167 // array value 168 } else { 169 err = json.Unmarshal(b, &sbjs) 170 } 171 if err == nil { 172 *s = sbjs 173 return nil 174 } 175 return err 176 } 177 178 // MarshalJSON marshals Subjects 179 func (s Subjects) MarshalJSON() ([]byte, error) { 180 181 // literal value 182 if len(s) == 1 && s[0].Name != "" && 183 s[0].SortAs == "" && s[0].Scheme == "" && s[0].Code == "" { 184 return json.Marshal(s[0].Name) 185 } 186 187 // object value 188 if len(s) == 1 { 189 sbj := s[0] 190 return json.Marshal(sbj) 191 } 192 193 // array value 194 var sbjs []Subject 195 sbjs = s 196 return json.Marshal(sbjs) 197 } 198 199 // Add adds a value to a subject array 200 func (s *Subjects) Add(item Subject) { 201 202 *s = append(*s, item) 203 } 204 205 // UnmarshalJSON unmarshals Subject 206 func (s *Subject) UnmarshalJSON(b []byte) error { 207 208 var literal string 209 var err error 210 if err = json.Unmarshal(b, &literal); err == nil { 211 s.Name = literal 212 s.SortAs = "" 213 s.Scheme = "" 214 s.Code = "" 215 return nil 216 } 217 type Alias Subject 218 var sbjAlias Alias 219 err = json.Unmarshal(b, &sbjAlias) 220 if err != nil { 221 return err 222 } 223 *s = Subject(sbjAlias) 224 return nil 225 } 226 227 // MarshalJSON marshals Subject 228 func (s Subject) MarshalJSON() ([]byte, error) { 229 230 // literal value 231 if s.Name != "" && s.SortAs == "" && s.Scheme == "" && s.Code == "" { 232 return json.Marshal(s.Name) 233 } 234 type Alias Subject 235 sbjAlias := Alias{s.Name, s.SortAs, s.Scheme, s.Code} 236 return json.Marshal(sbjAlias) 237 } 238 239 // BelongsTo is a list of collections/series that a publication belongs to 240 type BelongsTo struct { 241 Series []Collection `json:"series,omitempty"` 242 Collection []Collection `json:"collection,omitempty"` 243 } 244 245 // Collection construct used for collection/serie metadata 246 type Collection struct { 247 Name string `json:"name"` 248 SortAs string `json:"sort_as,omitempty"` 249 Identifier string `json:"identifier,omitempty"` 250 Position float32 `json:"position,omitempty"` 251 } 252 253 // Contributors is an array of contributors 254 type Contributors []Contributor 255 256 // UnmarshalJSON unmarshals contributors 257 func (c *Contributors) UnmarshalJSON(b []byte) error { 258 259 var ctors []Contributor 260 ctors = make([]Contributor, 1) 261 var ctor Contributor 262 263 // literal value 264 var literal string 265 var err error 266 if err = json.Unmarshal(b, &literal); err == nil { 267 ctors[0].Name.SetDefault(literal) 268 269 // object value 270 } else if err = json.Unmarshal(b, &ctor); err == nil { 271 ctors[0] = ctor 272 273 // array value 274 } else { 275 err = json.Unmarshal(b, &ctors) 276 } 277 if err == nil { 278 *c = ctors 279 return nil 280 } 281 return err 282 } 283 284 // MarshalJSON marshals Contributors 285 func (c Contributors) MarshalJSON() ([]byte, error) { 286 287 // literal value 288 if len(c) == 1 && c[0].Name.Text() != "" && 289 c[0].Identifier == "" && c[0].SortAs == "" && c[0].Role == "" { 290 return json.Marshal(c[0].Name.Text()) 291 } 292 293 // object value 294 if len(c) == 1 { 295 ctor := c[0] 296 return json.Marshal(ctor) 297 } 298 299 // array value 300 var ctors []Contributor 301 ctors = c 302 return json.Marshal(ctors) 303 } 304 305 // AddName adds a Contributor to Contributors 306 func (c *Contributors) AddName(name string) { 307 308 var ctor Contributor 309 ctor.Name.SetDefault(name) 310 c.Add(ctor) 311 } 312 313 // Add adds a Contributor to Contributors 314 func (c *Contributors) Add(ctor Contributor) { 315 316 *c = append(*c, ctor) 317 } 318 319 // Name gets the name of a contributor 320 func (c Contributors) Name() string { 321 322 if len(c) == 1 && c[0].Name.Text() != "" { 323 return c[0].Name.Text() 324 } 325 return "" 326 } 327 328 // Contributor construct used internally for all contributors 329 type Contributor struct { 330 Name MultiLanguage `json:"name,omitempty"` 331 SortAs string `json:"sortAs,omitempty"` 332 Identifier string `json:"identifier,omitempty"` 333 Role string `json:"role,omitempty"` 334 } 335 336 // UnmarshalJSON unmarshals Contributor 337 func (c *Contributor) UnmarshalJSON(b []byte) error { 338 339 var literal string 340 var err error 341 if err = json.Unmarshal(b, &literal); err == nil { 342 c.Name = make(map[string]string) 343 c.Name["und"] = literal 344 c.SortAs = "" 345 c.Identifier = "" 346 c.Role = "" 347 return nil 348 } 349 type Alias Contributor 350 var ctorAlias Alias 351 err = json.Unmarshal(b, &ctorAlias) 352 if err != nil { 353 return err 354 } 355 *c = Contributor(ctorAlias) 356 return nil 357 } 358 359 // MarshalJSON marshals Contributor 360 func (c Contributor) MarshalJSON() ([]byte, error) { 361 362 // literal value 363 if c.Name["und"] != "" && c.Identifier == "" && c.Role == "" && c.SortAs == "" { 364 return json.Marshal(c.Name["und"]) 365 } 366 type Alias Contributor 367 ctorAlias := Alias{c.Name, c.SortAs, c.Identifier, c.Role} 368 return json.Marshal(ctorAlias) 369 } 370 371 // MultiLanguage stores one or more values indexed by language. 372 type MultiLanguage map[string]string 373 374 // UnmarshalJSON unmarshalls Multilanguage 375 // The "und" (undefined)language corresponds to a literal value 376 func (m *MultiLanguage) UnmarshalJSON(b []byte) error { 377 378 var mmap map[string]string 379 mmap = make(map[string]string) 380 381 var literal string 382 var err error 383 if err = json.Unmarshal(b, &literal); err == nil { 384 mmap["und"] = literal 385 } else { 386 err = json.Unmarshal(b, &mmap) 387 } 388 if err != nil { 389 return err 390 } 391 *m = mmap 392 return nil 393 } 394 395 // MarshalJSON marshalls MultiLanguage 396 func (m MultiLanguage) MarshalJSON() ([]byte, error) { 397 398 if len(m) > 1 || m["und"] == "" { 399 var mmap map[string]string 400 mmap = make(map[string]string) 401 402 for key, value := range m { 403 mmap[key] = value 404 } 405 return json.Marshal(mmap) 406 } 407 return json.Marshal(m["und"]) 408 } 409 410 // Text returns the "und" language value or the single value found in the map 411 func (m MultiLanguage) Text() string { 412 413 if m["und"] != "" { 414 return m["und"] 415 } else if len(m) == 1 { 416 for _, v := range m { 417 return v 418 } 419 } 420 return "" 421 } 422 423 // SetDefault inits the "und" localized value 424 func (m *MultiLanguage) SetDefault(literal string) { 425 426 if *m == nil { 427 *m = make(map[string]string) 428 } 429 (*m)["und"] = literal 430 } 431 432 // Set inits a localized value 433 func (m *MultiLanguage) Set(language string, value string) { 434 435 if *m == nil { 436 *m = make(map[string]string) 437 } 438 (*m)[language] = value 439 } 440 441 // MultiString stores one or more strings 442 // Used for properties which take a string || an array of strings 443 type MultiString []string 444 445 // UnmarshalJSON unmarshals MultiString 446 func (m *MultiString) UnmarshalJSON(b []byte) error { 447 448 var mstring []string 449 var literal string 450 var err error 451 452 // literal value 453 if err = json.Unmarshal(b, &literal); err == nil { 454 mstring = append(mstring, literal) 455 456 // string array 457 } else { 458 err = json.Unmarshal(b, &mstring) 459 } 460 if err != nil { 461 return err 462 } 463 *m = mstring 464 return nil 465 } 466 467 // MarshalJSON marshalls MultiString 468 func (m MultiString) MarshalJSON() ([]byte, error) { 469 470 if len(m) == 1 { 471 literal := m[0] 472 return json.Marshal(literal) 473 } 474 var mstring []string 475 for _, v := range m { 476 mstring = append(mstring, v) 477 } 478 return json.Marshal(mstring) 479 } 480 481 // Add adds a value to a multistring array 482 func (m *MultiString) Add(value string) { 483 484 *m = append(*m, value) 485 } 486 487 // Text returns the concatenation of all string values 488 func (m MultiString) Text() string { 489 490 return strings.Join([]string(m), ", ") 491 }