diff --git a/README.md b/README.md index a9aa6d1b..b3737cd3 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,8 @@ docker-compose run --rm hugo Скрипты публикации могут быть вызваны при помощи make в директории `./publisher`: -- `make new` — создает шаблон нового выпуска, темы берутся с news.radio-t.com -- `make prep` — создает шаблон "Темы для ..." следующего выпуска -- `make print-mp3-tags EPISODE=685` - выводит mp3 теги файла эпизода подскаста +- `make new` — создает шаблон нового выпуска, темы берутся с news.radio-t.com, номер выпуска берется из api сайта +- `make prep` — создает шаблон "Темы для ..." следующего выпуска, номер выпуска берется из api сайта - `make upload-mp3 EPISODE=685` - добавляет mp3 теги и картинку в файл эпизода подкаста, после чего разносит его по нодам через внешний ansible контейнер. Для выполнения необходимо подключить в docker-compose конфиге директорию с mp3 файлами подкаста как volume в сервис publisher - `make deploy` — добавляет в гит и запускает push + build на мастер. После этого строит лог чата и очищает темы @@ -45,8 +44,6 @@ npm run start-turbo npm run production ``` -лого в `src/images/` - -фавиконки в `static/` и описаны в `layouts/partials/favicons.html` - -обложки в `static/images/covers/` (для сохранения совместимости также оставлены обложки `static/images/cover.jpg` и `static/images/cover_rt_big_archive.png`) +- лого в `src/images/` +- фавиконки в `static/` и описаны в `layouts/partials/favicons.html` +- обложки в `static/images/covers/` (для сохранения совместимости также оставлены обложки `static/images/cover.jpg` и `static/images/cover_rt_big_archive.png`) diff --git a/publisher/Makefile b/publisher/Makefile index bc06fe7d..614eed2a 100644 --- a/publisher/Makefile +++ b/publisher/Makefile @@ -13,7 +13,7 @@ GITREV=$(shell git describe --abbrev=7 --always --tags) REV=$(GITREV)-$(BRANCH)-$(shell date +%Y%m%d-%H:%M:%S) help: - @echo 'Available commands: new, prep, upload-mp3, deploy' + @echo 'available commands: new, prep, upload-mp3, deploy' # generate new episode post markdown file and open it using SublimeText new: @@ -25,11 +25,11 @@ prep: @docker-compose run --rm -it publisher prep | tee ${TMPFILE}; @${subl} ${makefile_dir}/../hugo/`tail -n 1 ${TMPFILE} | tr -d '\r'`; -# set necessary tags to episode mp3 file and upload it +# set necessary tags to episode mp3 file and upload it to master and all nodes upload-mp3: @docker-compose run --rm -it publisher upload --episode="$$EPISODE" --dbg -# deploy new podcast episode page to https://radio-t.com +# deploy new podcast episode page to https://radio-t.com and regenerate site deploy: @docker-compose run --rm -it publisher deploy diff --git a/publisher/README.md b/publisher/README.md index 6e04b6b3..beb5d53c 100644 --- a/publisher/README.md +++ b/publisher/README.md @@ -2,19 +2,12 @@ ## Как пользоваться скриптами в этой директории? -Перед использованием, необходимо собрать docker образ при помощи docker-compose (конфиг в руте репозитария), команда: `docker-compose build publisher`. +Перед использованием, необходимо собрать docker образ при помощи docker-compose (конфиг в `publisher` репозитария), команда: `docker-compose build`. -После сборки образа, скриптами публикации можно пользоваться как с помощью `make`: +После сборки образа, скриптами публикации можно пользоваться с помощью `make`: - `make new` — создает шаблон нового выпуска, темы берутся с news.radio-t.com - `make prep` — создает шаблон "Темы для ..." следующего выпуска - `make print-mp3-tags EPISODE=685` - выводит mp3 теги файла эпизода подскаста - `make upload-mp3 EPISODE=685` - добавляет mp3 теги и картинку в файл эпизода подкаста, после чего разносит его по нодам через внешний ansible контейнер. Для выполнения необходимо подключить в docker-compose конфиге директорию с mp3 файлами подкаста как volume в сервис publisher - `make deploy` — добавляет в гит и запускает pull + build на мастер. После этого строит лог чата и очищает темы - -## Для разработчика - -После изменений python скриптов, желательно прогнать следующие линтеры и форматтеры: - - `isort --lines 119 -y` - - `black --line-length 120 --target-version py38 ./*/*.py` - - `flake8 . --max-line-length=120` \ No newline at end of file diff --git a/publisher/app/cmd/cmd.go b/publisher/app/cmd/cmd.go index 0ea72cc8..eb185867 100644 --- a/publisher/app/cmd/cmd.go +++ b/publisher/app/cmd/cmd.go @@ -59,7 +59,7 @@ func (c *ShellExecutor) Run(cmd string, params ...string) { // Do executes command and returns error if failed func (c *ShellExecutor) do(cmd string) error { - log.Printf("[DEBUG] execute: %s", cmd) + log.Printf("[INFO] execute: %s", cmd) if c.Dry { return nil } diff --git a/publisher/app/cmd/upload.go b/publisher/app/cmd/upload.go index 19282231..2a28d2fb 100644 --- a/publisher/app/cmd/upload.go +++ b/publisher/app/cmd/upload.go @@ -1,16 +1,20 @@ package cmd import ( + "bytes" "embed" "fmt" + "io" "os" "regexp" "strconv" "strings" "time" + "unicode/utf8" "github.com/bogem/id3v2/v2" log "github.com/go-pkgz/lgr" + "github.com/tcolgate/mp3" ) //go:embed artifacts/* @@ -22,6 +26,7 @@ type Upload struct { LocationMp3 string LocationPosts string Dry bool + SkipTransfer bool } // Do uploads an episode to all destinations. It takes an episode number as input and returns an error if any of the actions fail. @@ -52,8 +57,13 @@ func (u *Upload) Do(episodeNum int) error { log.Printf("[WARN] can't set mp3 tags for %s, %v", mp3file, err) } - u.Run("spot", "-e mp3:"+mp3file, `--task="deploy to master`, "-v", mp3file) - u.Run("spot", "-e mp3:"+mp3file, `--task="deploy to nodes"`, "-v", mp3file) + if u.SkipTransfer { + log.Printf("[WARN] skip transfer of %s", mp3file) + return nil + } + + u.Run("spot", "-e mp3:"+mp3file, `--task="deploy to master"`, "-v") + u.Run("spot", "-e mp3:"+mp3file, `--task="deploy to nodes"`, "-v") return nil } @@ -72,24 +82,29 @@ func (u *Upload) setMp3Tags(episodeNum int, chapters []chapter) error { if u.Dry { return nil } + tag, err := id3v2.Open(mp3file, id3v2.Options{Parse: true}) if err != nil { return fmt.Errorf("can't open mp3 file %s, %w", mp3file, err) } defer tag.Close() - tag.SetTitle(fmt.Sprintf("Радио-Т %d", episodeNum)) + tag.DeleteAllFrames() // clear all existing tags + + tag.SetDefaultEncoding(id3v2.EncodingUTF8) + + title := fmt.Sprintf("Радио-Т %d", episodeNum) + tag.SetTitle(title) tag.SetArtist("Umputun, Bobuk, Gray, Ksenks, Alek.sys") tag.SetAlbum("Радио-Т") tag.SetYear(fmt.Sprintf("%d", time.Now().Year())) tag.SetGenre("Podcast") - tag.SetDefaultEncoding(id3v2.EncodingUTF8) + // Set artwork artwork, err := artifactsFS.ReadFile("artifacts/cover.png") if err != nil { - return fmt.Errorf("can't read cover.jpg from artifacts, %w", err) + return fmt.Errorf("can't read cover.png from artifacts, %w", err) } - pic := id3v2.PictureFrame{ Encoding: id3v2.EncodingUTF8, MimeType: "image/png", @@ -99,13 +114,27 @@ func (u *Upload) setMp3Tags(episodeNum int, chapters []chapter) error { } tag.AddAttachedPicture(pic) + // we need to get mp3 duration to set the correct end time for the last chapter + duration, err := u.getMP3Duration(mp3file) + if err != nil { + return fmt.Errorf("can't get mp3 duration, %w", err) + } + + // create a CTOC frame manually + ctocFrame := u.createCTOCFrame(chapters) + tag.AddFrame(tag.CommonID("CTOC"), ctocFrame) + + // add chapters for i, chapter := range chapters { var endTime time.Duration if i < len(chapters)-1 { - // use the start time of the next chapter as the end time endTime = chapters[i+1].Begin } else { - endTime = 0 + endTime = duration + } + chapterTitle := chapter.Title + if !utf8.ValidString(chapterTitle) { + return fmt.Errorf("chapter title contains invalid UTF-8 characters") } chapFrame := id3v2.ChapterFrame{ ElementID: strconv.Itoa(i + 1), @@ -115,11 +144,11 @@ func (u *Upload) setMp3Tags(episodeNum int, chapters []chapter) error { EndOffset: id3v2.IgnoredOffset, Title: &id3v2.TextFrame{ Encoding: id3v2.EncodingUTF8, - Text: chapter.Title, + Text: chapterTitle, }, Description: &id3v2.TextFrame{ Encoding: id3v2.EncodingUTF8, - Text: chapter.Title, + Text: chapterTitle, }, } tag.AddChapterFrame(chapFrame) @@ -152,8 +181,11 @@ func (u *Upload) parseChapters(content string) ([]chapter, error) { return time.Duration(hours)*time.Hour + time.Duration(minutes)*time.Minute + time.Duration(seconds)*time.Second, nil } - chapters := []chapter{} - // - [Chapter One](http://example.com/one) - *00:01:00*. + chapters := []chapter{ + {Title: "Вступление", Begin: 0}, + } + + // get form md like this "- [Chapter One](http://example.com/one) - *00:01:00*." chapterRegex := regexp.MustCompile(`-\s+\[(.*?)\]\((.*?)\)\s+-\s+\*(.*?)\*\.`) matches := chapterRegex.FindAllStringSubmatch(content, -1) for _, match := range matches { @@ -174,6 +206,45 @@ func (u *Upload) parseChapters(content string) ([]chapter, error) { }) } } - + if len(chapters) == 1 { + return []chapter{}, nil // no chapters found, don't return the introduction chapter + } return chapters, nil } + +func (u *Upload) getMP3Duration(filePath string) (time.Duration, error) { + file, err := os.Open(filePath) + if err != nil { + return 0, err + } + defer file.Close() + d := mp3.NewDecoder(file) + var f mp3.Frame + var skipped int + var duration float64 + + for err == nil { + if err = d.Decode(&f, &skipped); err != nil && err != io.EOF { + log.Printf("[WARN] can't get duration for provided stream: %v", err) + return 0, nil + } + duration += f.Duration().Seconds() + } + return time.Second * time.Duration(duration), nil +} + +func (u *Upload) createCTOCFrame(chapters []chapter) *id3v2.UnknownFrame { + var frameBody bytes.Buffer + frameBody.WriteByte(0x03) // write flags (e.g., 0x03 for top-level and ordered chapters) + frameBody.WriteByte(byte(len(chapters))) // write the number of child elements + + // append child element IDs (chapter IDs) + for i, _ := range chapters { + elementID := fmt.Sprintf("%d", i+1) + frameBody.WriteString(elementID) + frameBody.WriteByte(0x00) // Null separator for IDs + } + + // create and return an UnknownFrame with the constructed body + return &id3v2.UnknownFrame{Body: frameBody.Bytes()} +} diff --git a/publisher/app/cmd/upload_test.go b/publisher/app/cmd/upload_test.go index d06e0030..eabb6f51 100644 --- a/publisher/app/cmd/upload_test.go +++ b/publisher/app/cmd/upload_test.go @@ -47,11 +47,12 @@ func TestUpload_Do(t *testing.T) { require.Equal(t, 2, len(ex.RunCalls())) assert.Equal(t, "spot", ex.RunCalls()[0].Cmd) - assert.Equal(t, []string{"-e mp3:/tmp/publisher_test/rt_podcast123/rt_podcast123.mp3", "--task=\"deploy to master", "-v", "/tmp/publisher_test/rt_podcast123/rt_podcast123.mp3"}, ex.RunCalls()[0].Params) + assert.Equal(t, []string{"-e mp3:/tmp/publisher_test/rt_podcast123/rt_podcast123.mp3", "--task=\"deploy to master\"", "-v"}, + ex.RunCalls()[0].Params) assert.Equal(t, "spot", ex.RunCalls()[1].Cmd) - assert.Equal(t, 4, len(ex.RunCalls()[1].Params)) - assert.Equal(t, []string{"-e mp3:/tmp/publisher_test/rt_podcast123/rt_podcast123.mp3", "--task=\"deploy to nodes\"", "-v", "/tmp/publisher_test/rt_podcast123/rt_podcast123.mp3"}, ex.RunCalls()[1].Params) + assert.Equal(t, []string{"-e mp3:/tmp/publisher_test/rt_podcast123/rt_podcast123.mp3", "--task=\"deploy to nodes\"", "-v"}, + ex.RunCalls()[1].Params) } func TestUpload_setMp3Tags(t *testing.T) { @@ -124,12 +125,13 @@ func TestUpload_parseChapters(t *testing.T) { { name: "Valid chapters", content: ` -- [Chapter One](http://example.com/one) - *00:01:00*. -- [Chapter Two](http://example.com/two) - *00:02:30*. +- [Часть номер One](http://example.com/one) - *00:01:00*. +- [Часть номер Two](http://example.com/two) - *00:02:30*. `, expected: []chapter{ - {"Chapter One", "http://example.com/one", time.Minute}, - {"Chapter Two", "http://example.com/two", time.Minute*2 + time.Second*30}, + {"Вступление", "", 0}, + {"Часть номер One", "http://example.com/one", time.Minute}, + {"Часть номер Two", "http://example.com/two", time.Minute*2 + time.Second*30}, }, expectError: false, }, @@ -193,6 +195,7 @@ filename = "rt_podcast686" ` expectedChapters := []chapter{ + {"Вступление", "", 0}, {"Первому Macintosh 36 лет", "https://www.macrumors.com/2020/01/24/macintosh-36th-anniversary/", 4*time.Minute + 18*time.Second}, {"JetBrains придумает новую IntelliJ", "https://devclass.com/2020/01/21/jetbrains-reimagines-intellij-as-text-editor-machine-learning/", 11*time.Minute + 10*time.Second}, {"Мы учим не тому", "https://www.bloomberg.com/tosv2.html?vid=&uuid=59d32d10-31cd-11ea-a482-59e1177b04c0&url=L29waW5pb24vYXJ0aWNsZXMvMjAyMC0wMS0wNy9jb2RpbmctaXMtY29sbGFib3JhdGl2ZS1hbmQtc3RlbS1lZHVjYXRpb24tc2hvdWxkLWJlLXRvbw==", 28*time.Minute + 16*time.Second}, diff --git a/publisher/app/main.go b/publisher/app/main.go index 879d34e1..694c8d4d 100644 --- a/publisher/app/main.go +++ b/publisher/app/main.go @@ -27,8 +27,9 @@ var opts struct { } `command:"prep" description:"make new prep podcast post"` UploadCmd struct { - Location string `long:"location" env:"LOCATION" default:"/Volumes/Podcasts/radio-t/" description:"podcast location"` - HugoPosts string `long:"hugo-posts" env:"HUGO_POSTS" default:"/srv/hugo/content/posts" description:"hugo posts location"` + Location string `long:"location" env:"LOCATION" default:"/episodes" description:"podcast location"` + HugoPosts string `long:"hugo-posts" env:"HUGO_POSTS" default:"/srv/hugo/content/posts" description:"hugo posts location"` + SkipTransfer bool `long:"skip-transfer" env:"SKIP_TRANSFER" description:"skip transfer to remote locations"` } `command:"upload" description:"upload podcast"` DeployCmd struct { @@ -58,7 +59,7 @@ func main() { if err != nil { log.Fatalf("[ERROR] can't get last podcast number, %v", err) } - log.Printf("[DEBUG] episode %d", episodeNum) + log.Printf("[DEBUG] dtected episode: %d", episodeNum) if p.Active != nil && p.Command.Find("new") == p.Active { runNew(episodeNum) @@ -126,6 +127,7 @@ func runUpload(episodeNum int) { Executor: &cmd.ShellExecutor{Dry: opts.Dry}, LocationMp3: opts.UploadCmd.Location, LocationPosts: opts.UploadCmd.HugoPosts, + SkipTransfer: opts.UploadCmd.SkipTransfer, Dry: opts.Dry, } if err := upload.Do(episodeNum); err != nil { diff --git a/publisher/docker-compose.yml b/publisher/docker-compose.yml new file mode 100644 index 00000000..a3fed504 --- /dev/null +++ b/publisher/docker-compose.yml @@ -0,0 +1,21 @@ + +services: + publisher: + image: radio-t/publisher + build: . + hostname: publisher + container_name: publisher + restart: always + logging: + driver: json-file + options: + max-size: "10m" + max-file: "5" + environment: + PYTHONPATH: /srv/publisher + RT_NEWS_ADMIN: + volumes: + - ../:/srv/ + - /Volumes/Podcasts/radio-t:/episodes + - /Users/umputun/.ssh/id_rsa.pub:/home/app/.ssh/id_rsa.pub:ro + - /Users/umputun/.ssh/id_rsa:/home/app/.ssh/id_rsa:ro diff --git a/publisher/go.mod b/publisher/go.mod index 3a0e4610..001f46d6 100644 --- a/publisher/go.mod +++ b/publisher/go.mod @@ -7,6 +7,7 @@ require ( github.com/go-pkgz/lgr v0.11.1 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.8.4 + github.com/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300 github.com/umputun/go-flags v1.5.1 ) diff --git a/publisher/go.sum b/publisher/go.sum index 33a0b4c9..d9b79564 100644 --- a/publisher/go.sum +++ b/publisher/go.sum @@ -10,6 +10,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300 h1:XQdibLKagjdevRB6vAjVY4qbSr8rQ610YzTkWcxzxSI= +github.com/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300/go.mod h1:FNa/dfN95vAYCNFrIKRrlRo+MBLbwmR9Asa5f2ljmBI= github.com/umputun/go-flags v1.5.1 h1:vRauoXV3Ultt1HrxivSxowbintgZLJE+EcBy5ta3/mY= github.com/umputun/go-flags v1.5.1/go.mod h1:nTbvsO/hKqe7Utri/NoyN18GR3+EWf+9RrmsdwdhrEc= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= diff --git a/publisher/vendor/github.com/bogem/id3v2/v2/common_ids.go b/publisher/vendor/github.com/bogem/id3v2/v2/common_ids.go index 8d91a822..aeac14d7 100644 --- a/publisher/vendor/github.com/bogem/id3v2/v2/common_ids.go +++ b/publisher/vendor/github.com/bogem/id3v2/v2/common_ids.go @@ -134,10 +134,9 @@ var ( // parsing of corresponding frame. // You should consider that there is no text frame parser. That's why you should // check at first, if it's a text frame: -// -// if strings.HasPrefix(id, "T") { -// ... -// } +// if strings.HasPrefix(id, "T") { +// ... +// } var parsers = map[string]func(*bufReader, byte) (Framer, error){ "APIC": parsePictureFrame, "CHAP": parseChapterFrame, diff --git a/publisher/vendor/github.com/davecgh/go-spew/spew/bypass.go b/publisher/vendor/github.com/davecgh/go-spew/spew/bypass.go index 70ddeaad..79299478 100644 --- a/publisher/vendor/github.com/davecgh/go-spew/spew/bypass.go +++ b/publisher/vendor/github.com/davecgh/go-spew/spew/bypass.go @@ -18,7 +18,6 @@ // tag is deprecated and thus should not be used. // Go versions prior to 1.4 are disabled because they use a different layout // for interfaces which make the implementation of unsafeReflectValue more complex. -//go:build !js && !appengine && !safe && !disableunsafe && go1.4 // +build !js,!appengine,!safe,!disableunsafe,go1.4 package spew diff --git a/publisher/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go b/publisher/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go index 5e2d890d..205c28d6 100644 --- a/publisher/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go +++ b/publisher/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go @@ -16,7 +16,6 @@ // when the code is running on Google App Engine, compiled by GopherJS, or // "-tags safe" is added to the go build command line. The "disableunsafe" // tag is deprecated and thus should not be used. -//go:build js || appengine || safe || disableunsafe || !go1.4 // +build js appengine safe disableunsafe !go1.4 package spew diff --git a/publisher/vendor/github.com/davecgh/go-spew/spew/config.go b/publisher/vendor/github.com/davecgh/go-spew/spew/config.go index 161895fc..2e3d22f3 100644 --- a/publisher/vendor/github.com/davecgh/go-spew/spew/config.go +++ b/publisher/vendor/github.com/davecgh/go-spew/spew/config.go @@ -254,15 +254,15 @@ pointer addresses used to indirect to the final value. It provides the following features over the built-in printing facilities provided by the fmt package: - - Pointers are dereferenced and followed - - Circular data structures are detected and handled properly - - Custom Stringer/error interfaces are optionally invoked, including - on unexported types - - Custom types which only implement the Stringer/error interfaces via - a pointer receiver are optionally invoked when passing non-pointer - variables - - Byte arrays and slices are dumped like the hexdump -C command which - includes offsets, byte values in hex, and ASCII output + * Pointers are dereferenced and followed + * Circular data structures are detected and handled properly + * Custom Stringer/error interfaces are optionally invoked, including + on unexported types + * Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + * Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output The configuration options are controlled by modifying the public members of c. See ConfigState for options documentation. @@ -295,12 +295,12 @@ func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) // NewDefaultConfig returns a ConfigState with the following default settings. // -// Indent: " " -// MaxDepth: 0 -// DisableMethods: false -// DisablePointerMethods: false -// ContinueOnMethod: false -// SortKeys: false +// Indent: " " +// MaxDepth: 0 +// DisableMethods: false +// DisablePointerMethods: false +// ContinueOnMethod: false +// SortKeys: false func NewDefaultConfig() *ConfigState { return &ConfigState{Indent: " "} } diff --git a/publisher/vendor/github.com/davecgh/go-spew/spew/doc.go b/publisher/vendor/github.com/davecgh/go-spew/spew/doc.go index 722e9aa7..aacaac6f 100644 --- a/publisher/vendor/github.com/davecgh/go-spew/spew/doc.go +++ b/publisher/vendor/github.com/davecgh/go-spew/spew/doc.go @@ -21,36 +21,35 @@ debugging. A quick overview of the additional features spew provides over the built-in printing facilities for Go data types are as follows: - - Pointers are dereferenced and followed - - Circular data structures are detected and handled properly - - Custom Stringer/error interfaces are optionally invoked, including - on unexported types - - Custom types which only implement the Stringer/error interfaces via - a pointer receiver are optionally invoked when passing non-pointer - variables - - Byte arrays and slices are dumped like the hexdump -C command which - includes offsets, byte values in hex, and ASCII output (only when using - Dump style) + * Pointers are dereferenced and followed + * Circular data structures are detected and handled properly + * Custom Stringer/error interfaces are optionally invoked, including + on unexported types + * Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + * Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output (only when using + Dump style) There are two different approaches spew allows for dumping Go data structures: - - Dump style which prints with newlines, customizable indentation, - and additional debug information such as types and all pointer addresses - used to indirect to the final value - - A custom Formatter interface that integrates cleanly with the standard fmt - package and replaces %v, %+v, %#v, and %#+v to provide inline printing - similar to the default %v while providing the additional functionality - outlined above and passing unsupported format verbs such as %x and %q - along to fmt + * Dump style which prints with newlines, customizable indentation, + and additional debug information such as types and all pointer addresses + used to indirect to the final value + * A custom Formatter interface that integrates cleanly with the standard fmt + package and replaces %v, %+v, %#v, and %#+v to provide inline printing + similar to the default %v while providing the additional functionality + outlined above and passing unsupported format verbs such as %x and %q + along to fmt -# Quick Start +Quick Start This section demonstrates how to quickly get started with spew. See the sections below for further details on formatting and configuration options. To dump a variable with full newlines, indentation, type, and pointer information use Dump, Fdump, or Sdump: - spew.Dump(myVar1, myVar2, ...) spew.Fdump(someWriter, myVar1, myVar2, ...) str := spew.Sdump(myVar1, myVar2, ...) @@ -59,13 +58,12 @@ Alternatively, if you would prefer to use format strings with a compacted inline printing style, use the convenience wrappers Printf, Fprintf, etc with %v (most compact), %+v (adds pointer addresses), %#v (adds types), or %#+v (adds types and pointer addresses): - spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) -# Configuration Options +Configuration Options Configuration of spew is handled by fields in the ConfigState type. For convenience, all of the top-level functions use a global state available @@ -76,52 +74,51 @@ equivalent to the top-level functions. This allows concurrent configuration options. See the ConfigState documentation for more details. The following configuration options are available: - - - Indent - String to use for each indentation level for Dump functions. - It is a single space by default. A popular alternative is "\t". - - - MaxDepth - Maximum number of levels to descend into nested data structures. - There is no limit by default. - - - DisableMethods - Disables invocation of error and Stringer interface methods. - Method invocation is enabled by default. - - - DisablePointerMethods - Disables invocation of error and Stringer interface methods on types - which only accept pointer receivers from non-pointer variables. - Pointer method invocation is enabled by default. - - - DisablePointerAddresses - DisablePointerAddresses specifies whether to disable the printing of - pointer addresses. This is useful when diffing data structures in tests. - - - DisableCapacities - DisableCapacities specifies whether to disable the printing of - capacities for arrays, slices, maps and channels. This is useful when - diffing data structures in tests. - - - ContinueOnMethod - Enables recursion into types after invoking error and Stringer interface - methods. Recursion after method invocation is disabled by default. - - - SortKeys - Specifies map keys should be sorted before being printed. Use - this to have a more deterministic, diffable output. Note that - only native types (bool, int, uint, floats, uintptr and string) - and types which implement error or Stringer interfaces are - supported with other types sorted according to the - reflect.Value.String() output which guarantees display - stability. Natural map order is used by default. - - - SpewKeys - Specifies that, as a last resort attempt, map keys should be - spewed to strings and sorted by those strings. This is only - considered if SortKeys is true. - -# Dump Usage + * Indent + String to use for each indentation level for Dump functions. + It is a single space by default. A popular alternative is "\t". + + * MaxDepth + Maximum number of levels to descend into nested data structures. + There is no limit by default. + + * DisableMethods + Disables invocation of error and Stringer interface methods. + Method invocation is enabled by default. + + * DisablePointerMethods + Disables invocation of error and Stringer interface methods on types + which only accept pointer receivers from non-pointer variables. + Pointer method invocation is enabled by default. + + * DisablePointerAddresses + DisablePointerAddresses specifies whether to disable the printing of + pointer addresses. This is useful when diffing data structures in tests. + + * DisableCapacities + DisableCapacities specifies whether to disable the printing of + capacities for arrays, slices, maps and channels. This is useful when + diffing data structures in tests. + + * ContinueOnMethod + Enables recursion into types after invoking error and Stringer interface + methods. Recursion after method invocation is disabled by default. + + * SortKeys + Specifies map keys should be sorted before being printed. Use + this to have a more deterministic, diffable output. Note that + only native types (bool, int, uint, floats, uintptr and string) + and types which implement error or Stringer interfaces are + supported with other types sorted according to the + reflect.Value.String() output which guarantees display + stability. Natural map order is used by default. + + * SpewKeys + Specifies that, as a last resort attempt, map keys should be + spewed to strings and sorted by those strings. This is only + considered if SortKeys is true. + +Dump Usage Simply call spew.Dump with a list of variables you want to dump: @@ -136,7 +133,7 @@ A third option is to call spew.Sdump to get the formatted output as a string: str := spew.Sdump(myVar1, myVar2, ...) -# Sample Dump Output +Sample Dump Output See the Dump example for details on the setup of the types and variables being shown here. @@ -153,14 +150,13 @@ shown here. Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C command as shown. - ([]uint8) (len=32 cap=32) { 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... | 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0| 00000020 31 32 |12| } -# Custom Formatter +Custom Formatter Spew provides a custom formatter that implements the fmt.Formatter interface so that it integrates cleanly with standard fmt package printing functions. The @@ -174,7 +170,7 @@ standard fmt package for formatting. In addition, the custom formatter ignores the width and precision arguments (however they will still work on the format specifiers not handled by the custom formatter). -# Custom Formatter Usage +Custom Formatter Usage The simplest way to make use of the spew custom formatter is to call one of the convenience functions such as spew.Printf, spew.Println, or spew.Printf. The @@ -188,17 +184,15 @@ functions have syntax you are most likely already familiar with: See the Index for the full list convenience functions. -# Sample Formatter Output +Sample Formatter Output Double pointer to a uint8: - %v: <**>5 %+v: <**>(0xf8400420d0->0xf8400420c8)5 %#v: (**uint8)5 %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5 Pointer to circular struct with a uint8 field and a pointer to itself: - %v: <*>{1 <*>} %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)} %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)} @@ -207,7 +201,7 @@ Pointer to circular struct with a uint8 field and a pointer to itself: See the Printf example for details on the setup of variables being shown here. -# Errors +Errors Since it is possible for custom Stringer/error interfaces to panic, spew detects them and handles them internally by printing the panic information diff --git a/publisher/vendor/github.com/davecgh/go-spew/spew/dump.go b/publisher/vendor/github.com/davecgh/go-spew/spew/dump.go index 8323041a..f78d89fc 100644 --- a/publisher/vendor/github.com/davecgh/go-spew/spew/dump.go +++ b/publisher/vendor/github.com/davecgh/go-spew/spew/dump.go @@ -488,15 +488,15 @@ pointer addresses used to indirect to the final value. It provides the following features over the built-in printing facilities provided by the fmt package: - - Pointers are dereferenced and followed - - Circular data structures are detected and handled properly - - Custom Stringer/error interfaces are optionally invoked, including - on unexported types - - Custom types which only implement the Stringer/error interfaces via - a pointer receiver are optionally invoked when passing non-pointer - variables - - Byte arrays and slices are dumped like the hexdump -C command which - includes offsets, byte values in hex, and ASCII output + * Pointers are dereferenced and followed + * Circular data structures are detected and handled properly + * Custom Stringer/error interfaces are optionally invoked, including + on unexported types + * Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + * Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output The configuration options are controlled by an exported package global, spew.Config. See ConfigState for options documentation. diff --git a/publisher/vendor/github.com/pkg/errors/errors.go b/publisher/vendor/github.com/pkg/errors/errors.go index 5d90dd4c..161aea25 100644 --- a/publisher/vendor/github.com/pkg/errors/errors.go +++ b/publisher/vendor/github.com/pkg/errors/errors.go @@ -2,89 +2,89 @@ // // The traditional error handling idiom in Go is roughly akin to // -// if err != nil { -// return err -// } +// if err != nil { +// return err +// } // // which when applied recursively up the call stack results in error reports // without context or debugging information. The errors package allows // programmers to add context to the failure path in their code in a way // that does not destroy the original value of the error. // -// # Adding context to an error +// Adding context to an error // // The errors.Wrap function returns a new error that adds context to the // original error by recording a stack trace at the point Wrap is called, // together with the supplied message. For example // -// _, err := ioutil.ReadAll(r) -// if err != nil { -// return errors.Wrap(err, "read failed") -// } +// _, err := ioutil.ReadAll(r) +// if err != nil { +// return errors.Wrap(err, "read failed") +// } // // If additional control is required, the errors.WithStack and // errors.WithMessage functions destructure errors.Wrap into its component // operations: annotating an error with a stack trace and with a message, // respectively. // -// # Retrieving the cause of an error +// Retrieving the cause of an error // // Using errors.Wrap constructs a stack of errors, adding context to the // preceding error. Depending on the nature of the error it may be necessary // to reverse the operation of errors.Wrap to retrieve the original error // for inspection. Any error value which implements this interface // -// type causer interface { -// Cause() error -// } +// type causer interface { +// Cause() error +// } // // can be inspected by errors.Cause. errors.Cause will recursively retrieve // the topmost error that does not implement causer, which is assumed to be // the original cause. For example: // -// switch err := errors.Cause(err).(type) { -// case *MyError: -// // handle specifically -// default: -// // unknown error -// } +// switch err := errors.Cause(err).(type) { +// case *MyError: +// // handle specifically +// default: +// // unknown error +// } // // Although the causer interface is not exported by this package, it is // considered a part of its stable public interface. // -// # Formatted printing of errors +// Formatted printing of errors // // All error values returned from this package implement fmt.Formatter and can // be formatted by the fmt package. The following verbs are supported: // -// %s print the error. If the error has a Cause it will be -// printed recursively. -// %v see %s -// %+v extended format. Each Frame of the error's StackTrace will -// be printed in detail. +// %s print the error. If the error has a Cause it will be +// printed recursively. +// %v see %s +// %+v extended format. Each Frame of the error's StackTrace will +// be printed in detail. // -// # Retrieving the stack trace of an error or wrapper +// Retrieving the stack trace of an error or wrapper // // New, Errorf, Wrap, and Wrapf record a stack trace at the point they are // invoked. This information can be retrieved with the following interface: // -// type stackTracer interface { -// StackTrace() errors.StackTrace -// } +// type stackTracer interface { +// StackTrace() errors.StackTrace +// } // // The returned errors.StackTrace type is defined as // -// type StackTrace []Frame +// type StackTrace []Frame // // The Frame type represents a call site in the stack trace. Frame supports // the fmt.Formatter interface that can be used for printing information about // the stack trace of this error. For example: // -// if err, ok := err.(stackTracer); ok { -// for _, f := range err.StackTrace() { -// fmt.Printf("%+s:%d\n", f, f) -// } -// } +// if err, ok := err.(stackTracer); ok { +// for _, f := range err.StackTrace() { +// fmt.Printf("%+s:%d\n", f, f) +// } +// } // // Although the stackTracer interface is not exported by this package, it is // considered a part of its stable public interface. @@ -265,9 +265,9 @@ func (w *withMessage) Format(s fmt.State, verb rune) { // An error value has a cause if it implements the following // interface: // -// type causer interface { -// Cause() error -// } +// type causer interface { +// Cause() error +// } // // If the error does not implement Cause, the original error will // be returned. If the error is nil, nil will be returned without further diff --git a/publisher/vendor/github.com/pkg/errors/go113.go b/publisher/vendor/github.com/pkg/errors/go113.go index 2c83c724..be0d10d0 100644 --- a/publisher/vendor/github.com/pkg/errors/go113.go +++ b/publisher/vendor/github.com/pkg/errors/go113.go @@ -1,4 +1,3 @@ -//go:build go1.13 // +build go1.13 package errors diff --git a/publisher/vendor/github.com/pkg/errors/stack.go b/publisher/vendor/github.com/pkg/errors/stack.go index 82784d70..779a8348 100644 --- a/publisher/vendor/github.com/pkg/errors/stack.go +++ b/publisher/vendor/github.com/pkg/errors/stack.go @@ -51,16 +51,16 @@ func (f Frame) name() string { // Format formats the frame according to the fmt.Formatter interface. // -// %s source file -// %d source line -// %n function name -// %v equivalent to %s:%d +// %s source file +// %d source line +// %n function name +// %v equivalent to %s:%d // // Format accepts flags that alter the printing of some verbs, as follows: // -// %+s function name and path of source file relative to the compile time -// GOPATH separated by \n\t (\n\t) -// %+v equivalent to %+s:%d +// %+s function name and path of source file relative to the compile time +// GOPATH separated by \n\t (\n\t) +// %+v equivalent to %+s:%d func (f Frame) Format(s fmt.State, verb rune) { switch verb { case 's': @@ -98,12 +98,12 @@ type StackTrace []Frame // Format formats the stack of Frames according to the fmt.Formatter interface. // -// %s lists source files for each Frame in the stack -// %v lists the source file and line number for each Frame in the stack +// %s lists source files for each Frame in the stack +// %v lists the source file and line number for each Frame in the stack // // Format accepts flags that alter the printing of some verbs, as follows: // -// %+v Prints filename, function, and line number for each Frame in the stack. +// %+v Prints filename, function, and line number for each Frame in the stack. func (st StackTrace) Format(s fmt.State, verb rune) { switch verb { case 'v': diff --git a/publisher/vendor/github.com/pmezard/go-difflib/difflib/difflib.go b/publisher/vendor/github.com/pmezard/go-difflib/difflib/difflib.go index 2a73737a..003e99fa 100644 --- a/publisher/vendor/github.com/pmezard/go-difflib/difflib/difflib.go +++ b/publisher/vendor/github.com/pmezard/go-difflib/difflib/difflib.go @@ -199,15 +199,12 @@ func (m *SequenceMatcher) isBJunk(s string) bool { // If IsJunk is not defined: // // Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where -// -// alo <= i <= i+k <= ahi -// blo <= j <= j+k <= bhi -// +// alo <= i <= i+k <= ahi +// blo <= j <= j+k <= bhi // and for all (i',j',k') meeting those conditions, -// -// k >= k' -// i <= i' -// and if i == i', j <= j' +// k >= k' +// i <= i' +// and if i == i', j <= j' // // In other words, of all maximal matching blocks, return one that // starts earliest in a, and of all those maximal matching blocks that diff --git a/publisher/vendor/github.com/tcolgate/mp3/.gitignore b/publisher/vendor/github.com/tcolgate/mp3/.gitignore new file mode 100644 index 00000000..6b2c8938 --- /dev/null +++ b/publisher/vendor/github.com/tcolgate/mp3/.gitignore @@ -0,0 +1,2 @@ +*.mp3 +!internal/data/*.mp3 diff --git a/publisher/vendor/github.com/tcolgate/mp3/LICENSE b/publisher/vendor/github.com/tcolgate/mp3/LICENSE new file mode 100644 index 00000000..850953da --- /dev/null +++ b/publisher/vendor/github.com/tcolgate/mp3/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2015 Tristan Colgate-McFarlane and badgerodon + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/publisher/vendor/github.com/tcolgate/mp3/README.md b/publisher/vendor/github.com/tcolgate/mp3/README.md new file mode 100644 index 00000000..a8ddb39e --- /dev/null +++ b/publisher/vendor/github.com/tcolgate/mp3/README.md @@ -0,0 +1,6 @@ +MP3 + +Stream orientated mp3 frame decoder + +[![GoDoc](https://godoc.org/github.com/tcolgate/mp3?status.svg)](https://godoc.org/github.com/tcolgate/mp3) + diff --git a/publisher/vendor/github.com/tcolgate/mp3/doc.go b/publisher/vendor/github.com/tcolgate/mp3/doc.go new file mode 100644 index 00000000..2f515980 --- /dev/null +++ b/publisher/vendor/github.com/tcolgate/mp3/doc.go @@ -0,0 +1,31 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015 Tristan Colgate-McFarlane and badgerodon +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +// Package mp3 provides decoding of mp3 files into their underlying frames. It +// is primarily intended for streaming tasks with minimal internal buffering +// and no requirement to seek. +// +// The implementation started as a reworking of github.com/badgerodon/mp3, and +// has also drawn from Konrad Windszus' excellent article on mp3 frame parsing +// http://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header +// +// TODO CRC isn't currently checked. +package mp3 diff --git a/publisher/vendor/github.com/tcolgate/mp3/framechannelmode_string.go b/publisher/vendor/github.com/tcolgate/mp3/framechannelmode_string.go new file mode 100644 index 00000000..0564d06a --- /dev/null +++ b/publisher/vendor/github.com/tcolgate/mp3/framechannelmode_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type=FrameChannelMode"; DO NOT EDIT + +package mp3 + +import "fmt" + +const _FrameChannelMode_name = "StereoJointStereoDualChannelSingleChannelChannelModeMax" + +var _FrameChannelMode_index = [...]uint8{0, 6, 17, 28, 41, 55} + +func (i FrameChannelMode) String() string { + if i >= FrameChannelMode(len(_FrameChannelMode_index)-1) { + return fmt.Sprintf("FrameChannelMode(%d)", i) + } + return _FrameChannelMode_name[_FrameChannelMode_index[i]:_FrameChannelMode_index[i+1]] +} diff --git a/publisher/vendor/github.com/tcolgate/mp3/frameemphasis_string.go b/publisher/vendor/github.com/tcolgate/mp3/frameemphasis_string.go new file mode 100644 index 00000000..2b3f0775 --- /dev/null +++ b/publisher/vendor/github.com/tcolgate/mp3/frameemphasis_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type=FrameEmphasis"; DO NOT EDIT + +package mp3 + +import "fmt" + +const _FrameEmphasis_name = "EmphNoneEmph5015EmphReservedEmphCCITJ17EmphMax" + +var _FrameEmphasis_index = [...]uint8{0, 8, 16, 28, 39, 46} + +func (i FrameEmphasis) String() string { + if i >= FrameEmphasis(len(_FrameEmphasis_index)-1) { + return fmt.Sprintf("FrameEmphasis(%d)", i) + } + return _FrameEmphasis_name[_FrameEmphasis_index[i]:_FrameEmphasis_index[i+1]] +} diff --git a/publisher/vendor/github.com/tcolgate/mp3/framelayer_string.go b/publisher/vendor/github.com/tcolgate/mp3/framelayer_string.go new file mode 100644 index 00000000..e5c2f9bc --- /dev/null +++ b/publisher/vendor/github.com/tcolgate/mp3/framelayer_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type=FrameLayer"; DO NOT EDIT + +package mp3 + +import "fmt" + +const _FrameLayer_name = "LayerReservedLayer3Layer2Layer1LayerMax" + +var _FrameLayer_index = [...]uint8{0, 13, 19, 25, 31, 39} + +func (i FrameLayer) String() string { + if i >= FrameLayer(len(_FrameLayer_index)-1) { + return fmt.Sprintf("FrameLayer(%d)", i) + } + return _FrameLayer_name[_FrameLayer_index[i]:_FrameLayer_index[i+1]] +} diff --git a/publisher/vendor/github.com/tcolgate/mp3/frames.go b/publisher/vendor/github.com/tcolgate/mp3/frames.go new file mode 100644 index 00000000..25351356 --- /dev/null +++ b/publisher/vendor/github.com/tcolgate/mp3/frames.go @@ -0,0 +1,448 @@ +package mp3 + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "time" +) + +type ( + // Decoder translates a io.Reader into a series of frames + Decoder struct { + src io.Reader + err error + } + + // Frame represents one individual mp3 frame + Frame struct { + buf []byte + } + + // FrameHeader represents the entire header of a frame + FrameHeader []byte + + // FrameVersion is the MPEG version given in the frame header + FrameVersion byte + // FrameLayer is the MPEG layer given in the frame header + FrameLayer byte + // FrameEmphasis is the Emphasis value from the frame header + FrameEmphasis byte + // FrameChannelMode is the Channel mode from the frame header + FrameChannelMode byte + // FrameBitRate is the bit rate from the frame header + FrameBitRate int + // FrameSampleRate is the sample rate from teh frame header + FrameSampleRate int + + // FrameSideInfo holds the SideInfo bytes from the frame + FrameSideInfo []byte +) + +//go:generate stringer -type=FrameVersion +const ( + MPEG25 FrameVersion = iota + MPEGReserved + MPEG2 + MPEG1 + VERSIONMAX +) + +//go:generate stringer -type=FrameLayer +const ( + LayerReserved FrameLayer = iota + Layer3 + Layer2 + Layer1 + LayerMax +) + +//go:generate stringer -type=FrameEmphasis +const ( + EmphNone FrameEmphasis = iota + Emph5015 + EmphReserved + EmphCCITJ17 + EmphMax +) + +//go:generate stringer -type=FrameChannelMode +const ( + Stereo FrameChannelMode = iota + JointStereo + DualChannel + SingleChannel + ChannelModeMax +) + +const ( + // ErrInvalidBitrate indicates that the header information did not contain a recognized bitrate + ErrInvalidBitrate FrameBitRate = -1 +) + +var ( + bitrates = [VERSIONMAX][LayerMax][15]int{ + { // MPEG 2.5 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // LayerReserved + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}, // Layer3 + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}, // Layer2 + {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256}, // Layer1 + }, + { // Reserved + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // LayerReserved + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // Layer3 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // Layer2 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // Layer1 + }, + { // MPEG 2 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // LayerReserved + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}, // Layer3 + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}, // Layer2 + {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256}, // Layer1 + }, + { // MPEG 1 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // LayerReserved + {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320}, // Layer3 + {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384}, // Layer2 + {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448}, // Layer1 + }, + } + sampleRates = [int(VERSIONMAX)][3]int{ + {11025, 12000, 8000}, //MPEG25 + {0, 0, 0}, //MPEGReserved + {22050, 24000, 16000}, //MPEG2 + {44100, 48000, 32000}, //MPEG1 + } + + // ErrInvalidSampleRate indicates that no samplerate could be found for the frame header provided + ErrInvalidSampleRate = FrameSampleRate(-1) + + samplesPerFrame = [VERSIONMAX][LayerMax]int{ + { // MPEG25 + 0, + 576, + 1152, + 384, + }, + { // Reserved + 0, + 0, + 0, + 0, + }, + { // MPEG2 + 0, + 576, + 1152, + 384, + }, + { // MPEG1 + 0, + 1152, + 1152, + 384, + }, + } + slotSize = [LayerMax]int{ + 0, // LayerReserved + 1, // Layer3 + 1, // Layer2 + 4, // Layer1 + } + + // ErrNoSyncBits implies we could not find a valid frame header sync bit before EOF + ErrNoSyncBits = errors.New("EOF before sync bits found") + + // ErrPrematureEOF indicates that the filed ended before a complete frame could be read + ErrPrematureEOF = errors.New("EOF mid stream") +) + +func init() { + bitrates[MPEG25] = bitrates[MPEG2] + samplesPerFrame[MPEG25] = samplesPerFrame[MPEG2] +} + +// NewDecoder returns a decoder that will process the provided reader. +func NewDecoder(r io.Reader) *Decoder { + return &Decoder{r, nil} +} + +// fill slice d until it is of len l, using bytes from reader r +func fillbuf(d []byte, r io.Reader, l int) (res []byte, err error) { + if len(d) >= l { + // we already have enough bytes + return d, nil + } + + // How many bytes do we need to fetch + missing := l - len(d) + + // Does d have sufficient capacity? if not extent it + if cap(d) < l { + if d == nil { + d = make([]byte, l) + } else { + il := len(d) + d = d[:cap(d)] // stretch d to it's full capacity + d = append(d, make([]byte, l-cap(d))...) + d = d[:il] //we've extended the capm reset len + } + } + + d = d[:l] + _, err = io.ReadFull(r, d[len(d)-missing:]) + + return d, err +} + +// Decode reads the next complete discovered frame into the provided +// Frame struct. A count of skipped bytes will be written to skipped. +func (d *Decoder) Decode(v *Frame, skipped *int) (err error) { + // Truncate the array + v.buf = v.buf[:0] + + hLen := 4 + // locate a sync frame + *skipped = 0 + for { + v.buf, err = fillbuf(v.buf, d.src, hLen) + if err != nil { + return err + } + if v.buf[0] == 0xFF && (v.buf[1]&0xE0 == 0xE0) && + v.Header().Emphasis() != EmphReserved && + v.Header().Layer() != LayerReserved && + v.Header().Version() != MPEGReserved && + v.Header().SampleRate() != -1 && + v.Header().BitRate() != -1 { + break + } + switch { + case v.buf[1] == 0xFF: + v.buf = v.buf[1:] + *skipped++ + default: + v.buf = v.buf[2:] + *skipped += 2 + } + } + + crcLen := 0 + if v.Header().Protection() { + crcLen = 2 + v.buf, err = fillbuf(v.buf, d.src, hLen+crcLen) + if err != nil { + return err + } + } + + sideLen, err := v.SideInfoLength() + if err != nil { + return err + } + + v.buf, err = fillbuf(v.buf, d.src, hLen+crcLen+sideLen) + if err != nil { + return err + } + + dataLen := v.Size() + v.buf, err = fillbuf(v.buf, d.src, dataLen) + if err != nil { + return err + } + + return nil +} + +// SideInfoLength retursn the expected side info length for this +// mp3 frame +func (f *Frame) SideInfoLength() (int, error) { + switch f.Header().Version() { + case MPEG1: + switch f.Header().ChannelMode() { + case SingleChannel: + return 17, nil + case Stereo, JointStereo, DualChannel: + return 32, nil + default: + return 0, errors.New("bad channel mode") + } + case MPEG2, MPEG25: + switch f.Header().ChannelMode() { + case SingleChannel: + return 9, nil + case Stereo, JointStereo, DualChannel: + return 17, nil + default: + return 0, errors.New("bad channel mode") + } + default: + return 0, fmt.Errorf("bad version (%v)", f.Header().Version()) + } +} + +// Header returns the header for this frame +func (f *Frame) Header() FrameHeader { + return FrameHeader(f.buf[0:4]) +} + +// CRC returns the CRC word stored in this frame +func (f *Frame) CRC() (uint16, error) { + var crc uint16 + if !f.Header().Protection() { + return 0, nil + } + crcdata := bytes.NewReader(f.buf[4:6]) + err := binary.Read(crcdata, binary.BigEndian, &crc) + return crc, err +} + +// SideInfo returns the side info for this frame +func (f *Frame) SideInfo() FrameSideInfo { + if f.Header().Protection() { + return FrameSideInfo(f.buf[6:]) + } + return FrameSideInfo(f.buf[4:]) +} + +// Frame returns a string describing this frame, header and side info +func (f *Frame) String() string { + str := "" + str += fmt.Sprintf("Header: \n%s", f.Header()) + str += fmt.Sprintf("SideInfo: \n%s", f.SideInfo()) + crc, err := f.CRC() + str += fmt.Sprintf("CRC: %x (err: %v)\n", crc, err) + str += fmt.Sprintf("Samples: %v\n", f.Samples()) + str += fmt.Sprintf("Size: %v\n", f.Size()) + str += fmt.Sprintf("Duration: %v\n", f.Duration()) + return str +} + +// Version returns the MPEG version from the header +func (h FrameHeader) Version() FrameVersion { + return FrameVersion((h[1] >> 3) & 0x03) +} + +// Layer returns the MPEG layer from the header +func (h FrameHeader) Layer() FrameLayer { + return FrameLayer((h[1] >> 1) & 0x03) +} + +// Protection indicates if there is a CRC present after the header (before the side data) +func (h FrameHeader) Protection() bool { + return (h[1] & 0x01) != 0x01 +} + +// BitRate returns the calculated bit rate from the header +func (h FrameHeader) BitRate() FrameBitRate { + bitrateIdx := (h[2] >> 4) & 0x0F + if bitrateIdx == 0x0F { + return ErrInvalidBitrate + } + br := bitrates[h.Version()][h.Layer()][bitrateIdx] * 1000 + if br == 0 { + return ErrInvalidBitrate + } + return FrameBitRate(br) +} + +// SampleRate returns the samplerate from the header +func (h FrameHeader) SampleRate() FrameSampleRate { + sri := (h[2] >> 2) & 0x03 + if sri == 0x03 { + return ErrInvalidSampleRate + } + return FrameSampleRate(sampleRates[h.Version()][sri]) +} + +// Pad returns the pad bit, indicating if there are extra samples +// in this frame to make up the correct bitrate +func (h FrameHeader) Pad() bool { + return ((h[2] >> 1) & 0x01) == 0x01 +} + +// Private retrusn the Private bit from the header +func (h FrameHeader) Private() bool { + return (h[2] & 0x01) == 0x01 +} + +// ChannelMode returns the channel mode from the header +func (h FrameHeader) ChannelMode() FrameChannelMode { + return FrameChannelMode((h[3] >> 6) & 0x03) +} + +// CopyRight returns the CopyRight bit from the header +func (h FrameHeader) CopyRight() bool { + return (h[3]>>3)&0x01 == 0x01 +} + +// Original returns the "original content" bit from the header +func (h FrameHeader) Original() bool { + return (h[3]>>2)&0x01 == 0x01 +} + +// Emphasis returns the Emphasis from the header +func (h FrameHeader) Emphasis() FrameEmphasis { + return FrameEmphasis((h[3] & 0x03)) +} + +// String dumps the frame header as a string for display purposes +func (h FrameHeader) String() string { + str := "" + str += fmt.Sprintf(" Layer: %v\n", h.Layer()) + str += fmt.Sprintf(" Version: %v\n", h.Version()) + str += fmt.Sprintf(" Protection: %v\n", h.Protection()) + str += fmt.Sprintf(" BitRate: %v\n", h.BitRate()) + str += fmt.Sprintf(" SampleRate: %v\n", h.SampleRate()) + str += fmt.Sprintf(" Pad: %v\n", h.Pad()) + str += fmt.Sprintf(" Private: %v\n", h.Private()) + str += fmt.Sprintf(" ChannelMode: %v\n", h.ChannelMode()) + str += fmt.Sprintf(" CopyRight: %v\n", h.CopyRight()) + str += fmt.Sprintf(" Original: %v\n", h.Original()) + str += fmt.Sprintf(" Emphasis: %v\n", h.Emphasis()) + return str +} + +// NDataBegin is the number of bytes before the frame header at which the sample data begins +// 0 indicates that the data begins after the side channel information. This data is the +// data from the "bit reservoir" and can be up to 511 bytes +func (i FrameSideInfo) NDataBegin() uint16 { + return (uint16(i[0]) << 1 & (uint16(i[1]) >> 7)) +} + +// Samples determines the number of samples based on the MPEG version and Layer from the header +func (f *Frame) Samples() int { + return samplesPerFrame[f.Header().Version()][f.Header().Layer()] +} + +// Size clculates the expected size of this frame in bytes based on the header +// information +func (f *Frame) Size() int { + bps := float64(f.Samples()) / 8 + fsize := (bps * float64(f.Header().BitRate())) / float64(f.Header().SampleRate()) + if f.Header().Pad() { + fsize += float64(slotSize[f.Header().Layer()]) + } + return int(fsize) +} + +// Duration calculates the time duration of this frame based on the samplerate and number of samples +func (f *Frame) Duration() time.Duration { + ms := (1000 / float64(f.Header().SampleRate())) * float64(f.Samples()) + return time.Duration(int(float64(time.Millisecond) * ms)) +} + +// String renders the side info as a string for display purposes +func (i FrameSideInfo) String() string { + str := "" + str += fmt.Sprintf(" NDataBegin: %v\n", i.NDataBegin()) + return str +} + +// Reader returns an io.Reader that reads the individual bytes from the frame +func (f *Frame) Reader() io.Reader { + return bytes.NewReader(f.buf) +} diff --git a/publisher/vendor/github.com/tcolgate/mp3/frameversion_string.go b/publisher/vendor/github.com/tcolgate/mp3/frameversion_string.go new file mode 100644 index 00000000..4e2f8417 --- /dev/null +++ b/publisher/vendor/github.com/tcolgate/mp3/frameversion_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type=FrameVersion"; DO NOT EDIT + +package mp3 + +import "fmt" + +const _FrameVersion_name = "MPEG25MPEGReservedMPEG2MPEG1VERSIONMAX" + +var _FrameVersion_index = [...]uint8{0, 6, 18, 23, 28, 38} + +func (i FrameVersion) String() string { + if i >= FrameVersion(len(_FrameVersion_index)-1) { + return fmt.Sprintf("FrameVersion(%d)", i) + } + return _FrameVersion_name[_FrameVersion_index[i]:_FrameVersion_index[i+1]] +} diff --git a/publisher/vendor/github.com/tcolgate/mp3/header.go b/publisher/vendor/github.com/tcolgate/mp3/header.go new file mode 100644 index 00000000..4bb8dd68 --- /dev/null +++ b/publisher/vendor/github.com/tcolgate/mp3/header.go @@ -0,0 +1,83 @@ +package mp3 + +/* +func (this *FrameHeader) Parse(bs []byte) error { + this.Size = 0 + this.Samples = 0 + this.Duration = 0 + + if len(bs) < 4 { + return fmt.Errorf("not enough bytes") + } + if bs[0] != 0xFF || (bs[1]&0xE0) != 0xE0 { + return fmt.Errorf("missing sync word, got: %x, %x", bs[0], bs[1]) + } + this.Version = Version((bs[1] >> 3) & 0x03) + if this.Version == MPEGReserved { + return fmt.Errorf("reserved mpeg version") + } + + this.Layer = Layer(((bs[1] >> 1) & 0x03)) + if this.Layer == LayerReserved { + return fmt.Errorf("reserved layer") + } + + this.Protection = (bs[1] & 0x01) != 0x01 + + bitrateIdx := (bs[2] >> 4) & 0x0F + if bitrateIdx == 0x0F { + return fmt.Errorf("invalid bitrate: %v", bitrateIdx) + } + this.Bitrate = bitrates[this.Version][this.Layer][bitrateIdx] * 1000 + if this.Bitrate == 0 { + return fmt.Errorf("invalid bitrate: %v", bitrateIdx) + } + + sampleRateIdx := (bs[2] >> 2) & 0x03 + if sampleRateIdx == 0x03 { + return fmt.Errorf("invalid sample rate: %v", sampleRateIdx) + } + this.SampleRate = sampleRates[this.Version][sampleRateIdx] + + this.Pad = ((bs[2] >> 1) & 0x01) == 0x01 + + this.Private = (bs[2] & 0x01) == 0x01 + + this.ChannelMode = ChannelMode(bs[3]>>6) & 0x03 + + // todo: mode extension + + this.CopyRight = (bs[3]>>3)&0x01 == 0x01 + + this.Original = (bs[3]>>2)&0x01 == 0x01 + + this.Emphasis = Emphasis(bs[3] & 0x03) + if this.Emphasis == EmphReserved { + return fmt.Errorf("reserved emphasis") + } + + this.Size = this.size() + this.Samples = this.samples() + this.Duration = this.duration() + + return nil +} + +func (this *FrameHeader) samples() int { + return samplesPerFrame[this.Version][this.Layer] +} + +func (this *FrameHeader) size() int64 { + bps := float64(this.samples()) / 8 + fsize := (bps * float64(this.Bitrate)) / float64(this.SampleRate) + if this.Pad { + fsize += float64(slotSize[this.Layer]) + } + return int64(fsize) +} + +func (this *FrameHeader) duration() time.Duration { + ms := (1000 / float64(this.SampleRate)) * float64(this.samples()) + return time.Duration(time.Duration(float64(time.Millisecond) * ms)) +} +*/ diff --git a/publisher/vendor/github.com/tcolgate/mp3/internal/data/bindata.go b/publisher/vendor/github.com/tcolgate/mp3/internal/data/bindata.go new file mode 100644 index 00000000..6ce18e71 --- /dev/null +++ b/publisher/vendor/github.com/tcolgate/mp3/internal/data/bindata.go @@ -0,0 +1,250 @@ +package data + +import ( + "bytes" + "compress/gzip" + "fmt" + "io" + "reflect" + "strings" + "unsafe" + "os" + "time" + "io/ioutil" + "path" + "path/filepath" +) + +func bindata_read(data, name string) ([]byte, error) { + var empty [0]byte + sx := (*reflect.StringHeader)(unsafe.Pointer(&data)) + b := empty[:] + bx := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + bx.Data = sx.Data + bx.Len = len(data) + bx.Cap = bx.Len + + gz, err := gzip.NewReader(bytes.NewBuffer(b)) + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + + var buf bytes.Buffer + _, err = io.Copy(&buf, gz) + gz.Close() + + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + + return buf.Bytes(), nil +} + +type asset struct { + bytes []byte + info os.FileInfo +} + +type bindata_file_info struct { + name string + size int64 + mode os.FileMode + modTime time.Time +} + +func (fi bindata_file_info) Name() string { + return fi.name +} +func (fi bindata_file_info) Size() int64 { + return fi.size +} +func (fi bindata_file_info) Mode() os.FileMode { + return fi.mode +} +func (fi bindata_file_info) ModTime() time.Time { + return fi.modTime +} +func (fi bindata_file_info) IsDir() bool { + return false +} +func (fi bindata_file_info) Sys() interface{} { + return nil +} + +var _silent_1frame_go = "\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x64\x90\x41\x4b\x3b\x31\x10\xc5\xcf\x3b\x9f\xe2\xfd\xf7\xb4\x85\x7f\x1b\xaa\x17\x11\x7a\x50\xc1\x8b\x47\x8f\x22\x92\xee\xce\xa6\xa1\x9b\x99\x90\xa4\x4a\x91\x7e\x77\x77\xe3\x45\xf1\x10\x02\x33\x6f\x7e\xef\xcd\x44\xdb\x1f\xad\x63\x0c\xb6\x58\x22\x1f\xa2\xa6\x82\x76\x52\xd7\x12\xbd\xdb\x84\x8e\x1a\x63\xf0\xec\x27\x96\x72\x7f\x2e\x9c\xe1\x33\xca\x81\x91\xec\x47\x1d\xc2\x98\x34\xc0\x62\x7b\x75\x83\xa7\xbd\xc9\xd0\x11\x93\x0d\x0c\x96\x5e\x07\x1e\x20\x5a\x0e\x5e\x9c\x70\xce\xd4\xfc\x04\xbd\xbc\xee\xe7\x9f\x56\x44\xc6\x38\xbd\x75\x2c\x9c\x6c\x61\x38\x5d\xef\xbd\x54\xf6\x3a\x1e\xdd\xb7\xcb\x5a\x34\x70\xe8\x35\x9e\xb1\x31\x34\x9e\xa4\x87\x17\x5f\xba\x15\x3e\xa9\x59\x82\x72\xaa\x4f\xd3\x2f\x93\xff\xb5\xbe\xc3\x5d\xce\x5c\xba\x76\x41\x99\x5c\xdb\x6f\xdb\x31\xcd\x31\x37\x21\x5e\xb7\x2b\x6a\xfc\x58\x95\xff\x76\x10\x3f\x2d\xcc\x66\xbe\xc1\xe6\x71\xd6\x4f\x63\xd7\x3e\xe8\x69\xaa\x9b\x40\x23\x0b\xfe\x10\x60\x17\xfe\xc2\xb9\xd0\x85\xbe\x02\x00\x00\xff\xff\x0e\x9e\x37\x70\x54\x01\x00\x00" + +func silent_1frame_go_bytes() ([]byte, error) { + return bindata_read( + _silent_1frame_go, + "silent_1frame.go", + ) +} + +func silent_1frame_go() (*asset, error) { + bytes, err := silent_1frame_go_bytes() + if err != nil { + return nil, err + } + + info := bindata_file_info{name: "silent_1frame.go", size: 340, mode: os.FileMode(420), modTime: time.Unix(1424984811, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _silent_1frame_mp3 = "\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xfa\xff\x7b\x43\x0a\x03\xff\x07\x06\x86\x4c\x06\x06\x06\x0e\x06\x06\x5e\x05\x06\x06\x46\x20\x5a\x02\xe4\x02\x99\x26\x0d\x0c\x0c\x2c\x3e\x8e\xbe\xae\xc6\x7a\x96\x96\x7a\xa6\x0c\xa3\x60\x14\x50\x08\x00\x01\x00\x00\xff\xff\xa1\x6f\x84\x53\x72\x02\x00\x00" + +func silent_1frame_mp3_bytes() ([]byte, error) { + return bindata_read( + _silent_1frame_mp3, + "silent_1frame.mp3", + ) +} + +func silent_1frame_mp3() (*asset, error) { + bytes, err := silent_1frame_mp3_bytes() + if err != nil { + return nil, err + } + + info := bindata_file_info{name: "silent_1frame.mp3", size: 626, mode: os.FileMode(420), modTime: time.Unix(1424763406, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +// Asset loads and returns the asset for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func Asset(name string) ([]byte, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + } + return a.bytes, nil + } + return nil, fmt.Errorf("Asset %s not found", name) +} + +// AssetInfo loads and returns the asset info for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func AssetInfo(name string) (os.FileInfo, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + } + return a.info, nil + } + return nil, fmt.Errorf("AssetInfo %s not found", name) +} + +// AssetNames returns the names of the assets. +func AssetNames() []string { + names := make([]string, 0, len(_bindata)) + for name := range _bindata { + names = append(names, name) + } + return names +} + +// _bindata is a table, holding each asset generator, mapped to its name. +var _bindata = map[string]func() (*asset, error){ + "silent_1frame.go": silent_1frame_go, + "silent_1frame.mp3": silent_1frame_mp3, +} + +// AssetDir returns the file names below a certain +// directory embedded in the file by go-bindata. +// For example if you run go-bindata on data/... and data contains the +// following hierarchy: +// data/ +// foo.txt +// img/ +// a.png +// b.png +// then AssetDir("data") would return []string{"foo.txt", "img"} +// AssetDir("data/img") would return []string{"a.png", "b.png"} +// AssetDir("foo.txt") and AssetDir("notexist") would return an error +// AssetDir("") will return []string{"data"}. +func AssetDir(name string) ([]string, error) { + node := _bintree + if len(name) != 0 { + cannonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(cannonicalName, "/") + for _, p := range pathList { + node = node.Children[p] + if node == nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + } + } + if node.Func != nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + rv := make([]string, 0, len(node.Children)) + for name := range node.Children { + rv = append(rv, name) + } + return rv, nil +} + +type _bintree_t struct { + Func func() (*asset, error) + Children map[string]*_bintree_t +} +var _bintree = &_bintree_t{nil, map[string]*_bintree_t{ + "silent_1frame.go": &_bintree_t{silent_1frame_go, map[string]*_bintree_t{ + }}, + "silent_1frame.mp3": &_bintree_t{silent_1frame_mp3, map[string]*_bintree_t{ + }}, +}} + +// Restore an asset under the given directory +func RestoreAsset(dir, name string) error { + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, path.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) + if err != nil { + return err + } + return nil +} + +// Restore assets under the given directory recursively +func RestoreAssets(dir, name string) error { + children, err := AssetDir(name) + if err != nil { // File + return RestoreAsset(dir, name) + } else { // Dir + for _, child := range children { + err = RestoreAssets(dir, path.Join(name, child)) + if err != nil { + return err + } + } + } + return nil +} + +func _filePath(dir, name string) string { + cannonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) +} + diff --git a/publisher/vendor/github.com/tcolgate/mp3/internal/data/silent_1frame.go b/publisher/vendor/github.com/tcolgate/mp3/internal/data/silent_1frame.go new file mode 100644 index 00000000..8a2b1cba --- /dev/null +++ b/publisher/vendor/github.com/tcolgate/mp3/internal/data/silent_1frame.go @@ -0,0 +1,17 @@ +package data + +import "log" + +var ( + // SilentBytes is the raw data from a 128 Kb/s of lame encoded nothingness + SilentBytes []byte +) + +//go:generate go-bindata -pkg data -nomemcopy ./ +func init() { + var err error + SilentBytes, err = Asset("silent_1frame.mp3") + if err != nil { + log.Fatalf("Could not open silent_1frame.mp3 asset") + } +} diff --git a/publisher/vendor/github.com/tcolgate/mp3/internal/data/silent_1frame.mp3 b/publisher/vendor/github.com/tcolgate/mp3/internal/data/silent_1frame.mp3 new file mode 100644 index 00000000..e8c85be1 Binary files /dev/null and b/publisher/vendor/github.com/tcolgate/mp3/internal/data/silent_1frame.mp3 differ diff --git a/publisher/vendor/github.com/tcolgate/mp3/silence.go b/publisher/vendor/github.com/tcolgate/mp3/silence.go new file mode 100644 index 00000000..c8e604a8 --- /dev/null +++ b/publisher/vendor/github.com/tcolgate/mp3/silence.go @@ -0,0 +1,51 @@ +package mp3 + +import ( + "bytes" + "io" + + "github.com/tcolgate/mp3/internal/data" +) + +var ( + // SilentFrame is the sound of Ripley screaming on the Nostromo, from the outside + SilentFrame *Frame + + // SilentBytes is the raw raw data behind SilentFrame + SilentBytes []byte +) + +func init() { + skipped := 0 + SilentBytes = data.SilentBytes + + dec := NewDecoder(bytes.NewBuffer(SilentBytes)) + frame := Frame{} + SilentFrame = &frame + dec.Decode(&frame, &skipped) +} + +type silenceReader struct { + int // Location into the silence frame +} + +func (s *silenceReader) Close() error { + return nil +} + +func (s *silenceReader) Read(out []byte) (int, error) { + for i := 0; i < len(out); i++ { + out[i] = SilentBytes[s.int] + s.int++ + if s.int >= len(SilentBytes) { + s.int = 0 + } + } + + return len(out), nil +} + +// MakeSilence provides a constant stream of silenct frames. +func MakeSilence() io.ReadCloser { + return &silenceReader{0} +} diff --git a/publisher/vendor/github.com/umputun/go-flags/flags.go b/publisher/vendor/github.com/umputun/go-flags/flags.go index a6acf1be..ac2157dd 100644 --- a/publisher/vendor/github.com/umputun/go-flags/flags.go +++ b/publisher/vendor/github.com/umputun/go-flags/flags.go @@ -8,45 +8,46 @@ The flags package is similar in functionality to the go built-in flag package but provides more options and uses reflection to provide a convenient and succinct way of specifying command line options. -# Supported features + +Supported features The following features are supported in go-flags: - Options with short names (-v) - Options with long names (--verbose) - Options with and without arguments (bool v.s. other type) - Options with optional arguments and default values - Option default values from ENVIRONMENT_VARIABLES, including slice and map values - Multiple option groups each containing a set of options - Generate and print well-formatted help message - Passing remaining command line arguments after -- (optional) - Ignoring unknown command line options (optional) - Supports -I/usr/include -I=/usr/include -I /usr/include option argument specification - Supports multiple short options -aux - Supports all primitive go types (string, int{8..64}, uint{8..64}, float) - Supports same option multiple times (can store in slice or last option counts) - Supports maps - Supports function callbacks - Supports namespaces for (nested) option groups + Options with short names (-v) + Options with long names (--verbose) + Options with and without arguments (bool v.s. other type) + Options with optional arguments and default values + Option default values from ENVIRONMENT_VARIABLES, including slice and map values + Multiple option groups each containing a set of options + Generate and print well-formatted help message + Passing remaining command line arguments after -- (optional) + Ignoring unknown command line options (optional) + Supports -I/usr/include -I=/usr/include -I /usr/include option argument specification + Supports multiple short options -aux + Supports all primitive go types (string, int{8..64}, uint{8..64}, float) + Supports same option multiple times (can store in slice or last option counts) + Supports maps + Supports function callbacks + Supports namespaces for (nested) option groups Additional features specific to Windows: + Options with short names (/v) + Options with long names (/verbose) + Windows-style options with arguments use a colon as the delimiter + Modify generated help message with Windows-style / options + Windows style options can be disabled at build time using the "forceposix" + build tag - Options with short names (/v) - Options with long names (/verbose) - Windows-style options with arguments use a colon as the delimiter - Modify generated help message with Windows-style / options - Windows style options can be disabled at build time using the "forceposix" - build tag -# Basic usage +Basic usage The flags package uses structs, reflection and struct field tags to allow users to specify command line options. This results in very simple and concise specification of your application options. For example: - type Options struct { - Verbose []bool `short:"v" long:"verbose" description:"Show verbose debug information"` - } + type Options struct { + Verbose []bool `short:"v" long:"verbose" description:"Show verbose debug information"` + } This specifies one option with a short name -v and a long name --verbose. When either -v or --verbose is found on the command line, a 'true' value @@ -59,9 +60,9 @@ whenever the option is encountered, a value is appended to the slice. Map options from string to primitive type are also supported. On the command line, you specify the value for such an option as key:value. For example - type Options struct { - AuthorInfo string[string] `short:"a"` - } + type Options struct { + AuthorInfo string[string] `short:"a"` + } Then, the AuthorInfo map can be filled with something like -a name:Jesse -a "surname:van den Kieboom". @@ -70,94 +71,96 @@ Finally, for full control over the conversion between command line argument values and options, user defined types can choose to implement the Marshaler and Unmarshaler interfaces. -# Available field tags + +Available field tags The following is a list of tags for struct fields supported by go-flags: - short: the short name of the option (single character) - long: the long name of the option - required: if non empty, makes the option required to appear on the command - line. If a required option is not present, the parser will - return ErrRequired (optional) - description: the description of the option (optional) - long-description: the long description of the option. Currently only - displayed in generated man pages (optional) - no-flag: if non-empty, this field is ignored as an option (optional) - - optional: if non-empty, makes the argument of the option optional. When an - argument is optional it can only be specified using - --option=argument (optional) - optional-value: the value of an optional option when the option occurs - without an argument. This tag can be specified multiple - times in the case of maps or slices (optional) - default: the default value of an option. This tag can be specified - multiple times in the case of slices or maps (optional) - default-mask: when specified, this value will be displayed in the help - instead of the actual default value. This is useful - mostly for hiding otherwise sensitive information from - showing up in the help. If default-mask takes the special - value "-", then no default value will be shown at all - (optional) - env: the default value of the option is overridden from the - specified environment variable, if one has been defined. - (optional) - env-delim: the 'env' default value from environment is split into - multiple values with the given delimiter string, use with - slices and maps (optional) - value-name: the name of the argument value (to be shown in the help) - (optional) - choice: limits the values for an option to a set of values. - Repeat this tag once for each allowable value. - e.g. `long:"animal" choice:"cat" choice:"dog"` - hidden: if non-empty, the option is not visible in the help or man page. - - base: a base (radix) used to convert strings to integer values, the - default base is 10 (i.e. decimal) (optional) - - ini-name: the explicit ini option name (optional) - no-ini: if non-empty this field is ignored as an ini option - (optional) - - group: when specified on a struct field, makes the struct - field a separate group with the given name (optional) - namespace: when specified on a group struct field, the namespace - gets prepended to every option's long name and - subgroup's namespace of this group, separated by - the parser's namespace delimiter (optional) - env-namespace: when specified on a group struct field, the env-namespace - gets prepended to every option's env key and - subgroup's env-namespace of this group, separated by - the parser's env-namespace delimiter (optional) - command: when specified on a struct field, makes the struct - field a (sub)command with the given name (optional) - subcommands-optional: when specified on a command struct field, makes - any subcommands of that command optional (optional) - alias: when specified on a command struct field, adds the - specified name as an alias for the command. Can be - be specified multiple times to add more than one - alias (optional) - positional-args: when specified on a field with a struct type, - uses the fields of that struct to parse remaining - positional command line arguments into (in order - of the fields). If a field has a slice type, - then all remaining arguments will be added to it. - Positional arguments are optional by default, - unless the "required" tag is specified together - with the "positional-args" tag. The "required" tag - can also be set on the individual rest argument - fields, to require only the first N positional - arguments. If the "required" tag is set on the - rest arguments slice, then its value determines - the minimum amount of rest arguments that needs to - be provided (e.g. `required:"2"`) (optional) - positional-arg-name: used on a field in a positional argument struct; name - of the positional argument placeholder to be shown in - the help (optional) + short: the short name of the option (single character) + long: the long name of the option + required: if non empty, makes the option required to appear on the command + line. If a required option is not present, the parser will + return ErrRequired (optional) + description: the description of the option (optional) + long-description: the long description of the option. Currently only + displayed in generated man pages (optional) + no-flag: if non-empty, this field is ignored as an option (optional) + + optional: if non-empty, makes the argument of the option optional. When an + argument is optional it can only be specified using + --option=argument (optional) + optional-value: the value of an optional option when the option occurs + without an argument. This tag can be specified multiple + times in the case of maps or slices (optional) + default: the default value of an option. This tag can be specified + multiple times in the case of slices or maps (optional) + default-mask: when specified, this value will be displayed in the help + instead of the actual default value. This is useful + mostly for hiding otherwise sensitive information from + showing up in the help. If default-mask takes the special + value "-", then no default value will be shown at all + (optional) + env: the default value of the option is overridden from the + specified environment variable, if one has been defined. + (optional) + env-delim: the 'env' default value from environment is split into + multiple values with the given delimiter string, use with + slices and maps (optional) + value-name: the name of the argument value (to be shown in the help) + (optional) + choice: limits the values for an option to a set of values. + Repeat this tag once for each allowable value. + e.g. `long:"animal" choice:"cat" choice:"dog"` + hidden: if non-empty, the option is not visible in the help or man page. + + base: a base (radix) used to convert strings to integer values, the + default base is 10 (i.e. decimal) (optional) + + ini-name: the explicit ini option name (optional) + no-ini: if non-empty this field is ignored as an ini option + (optional) + + group: when specified on a struct field, makes the struct + field a separate group with the given name (optional) + namespace: when specified on a group struct field, the namespace + gets prepended to every option's long name and + subgroup's namespace of this group, separated by + the parser's namespace delimiter (optional) + env-namespace: when specified on a group struct field, the env-namespace + gets prepended to every option's env key and + subgroup's env-namespace of this group, separated by + the parser's env-namespace delimiter (optional) + command: when specified on a struct field, makes the struct + field a (sub)command with the given name (optional) + subcommands-optional: when specified on a command struct field, makes + any subcommands of that command optional (optional) + alias: when specified on a command struct field, adds the + specified name as an alias for the command. Can be + be specified multiple times to add more than one + alias (optional) + positional-args: when specified on a field with a struct type, + uses the fields of that struct to parse remaining + positional command line arguments into (in order + of the fields). If a field has a slice type, + then all remaining arguments will be added to it. + Positional arguments are optional by default, + unless the "required" tag is specified together + with the "positional-args" tag. The "required" tag + can also be set on the individual rest argument + fields, to require only the first N positional + arguments. If the "required" tag is set on the + rest arguments slice, then its value determines + the minimum amount of rest arguments that needs to + be provided (e.g. `required:"2"`) (optional) + positional-arg-name: used on a field in a positional argument struct; name + of the positional argument placeholder to be shown in + the help (optional) Either the `short:` tag or the `long:` must be specified to make the field eligible as an option. -# Option groups + +Option groups Option groups are a simple way to semantically separate your options. All options in a particular group are shown together in the help under the name @@ -166,12 +169,14 @@ precisely and emphasize the options affiliation to their group. There are currently three ways to specify option groups. - 1. Use NewNamedParser specifying the various option groups. - 2. Use AddGroup to add a group to an existing parser. - 3. Add a struct field to the top-level options annotated with the - group:"group-name" tag. + 1. Use NewNamedParser specifying the various option groups. + 2. Use AddGroup to add a group to an existing parser. + 3. Add a struct field to the top-level options annotated with the + group:"group-name" tag. -# Commands + + +Commands The flags package also has basic support for commands. Commands are often used in monolithic applications that support various commands or actions. @@ -181,9 +186,9 @@ application. There are currently two ways to specify a command. - 1. Use AddCommand on an existing parser. - 2. Add a struct field to your options struct annotated with the - command:"command-name" tag. + 1. Use AddCommand on an existing parser. + 2. Add a struct field to your options struct annotated with the + command:"command-name" tag. The most common, idiomatic way to implement commands is to define a global parser instance and implement each command in a separate file. These @@ -199,14 +204,15 @@ command has been specified on the command line, in addition to the options of all the parent commands. I.e. considering a -v flag on the parser and an add command, the following are equivalent: - ./app -v add - ./app add -v + ./app -v add + ./app add -v However, if the -v flag is defined on the add command, then the first of the two examples above would fail since the -v flag is not defined before the add command. -# Completion + +Completion go-flags has builtin support to provide bash completion of flags, commands and argument values. To use completion, the binary which uses go-flags @@ -220,7 +226,7 @@ by replacing the argument parsing routine with the completion routine which outputs completions for the passed arguments. The basic invocation to complete a set of arguments is therefore: - GO_FLAGS_COMPLETION=1 ./completion-example arg1 arg2 arg3 + GO_FLAGS_COMPLETION=1 ./completion-example arg1 arg2 arg3 where `completion-example` is the binary, `arg1` and `arg2` are the current arguments, and `arg3` (the last argument) is the argument @@ -231,20 +237,20 @@ are more than 1 completion items. To use this with bash completion, a simple file can be written which calls the binary which supports go-flags completion: - _completion_example() { - # All arguments except the first one - args=("${COMP_WORDS[@]:1:$COMP_CWORD}") + _completion_example() { + # All arguments except the first one + args=("${COMP_WORDS[@]:1:$COMP_CWORD}") - # Only split on newlines - local IFS=$'\n' + # Only split on newlines + local IFS=$'\n' - # Call completion (note that the first element of COMP_WORDS is - # the executable itself) - COMPREPLY=($(GO_FLAGS_COMPLETION=1 ${COMP_WORDS[0]} "${args[@]}")) - return 0 - } + # Call completion (note that the first element of COMP_WORDS is + # the executable itself) + COMPREPLY=($(GO_FLAGS_COMPLETION=1 ${COMP_WORDS[0]} "${args[@]}")) + return 0 + } - complete -F _completion_example completion-example + complete -F _completion_example completion-example Completion requires the parser option PassDoubleDash and is therefore enforced if the environment variable GO_FLAGS_COMPLETION is set. diff --git a/publisher/vendor/github.com/umputun/go-flags/ini.go b/publisher/vendor/github.com/umputun/go-flags/ini.go index 42006267..e714d3d3 100644 --- a/publisher/vendor/github.com/umputun/go-flags/ini.go +++ b/publisher/vendor/github.com/umputun/go-flags/ini.go @@ -113,18 +113,18 @@ func (i *IniParser) ParseFile(filename string) error { // // The format of the ini file is as follows: // -// [Option group name] -// option = value +// [Option group name] +// option = value // // Each section in the ini file represents an option group or command in the // flags parser. The default flags parser option group (i.e. when using // flags.Parse) is named 'Application Options'. The ini option name is matched // in the following order: // -// 1. Compared to the ini-name tag on the option struct field (if present) -// 2. Compared to the struct field name -// 3. Compared to the option long name (if present) -// 4. Compared to the option short name (if present) +// 1. Compared to the ini-name tag on the option struct field (if present) +// 2. Compared to the struct field name +// 3. Compared to the option long name (if present) +// 4. Compared to the option short name (if present) // // Sections for nested groups and commands can be addressed using a dot `.' // namespacing notation (i.e [subcommand.Options]). Group section names are diff --git a/publisher/vendor/github.com/umputun/go-flags/optstyle_other.go b/publisher/vendor/github.com/umputun/go-flags/optstyle_other.go index f84b6974..56dfdae1 100644 --- a/publisher/vendor/github.com/umputun/go-flags/optstyle_other.go +++ b/publisher/vendor/github.com/umputun/go-flags/optstyle_other.go @@ -1,4 +1,3 @@ -//go:build !windows || forceposix // +build !windows forceposix package flags diff --git a/publisher/vendor/github.com/umputun/go-flags/optstyle_windows.go b/publisher/vendor/github.com/umputun/go-flags/optstyle_windows.go index e8029042..f3f28aee 100644 --- a/publisher/vendor/github.com/umputun/go-flags/optstyle_windows.go +++ b/publisher/vendor/github.com/umputun/go-flags/optstyle_windows.go @@ -1,4 +1,3 @@ -//go:build !forceposix // +build !forceposix package flags diff --git a/publisher/vendor/github.com/umputun/go-flags/termsize.go b/publisher/vendor/github.com/umputun/go-flags/termsize.go index 8102594d..29b1110b 100644 --- a/publisher/vendor/github.com/umputun/go-flags/termsize.go +++ b/publisher/vendor/github.com/umputun/go-flags/termsize.go @@ -1,4 +1,3 @@ -//go:build !windows && !plan9 && !solaris && !appengine && !wasm // +build !windows,!plan9,!solaris,!appengine,!wasm package flags diff --git a/publisher/vendor/github.com/umputun/go-flags/termsize_nosysioctl.go b/publisher/vendor/github.com/umputun/go-flags/termsize_nosysioctl.go index c6e68f66..12a54580 100644 --- a/publisher/vendor/github.com/umputun/go-flags/termsize_nosysioctl.go +++ b/publisher/vendor/github.com/umputun/go-flags/termsize_nosysioctl.go @@ -1,4 +1,3 @@ -//go:build plan9 || solaris || appengine || wasm // +build plan9 solaris appengine wasm package flags diff --git a/publisher/vendor/github.com/umputun/go-flags/termsize_windows.go b/publisher/vendor/github.com/umputun/go-flags/termsize_windows.go index 189a1b3f..5c0fa6ba 100644 --- a/publisher/vendor/github.com/umputun/go-flags/termsize_windows.go +++ b/publisher/vendor/github.com/umputun/go-flags/termsize_windows.go @@ -1,4 +1,3 @@ -//go:build windows // +build windows package flags diff --git a/publisher/vendor/github.com/umputun/go-flags/tiocgwinsz_bsdish.go b/publisher/vendor/github.com/umputun/go-flags/tiocgwinsz_bsdish.go index 57c9b84a..fcc11860 100644 --- a/publisher/vendor/github.com/umputun/go-flags/tiocgwinsz_bsdish.go +++ b/publisher/vendor/github.com/umputun/go-flags/tiocgwinsz_bsdish.go @@ -1,4 +1,3 @@ -//go:build darwin || freebsd || netbsd || openbsd // +build darwin freebsd netbsd openbsd package flags diff --git a/publisher/vendor/github.com/umputun/go-flags/tiocgwinsz_linux.go b/publisher/vendor/github.com/umputun/go-flags/tiocgwinsz_linux.go index 80cadf00..e3975e28 100644 --- a/publisher/vendor/github.com/umputun/go-flags/tiocgwinsz_linux.go +++ b/publisher/vendor/github.com/umputun/go-flags/tiocgwinsz_linux.go @@ -1,4 +1,3 @@ -//go:build linux // +build linux package flags diff --git a/publisher/vendor/github.com/umputun/go-flags/tiocgwinsz_other.go b/publisher/vendor/github.com/umputun/go-flags/tiocgwinsz_other.go index 8bcc2a46..30821515 100644 --- a/publisher/vendor/github.com/umputun/go-flags/tiocgwinsz_other.go +++ b/publisher/vendor/github.com/umputun/go-flags/tiocgwinsz_other.go @@ -1,4 +1,3 @@ -//go:build !darwin && !freebsd && !netbsd && !openbsd && !linux // +build !darwin,!freebsd,!netbsd,!openbsd,!linux package flags diff --git a/publisher/vendor/gopkg.in/yaml.v3/apic.go b/publisher/vendor/gopkg.in/yaml.v3/apic.go index 05fd305d..ae7d049f 100644 --- a/publisher/vendor/gopkg.in/yaml.v3/apic.go +++ b/publisher/vendor/gopkg.in/yaml.v3/apic.go @@ -1,17 +1,17 @@ -// +// // Copyright (c) 2011-2019 Canonical Ltd // Copyright (c) 2006-2010 Kirill Simonov -// +// // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE diff --git a/publisher/vendor/gopkg.in/yaml.v3/emitterc.go b/publisher/vendor/gopkg.in/yaml.v3/emitterc.go index dde20e50..0f47c9ca 100644 --- a/publisher/vendor/gopkg.in/yaml.v3/emitterc.go +++ b/publisher/vendor/gopkg.in/yaml.v3/emitterc.go @@ -162,9 +162,10 @@ func yaml_emitter_emit(emitter *yaml_emitter_t, event *yaml_event_t) bool { // Check if we need to accumulate more events before emitting. // // We accumulate extra -// - 1 event for DOCUMENT-START -// - 2 events for SEQUENCE-START -// - 3 events for MAPPING-START +// - 1 event for DOCUMENT-START +// - 2 events for SEQUENCE-START +// - 3 events for MAPPING-START +// func yaml_emitter_need_more_events(emitter *yaml_emitter_t) bool { if emitter.events_head == len(emitter.events) { return true @@ -240,7 +241,7 @@ func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool emitter.indent += 2 } else { // Everything else aligns to the chosen indentation. - emitter.indent = emitter.best_indent * ((emitter.indent + emitter.best_indent) / emitter.best_indent) + emitter.indent = emitter.best_indent*((emitter.indent+emitter.best_indent)/emitter.best_indent) } } return true diff --git a/publisher/vendor/gopkg.in/yaml.v3/parserc.go b/publisher/vendor/gopkg.in/yaml.v3/parserc.go index 25fe8236..268558a0 100644 --- a/publisher/vendor/gopkg.in/yaml.v3/parserc.go +++ b/publisher/vendor/gopkg.in/yaml.v3/parserc.go @@ -227,8 +227,7 @@ func yaml_parser_state_machine(parser *yaml_parser_t, event *yaml_event_t) bool // Parse the production: // stream ::= STREAM-START implicit_document? explicit_document* STREAM-END -// -// ************ +// ************ func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -250,12 +249,9 @@ func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) // Parse the productions: // implicit_document ::= block_node DOCUMENT-END* -// -// * -// +// * // explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* -// -// ************************* +// ************************* func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t, implicit bool) bool { token := peek_token(parser) @@ -360,8 +356,8 @@ func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t // Parse the productions: // explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// *********** // -// *********** func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -383,10 +379,9 @@ func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event // Parse the productions: // implicit_document ::= block_node DOCUMENT-END* -// -// ************* -// +// ************* // explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -433,41 +428,30 @@ func yaml_parser_set_event_comments(parser *yaml_parser_t, event *yaml_event_t) // Parse the productions: // block_node_or_indentless_sequence ::= -// -// ALIAS -// ***** -// | properties (block_content | indentless_block_sequence)? -// ********** * -// | block_content | indentless_block_sequence -// * -// +// ALIAS +// ***** +// | properties (block_content | indentless_block_sequence)? +// ********** * +// | block_content | indentless_block_sequence +// * // block_node ::= ALIAS -// -// ***** -// | properties block_content? -// ********** * -// | block_content -// * -// +// ***** +// | properties block_content? +// ********** * +// | block_content +// * // flow_node ::= ALIAS -// -// ***** -// | properties flow_content? -// ********** * -// | flow_content -// * -// +// ***** +// | properties flow_content? +// ********** * +// | flow_content +// * // properties ::= TAG ANCHOR? | ANCHOR TAG? -// -// ************************* -// +// ************************* // block_content ::= block_collection | flow_collection | SCALAR -// -// ****** -// +// ****** // flow_content ::= flow_collection | SCALAR -// -// ****** +// ****** func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, indentless_sequence bool) bool { //defer trace("yaml_parser_parse_node", "block:", block, "indentless_sequence:", indentless_sequence)() @@ -698,8 +682,8 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i // Parse the productions: // block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END +// ******************** *********** * ********* // -// ******************** *********** * ********* func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) @@ -756,8 +740,7 @@ func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_e // Parse the productions: // indentless_sequence ::= (BLOCK-ENTRY block_node?)+ -// -// *********** * +// *********** * func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -822,14 +805,14 @@ func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) { // Parse the productions: // block_mapping ::= BLOCK-MAPPING_START +// ******************* +// ((KEY block_node_or_indentless_sequence?)? +// *** * +// (VALUE block_node_or_indentless_sequence?)?)* // -// ******************* -// ((KEY block_node_or_indentless_sequence?)? -// *** * -// (VALUE block_node_or_indentless_sequence?)?)* +// BLOCK-END +// ********* // -// BLOCK-END -// ********* func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) @@ -898,11 +881,13 @@ func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_even // Parse the productions: // block_mapping ::= BLOCK-MAPPING_START // -// ((KEY block_node_or_indentless_sequence?)? +// ((KEY block_node_or_indentless_sequence?)? +// +// (VALUE block_node_or_indentless_sequence?)?)* +// ***** * +// BLOCK-END +// // -// (VALUE block_node_or_indentless_sequence?)?)* -// ***** * -// BLOCK-END func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -930,18 +915,16 @@ func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_ev // Parse the productions: // flow_sequence ::= FLOW-SEQUENCE-START -// -// ******************* -// (flow_sequence_entry FLOW-ENTRY)* -// * ********** -// flow_sequence_entry? -// * -// FLOW-SEQUENCE-END -// ***************** -// +// ******************* +// (flow_sequence_entry FLOW-ENTRY)* +// * ********** +// flow_sequence_entry? +// * +// FLOW-SEQUENCE-END +// ***************** // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * // -// * func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) @@ -1004,10 +987,11 @@ func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_ev return true } +// // Parse the productions: // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// *** * // -// *** * func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -1027,8 +1011,8 @@ func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, ev // Parse the productions: // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// ***** * // -// ***** * func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -1051,8 +1035,8 @@ func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, // Parse the productions: // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * // -// * func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -1069,17 +1053,16 @@ func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, ev // Parse the productions: // flow_mapping ::= FLOW-MAPPING-START -// -// ****************** -// (flow_mapping_entry FLOW-ENTRY)* -// * ********** -// flow_mapping_entry? -// ****************** -// FLOW-MAPPING-END -// **************** -// +// ****************** +// (flow_mapping_entry FLOW-ENTRY)* +// * ********** +// flow_mapping_entry? +// ****************** +// FLOW-MAPPING-END +// **************** // flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -// - *** * +// * *** * +// func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) @@ -1145,7 +1128,8 @@ func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event // Parse the productions: // flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -// - ***** * +// * ***** * +// func yaml_parser_parse_flow_mapping_value(parser *yaml_parser_t, event *yaml_event_t, empty bool) bool { token := peek_token(parser) if token == nil { diff --git a/publisher/vendor/gopkg.in/yaml.v3/readerc.go b/publisher/vendor/gopkg.in/yaml.v3/readerc.go index 56af2453..b7de0a89 100644 --- a/publisher/vendor/gopkg.in/yaml.v3/readerc.go +++ b/publisher/vendor/gopkg.in/yaml.v3/readerc.go @@ -1,17 +1,17 @@ -// +// // Copyright (c) 2011-2019 Canonical Ltd // Copyright (c) 2006-2010 Kirill Simonov -// +// // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE diff --git a/publisher/vendor/gopkg.in/yaml.v3/scannerc.go b/publisher/vendor/gopkg.in/yaml.v3/scannerc.go index 30b1f089..ca007010 100644 --- a/publisher/vendor/gopkg.in/yaml.v3/scannerc.go +++ b/publisher/vendor/gopkg.in/yaml.v3/scannerc.go @@ -1614,11 +1614,11 @@ func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool { // Scan a YAML-DIRECTIVE or TAG-DIRECTIVE token. // // Scope: +// %YAML 1.1 # a comment \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // -// %YAML 1.1 # a comment \n -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -// %TAG !yaml! tag:yaml.org,2002: \n -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool { // Eat '%'. start_mark := parser.mark @@ -1719,11 +1719,11 @@ func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool // Scan the directive name. // // Scope: +// %YAML 1.1 # a comment \n +// ^^^^ +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^ // -// %YAML 1.1 # a comment \n -// ^^^^ -// %TAG !yaml! tag:yaml.org,2002: \n -// ^^^ func yaml_parser_scan_directive_name(parser *yaml_parser_t, start_mark yaml_mark_t, name *[]byte) bool { // Consume the directive name. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { @@ -1758,9 +1758,8 @@ func yaml_parser_scan_directive_name(parser *yaml_parser_t, start_mark yaml_mark // Scan the value of VERSION-DIRECTIVE. // // Scope: -// -// %YAML 1.1 # a comment \n -// ^^^^^^ +// %YAML 1.1 # a comment \n +// ^^^^^^ func yaml_parser_scan_version_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, major, minor *int8) bool { // Eat whitespaces. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { @@ -1798,11 +1797,10 @@ const max_number_length = 2 // Scan the version number of VERSION-DIRECTIVE. // // Scope: -// -// %YAML 1.1 # a comment \n -// ^ -// %YAML 1.1 # a comment \n -// ^ +// %YAML 1.1 # a comment \n +// ^ +// %YAML 1.1 # a comment \n +// ^ func yaml_parser_scan_version_directive_number(parser *yaml_parser_t, start_mark yaml_mark_t, number *int8) bool { // Repeat while the next character is digit. @@ -1836,9 +1834,9 @@ func yaml_parser_scan_version_directive_number(parser *yaml_parser_t, start_mark // Scan the value of a TAG-DIRECTIVE token. // // Scope: +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // -// %TAG !yaml! tag:yaml.org,2002: \n -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ func yaml_parser_scan_tag_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, handle, prefix *[]byte) bool { var handle_value, prefix_value []byte @@ -2849,7 +2847,7 @@ func yaml_parser_scan_line_comment(parser *yaml_parser_t, token_mark yaml_mark_t continue } if parser.buffer[parser.buffer_pos+peek] == '#' { - seen := parser.mark.index + peek + seen := parser.mark.index+peek for { if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false @@ -2878,7 +2876,7 @@ func yaml_parser_scan_line_comment(parser *yaml_parser_t, token_mark yaml_mark_t parser.comments = append(parser.comments, yaml_comment_t{ token_mark: token_mark, start_mark: start_mark, - line: text, + line: text, }) } return true @@ -2912,7 +2910,7 @@ func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) boo // the foot is the line below it. var foot_line = -1 if scan_mark.line > 0 { - foot_line = parser.mark.line - parser.newlines + 1 + foot_line = parser.mark.line-parser.newlines+1 if parser.newlines == 0 && parser.mark.column > 1 { foot_line++ } @@ -2998,7 +2996,7 @@ func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) boo recent_empty = false // Consume until after the consumed comment line. - seen := parser.mark.index + peek + seen := parser.mark.index+peek for { if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false diff --git a/publisher/vendor/gopkg.in/yaml.v3/writerc.go b/publisher/vendor/gopkg.in/yaml.v3/writerc.go index 266d0b09..b8a116bf 100644 --- a/publisher/vendor/gopkg.in/yaml.v3/writerc.go +++ b/publisher/vendor/gopkg.in/yaml.v3/writerc.go @@ -1,17 +1,17 @@ -// +// // Copyright (c) 2011-2019 Canonical Ltd // Copyright (c) 2006-2010 Kirill Simonov -// +// // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE diff --git a/publisher/vendor/gopkg.in/yaml.v3/yaml.go b/publisher/vendor/gopkg.in/yaml.v3/yaml.go index f0bedf3d..8cec6da4 100644 --- a/publisher/vendor/gopkg.in/yaml.v3/yaml.go +++ b/publisher/vendor/gopkg.in/yaml.v3/yaml.go @@ -17,7 +17,8 @@ // // Source code and other details for the project are available at GitHub: // -// https://github.com/go-yaml/yaml +// https://github.com/go-yaml/yaml +// package yaml import ( @@ -74,15 +75,16 @@ type Marshaler interface { // // For example: // -// type T struct { -// F int `yaml:"a,omitempty"` -// B int -// } -// var t T -// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t) +// type T struct { +// F int `yaml:"a,omitempty"` +// B int +// } +// var t T +// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t) // // See the documentation of Marshal for the format of tags and a list of // supported tag options. +// func Unmarshal(in []byte, out interface{}) (err error) { return unmarshal(in, out, false) } @@ -183,35 +185,36 @@ func unmarshal(in []byte, out interface{}, strict bool) (err error) { // // The field tag format accepted is: // -// `(...) yaml:"[][,[,]]" (...)` +// `(...) yaml:"[][,[,]]" (...)` // // The following flags are currently supported: // -// omitempty Only include the field if it's not set to the zero -// value for the type or to empty slices or maps. -// Zero valued structs will be omitted if all their public -// fields are zero, unless they implement an IsZero -// method (see the IsZeroer interface type), in which -// case the field will be excluded if IsZero returns true. +// omitempty Only include the field if it's not set to the zero +// value for the type or to empty slices or maps. +// Zero valued structs will be omitted if all their public +// fields are zero, unless they implement an IsZero +// method (see the IsZeroer interface type), in which +// case the field will be excluded if IsZero returns true. // -// flow Marshal using a flow style (useful for structs, -// sequences and maps). +// flow Marshal using a flow style (useful for structs, +// sequences and maps). // -// inline Inline the field, which must be a struct or a map, -// causing all of its fields or keys to be processed as if -// they were part of the outer struct. For maps, keys must -// not conflict with the yaml keys of other struct fields. +// inline Inline the field, which must be a struct or a map, +// causing all of its fields or keys to be processed as if +// they were part of the outer struct. For maps, keys must +// not conflict with the yaml keys of other struct fields. // // In addition, if the key is "-", the field is ignored. // // For example: // -// type T struct { -// F int `yaml:"a,omitempty"` -// B int -// } -// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n" -// yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n" +// type T struct { +// F int `yaml:"a,omitempty"` +// B int +// } +// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n" +// yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n" +// func Marshal(in interface{}) (out []byte, err error) { defer handleErr(&err) e := newEncoder() @@ -355,21 +358,22 @@ const ( // // For example: // -// var person struct { -// Name string -// Address yaml.Node -// } -// err := yaml.Unmarshal(data, &person) -// +// var person struct { +// Name string +// Address yaml.Node +// } +// err := yaml.Unmarshal(data, &person) +// // Or by itself: // -// var person Node -// err := yaml.Unmarshal(data, &person) +// var person Node +// err := yaml.Unmarshal(data, &person) +// type Node struct { // Kind defines whether the node is a document, a mapping, a sequence, // a scalar value, or an alias to another node. The specific data type of // scalar nodes may be obtained via the ShortTag and LongTag methods. - Kind Kind + Kind Kind // Style allows customizing the apperance of the node in the tree. Style Style @@ -417,6 +421,7 @@ func (n *Node) IsZero() bool { n.HeadComment == "" && n.LineComment == "" && n.FootComment == "" && n.Line == 0 && n.Column == 0 } + // LongTag returns the long form of the tag that indicates the data type for // the node. If the Tag field isn't explicitly defined, one will be computed // based on the node properties. diff --git a/publisher/vendor/gopkg.in/yaml.v3/yamlh.go b/publisher/vendor/gopkg.in/yaml.v3/yamlh.go index ddcd5513..7c6d0077 100644 --- a/publisher/vendor/gopkg.in/yaml.v3/yamlh.go +++ b/publisher/vendor/gopkg.in/yaml.v3/yamlh.go @@ -438,9 +438,7 @@ type yaml_document_t struct { // The number of written bytes should be set to the size_read variable. // // [in,out] data A pointer to an application data specified by -// -// yaml_parser_set_input(). -// +// yaml_parser_set_input(). // [out] buffer The buffer to write the data from the source. // [in] size The size of the buffer. // [out] size_read The actual number of bytes read from the source. @@ -641,6 +639,7 @@ type yaml_parser_t struct { } type yaml_comment_t struct { + scan_mark yaml_mark_t // Position where scanning for comments started token_mark yaml_mark_t // Position after which tokens will be associated with this comment start_mark yaml_mark_t // Position of '#' comment mark @@ -660,14 +659,13 @@ type yaml_comment_t struct { // @a buffer to the output. // // @param[in,out] data A pointer to an application data specified by -// -// yaml_emitter_set_output(). -// +// yaml_emitter_set_output(). // @param[in] buffer The buffer with bytes to be written. // @param[in] size The size of the buffer. // // @returns On success, the handler should return @c 1. If the handler failed, // the returned value should be @c 0. +// type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error type yaml_emitter_state_t int diff --git a/publisher/vendor/gopkg.in/yaml.v3/yamlprivateh.go b/publisher/vendor/gopkg.in/yaml.v3/yamlprivateh.go index dea1ba96..e88f9c54 100644 --- a/publisher/vendor/gopkg.in/yaml.v3/yamlprivateh.go +++ b/publisher/vendor/gopkg.in/yaml.v3/yamlprivateh.go @@ -1,17 +1,17 @@ -// +// // Copyright (c) 2011-2019 Canonical Ltd // Copyright (c) 2006-2010 Kirill Simonov -// +// // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -137,8 +137,8 @@ func is_crlf(b []byte, i int) bool { func is_breakz(b []byte, i int) bool { //return is_break(b, i) || is_z(b, i) return ( - // is_break: - b[i] == '\r' || // CR (#xD) + // is_break: + b[i] == '\r' || // CR (#xD) b[i] == '\n' || // LF (#xA) b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) @@ -151,8 +151,8 @@ func is_breakz(b []byte, i int) bool { func is_spacez(b []byte, i int) bool { //return is_space(b, i) || is_breakz(b, i) return ( - // is_space: - b[i] == ' ' || + // is_space: + b[i] == ' ' || // is_breakz: b[i] == '\r' || // CR (#xD) b[i] == '\n' || // LF (#xA) @@ -166,8 +166,8 @@ func is_spacez(b []byte, i int) bool { func is_blankz(b []byte, i int) bool { //return is_blank(b, i) || is_breakz(b, i) return ( - // is_blank: - b[i] == ' ' || b[i] == '\t' || + // is_blank: + b[i] == ' ' || b[i] == '\t' || // is_breakz: b[i] == '\r' || // CR (#xD) b[i] == '\n' || // LF (#xA) diff --git a/publisher/vendor/modules.txt b/publisher/vendor/modules.txt index 748df0fa..11f9ed21 100644 --- a/publisher/vendor/modules.txt +++ b/publisher/vendor/modules.txt @@ -17,6 +17,10 @@ github.com/pmezard/go-difflib/difflib ## explicit; go 1.20 github.com/stretchr/testify/assert github.com/stretchr/testify/require +# github.com/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300 +## explicit +github.com/tcolgate/mp3 +github.com/tcolgate/mp3/internal/data # github.com/umputun/go-flags v1.5.1 ## explicit; go 1.12 github.com/umputun/go-flags