From 34a1796478a408e3406e39c2bc2260050ee98721 Mon Sep 17 00:00:00 2001 From: ChaotiCryptidz Date: Tue, 22 Feb 2022 14:02:58 +0000 Subject: [PATCH] rewrite in go --- .gitignore | 5 +- README.md | 5 +- commands/copy/copy.go | 271 +++++ commands/genhtml/genhtml.go | 181 +++ commands/process/process.go | 165 +++ commands/transcode/transcode.go | 102 ++ config/config.go | 24 + flake.nix | 38 +- formatter.sh | 4 - go.mod | 30 + go.sum | 784 +++++++++++++ hm-module.nix | 37 + main.go | 58 + meta/meta.go | 4 + musicutil/__init__.py | 0 musicutil/__main__.py | 39 - musicutil/commands/copy_command.py | 226 ---- musicutil/commands/genhtml_command.py | 141 --- musicutil/commands/process_command.py | 115 -- musicutil/commands/stats_command.py | 54 - musicutil/commands/transcode_command.py | 92 -- musicutil/meta.py | 14 - musicutil/transcode_presets.py | 119 -- musicutil/types.py | 46 - musicutil/utils/do_replaygain.py | 19 - musicutil/utils/load_tag_information.py | 86 -- musicutil/utils/scan_for_music.py | 23 - musicutil/utils/substitutions.py | 13 - musicutil/utils/transcoder.py | 155 --- nix-extra-deps/fold-to-ascii.nix | 12 - pyproject.toml | 5 - setup.py | 14 - tconf.json | 1 - types/file.go | 67 ++ utils/ascii_reduce/mappings.go | 25 + utils/ascii_reduce/mappings.json | 1371 ++++++++++++++++++++++ utils/ascii_reduce/reduce.go | 40 + utils/extract_tags.go | 10 + utils/scan_for_music.go | 53 + utils/tag_cache/tag_cache.go | 164 +++ utils/tag_cache/tag_cache_settings.go | 17 + utils/tag_extractor/extractor.go | 45 + utils/tag_extractor/ffprobe_extractor.go | 108 ++ utils/tag_extractor/taglib_extractor.go | 25 + utils/transcoder/transcode_presets.go | 198 ++++ utils/transcoder/transcoder.go | 79 ++ 46 files changed, 3876 insertions(+), 1208 deletions(-) create mode 100644 commands/copy/copy.go create mode 100644 commands/genhtml/genhtml.go create mode 100644 commands/process/process.go create mode 100644 commands/transcode/transcode.go create mode 100644 config/config.go delete mode 100755 formatter.sh create mode 100644 go.mod create mode 100644 go.sum create mode 100644 hm-module.nix create mode 100644 main.go create mode 100644 meta/meta.go delete mode 100644 musicutil/__init__.py delete mode 100644 musicutil/__main__.py delete mode 100644 musicutil/commands/copy_command.py delete mode 100644 musicutil/commands/genhtml_command.py delete mode 100644 musicutil/commands/process_command.py delete mode 100644 musicutil/commands/stats_command.py delete mode 100644 musicutil/commands/transcode_command.py delete mode 100644 musicutil/meta.py delete mode 100644 musicutil/transcode_presets.py delete mode 100644 musicutil/types.py delete mode 100644 musicutil/utils/do_replaygain.py delete mode 100644 musicutil/utils/load_tag_information.py delete mode 100644 musicutil/utils/scan_for_music.py delete mode 100644 musicutil/utils/substitutions.py delete mode 100644 musicutil/utils/transcoder.py delete mode 100644 nix-extra-deps/fold-to-ascii.nix delete mode 100644 pyproject.toml delete mode 100755 setup.py delete mode 100644 tconf.json create mode 100644 types/file.go create mode 100644 utils/ascii_reduce/mappings.go create mode 100644 utils/ascii_reduce/mappings.json create mode 100644 utils/ascii_reduce/reduce.go create mode 100644 utils/extract_tags.go create mode 100644 utils/scan_for_music.go create mode 100644 utils/tag_cache/tag_cache.go create mode 100644 utils/tag_cache/tag_cache_settings.go create mode 100644 utils/tag_extractor/extractor.go create mode 100644 utils/tag_extractor/ffprobe_extractor.go create mode 100644 utils/tag_extractor/taglib_extractor.go create mode 100644 utils/transcoder/transcode_presets.go create mode 100644 utils/transcoder/transcoder.go diff --git a/.gitignore b/.gitignore index 04d1a2b..3372389 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ -__pycache__ dist build -musicutil.egg-info result -.direnv \ No newline at end of file +.direnv +musicutil_old \ No newline at end of file diff --git a/README.md b/README.md index d48df89..cf6776d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ # musicutil -My tool for managing a local music library. \ No newline at end of file +My tool for managing a local music library. + +## DISCLAMER: +if you turn on the tag cache, i am not not responsible for any loss of data caused by this mode \ No newline at end of file diff --git a/commands/copy/copy.go b/commands/copy/copy.go new file mode 100644 index 0000000..04164a8 --- /dev/null +++ b/commands/copy/copy.go @@ -0,0 +1,271 @@ +package copy + +import ( + "fmt" + "io" + "os" + + "github.com/akamensky/argparse" + "github.com/rs/zerolog/log" + "gitlab.com/ChaotiCryptidz/musicutil/types" + "gitlab.com/ChaotiCryptidz/musicutil/utils" + "gitlab.com/ChaotiCryptidz/musicutil/utils/transcoder" +) + +type CopyCommandArgs struct { + Source *string + Dest *string + TranscodePreset *string + SkipExisting *bool + SingleDirectory *bool +} + +func dirNotExistValidator(directory string) error { + fileInfo, err := os.Stat(directory) + if err != nil { + return fmt.Errorf("%s does not exist", directory) + } + + if !fileInfo.IsDir() { + return fmt.Errorf("%s is not a directory", directory) + } + + return nil +} + +func fileExists(filepath string) bool { + if _, err := os.Stat(filepath); err == nil { + return true + } else { + return false + } +} + +func copyFile(src, dest string) error { + _, err := os.Stat(src) + if err != nil { + return err + } + + source, err := os.Open(src) + if err != nil { + return err + } + defer source.Close() + + destination, err := os.Create(dest) + if err != nil { + return err + } + defer destination.Close() + + _, err = io.Copy(destination, source) + return err +} + +func RegisterCopyCommand(parser *argparse.Parser) (*argparse.Command, *CopyCommandArgs) { + cmd := parser.NewCommand("copy", "copies files with or without transcoding") + + arguments := &CopyCommandArgs{} + arguments.Source = cmd.String("s", "src", &argparse.Options{ + Required: true, + Validate: func(args []string) error { + return dirNotExistValidator(args[0]) + }, + }) + + arguments.Dest = cmd.String("d", "dest", &argparse.Options{ + Required: true, + Validate: func(args []string) error { + return dirNotExistValidator(args[0]) + }, + }) + + arguments.TranscodePreset = cmd.String("p", "transcode-preset", &argparse.Options{ + Required: true, + Validate: func(args []string) error { + if args[0] == "list" { + transcoder.PrintTranscodePresets() + os.Exit(0) + } else if args[0] == "copy" { + return nil + } + + if _, err := transcoder.GetPresetByName(args[0]); err != nil { + return fmt.Errorf("Preset does not exist") + } + + return nil + }, + }) + + arguments.SkipExisting = cmd.Flag("", "skip-existing", &argparse.Options{ + Default: false, + }) + + arguments.SingleDirectory = cmd.Flag("", "single-directory", &argparse.Options{ + Default: false, + }) + + return cmd, arguments +} + +type CopyCommand struct { + Args *CopyCommandArgs + Files []*types.File +} + +func NewCopyCommand(args *CopyCommandArgs) *CopyCommand { + return &CopyCommand{ + Args: args, + Files: make([]*types.File, 0), + } +} + +func (c *CopyCommand) scan_for_music() { + log.Info().Msg("Scanning For Music") + + files, err := utils.ScanForMusic(*c.Args.Source) + if err != nil { + panic(err) + } + + c.Files = files +} + +func (c *CopyCommand) check_for_duplicates() { + log.Info().Msg("Creating For Duplicates") + seen := make(map[string]bool) + dupes := make([]string, 0) + + for _, file := range c.Files { + filename := file.JoinFilename() + if _, ok := seen[filename]; ok { + dupes = append(dupes, filename) + } else { + seen[filename] = true + } + } + + if len(dupes) > 0 { + log.Fatal().Strs("dupes", dupes).Msg("Duplicates Found, please rename/remove.") + } +} + +func (c *CopyCommand) create_directories() { + log.Info().Msg("Creating Directories") + + directories := make(map[string]bool) + for _, file := range c.Files { + directories[file.PathFromSource] = true + } + + for dir := range directories { + if file_info, err := os.Stat(*c.Args.Dest + "/" + dir); err == nil { + if file_info.IsDir() { + continue + } + } + + log.Info().Str("dir", dir).Msg("Creating Directory") + + err := os.MkdirAll(*c.Args.Dest+"/"+dir, os.ModePerm) + if err != nil { + log.Fatal().Err(err).Msg("Could not create directory") + } + } +} + +func (c *CopyCommand) _transcode_file(file *types.File, trans_config *transcoder.TranscodeConfig) { + new_filename := file.Filename + "." + trans_config.FileExtension + dest_filepath := *c.Args.Dest + "/" + + if *c.Args.SingleDirectory { + dest_filepath = dest_filepath + new_filename + } else { + dest_filepath = dest_filepath + file.PathFromSource + "/" + new_filename + } + + if *c.Args.SkipExisting && fileExists(dest_filepath) { + log.Info().Str("file", new_filename). + Msg("Skipping transcode as file already exists") + return + } else { + log.Info().Str("file", new_filename).Msg("Transcoding File") + } + + output, err := transcoder.Transcode(file, trans_config, dest_filepath) + if err != nil { + log.Fatal().Err(err).Str("output", output).Msg("Transcode Failed") + } +} + +func (c *CopyCommand) transcode_files() { + log.Info().Msg("Transcoding Files") + + // TODO: Implement custom transcode config file + trans_preset, err := + transcoder.GetPresetByName(*c.Args.TranscodePreset) + if err != nil { + log.Fatal().Err(err).Msg("Could not get transcode preset") + } + + for _, file := range c.Files { + c._transcode_file(file, trans_preset.Config) + } +} + +func (c *CopyCommand) _copy_file(file *types.File) { + src_filepath := file.JoinPathTo() + dest_filepath := "" + if *c.Args.SingleDirectory { + dest_filepath = *c.Args.Dest + "/" + file.JoinFilename() + } else { + dest_filepath = *c.Args.Dest + "/" + file.JoinPathFromSource() + } + + exists := fileExists(dest_filepath) + + if exists { + log.Info().Str("file", dest_filepath). + Msg("Skipping as already exists in destination") + } else { + log.Info(). + Str("file", file.JoinFilename()). + Msg("Copying File") + err := copyFile(src_filepath, dest_filepath) + if err != nil { + log.Panic(). + Err(err). + Str("file", file.JoinFilename()). + Msg("Error Copying File") + } + } +} + +func (c *CopyCommand) copy_files() { + log.Info().Msg("Copying Files Into Dest") + + for _, file := range c.Files { + c._copy_file(file) + } +} + +func (c *CopyCommand) Run() { + log.Info().Msg("Copying Files") + + c.scan_for_music() + + if !*c.Args.SingleDirectory { + c.create_directories() + } else { + c.check_for_duplicates() + } + + if *c.Args.TranscodePreset == "copy" { + c.copy_files() + } else { + c.transcode_files() + } + +} diff --git a/commands/genhtml/genhtml.go b/commands/genhtml/genhtml.go new file mode 100644 index 0000000..dd9f6b7 --- /dev/null +++ b/commands/genhtml/genhtml.go @@ -0,0 +1,181 @@ +package genhtml + +import ( + "fmt" + "html" + "os" + "sort" + + "github.com/akamensky/argparse" + "github.com/rs/zerolog/log" + "gitlab.com/ChaotiCryptidz/musicutil/types" + "gitlab.com/ChaotiCryptidz/musicutil/utils" +) + +type GenHTMLCommandArgs struct { + Source *string + Dest *string + Title *string + Description *string +} + +func RegisterGenHTMLCommand(parser *argparse.Parser) (*argparse.Command, *GenHTMLCommandArgs) { + cmd := parser.NewCommand("genhtml", "generates a html file or multiple html files for showing music library") + + arguments := &GenHTMLCommandArgs{} + arguments.Source = cmd.String("s", "src", &argparse.Options{ + Required: true, + Validate: func(args []string) error { + fileInfo, err := os.Stat(args[0]) + if err != nil { + return fmt.Errorf("Source does not exist") + } + + if !fileInfo.IsDir() { + return fmt.Errorf("Source is not a directory") + } + + return nil + }, + }) + + arguments.Dest = cmd.String("d", "dest", &argparse.Options{Required: true}) + + arguments.Title = cmd.String("", "title", &argparse.Options{Default: ""}) + arguments.Description = cmd.String("", "description", &argparse.Options{Default: ""}) + + return cmd, arguments +} + +type GenHTMLCommand struct { + Args *GenHTMLCommandArgs + Files []*types.File +} + +func NewGenHTMLCommand(args *GenHTMLCommandArgs) *GenHTMLCommand { + return &GenHTMLCommand{ + Args: args, + Files: make([]*types.File, 0), + } +} + +func (c *GenHTMLCommand) scan_for_music() { + log.Info().Msg("Scanning For Music") + + files, err := utils.ScanForMusic(*c.Args.Source) + if err != nil { + panic(err) + } + + c.Files = files +} + +func (c *GenHTMLCommand) load_tag_information() { + log.Info().Msg("Loading Tag Information") + + for _, file := range c.Files { + log.Info().Str("file", file.JoinFilename()).Msg("Loading Tags") + tags, err := utils.ExtractTags(file) + if err != nil { + panic(err) + } + file.Tags = tags + } +} + +func (c *GenHTMLCommand) Run() { + log.Info().Msg("Generating HTML") + + c.scan_for_music() + c.load_tag_information() + + sort.Slice(c.Files, func(i, j int) bool { + if c.Files[i].PathFromSource != c.Files[j].PathFromSource { + return c.Files[i].PathFromSource < c.Files[j].PathFromSource + } + if c.Files[i].Tags.Title != c.Files[j].Tags.Title { + return c.Files[i].Tags.Title < c.Files[j].Tags.Title + } + + return c.Files[i].Tags.Artist < c.Files[j].Tags.Artist + }) + + html_content := "" + html_content = html_content + fmt.Sprintf(` + + + + + + %s + + + + + `, *c.Args.Title, *c.Args.Title, *c.Args.Description) + + html_content = html_content + tableForFiles(c.Files, true) + + html_content = html_content + "" + + html_file, err := os.Create(*c.Args.Dest + "/index.html") + if err != nil { + log.Fatal().Err(err).Msg("Could not write HTML file") + } + defer html_file.Close() + html_file.Write([]byte(html_content)) +} + +func tableForFiles(files []*types.File, includesPath bool) string { + html_content := "" + path_head := "" + if includesPath { + path_head = "Path" + } + + html_content += fmt.Sprintf(` + + + + %s + + + + + + + `, path_head) + + isOdd := true + for _, file := range files { + tdClass := "pure-table-even" + if isOdd { + tdClass = "pure-table-odd" + } + + data_title := html.EscapeString(file.Tags.Title) + data_artist := html.EscapeString(file.Tags.Artist) + data_extension := html.EscapeString(file.Extension) + + path_data := "" + if includesPath { + data_path := html.EscapeString(file.PathFromSource) + path_data = fmt.Sprintf("", data_path) + } + + html_content = html_content + fmt.Sprintf(` + + %s + + + + + `, tdClass, path_data, data_title, data_artist, data_extension) + isOdd = !isOdd + } + html_content = html_content + ` + +
TitleArtistFormat
%s
%s%s%s
+ ` + return html_content +} diff --git a/commands/process/process.go b/commands/process/process.go new file mode 100644 index 0000000..292334c --- /dev/null +++ b/commands/process/process.go @@ -0,0 +1,165 @@ +package process + +import ( + "fmt" + "os" + "strings" + + "github.com/akamensky/argparse" + "github.com/rs/zerolog/log" + "gitlab.com/ChaotiCryptidz/musicutil/types" + "gitlab.com/ChaotiCryptidz/musicutil/utils" + "gitlab.com/ChaotiCryptidz/musicutil/utils/ascii_reduce" + "gitlab.com/ChaotiCryptidz/musicutil/utils/tag_cache" +) + +type ProcessCommandArgs struct { + Source *string + DryRun *bool +} + +func RegisterProcessCommand(parser *argparse.Parser) (*argparse.Command, *ProcessCommandArgs) { + cmd := parser.NewCommand("process", "") + + arguments := &ProcessCommandArgs{} + arguments.Source = cmd.String("s", "src", &argparse.Options{ + Required: true, + Validate: func(args []string) error { + fileInfo, err := os.Stat(args[0]) + if err != nil { + return fmt.Errorf("Source does not exist") + } + + if !fileInfo.IsDir() { + return fmt.Errorf("Source is not a directory") + } + + return nil + }, + }) + + arguments.DryRun = cmd.Flag("", "dry-run", &argparse.Options{}) + + return cmd, arguments +} + +type ProcessCommand struct { + Args *ProcessCommandArgs + Files []*types.File + TagCache *tag_cache.TagCache +} + +func NewProcessCommand(args *ProcessCommandArgs) *ProcessCommand { + return &ProcessCommand{ + Args: args, + Files: make([]*types.File, 0), + } +} + +func (c *ProcessCommand) Run() { + log.Info().Str("dir", *c.Args.Source).Msg("Processing") + + c.TagCache = tag_cache.NewTagCache(*c.Args.Source) + defer c.TagCache.Close() + c.scan_for_music() + c.load_tag_information() + c.rename_files() +} + +func (c *ProcessCommand) scan_for_music() { + log.Info().Msg("Scanning For Music") + files, err := utils.ScanForMusic(*c.Args.Source) + if err != nil { + panic(err) + } + + c.Files = files +} + +func (c *ProcessCommand) load_tag_information() { + log.Info().Msg("Load Tag Information") + + for _, file := range c.Files { + if cached_tags, ok := c.TagCache.IsCached(file); !ok { + log.Info().Str("file", file.JoinFilename()).Msg("Loading Tags") + + tags, err := utils.ExtractTags(file) + if err != nil { + panic(err) + } + file.Tags = tags + c.TagCache.AddToCache(file, tags) + } else { + log.Info().Str("file", file.JoinFilename()).Msg("Loading Tags From Cache") + file.Tags = cached_tags + } + } +} + +// returns the new file so if renamed, +// any step after it is ran can operate on file +// if dry run then returns orig file without changed filename +func (c *ProcessCommand) rename_file(file *types.File) *types.File { + title := file.Tags.Title + artist := file.Tags.Artist + + replace_char := "_" + + // Step 1: Remove Newlines + title = strings.ReplaceAll(title, "\n", "") + artist = strings.ReplaceAll(artist, "\n", "") + + // Step 2: Strip ASCII + title = ascii_reduce.Reduce(title, replace_char) + artist = ascii_reduce.Reduce(artist, replace_char) + + // Step 3: Remove File Seperators + title = strings.ReplaceAll(title, "\\", replace_char) + title = strings.ReplaceAll(title, "/", replace_char) + artist = strings.ReplaceAll(artist, "\\", replace_char) + artist = strings.ReplaceAll(artist, "/", replace_char) + + // Step 4: Join Filename + filename := fmt.Sprintf("%s - %s", artist, title) + + if filename == file.Filename { + return file + } + + // Step 5: Make new File with filename set + new_file := file.DeepCopy() + if !*c.Args.DryRun { + new_file.Filename = filename + } + + // Step 6: Rename File + log.Info(). + Str("old", file.Filename). + Str("new", filename). + Msg("Renaming File") + + if !*c.Args.DryRun { + if _, err := os.Stat(new_file.JoinPathTo()); err == nil { + log.Fatal(). + Str("old", file.JoinFilename()). + Str("new", new_file.JoinFilename()). + Msg("Refusing to rename files, please retag to be distinct") + } + + err := os.Rename(file.JoinPathTo(), new_file.JoinPathTo()) + if err != nil { + log.Fatal().Err(err).Msg("Failed to rename file") + } + } + + return new_file +} + +func (c *ProcessCommand) rename_files() { + log.Info().Msg("Renaming Files") + + for _, file := range c.Files { + c.rename_file(file) + } + +} diff --git a/commands/transcode/transcode.go b/commands/transcode/transcode.go new file mode 100644 index 0000000..51d424c --- /dev/null +++ b/commands/transcode/transcode.go @@ -0,0 +1,102 @@ +package transcode + +import ( + "fmt" + "os" + + "github.com/akamensky/argparse" + "github.com/rs/zerolog/log" + "gitlab.com/ChaotiCryptidz/musicutil/types" + "gitlab.com/ChaotiCryptidz/musicutil/utils/transcoder" +) + +type TranscodeCommandArgs struct { + Source *string + Dest *string + TranscodePreset *string + IgnoreExtension *bool +} + +func RegisterTranscodeCommand(parser *argparse.Parser) (*argparse.Command, *TranscodeCommandArgs) { + cmd := parser.NewCommand("transcode", "Transcode a audio file from one format to another") + + arguments := &TranscodeCommandArgs{} + arguments.Source = cmd.String("s", "src", &argparse.Options{ + Required: true, + Validate: func(args []string) error { + if _, err := os.Stat(args[0]); err != nil { + return fmt.Errorf("Source file does not exist") + } + + return nil + }, + }) + + arguments.Dest = cmd.String("d", "dest", &argparse.Options{}) + + arguments.TranscodePreset = cmd.String("p", "transcode-preset", &argparse.Options{ + Required: true, + Validate: func(args []string) error { + if args[0] == "list" { + transcoder.PrintTranscodePresets() + os.Exit(0) + } + + if _, err := transcoder.GetPresetByName(args[0]); err != nil { + return fmt.Errorf("Preset does not exist") + } + + return nil + }, + }) + + arguments.IgnoreExtension = cmd.Flag("", "ignore-extension", &argparse.Options{}) + + return cmd, arguments +} + +type TranscodeCommand struct { + Args *TranscodeCommandArgs +} + +func NewTranscodeCommand(args *TranscodeCommandArgs) *TranscodeCommand { + return &TranscodeCommand{ + Args: args, + } +} + +func (c *TranscodeCommand) Run() { + log.Info(). + Str("src", *c.Args.Source). + Str("dest", *c.Args.Dest). + Msg("Transcoding") + + var transcode_config *transcoder.TranscodeConfig + + // TODO: Reimplement custom transcode config file loading + if len(*c.Args.TranscodePreset) != 0 { + preset, err := transcoder.GetPresetByName(*c.Args.TranscodePreset) + if err != nil { + panic(err) + } else { + transcode_config = preset.Config + } + } + + input_file := types.FileFromPath("", *c.Args.Source) + output_file := types.FileFromPath("", *c.Args.Dest) + + if !*c.Args.IgnoreExtension && transcode_config.FileExtension != output_file.Extension { + log.Error().Msgf( + "%s is not the recommended "+ + "extension for specified transcode config "+ + "please change it to %s "+ + "or run with --ignore-extension", + output_file.Extension, transcode_config.FileExtension) + } + + output, err := transcoder.Transcode(input_file, transcode_config, output_file.JoinPathTo()) + if err != nil { + log.Fatal().Err(err).Str("output", output).Msg("Transcode Failed") + } +} diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..8be37c4 --- /dev/null +++ b/config/config.go @@ -0,0 +1,24 @@ +package config + +import ( + "fmt" + + "github.com/spf13/viper" +) + +func init() { + viper.SetConfigName("musicutil") + viper.SetConfigType("yaml") + viper.AddConfigPath("$HOME/.config") + viper.AddConfigPath("$XDG_CONFIG_HOME") + + viper.SetDefault("log_level", "info") + viper.SetDefault("cache", false) + viper.SetDefault("cache_dir", "source_dir") + + if err := viper.ReadInConfig(); err != nil { + if _, isNotFound := err.(viper.ConfigFileNotFoundError); !isNotFound { + panic(fmt.Errorf("Fatal error config file: %w \n", err)) + } + } +} diff --git a/flake.nix b/flake.nix index 567bdfa..e311503 100644 --- a/flake.nix +++ b/flake.nix @@ -13,33 +13,24 @@ outputs = { self, nixpkgs, utils, ... }: { overlay = final: prev: - let - system = final.system; - fold-to-ascii = - final.python3Packages.callPackage ./nix-extra-deps/fold-to-ascii.nix - { }; + let system = final.system; in { - inherit fold-to-ascii; - musicutil = final.python3Packages.buildPythonApplication rec { + musicutil = final.buildGoModule rec { pname = "musicutil"; version = "latest"; src = ./.; postPatch = '' - substituteInPlace musicutil/meta.py --replace 'ffmpeg_path = "ffmpeg"' 'ffmpeg_path = "${final.ffmpeg}/bin/ffmpeg"' - substituteInPlace musicutil/meta.py --replace 'ffprobe_path = "ffprobe"' 'ffprobe_path = "${final.ffmpeg}/bin/ffprobe"' - substituteInPlace musicutil/meta.py --replace 'r128gain_path = "r128gain"' 'r128gain_path = "${final.r128gain}/bin/r128gain"' + substituteInPlace meta/meta.go --replace 'var ffmpeg_path = "ffmpeg"' 'var ffmpeg_path = "${final.ffmpeg}/bin/ffmpeg"' + substituteInPlace meta/meta.go --replace 'var ffprobe_path = "ffprobe"' 'var ffprobe_path = "${final.ffmpeg}/bin/ffprobe"' ''; - doCheck = false; - buildInputs = with final; [ r128gain ffmpeg ]; + vendorSha256 = + "sha256-8mL467JTgtlmXKElDDtHRjkPpzvcB4G7qcPcLrA7Pzw="; - propagatedBuildInputs = with final.python3Packages; [ - mutagen - fold-to-ascii - pyyaml - ]; + doCheck = false; + buildInputs = with final; [ ffmpeg ]; }; }; } // utils.lib.eachSystem (utils.lib.defaultSystems) (system: @@ -58,15 +49,10 @@ program = "${self.defaultPackage."${system}"}/bin/musicutil"; }; - devShell = pkgs.mkShell { - inputsFrom = [ self.packages.${system}.musicutil ]; - buildInputs = with pkgs; [ nixUnstable ffmpeg r128gain ]; - propagatedBuildInputs = with pkgs.python3Packages; [ - mutagen - pkgs.fold-to-ascii - pyyaml - ]; - }; + devShell = pkgs.mkShell { buildInputs = with pkgs; [ go ffmpeg ]; }; + + #hmModule = ./hm-module.nix; + lib = pkgs.musicutil.lib; }); } diff --git a/formatter.sh b/formatter.sh deleted file mode 100755 index fea8b00..0000000 --- a/formatter.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/run/current-system/sw/bin/env nix-shell -#!nix-shell -i bash - -autopep8 $(fd -e py) --in-place \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..655c7e4 --- /dev/null +++ b/go.mod @@ -0,0 +1,30 @@ +module gitlab.com/ChaotiCryptidz/musicutil + +go 1.17 + +require ( + github.com/akamensky/argparse v1.3.1 + github.com/cespare/xxhash v1.1.0 + github.com/dhowden/tag v0.0.0-20201120070457-d52dcb253c63 + github.com/rs/zerolog v1.26.1 + github.com/spf13/viper v1.10.1 +) + +require ( + github.com/fsnotify/fsnotify v1.5.1 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/kr/pretty v0.3.0 // indirect + github.com/magiconair/properties v1.8.5 // indirect + github.com/mitchellh/mapstructure v1.4.3 // indirect + github.com/pelletier/go-toml v1.9.4 // indirect + github.com/spf13/afero v1.6.0 // indirect + github.com/spf13/cast v1.4.1 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.2.0 // indirect + golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect + golang.org/x/text v0.3.7 // indirect + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect + gopkg.in/ini.v1 v1.66.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..f2f7c5b --- /dev/null +++ b/go.sum @@ -0,0 +1,784 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/akamensky/argparse v1.3.1 h1:kP6+OyvR0fuBH6UhbE6yh/nskrDEIQgEA1SUXDPjx4g= +github.com/akamensky/argparse v1.3.1/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dhowden/tag v0.0.0-20201120070457-d52dcb253c63 h1:/u5RVRk3Nh7Zw1QQnPtUH5kzcc8JmSSRpHSlGU/zGTE= +github.com/dhowden/tag v0.0.0-20201120070457-d52dcb253c63/go.mod h1:SniNVYuaD1jmdEEvi+7ywb1QFR7agjeTdGKyFb0p7Rw= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= +github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= +github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= +github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= +github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= +github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc= +github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sagikazarmark/crypt v0.4.0/go.mod h1:ALv2SRj7GxYV4HO9elxH9nS6M9gW+xDNxqmyJ6RfDFM= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= +github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.10.1 h1:nuJZuYpG7gTj/XqiUwg8bA0cp1+M2mC3J4g5luUYBKk= +github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486 h1:5hpz5aRr+W1erYCL5JRhSUBJRph7l9XkNveoExlrKYk= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI= +gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/hm-module.nix b/hm-module.nix new file mode 100644 index 0000000..c68426c --- /dev/null +++ b/hm-module.nix @@ -0,0 +1,37 @@ +{ config, lib, pkgs, ... }: +with lib; +let + cfg = config.programs.musicutil; + yamlFormat = pkgs.formats.yaml { }; + optionalPackage = opt: + optional (opt != null && opt.package != null) opt.package; + +in { + options = { + programs.musicutil = { + enable = mkOption { + type = types.bool; + default = false; + }; + + package = mkOption { + type = types.nullOr types.package; + default = null; + }; + + settings = { + logLevel = mkOption { + type = types.str; + default = "info"; + }; + }; + }; + }; + + config = mkIf cfg.enable { + home.packages = optionalPackage cfg.package; + + xdg.configFile."musicutil.yaml".source = + yamlFormat.generate "musicutil-config" cfg.settings; + }; +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..546e27e --- /dev/null +++ b/main.go @@ -0,0 +1,58 @@ +package main + +import ( + "os" + + "gitlab.com/ChaotiCryptidz/musicutil/commands/copy" + "gitlab.com/ChaotiCryptidz/musicutil/commands/genhtml" + "gitlab.com/ChaotiCryptidz/musicutil/commands/process" + "gitlab.com/ChaotiCryptidz/musicutil/commands/transcode" + + _ "gitlab.com/ChaotiCryptidz/musicutil/config" + _ "gitlab.com/ChaotiCryptidz/musicutil/utils/transcoder" + + "github.com/akamensky/argparse" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "github.com/spf13/viper" +) + +func main() { + log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) + + log_level := viper.GetString("log_level") + if log_level == "info" { + zerolog.SetGlobalLevel(zerolog.InfoLevel) + } else if log_level == "debug" { + zerolog.SetGlobalLevel(zerolog.DebugLevel) + } else { + zerolog.SetGlobalLevel(zerolog.InfoLevel) + } + + parser := argparse.NewParser("musicutil", "A tool for organising a music library and more.") + + process_cmd, process_cmd_args := process.RegisterProcessCommand(parser) + transcode_cmd, transcode_cmd_args := transcode.RegisterTranscodeCommand(parser) + genhtml_cmd, genhtml_cmd_args := genhtml.RegisterGenHTMLCommand(parser) + copy_cmd, copy_cmd_args := copy.RegisterCopyCommand(parser) + + if err := parser.Parse(os.Args); err != nil { + panic(err) + } + + if process_cmd.Happened() { + process.NewProcessCommand(process_cmd_args).Run() + } + + if transcode_cmd.Happened() { + transcode.NewTranscodeCommand(transcode_cmd_args).Run() + } + + if genhtml_cmd.Happened() { + genhtml.NewGenHTMLCommand(genhtml_cmd_args).Run() + } + + if copy_cmd.Happened() { + copy.NewCopyCommand(copy_cmd_args).Run() + } +} diff --git a/meta/meta.go b/meta/meta.go new file mode 100644 index 0000000..4857b83 --- /dev/null +++ b/meta/meta.go @@ -0,0 +1,4 @@ +package meta + +var ffmpeg_path = "ffmpeg" +var ffprobe_path = "ffprobe" diff --git a/musicutil/__init__.py b/musicutil/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/musicutil/__main__.py b/musicutil/__main__.py deleted file mode 100644 index be65b54..0000000 --- a/musicutil/__main__.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python3 - -import argparse - -from musicutil.commands.stats_command import StatsCommand, add_stats_command, get_stats_args - -from .commands.genhtml_command import GenHTMLCommand, add_genhtml_command, get_genhtml_args -from .commands.process_command import ProcessCommand, add_process_command, get_process_args -from .commands.copy_command import CopyCommand, add_copy_command, get_copy_args -from .commands.transcode_command import TranscodeCommand, add_transcode_command, get_transcode_args - -def main(): - parser = argparse.ArgumentParser( - description="chaos's musicutil") - subparsers = parser.add_subparsers(dest="subparser_name") - - add_copy_command(subparsers) - add_process_command(subparsers) - add_transcode_command(subparsers) - add_genhtml_command(subparsers) - add_stats_command(subparsers) - - args = parser.parse_args() - - if args.subparser_name == "process": - ProcessCommand(get_process_args(args)).run() - elif args.subparser_name == "copy": - CopyCommand(get_copy_args(args)).run() - elif args.subparser_name == "transcode": - TranscodeCommand(get_transcode_args(args)).run() - elif args.subparser_name == "genhtml": - GenHTMLCommand(get_genhtml_args(args)).run() - elif args.subparser_name == "stats": - StatsCommand(get_stats_args(args)).run() - else: - parser.print_help() - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/musicutil/commands/copy_command.py b/musicutil/commands/copy_command.py deleted file mode 100644 index 17b823f..0000000 --- a/musicutil/commands/copy_command.py +++ /dev/null @@ -1,226 +0,0 @@ -from musicutil.utils.do_replaygain import do_replaygain -from ..types import File -from ..utils.scan_for_music import scan_for_music -from ..utils.load_tag_information import load_tag_information -from ..transcode_presets import print_transcode_presets -from ..utils.transcoder import TranscodeConfig, transcode, get_transcode_config - -from os import makedirs as make_directories -from os.path import exists as path_exists -from shutil import copy as copy_file -from copy import deepcopy as deep_copy - -class CopyCommandState: - files: list[File] = [] - transcoded_files: list[File] = [] - - -class CopyCommandArgs(): - src: str - dest: str - transcode_preset: str - custom_transcoder_config: str - single_directory: bool - skip_existing: bool - skip_replaygain: bool - - -def add_copy_command(subparsers): - copy_parser = subparsers.add_parser('copy') - copy_parser.add_argument( - 'src', - type=str, - help='src base music directory') - copy_parser.add_argument( - 'dest', - type=str, - help='dest music directory') - copy_parser.add_argument( - '--transcode-preset', - type=str, - help='transcode preset (copy = no transcode)', - default="copy") - copy_parser.add_argument( - '--custom-transcoder-config', - type=str, - help='custom transcoder config') - copy_parser.add_argument( - '--skip-existing', - action='store_true') - copy_parser.add_argument( - '--skip-replaygain', - action='store_true') - copy_parser.add_argument( - '--single-directory', - action='store_true') - - -def get_copy_args(args) -> CopyCommandArgs: - command_args = CopyCommandArgs() - command_args.src = args.src - command_args.dest = args.dest - command_args.transcode_preset = args.transcode_preset - command_args.custom_transcoder_config = args.custom_transcoder_config - command_args.single_directory = args.single_directory - command_args.skip_existing = args.skip_existing - command_args.skip_replaygain = args.skip_replaygain - return command_args - - -class CopyCommand(): - def __init__(self, args: CopyCommandArgs): - self.args = args - self.state = CopyCommandState() - - def run(self): - if self.args.transcode_preset == "list": - print_transcode_presets() - exit() - - print("Copying") - self.scan_for_music() - self.load_tag_information() - if self.args.single_directory: - self.check_for_collisions() - self.transcode_files() - if self.args.single_directory: - self.create_mappings() - if not self.args.skip_replaygain: - self.add_replaygain_tags() - - def scan_for_music(self): - print("Scanning For Music") - self.state.files = scan_for_music(self.args.src) - - def load_tag_information(self): - print("Loading Tag Information") - - for file in self.state.files: - file.tags = load_tag_information(file) - - def check_for_collisions(self): - print("Checking For Colisions") - seen = set() - dupes = [] - - for file in self.state.files: - filename = file.filename - if filename in seen: - dupes.append(filename) - else: - seen.add(filename) - - if len(dupes) > 0: - print("Dupes Found:", dupes) - print("Cannot continue using --single-directory") - print("Please rename or remove duplicates") - exit() - - def _transcode_copy(self, file: File): - src = file.join_path_to() - dest = file.join_filename( - ) if self.args.single_directory else file.join_path_from_src() - dest = self.args.dest + "/" + dest - - exists = path_exists(dest) - - should_skip = False - if self.args.skip_existing and exists: - should_skip = True - - if should_skip: - print("Copying", src, "to", dest) - copy_file( - src, - dest, - ) - else: - print( - "Skipping", - src, - "as already is copied at", - dest) - - self.state.transcoded_files.append(file) - - def _transcode_with_config( - self, - file: File, - trans_config: TranscodeConfig): - src = file.join_path_to() - - new_file = deep_copy(file) - new_file.extension = trans_config.file_extension - - dest_filepath = "" - - if self.args.single_directory: - dest_filepath = new_file.join_filename() - else: - dest_filepath = new_file.join_path_from_src() - - dest_filepath = self.args.dest + "/" + dest_filepath - - should_skip_transcode = self.args.skip_existing and path_exists( - dest_filepath) - - if should_skip_transcode: - print("Skipping transcoding", dest_filepath) - self.state.transcoded_files.append(new_file) - return - - print("Transcoding", src, "to", dest_filepath) - - transcode(file, trans_config, dest_filepath) - - self.state.transcoded_files.append(new_file) - - def transcode_files(self): - print("Transcoding Files") - - if not self.args.single_directory: - # set can't contain duplicates - # so it only creates a dir once - directories = set() - for file in self.state.files: - directories.add(file.path_from_src) - for dir in directories: - make_directories( - self.args.dest + "/" + dir, - exist_ok=True) - - is_transcode_config_set = self.args.custom_transcoder_config is not None - - if self.args.transcode_preset == "copy" and not is_transcode_config_set: - for file in self.state.files: - self._transcode_copy(file) - return - else: - global trans_config - trans_config = None - if is_transcode_config_set: - with open(self.args.custom_transcoder_config, "r+") as f: - trans_config = TranscodeConfig() - trans_config.load_from_file(f) - else: - trans_config = get_transcode_config( - self.args.transcode_preset) - - for file in self.state.files: - self._transcode_with_config( - file, trans_config) - - def create_mappings(self): - with open(self.dest + "/" + "mappings.txt", "w") as f: - f.write("\n".join([ - f"{file.path_from_src} <- {file.filename}" for file in self.state.files - ])) - - def add_replaygain_tags(self): - print("Adding ReplayGain Tags") - - for file in self.state.files: - print( - f"Adding ReplayGain Tags to \"{file.filename}.{file.extension}\"") - - do_replaygain(file) diff --git a/musicutil/commands/genhtml_command.py b/musicutil/commands/genhtml_command.py deleted file mode 100644 index 2941865..0000000 --- a/musicutil/commands/genhtml_command.py +++ /dev/null @@ -1,141 +0,0 @@ -from musicutil.types import File -from musicutil.utils.load_tag_information import load_tag_information -from musicutil.utils.scan_for_music import scan_for_music - -from html import escape as escape_html - -class GenHTMLArgs(): - src: str - dest: str - title: str - description: str - by_folder: bool - -def add_genhtml_command(subparsers): - genhtml_parser = subparsers.add_parser('genhtml') - genhtml_parser.add_argument( - 'src', - type=str, - help='src base music directory') - genhtml_parser.add_argument( - 'dest', - type=str, - help='dest html file') - genhtml_parser.add_argument( - 'title', - type=str, - help='title') - genhtml_parser.add_argument( - 'description', - type=str, - help='description') - genhtml_parser.add_argument( - '--by-folder', - action='store_true') - -def get_genhtml_args(args) -> GenHTMLArgs: - command_args = GenHTMLArgs() - command_args.src = args.src - command_args.dest = args.dest - command_args.title = args.title - command_args.description = args.description - command_args.by_folder = args.by_folder - return command_args - -def make_table_for_files(files: list[File], by_folder: bool = False) -> str: - html_content = "" - path_head = "Path" if not by_folder else "" - - html_content += f""" - - - - {path_head} - - - - - - - """ - isOdd = True - print(files) - for file in files: - tdClass = "pure-table-odd" if isOdd else "pure-table-even" - data_path = escape_html(file.path_from_src) - data_title = escape_html(file.tags.title) - data_artist = escape_html(file.tags.artist) - data_extension = escape_html(file.extension) - path_data = f"" if not by_folder else "" - - html_content += f""" - - {path_data} - - - - - """ - isOdd = not isOdd - html_content += """ - -
TitleArtistFormat
{data_path}
{data_title}{data_artist}{data_extension}
- """ - return html_content - -class GenHTMLCommand(): - def __init__(self, args: GenHTMLArgs): - self.args = args - - def run(self): - print("Generating HTML...") - print("Scanning For Music...") - files = scan_for_music(self.args.src) - print("Loading Tag Information") - for file in files: - tags = load_tag_information(file) - file.tags = tags - - files.sort(key=lambda file: file.tags.artist) - files.sort(key=lambda file: file.tags.title) - files.sort(key=lambda file: file.path_from_src) - - html_content = "" - html_content += f""" - - - - - - {self.args.title} - - - - - """ - - if self.args.by_folder: - files_per_folder: dict[str, list[File]] = {} - for file in files: - if not file.path_from_src in files_per_folder.keys(): - files_per_folder[file.path_from_src] = [] - files_per_folder[file.path_from_src].append(file) - for folder in files_per_folder.keys(): - folder_name = folder - folder_files = files_per_folder[folder_name] - html_content += f"

{folder_name}

" - html_content += make_table_for_files(folder_files, True) - else: - html_content += make_table_for_files(files) - - html_content += """ - - - """ - - print("Writing HTML...") - with open(self.args.dest, "w+") as html_file: - html_file.write(html_content) - - - diff --git a/musicutil/commands/process_command.py b/musicutil/commands/process_command.py deleted file mode 100644 index 47cf666..0000000 --- a/musicutil/commands/process_command.py +++ /dev/null @@ -1,115 +0,0 @@ -from musicutil.utils.do_replaygain import do_replaygain -from ..types import File -from ..utils.scan_for_music import scan_for_music -from ..utils.load_tag_information import load_tag_information -from ..utils.substitutions import reduce_to_ascii_and_substitute - -from copy import deepcopy as deep_copy -from os import rename as rename_file - - -class ProcessCommandState: - files: list[File] = [] - - -class ProcessCommandArgs: - src: str - dry_run: bool - replaygain: str - -def add_process_command(subparsers): - process_parser = subparsers.add_parser('process') - process_parser.add_argument( - 'src', - type=str, - help='src base music directory') - process_parser.add_argument( - '--dry-run', action='store_true') - process_parser.add_argument( - '--replaygain', - type=str, - default="skip_existing", - help="skip,skip_existing,force" - ) - - -def get_process_args(args) -> ProcessCommandArgs: - command_args = ProcessCommandArgs() - command_args.src = args.src - command_args.dry_run = args.dry_run - command_args.replaygain = args.replaygain - return command_args - - -class ProcessCommand(): - def __init__(self, args: ProcessCommandArgs): - self.args = args - self.state = ProcessCommandState() - - def run(self): - print("Renaming") - self.scan_for_music() - self.load_tag_information() - self.rename_files() - if not self.args.dry_run: - if self.args.replaygain != "skip": - self.add_replaygain_tags() - - def scan_for_music(self): - print("Scanning For Music") - self.state.files = scan_for_music(self.args.src) - - def load_tag_information(self): - print("Loading Tag Information") - - for file in self.state.files: - tags = load_tag_information(file) - file.tags = tags - - def _rename_file(self, file: File) -> File: - filename = file.filename - artist = file.tags.artist.replace("\n", "") - title = file.tags.title.replace("\n", "") - - proper_filename = reduce_to_ascii_and_substitute( - f"{artist} - {title}") - - if filename != proper_filename: - print(f"Renaming \"{filename}\"", "to " + - f"\"{proper_filename}\"" + "\n") - - new_file = deep_copy(file) - new_file.filename = proper_filename - - if not self.args.dry_run: - rename_file( - file.join_path_to(), - new_file.join_path_to()) - # so that other steps after read the new file and not - # the orig pre-rename file when not dry run - return new_file - else: - return file - - def rename_files(self): - print("Renaming files") - - for file in self.state.files: - index = self.state.files.index(file) - self.state.files[index] = self._rename_file( - file) - - def add_replaygain_tags(self): - print("Adding ReplayGain Tags") - - for file in self.state.files: - # Even though r128gain has a flag to skip when tags already exist - # it's very slow compared to just checking when getting tags - # so skip early - if self.args.replaygain == "skip_existing": - if file.tags.has_replaygain: - continue - print( - f"Adding ReplayGain Tags to \"{file.filename}.{file.extension}\"") - - do_replaygain(file, False) diff --git a/musicutil/commands/stats_command.py b/musicutil/commands/stats_command.py deleted file mode 100644 index 6faade5..0000000 --- a/musicutil/commands/stats_command.py +++ /dev/null @@ -1,54 +0,0 @@ -from musicutil.utils.load_tag_information import load_tag_information -from musicutil.utils.scan_for_music import scan_for_music - -from functools import reduce - -class StatsArgs(): - src: str - -def add_stats_command(subparsers): - stats_parser = subparsers.add_parser('stats') - stats_parser.add_argument( - 'src', - type=str, - help='src base music directory') - -def get_stats_args(args) -> StatsArgs: - command_args = StatsArgs() - command_args.src = args.src - return command_args - -class StatsCommand(): - def __init__(self, args: StatsArgs): - self.args = args - - def run(self): - print("Generating HTML...") - print("Scanning For Music...") - files = scan_for_music(self.args.src) - print("Loading Tag Information") - for file in files: - tags = load_tag_information(file) - file.tags = tags - - - # So far we only support MP3 and Flac so only have these - mp3_files_count = len([True for file in files if file.extension == "mp3"]) - flac_files_count = len([True for file in files if file.extension == "flac"]) - - lossy_files_count = mp3_files_count - lossless_files_count = flac_files_count - total_files_count = lossy_files_count + lossless_files_count - - lossy_percent = (lossy_files_count/total_files_count) - lossless_percent = (lossless_files_count/total_files_count) - - print("Stats:") - print(f" Total Files: {total_files_count}") - print(f" Lossy#: {lossy_files_count}/{total_files_count}") - print(f" Lossless#: {lossless_files_count}/{total_files_count}") - print(f" Lossy%: {lossy_percent:.2%}") - print(f" Lossless%: {lossless_percent:.2%}") - - - diff --git a/musicutil/commands/transcode_command.py b/musicutil/commands/transcode_command.py deleted file mode 100644 index 6f49bcb..0000000 --- a/musicutil/commands/transcode_command.py +++ /dev/null @@ -1,92 +0,0 @@ -from musicutil.utils.do_replaygain import do_replaygain -from ..utils.transcoder import get_transcode_config, transcode, TranscodeConfig -from ..utils.scan_for_music import file_from_path -from ..transcode_presets import print_transcode_presets, transcode_presets - -from pathlib import Path -from json import load as load_json_file - - -class TranscodeCommandArgs: - src: str - dest: str - transcode_preset: str - ignore_extension: bool - custom_transcoder_config_path: str - skip_replaygain: bool - - -def add_transcode_command(subparsers): - transcode_parser = subparsers.add_parser('transcode') - transcode_parser.add_argument( - 'src', - type=str, - help='src base music directory') - transcode_parser.add_argument( - 'dest', - type=str, - help='dest music directory') - transcode_parser.add_argument( - '--transcode-preset', - type=str, - help='transcode preset', - default="opus-96k") - transcode_parser.add_argument( - '--ignore-extension', - action='store_true') - transcode_parser.add_argument( - '--custom-transcoder-config', - type=str, - help='custom transcoder config') - transcode_parser.add_argument( - '--skip-replaygain', - action='store_true') - - -def get_transcode_args(args) -> TranscodeCommandArgs: - command_args = TranscodeCommandArgs() - command_args.src = args.src - command_args.dest = args.dest - command_args.transcode_preset = args.transcode_preset - command_args.ignore_extension = args.ignore_extension - command_args.custom_transcoder_config_path = args.custom_transcoder_config - command_args.skip_replaygain = args.skip_replaygain - return command_args - - -class TranscodeCommand: - def __init__(self, args: TranscodeCommandArgs): - self.args = args - - def run(self): - if self.args.transcode_preset == "list": - print_transcode_presets() - exit() - - print("Transcoding...") - input_file = file_from_path(Path(self.args.src), "") - - if self.args.custom_transcoder_config_path is None or len( - self.args.custom_transcoder_config_path) == 0: - trans_config = get_transcode_config( - self.args.transcode_preset) - else: - with open(self.args.custom_transcoder_config_path, "r+") as file: - trans_config = TranscodeConfig() - trans_config.load_from_file(file) - - output_file = file_from_path( - Path(self.args.dest), "") - - if trans_config.file_extension != output_file.extension and not self.args.ignore_extension: - print( - f"{output_file.extension} is not the recommended " + - f"extension for transcode config " + - f"please change it to {trans_config.file_extension} " + - f"or run with --ignore-extension") - exit() - transcode(input_file, trans_config, self.args.dest) - - if not self.args.skip_replaygain: - print("Adding ReplayGain Tags") - do_replaygain(output_file, False) diff --git a/musicutil/meta.py b/musicutil/meta.py deleted file mode 100644 index d266fd3..0000000 --- a/musicutil/meta.py +++ /dev/null @@ -1,14 +0,0 @@ -# All file extensions that are supported and have tag -# extraction -supported_formats = ["mp3", "flac"] - -sub_char = "_" - -substitutions = { - "α": "a", -} - -# Patch to whatever path ffmpeg is at -ffmpeg_path = "ffmpeg" -ffprobe_path = "ffprobe" -r128gain_path = "r128gain" diff --git a/musicutil/transcode_presets.py b/musicutil/transcode_presets.py deleted file mode 100644 index 0ff4147..0000000 --- a/musicutil/transcode_presets.py +++ /dev/null @@ -1,119 +0,0 @@ -from functools import reduce -import re - -def atoi(text) -> int or str: - try: - ret = int(text) - return ret - except: - return text - -def natural_keys(text: str): - return [ atoi(c) for c in re.split(r'(-?\d+)', text) ] - -def sort_natural(list: list[str]): - list.sort(key=natural_keys) - -def add_to_arr( - arr: list[str], - items: list[str]) -> list[str]: - for item in items: - arr.append(item) - - -# does not include copy -transcode_presets = {} - -transcode_presets["mp3"] = [] - -# mp3 v0 -> v9 -add_to_arr(transcode_presets["mp3"], [ - f"mp3-v{quality}" for quality in range(0, 10) -]) -# mp3 bitrates -mp3_bitrates = [ - 8, - 16, - 24, - 32, - 40, - 48, - 64, - 80, - 96, - 112, - 128, - 160, - 192, - 224, - 256, - 320] -add_to_arr(transcode_presets["mp3"], [ - f"mp3-{bitrate}k" for bitrate in mp3_bitrates -]) - -transcode_presets["opus"] = [] - -opus_bitrates = ["16", "24", "32", "64", "96", "128", "256"] -add_to_arr(transcode_presets["opus"], [ - f"opus-{bitrate}k" for bitrate in opus_bitrates -]) - -transcode_presets["vorbis"] = [] - -add_to_arr(transcode_presets["vorbis"], [ - f"vorbis-q{quality}" for quality in range(-2, 11) -]) - -transcode_presets["speex"] = [] - -add_to_arr(transcode_presets["speex"], [ - f"speex-q{quality}" for quality in range(0, 11) -]) - -transcode_presets["g726"] = [] - -g726_bitrates = ["16", "24", "32", "40"] -add_to_arr(transcode_presets["g726"], [ - f"g726-{bitrate}k" for bitrate in g726_bitrates -]) - -# Extra Default Mappings -preset_transcode_presets = {} - -mp3_presets = { - "mp3-low": "mp3-v4", - "mp3-medium": "mp3-v2", - "mp3-high": "mp3-v0", -} - -preset_transcode_presets = preset_transcode_presets | mp3_presets -add_to_arr(transcode_presets["mp3"], mp3_presets.keys()) - -opus_presets = { - "opus-low": "opus-32k", - "opus-medium": "opus-64k", - "opus-high": "opus-96k", - "opus-higher": "opus-128k", - "opus-extreme": "opus-256k" -} - -preset_transcode_presets = preset_transcode_presets | opus_presets -add_to_arr(transcode_presets["opus"], opus_presets.keys()) - -sort_natural(transcode_presets["mp3"]) -sort_natural(transcode_presets["opus"]) -sort_natural(transcode_presets["vorbis"]) -sort_natural(transcode_presets["speex"]) -sort_natural(transcode_presets["g726"]) - - -def print_transcode_presets(): - for category in transcode_presets.keys(): - print(f"Category {category}:") - for preset in transcode_presets[category]: - print(f"- {preset}") - - -transcode_presets_list = reduce( - lambda a, b: a + b, transcode_presets.values()) diff --git a/musicutil/types.py b/musicutil/types.py deleted file mode 100644 index 5d6e6da..0000000 --- a/musicutil/types.py +++ /dev/null @@ -1,46 +0,0 @@ -class Tags: - title = "" - artist = "" - has_replaygain = False - - def to_dict(self): - return { - "title": self.title, - "artist": self.artist, - "has_replaygain": self.has_replaygain - } - - def __repr__(self): - return repr(self.to_dict()) - - -class File: - filename = "" - extension = "" - # The path to the actual file - path_to = "" - # The path relative to the source directory - path_from_src = "" - - tags = Tags() - - def join_filename(self): - return f"{self.filename}.{self.extension}" - - def join_path_to(self): - return f"{self.path_to}/{self.filename}.{self.extension}" - - def join_path_from_src(self): - return f"{self.path_from_src}/{self.filename}.{self.extension}" - - def to_dict(self): - return { - "filename": self.filename, - "extension": self.extension, - "path_to": self.path_to, - "path_from_src": self.path_from_src, - "tags": self.tags.to_dict(), - } - - def __repr__(self): - return repr(self.to_dict()) diff --git a/musicutil/utils/do_replaygain.py b/musicutil/utils/do_replaygain.py deleted file mode 100644 index 125e443..0000000 --- a/musicutil/utils/do_replaygain.py +++ /dev/null @@ -1,19 +0,0 @@ -from ..types import File -from ..meta import r128gain_path, ffmpeg_path - -from subprocess import run as run_command - - -def do_replaygain( - file: File, - skip_if_existing: bool = True) -> None: - command_args = [ - r128gain_path, - "-f", ffmpeg_path, - "-v", "warning", - file.join_path_to() - ] - - if skip_if_existing: - command_args.append("-s") - run_command(command_args) diff --git a/musicutil/utils/load_tag_information.py b/musicutil/utils/load_tag_information.py deleted file mode 100644 index aafa443..0000000 --- a/musicutil/utils/load_tag_information.py +++ /dev/null @@ -1,86 +0,0 @@ -from ..types import File, Tags -from ..meta import ffprobe_path - -from subprocess import run as run_command -from json import loads as load_json_string - -from mutagen.mp3 import MP3, EasyMP3 -from mutagen.flac import FLAC - - -def load_tag_information_mutagen(file: File) -> Tags: - path = file.join_path_to() - tags = Tags() - if file.extension == "mp3": - easymp3 = EasyMP3(path) - mp3 = MP3(path) - tags.title = easymp3["title"][0] - tags.artist = easymp3["artist"][0] - if "REPLAYGAIN_TRACK_GAIN" in mp3.keys() or "TXXX:REPLAYGAIN_TRACK_PEAK" in mp3.keys(): - tags.has_replaygain = True - elif file.extension == "flac": - flac = FLAC(path) - if "replaygain_track_peak" in flac.keys(): - tags.has_replaygain = True - tags.title = flac["title"][0] - tags.artist = flac["artist"][0] - else: - raise Exception("Could Not Load Tags Using Mutagen") - return tags - - -def load_tag_information_ffmpeg(file: File) -> Tags: - path = file.join_path_to() - tags = Tags() - - command_args = [ - ffprobe_path, - "-v", "quiet", - "-print_format", "json", - "-show_format", - path - ] - - ffprobe_output = run_command( - command_args, capture_output=True).stdout - - data = load_json_string(ffprobe_output) - file_tags = data["format"]["tags"] - - title = None - artist = None - - try: - print(file_tags.keys()) - if "title" in file_tags.keys(): - title = file_tags["title"] - elif "TITLE" in file_tags.keys(): - title = file_tags["TITLE"] - - if "artist" in file_tags.keys(): - artist = file_tags["artist"] - elif "ARTIST" in file_tags.keys(): - artist = file_tags["ARTIST"] - except Exception as e: - print(data["format"]["tags"], file, e) - exit() - - if title is None or artist is None: - raise Exception("Could Not Load Tags Using FFprobe") - - tags.title = title - tags.artist = artist - return tags - - -def load_tag_information(file: File) -> Tags: - try: - tags = load_tag_information_mutagen(file) - return tags - except Exception as _: - tags = load_tag_information_ffmpeg(file) - return tags - except BaseException: - print( - f"Could not get tags for file {file.filename}. Exiting.") - exit() diff --git a/musicutil/utils/scan_for_music.py b/musicutil/utils/scan_for_music.py deleted file mode 100644 index 3730241..0000000 --- a/musicutil/utils/scan_for_music.py +++ /dev/null @@ -1,23 +0,0 @@ -from pathlib import Path -from os.path import relpath - -from ..types import File -from ..meta import supported_formats - - -def file_from_path(path: Path, src: str) -> File: - file = File() - file.path_to = str(path.parent) - file.path_from_src = relpath( - str(path.parent), src) - file.filename = path.stem - file.extension = path.suffix.replace(".", "") - return file - - -def scan_for_music(src: str) -> list[File]: - files: list[File] = [] - for format in supported_formats: - for path in Path(src).rglob("*." + format): - files.append(file_from_path(path, src)) - return files diff --git a/musicutil/utils/substitutions.py b/musicutil/utils/substitutions.py deleted file mode 100644 index db2a613..0000000 --- a/musicutil/utils/substitutions.py +++ /dev/null @@ -1,13 +0,0 @@ -from ..meta import sub_char, substitutions -from fold_to_ascii import fold - - -def reduce_to_ascii_and_substitute(filename: str): - filename = filename.replace("/", sub_char) - filename = filename.replace("\\", sub_char) - filename = filename.replace("\n", "") - for sub_before in substitutions.keys(): - filename = filename.replace( - sub_before, substitutions[sub_before]) - filename = fold(filename) - return filename diff --git a/musicutil/utils/transcoder.py b/musicutil/utils/transcoder.py deleted file mode 100644 index 7cac9cf..0000000 --- a/musicutil/utils/transcoder.py +++ /dev/null @@ -1,155 +0,0 @@ -from ..transcode_presets import preset_transcode_presets -from ..types import File -from ..meta import ffmpeg_path - -from yaml import load as load_yaml_file -from yaml import Loader as YamlLoader -from subprocess import run as run_command - - -class TranscodeConfig: - use_quality = False - use_bitrate = False - encoder = "" - file_extension = "" - container = "" - bitrate = "" - quality = "" - sample_rate = "" - channels = "" - - def load_from_file(self, file): - self.load_from_dict( - load_yaml_file( - file, Loader=YamlLoader)) - - def load_from_dict(self, data): - if "use_quality" in data: - self.use_quality = data["use_quality"] - if "use_bitrate" in data: - self.use_bitrate = data["use_bitrate"] - if "encoder" in data: - self.encoder = data["encoder"] - if "file_extension" in data: - self.file_extension = data["file_extension"] - if "container" in data: - self.container = data["container"] - if "bitrate" in data: - self.bitrate = data["bitrate"] - if "quality" in data: - self.quality = data["quality"] - if "sample_rate" in data: - self.sample_rate = data["sample_rate"] - if "channels" in data: - self.channels = data["channels"] - return self - - -def get_transcode_config(preset: str): - conf = TranscodeConfig() - if preset in preset_transcode_presets.keys(): - preset = preset_transcode_presets[preset] - - if preset.startswith("g726-") and preset.endswith("k"): - conf.load_from_dict({ - "container": "matroska", - "file_extension": "mka", - "encoder": "g726", - "sample_rate": "8000", - "channels": "1", - "use_bitrate": True, - "bitrate": preset.replace("g726-", "") - }) - return conf - - if preset.startswith("opus-") and preset.endswith("k"): - conf.load_from_dict({ - "container": "ogg", - "file_extension": "opus", - "encoder": "libopus", - "use_bitrate": True, - "bitrate": preset.replace("opus-", "") - }) - return conf - - if preset.startswith("mp3-"): - conf.load_from_dict({ - "container": "mp3", - "file_extension": "mp3", - "encoder": "libmp3lame", - }) - - if preset.startswith("mp3-v"): - conf.use_quality = True - conf.quality = preset.replace("mp3-v", "") - return conf - elif preset.startswith("mp3-") and preset.endswith("k"): - conf.use_bitrate = True - conf.bitrate = preset.replace("mp3-", "") - return conf - - if preset.startswith("vorbis-q"): - conf.load_from_dict({ - "container": "ogg", - "file_extension": "ogg", - "encoder": "libvorbis", - "use_quality": True, - "quality": preset.replace("vorbis-q", ""), - }) - return conf - - if preset.startswith("speex-q"): - conf.load_from_dict({ - "container": "ogg", - "file_extension": "ogg", - "encoder": "libspeex", - "use_quality": True, - "quality": preset.replace("speex-q", ""), - }) - return conf - - print("Unknown Level") - exit() - - -def transcode( - file: File, - config: TranscodeConfig, - dest: str): - ffmpeg_command = [ - ffmpeg_path, - "-y", - "-hide_banner", - "-loglevel", "warning", - "-i", file.join_path_to(), - ] - - if len(config.encoder) != 0: - ffmpeg_command.append("-c:a") - ffmpeg_command.append(config.encoder) - - if len(config.container) != 0: - ffmpeg_command.append("-f") - ffmpeg_command.append(config.container) - - if len(config.sample_rate) != 0: - ffmpeg_command.append("-ar") - ffmpeg_command.append(config.sample_rate) - - if len(config.channels) != 0: - ffmpeg_command.append("-ac") - ffmpeg_command.append(config.channels) - - if config.use_quality: - ffmpeg_command.append("-q:a") - ffmpeg_command.append(config.quality) - elif config.use_bitrate: - ffmpeg_command.append("-b:a") - ffmpeg_command.append(config.bitrate) - else: - pass - - ffmpeg_command.append(dest) - print(ffmpeg_command) - # TODO: check for errors - run_command(ffmpeg_command) diff --git a/nix-extra-deps/fold-to-ascii.nix b/nix-extra-deps/fold-to-ascii.nix deleted file mode 100644 index 4e9bd08..0000000 --- a/nix-extra-deps/fold-to-ascii.nix +++ /dev/null @@ -1,12 +0,0 @@ -{ lib, buildPythonPackage, fetchPypi }: - -buildPythonPackage rec { - pname = "fold-to-ascii"; - version = "1.0.2.post1"; - - src = fetchPypi { - pname = "fold_to_ascii"; - inherit version; - sha256 = "sha256-cWegf9wjC9XfU4HpIh/iRtDv86hutn45NfkfWuyjUzo="; - }; -} diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index a704cb7..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,5 +0,0 @@ -[tool.autopep8] -max_line_length = 60 -in-place = true -recursive = true -aggressive = 3 \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100755 index 99edb53..0000000 --- a/setup.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python - -from setuptools import setup - -setup( - name='musicutil', - version='1.0', - description='A utility for managing a music library', - author='Chaos', - author_email='chaoticryptidz@owo.monster', - url='https://gitlab.com/ChaotiCryptidz/musicutil', - packages=['musicutil', 'musicutil.commands', 'musicutil.utils'], - entry_points = {'console_scripts': ['musicutil = musicutil.__main__:main'],}, -) \ No newline at end of file diff --git a/tconf.json b/tconf.json deleted file mode 100644 index 3db7507..0000000 --- a/tconf.json +++ /dev/null @@ -1 +0,0 @@ -{"file_extension": "flac", "encoder": "flac"} diff --git a/types/file.go b/types/file.go new file mode 100644 index 0000000..2153b74 --- /dev/null +++ b/types/file.go @@ -0,0 +1,67 @@ +package types + +import ( + "fmt" + "path" + "path/filepath" + "strings" +) + +type Tags struct { + Title string `json:"title"` + Artist string `json:"artist"` +} + +func (tags *Tags) DeepCopy() *Tags { + return &Tags{ + Title: tags.Title, + Artist: tags.Artist, + } +} + +type File struct { + Filename string `json:"filename"` + Extension string `json:"extension"` + // The path to the actual file from current dir + PathTo string `json:"path_to"` + // The path of the file relative to the source directory + PathFromSource string `json:"path_from_source"` + Tags *Tags `json:"tags"` +} + +func FileFromPath(sourceDir string, fullPath string) *File { + path_to := filepath.Dir(fullPath) + path_from_src, _ := filepath.Rel(sourceDir, filepath.Dir(fullPath)) + filename := strings.TrimSuffix(path.Base(fullPath), path.Ext(fullPath)) + extension := strings.TrimPrefix(path.Ext(fullPath), ".") + + return &File{ + PathTo: path_to, + PathFromSource: path_from_src, + Filename: filename, + Extension: extension, + Tags: &Tags{Title: "", Artist: ""}, + } +} + +func (f *File) JoinFilename() string { + return fmt.Sprintf("%s.%s", f.Filename, f.Extension) +} + +func (f *File) JoinPathTo() string { + return fmt.Sprintf("%s/%s", f.PathTo, f.JoinFilename()) +} + +func (f *File) JoinPathFromSource() string { + return fmt.Sprintf("%s/%s", f.PathFromSource, f.JoinFilename()) +} + +func (f *File) DeepCopy() *File { + return &File{ + Filename: f.Filename, + Extension: f.Extension, + PathTo: f.PathTo, + PathFromSource: f.PathFromSource, + Tags: f.Tags.DeepCopy(), + } +} diff --git a/utils/ascii_reduce/mappings.go b/utils/ascii_reduce/mappings.go new file mode 100644 index 0000000..5110753 --- /dev/null +++ b/utils/ascii_reduce/mappings.go @@ -0,0 +1,25 @@ +package ascii_reduce + +import ( + _ "embed" + "encoding/json" + "strconv" +) + +//go:embed mappings.json +var mappingsData string + +var Mappings map[rune]string + +func init() { + var mappings map[string]string + + json.Unmarshal([]byte(mappingsData), &mappings) + + Mappings = make(map[rune]string) + + for key, value := range mappings { + r, _ := strconv.Atoi(key) + Mappings[rune(r)] = value + } +} diff --git a/utils/ascii_reduce/mappings.json b/utils/ascii_reduce/mappings.json new file mode 100644 index 0000000..6382dfc --- /dev/null +++ b/utils/ascii_reduce/mappings.json @@ -0,0 +1,1371 @@ +{ + "0": "\u0000", + "1": "\u0001", + "2": "\u0002", + "3": "\u0003", + "4": "\u0004", + "5": "\u0005", + "6": "\u0006", + "7": "\u0007", + "8": "\b", + "9": "\t", + "10": "\n", + "11": "\u000b", + "12": "\f", + "13": "\r", + "14": "\u000e", + "15": "\u000f", + "16": "\u0010", + "17": "\u0011", + "18": "\u0012", + "19": "\u0013", + "20": "\u0014", + "21": "\u0015", + "22": "\u0016", + "23": "\u0017", + "24": "\u0018", + "25": "\u0019", + "26": "\u001a", + "27": "\u001b", + "28": "\u001c", + "29": "\u001d", + "30": "\u001e", + "31": "\u001f", + "32": " ", + "33": "!", + "34": "\"", + "35": "#", + "36": "$", + "37": "%", + "38": "&", + "39": "'", + "40": "(", + "41": ")", + "42": "*", + "43": "+", + "44": ",", + "45": "-", + "46": ".", + "47": "/", + "48": "0", + "49": "1", + "50": "2", + "51": "3", + "52": "4", + "53": "5", + "54": "6", + "55": "7", + "56": "8", + "57": "9", + "58": ":", + "59": ";", + "60": "<", + "61": "=", + "62": ">", + "63": "?", + "64": "@", + "65": "A", + "66": "B", + "67": "C", + "68": "D", + "69": "E", + "70": "F", + "71": "G", + "72": "H", + "73": "I", + "74": "J", + "75": "K", + "76": "L", + "77": "M", + "78": "N", + "79": "O", + "80": "P", + "81": "Q", + "82": "R", + "83": "S", + "84": "T", + "85": "U", + "86": "V", + "87": "W", + "88": "X", + "89": "Y", + "90": "Z", + "91": "[", + "92": "\\", + "93": "]", + "94": "^", + "95": "_", + "96": "`", + "97": "a", + "98": "b", + "99": "c", + "100": "d", + "101": "e", + "102": "f", + "103": "g", + "104": "h", + "105": "i", + "106": "j", + "107": "k", + "108": "l", + "109": "m", + "110": "n", + "111": "o", + "112": "p", + "113": "q", + "114": "r", + "115": "s", + "116": "t", + "117": "u", + "118": "v", + "119": "w", + "120": "x", + "121": "y", + "122": "z", + "123": "{", + "124": "|", + "125": "}", + "126": "~", + "192": "A", + "193": "A", + "194": "A", + "195": "A", + "196": "A", + "197": "A", + "256": "A", + "258": "A", + "260": "A", + "399": "A", + "461": "A", + "478": "A", + "480": "A", + "506": "A", + "512": "A", + "514": "A", + "550": "A", + "570": "A", + "7424": "A", + "7680": "A", + "7840": "A", + "7842": "A", + "7844": "A", + "7846": "A", + "7848": "A", + "7850": "A", + "7852": "A", + "7854": "A", + "7856": "A", + "7858": "A", + "7860": "A", + "7862": "A", + "9398": "A", + "65313": "A", + "224": "a", + "225": "a", + "226": "a", + "227": "a", + "228": "a", + "229": "a", + "257": "a", + "259": "a", + "261": "a", + "462": "a", + "479": "a", + "481": "a", + "507": "a", + "513": "a", + "515": "a", + "551": "a", + "592": "a", + "601": "a", + "602": "a", + "7567": "a", + "7573": "a", + "7681": "a", + "7834": "a", + "7841": "a", + "7843": "a", + "7845": "a", + "7847": "a", + "7849": "a", + "7851": "a", + "7853": "a", + "7855": "a", + "7857": "a", + "7859": "a", + "7861": "a", + "7863": "a", + "8336": "a", + "8340": "a", + "9424": "a", + "11365": "a", + "11375": "a", + "65345": "a", + "42802": "AA", + "198": "AE", + "482": "AE", + "508": "AE", + "7425": "AE", + "42804": "AO", + "42806": "AU", + "42808": "AV", + "42810": "AV", + "42812": "AY", + "9372": "(a)", + "42803": "aa", + "230": "ae", + "483": "ae", + "509": "ae", + "7426": "ae", + "42805": "ao", + "42807": "au", + "42809": "av", + "42811": "av", + "42813": "ay", + "385": "B", + "386": "B", + "579": "B", + "665": "B", + "7427": "B", + "7682": "B", + "7684": "B", + "7686": "B", + "9399": "B", + "65314": "B", + "384": "b", + "387": "b", + "595": "b", + "7532": "b", + "7552": "b", + "7683": "b", + "7685": "b", + "7687": "b", + "9425": "b", + "65346": "b", + "9373": "(b)", + "199": "C", + "262": "C", + "264": "C", + "266": "C", + "268": "C", + "391": "C", + "571": "C", + "663": "C", + "7428": "C", + "7688": "C", + "9400": "C", + "65315": "C", + "231": "c", + "263": "c", + "265": "c", + "267": "c", + "269": "c", + "392": "c", + "572": "c", + "597": "c", + "7689": "c", + "8580": "c", + "9426": "c", + "42814": "c", + "42815": "c", + "65347": "c", + "9374": "(c)", + "208": "D", + "270": "D", + "272": "D", + "393": "D", + "394": "D", + "395": "D", + "7429": "D", + "7430": "D", + "7690": "D", + "7692": "D", + "7694": "D", + "7696": "D", + "7698": "D", + "9401": "D", + "42873": "D", + "65316": "D", + "240": "d", + "271": "d", + "273": "d", + "396": "d", + "545": "d", + "598": "d", + "599": "d", + "7533": "d", + "7553": "d", + "7569": "d", + "7691": "d", + "7693": "d", + "7695": "d", + "7697": "d", + "7699": "d", + "9427": "d", + "42874": "d", + "65348": "d", + "452": "DZ", + "497": "DZ", + "453": "Dz", + "498": "Dz", + "9375": "(d)", + "568": "db", + "454": "dz", + "499": "dz", + "675": "dz", + "677": "dz", + "200": "E", + "201": "E", + "202": "E", + "203": "E", + "274": "E", + "276": "E", + "278": "E", + "280": "E", + "282": "E", + "398": "E", + "400": "E", + "516": "E", + "518": "E", + "552": "E", + "582": "E", + "7431": "E", + "7700": "E", + "7702": "E", + "7704": "E", + "7706": "E", + "7708": "E", + "7864": "E", + "7866": "E", + "7868": "E", + "7870": "E", + "7872": "E", + "7874": "E", + "7876": "E", + "7878": "E", + "9402": "E", + "11387": "E", + "65317": "E", + "232": "e", + "233": "e", + "234": "e", + "235": "e", + "275": "e", + "277": "e", + "279": "e", + "281": "e", + "283": "e", + "477": "e", + "517": "e", + "519": "e", + "553": "e", + "583": "e", + "600": "e", + "603": "e", + "604": "e", + "605": "e", + "606": "e", + "666": "e", + "7432": "e", + "7570": "e", + "7571": "e", + "7572": "e", + "7701": "e", + "7703": "e", + "7705": "e", + "7707": "e", + "7709": "e", + "7865": "e", + "7867": "e", + "7869": "e", + "7871": "e", + "7873": "e", + "7875": "e", + "7877": "e", + "7879": "e", + "8337": "e", + "9428": "e", + "11384": "e", + "65349": "e", + "9376": "(e)", + "401": "F", + "7710": "F", + "9403": "F", + "42800": "F", + "42875": "F", + "43003": "F", + "65318": "F", + "402": "f", + "7534": "f", + "7554": "f", + "7711": "f", + "7835": "f", + "9429": "f", + "42876": "f", + "65350": "f", + "9377": "(f)", + "64256": "ff", + "64259": "ffi", + "64260": "ffl", + "64257": "fi", + "64258": "fl", + "284": "G", + "286": "G", + "288": "G", + "290": "G", + "403": "G", + "484": "G", + "485": "G", + "486": "G", + "487": "G", + "500": "G", + "610": "G", + "667": "G", + "7712": "G", + "9404": "G", + "42877": "G", + "42878": "G", + "65319": "G", + "285": "g", + "287": "g", + "289": "g", + "291": "g", + "501": "g", + "608": "g", + "609": "g", + "7543": "g", + "7545": "g", + "7555": "g", + "7713": "g", + "9430": "g", + "42879": "g", + "65351": "g", + "9378": "(g)", + "292": "H", + "294": "H", + "542": "H", + "668": "H", + "7714": "H", + "7716": "H", + "7718": "H", + "7720": "H", + "7722": "H", + "9405": "H", + "11367": "H", + "11381": "H", + "65320": "H", + "293": "h", + "295": "h", + "543": "h", + "613": "h", + "614": "h", + "686": "h", + "687": "h", + "7715": "h", + "7717": "h", + "7719": "h", + "7721": "h", + "7723": "h", + "7830": "h", + "9431": "h", + "11368": "h", + "11382": "h", + "65352": "h", + "502": "HV", + "9379": "(h)", + "405": "hv", + "204": "I", + "205": "I", + "206": "I", + "207": "I", + "296": "I", + "298": "I", + "300": "I", + "302": "I", + "304": "I", + "406": "I", + "407": "I", + "463": "I", + "520": "I", + "522": "I", + "618": "I", + "7547": "I", + "7724": "I", + "7726": "I", + "7880": "I", + "7882": "I", + "9406": "I", + "43006": "I", + "65321": "I", + "236": "i", + "237": "i", + "238": "i", + "239": "i", + "297": "i", + "299": "i", + "301": "i", + "303": "i", + "305": "i", + "464": "i", + "521": "i", + "523": "i", + "616": "i", + "7433": "i", + "7522": "i", + "7548": "i", + "7574": "i", + "7725": "i", + "7727": "i", + "7881": "i", + "7883": "i", + "8305": "i", + "9432": "i", + "65353": "i", + "306": "IJ", + "9380": "(i)", + "307": "ij", + "308": "J", + "584": "J", + "7434": "J", + "9407": "J", + "65322": "J", + "309": "j", + "496": "j", + "567": "j", + "585": "j", + "607": "j", + "644": "j", + "669": "j", + "9433": "j", + "11388": "j", + "65354": "j", + "9381": "(j)", + "310": "K", + "408": "K", + "488": "K", + "7435": "K", + "7728": "K", + "7730": "K", + "7732": "K", + "9408": "K", + "11369": "K", + "42816": "K", + "42818": "K", + "42820": "K", + "65323": "K", + "311": "k", + "409": "k", + "489": "k", + "670": "k", + "7556": "k", + "7729": "k", + "7731": "k", + "7733": "k", + "9434": "k", + "11370": "k", + "42817": "k", + "42819": "k", + "42821": "k", + "65355": "k", + "9382": "(k)", + "313": "L", + "315": "L", + "317": "L", + "319": "L", + "321": "L", + "573": "L", + "671": "L", + "7436": "L", + "7734": "L", + "7736": "L", + "7738": "L", + "7740": "L", + "9409": "L", + "11360": "L", + "11362": "L", + "42822": "L", + "42824": "L", + "42880": "L", + "65324": "L", + "314": "l", + "316": "l", + "318": "l", + "320": "l", + "322": "l", + "410": "l", + "564": "l", + "619": "l", + "620": "l", + "621": "l", + "7557": "l", + "7735": "l", + "7737": "l", + "7739": "l", + "7741": "l", + "9435": "l", + "11361": "l", + "42823": "l", + "42825": "l", + "42881": "l", + "65356": "l", + "455": "LJ", + "7930": "LL", + "456": "Lj", + "9383": "(l)", + "457": "lj", + "7931": "ll", + "682": "ls", + "683": "lz", + "412": "M", + "7437": "M", + "7742": "M", + "7744": "M", + "7746": "M", + "9410": "M", + "11374": "M", + "43005": "M", + "43007": "M", + "65325": "M", + "623": "m", + "624": "m", + "625": "m", + "7535": "m", + "7558": "m", + "7743": "m", + "7745": "m", + "7747": "m", + "9436": "m", + "65357": "m", + "9384": "(m)", + "209": "N", + "323": "N", + "325": "N", + "327": "N", + "330": "N", + "413": "N", + "504": "N", + "544": "N", + "628": "N", + "7438": "N", + "7748": "N", + "7750": "N", + "7752": "N", + "7754": "N", + "9411": "N", + "65326": "N", + "241": "n", + "324": "n", + "326": "n", + "328": "n", + "329": "n", + "331": "n", + "414": "n", + "505": "n", + "565": "n", + "626": "n", + "627": "n", + "7536": "n", + "7559": "n", + "7749": "n", + "7751": "n", + "7753": "n", + "7755": "n", + "8319": "n", + "9437": "n", + "65358": "n", + "458": "NJ", + "459": "Nj", + "9385": "(n)", + "460": "nj", + "210": "O", + "211": "O", + "212": "O", + "213": "O", + "214": "O", + "216": "O", + "332": "O", + "334": "O", + "336": "O", + "390": "O", + "415": "O", + "416": "O", + "465": "O", + "490": "O", + "492": "O", + "510": "O", + "524": "O", + "526": "O", + "554": "O", + "556": "O", + "558": "O", + "560": "O", + "7439": "O", + "7440": "O", + "7756": "O", + "7758": "O", + "7760": "O", + "7762": "O", + "7884": "O", + "7886": "O", + "7888": "O", + "7890": "O", + "7892": "O", + "7894": "O", + "7896": "O", + "7898": "O", + "7900": "O", + "7902": "O", + "7904": "O", + "7906": "O", + "9412": "O", + "42826": "O", + "42828": "O", + "65327": "O", + "242": "o", + "243": "o", + "244": "o", + "245": "o", + "246": "o", + "248": "o", + "333": "o", + "335": "o", + "337": "o", + "417": "o", + "466": "o", + "491": "o", + "493": "o", + "511": "o", + "525": "o", + "527": "o", + "555": "o", + "557": "o", + "559": "o", + "561": "o", + "596": "o", + "629": "o", + "7446": "o", + "7447": "o", + "7575": "o", + "7757": "o", + "7759": "o", + "7761": "o", + "7763": "o", + "7885": "o", + "7887": "o", + "7889": "o", + "7891": "o", + "7893": "o", + "7895": "o", + "7897": "o", + "7899": "o", + "7901": "o", + "7903": "o", + "7905": "o", + "7907": "o", + "8338": "o", + "9438": "o", + "11386": "o", + "42827": "o", + "42829": "o", + "65359": "o", + "338": "OE", + "630": "OE", + "42830": "OO", + "546": "OU", + "7445": "OU", + "9386": "(o)", + "339": "oe", + "7444": "oe", + "42831": "oo", + "547": "ou", + "420": "P", + "7448": "P", + "7764": "P", + "7766": "P", + "9413": "P", + "11363": "P", + "42832": "P", + "42834": "P", + "42836": "P", + "65328": "P", + "421": "p", + "7537": "p", + "7549": "p", + "7560": "p", + "7765": "p", + "7767": "p", + "9439": "p", + "42833": "p", + "42835": "p", + "42837": "p", + "43004": "p", + "65360": "p", + "9387": "(p)", + "586": "Q", + "9414": "Q", + "42838": "Q", + "42840": "Q", + "65329": "Q", + "312": "q", + "587": "q", + "672": "q", + "9440": "q", + "42839": "q", + "42841": "q", + "65361": "q", + "9388": "(q)", + "569": "qp", + "340": "R", + "342": "R", + "344": "R", + "528": "R", + "530": "R", + "588": "R", + "640": "R", + "641": "R", + "7449": "R", + "7450": "R", + "7768": "R", + "7770": "R", + "7772": "R", + "7774": "R", + "9415": "R", + "11364": "R", + "42842": "R", + "42882": "R", + "65330": "R", + "341": "r", + "343": "r", + "345": "r", + "529": "r", + "531": "r", + "589": "r", + "636": "r", + "637": "r", + "638": "r", + "639": "r", + "7523": "r", + "7538": "r", + "7539": "r", + "7561": "r", + "7769": "r", + "7771": "r", + "7773": "r", + "7775": "r", + "9441": "r", + "42843": "r", + "42883": "r", + "65362": "r", + "9389": "(r)", + "346": "S", + "348": "S", + "350": "S", + "352": "S", + "536": "S", + "7776": "S", + "7778": "S", + "7780": "S", + "7782": "S", + "7784": "S", + "9416": "S", + "42801": "S", + "42885": "S", + "65331": "S", + "347": "s", + "349": "s", + "351": "s", + "353": "s", + "383": "s", + "537": "s", + "575": "s", + "642": "s", + "7540": "s", + "7562": "s", + "7777": "s", + "7779": "s", + "7781": "s", + "7783": "s", + "7785": "s", + "7836": "s", + "7837": "s", + "9442": "s", + "42884": "s", + "65363": "s", + "7838": "SS", + "9390": "(s)", + "223": "ss", + "64262": "st", + "354": "T", + "356": "T", + "358": "T", + "428": "T", + "430": "T", + "538": "T", + "574": "T", + "7451": "T", + "7786": "T", + "7788": "T", + "7790": "T", + "7792": "T", + "9417": "T", + "42886": "T", + "65332": "T", + "355": "t", + "357": "t", + "359": "t", + "427": "t", + "429": "t", + "539": "t", + "566": "t", + "647": "t", + "648": "t", + "7541": "t", + "7787": "t", + "7789": "t", + "7791": "t", + "7793": "t", + "7831": "t", + "9443": "t", + "11366": "t", + "65364": "t", + "222": "TH", + "42854": "TH", + "42792": "TZ", + "9391": "(t)", + "680": "tc", + "254": "th", + "7546": "th", + "42855": "th", + "678": "ts", + "42793": "tz", + "217": "U", + "218": "U", + "219": "U", + "220": "U", + "360": "U", + "362": "U", + "364": "U", + "366": "U", + "368": "U", + "370": "U", + "431": "U", + "467": "U", + "469": "U", + "471": "U", + "473": "U", + "475": "U", + "532": "U", + "534": "U", + "580": "U", + "7452": "U", + "7550": "U", + "7794": "U", + "7796": "U", + "7798": "U", + "7800": "U", + "7802": "U", + "7908": "U", + "7910": "U", + "7912": "U", + "7914": "U", + "7916": "U", + "7918": "U", + "7920": "U", + "9418": "U", + "65333": "U", + "249": "u", + "250": "u", + "251": "u", + "252": "u", + "361": "u", + "363": "u", + "365": "u", + "367": "u", + "369": "u", + "371": "u", + "432": "u", + "468": "u", + "470": "u", + "472": "u", + "474": "u", + "476": "u", + "533": "u", + "535": "u", + "649": "u", + "7524": "u", + "7577": "u", + "7795": "u", + "7797": "u", + "7799": "u", + "7801": "u", + "7803": "u", + "7909": "u", + "7911": "u", + "7913": "u", + "7915": "u", + "7917": "u", + "7919": "u", + "7921": "u", + "9444": "u", + "65365": "u", + "9392": "(u)", + "7531": "ue", + "434": "V", + "581": "V", + "7456": "V", + "7804": "V", + "7806": "V", + "7932": "V", + "9419": "V", + "42846": "V", + "42856": "V", + "65334": "V", + "651": "v", + "652": "v", + "7525": "v", + "7564": "v", + "7805": "v", + "7807": "v", + "9445": "v", + "11377": "v", + "11380": "v", + "42847": "v", + "65366": "v", + "42848": "VY", + "9393": "(v)", + "42849": "vy", + "372": "W", + "503": "W", + "7457": "W", + "7808": "W", + "7810": "W", + "7812": "W", + "7814": "W", + "7816": "W", + "9420": "W", + "11378": "W", + "65335": "W", + "373": "w", + "447": "w", + "653": "w", + "7809": "w", + "7811": "w", + "7813": "w", + "7815": "w", + "7817": "w", + "7832": "w", + "9446": "w", + "11379": "w", + "65367": "w", + "9394": "(w)", + "7818": "X", + "7820": "X", + "9421": "X", + "65336": "X", + "7565": "x", + "7819": "x", + "7821": "x", + "8339": "x", + "9447": "x", + "65368": "x", + "9395": "(x)", + "221": "Y", + "374": "Y", + "376": "Y", + "435": "Y", + "562": "Y", + "590": "Y", + "655": "Y", + "7822": "Y", + "7922": "Y", + "7924": "Y", + "7926": "Y", + "7928": "Y", + "7934": "Y", + "9422": "Y", + "65337": "Y", + "253": "y", + "255": "y", + "375": "y", + "436": "y", + "563": "y", + "591": "y", + "654": "y", + "7823": "y", + "7833": "y", + "7923": "y", + "7925": "y", + "7927": "y", + "7929": "y", + "7935": "y", + "9448": "y", + "65369": "y", + "9396": "(y)", + "377": "Z", + "379": "Z", + "381": "Z", + "437": "Z", + "540": "Z", + "548": "Z", + "7458": "Z", + "7824": "Z", + "7826": "Z", + "7828": "Z", + "9423": "Z", + "11371": "Z", + "42850": "Z", + "65338": "Z", + "378": "z", + "380": "z", + "382": "z", + "438": "z", + "541": "z", + "549": "z", + "576": "z", + "656": "z", + "657": "z", + "7542": "z", + "7566": "z", + "7825": "z", + "7827": "z", + "7829": "z", + "9449": "z", + "11372": "z", + "42851": "z", + "65370": "z", + "9397": "(z)", + "8304": "0", + "8320": "0", + "9450": "0", + "9471": "0", + "65296": "0", + "185": "1", + "8321": "1", + "9312": "1", + "9461": "1", + "10102": "1", + "10112": "1", + "10122": "1", + "65297": "1", + "9352": "1.", + "9332": "(1)", + "178": "2", + "8322": "2", + "9313": "2", + "9462": "2", + "10103": "2", + "10113": "2", + "10123": "2", + "65298": "2", + "9353": "2.", + "9333": "(2)", + "179": "3", + "8323": "3", + "9314": "3", + "9463": "3", + "10104": "3", + "10114": "3", + "10124": "3", + "65299": "3", + "9354": "3.", + "9334": "(3)", + "8308": "4", + "8324": "4", + "9315": "4", + "9464": "4", + "10105": "4", + "10115": "4", + "10125": "4", + "65300": "4", + "9355": "4.", + "9335": "(4)", + "8309": "5", + "8325": "5", + "9316": "5", + "9465": "5", + "10106": "5", + "10116": "5", + "10126": "5", + "65301": "5", + "9356": "5.", + "9336": "(5)", + "8310": "6", + "8326": "6", + "9317": "6", + "9466": "6", + "10107": "6", + "10117": "6", + "10127": "6", + "65302": "6", + "9357": "6.", + "9337": "(6)", + "8311": "7", + "8327": "7", + "9318": "7", + "9467": "7", + "10108": "7", + "10118": "7", + "10128": "7", + "65303": "7", + "9358": "7.", + "9338": "(7)", + "8312": "8", + "8328": "8", + "9319": "8", + "9468": "8", + "10109": "8", + "10119": "8", + "10129": "8", + "65304": "8", + "9359": "8.", + "9339": "(8)", + "8313": "9", + "8329": "9", + "9320": "9", + "9469": "9", + "10110": "9", + "10120": "9", + "10130": "9", + "65305": "9", + "9360": "9.", + "9340": "(9)", + "9321": "10", + "9470": "10", + "10111": "10", + "10121": "10", + "10131": "10", + "9361": "10.", + "9341": "(10)", + "9322": "11", + "9451": "11", + "9362": "11.", + "9342": "(11)", + "9323": "12", + "9452": "12", + "9363": "12.", + "9343": "(12)", + "9324": "13", + "9453": "13", + "9364": "13.", + "9344": "(13)", + "9325": "14", + "9454": "14", + "9365": "14.", + "9345": "(14)", + "9326": "15", + "9455": "15", + "9366": "15.", + "9346": "(15)", + "9327": "16", + "9456": "16", + "9367": "16.", + "9347": "(16)", + "9328": "17", + "9457": "17", + "9368": "17.", + "9348": "(17)", + "9329": "18", + "9458": "18", + "9369": "18.", + "9349": "(18)", + "9330": "19", + "9459": "19", + "9370": "19.", + "9350": "(19)", + "9331": "20", + "9460": "20", + "9371": "20.", + "9351": "(20)", + "171": "\"", + "187": "\"", + "8220": "\"", + "8221": "\"", + "8222": "\"", + "8243": "\"", + "8246": "\"", + "10077": "\"", + "10078": "\"", + "10094": "\"", + "10095": "\"", + "65282": "\"", + "8216": "'", + "8217": "'", + "8218": "'", + "8219": "'", + "8242": "'", + "8245": "'", + "8249": "'", + "8250": "'", + "10075": "'", + "10076": "'", + "65287": "'", + "8208": "-", + "8209": "-", + "8210": "-", + "8211": "-", + "8212": "-", + "8315": "-", + "8331": "-", + "65293": "-", + "8261": "[", + "10098": "[", + "65339": "[", + "8262": "]", + "10099": "]", + "65341": "]", + "8317": "(", + "8333": "(", + "10088": "(", + "10090": "(", + "65288": "(", + "11816": "((", + "8318": ")", + "8334": ")", + "10089": ")", + "10091": ")", + "65289": ")", + "11817": "))", + "10092": "<", + "10096": "<", + "65308": "<", + "10093": ">", + "10097": ">", + "65310": ">", + "10100": "{", + "65371": "{", + "10101": "}", + "65373": "}", + "8314": "+", + "8330": "+", + "65291": "+", + "8316": "=", + "8332": "=", + "65309": "=", + "65281": "!", + "8252": "!!", + "8265": "!?", + "65283": "#", + "65284": "$", + "8274": "%", + "65285": "%", + "65286": "&", + "8270": "*", + "65290": "*", + "65292": ",", + "65294": ".", + "8260": "/", + "65295": "/", + "65306": ":", + "8271": ";", + "65307": ";", + "65311": "?", + "8263": "??", + "8264": "?!", + "65312": "@", + "65340": "\\", + "8248": "^", + "65342": "^", + "65343": "_", + "8275": "~", + "65374": "~" +} \ No newline at end of file diff --git a/utils/ascii_reduce/reduce.go b/utils/ascii_reduce/reduce.go new file mode 100644 index 0000000..12f52d1 --- /dev/null +++ b/utils/ascii_reduce/reduce.go @@ -0,0 +1,40 @@ +package ascii_reduce + +func getMapping(r rune) []rune { + if replacement, ok := Mappings[r]; ok { + return []rune(replacement) + } else { + return []rune("") + } +} + +func Reduce(s string, replace_non_ascii_char string) string { + runes := []rune(s) + + isAllAscii := true + for _, r := range runes { + if r > 0x7e { + isAllAscii = false + break + } + } + + if isAllAscii { + return s + } + + newRunes := make([]rune, 0) + + for _, sourceRune := range runes { + mapped := getMapping(sourceRune) + for _, replacedRune := range mapped { + if replacedRune > 0x7e { + replacedRune = []rune(replace_non_ascii_char)[0] + } + + newRunes = append(newRunes, replacedRune) + } + } + + return string(newRunes) +} diff --git a/utils/extract_tags.go b/utils/extract_tags.go new file mode 100644 index 0000000..2ffd7fb --- /dev/null +++ b/utils/extract_tags.go @@ -0,0 +1,10 @@ +package utils + +import ( + "gitlab.com/ChaotiCryptidz/musicutil/types" + "gitlab.com/ChaotiCryptidz/musicutil/utils/tag_extractor" +) + +func ExtractTags(file *types.File) (*types.Tags, error) { + return tag_extractor.ExtractTags(file.JoinPathTo()) +} diff --git a/utils/scan_for_music.go b/utils/scan_for_music.go new file mode 100644 index 0000000..182db1c --- /dev/null +++ b/utils/scan_for_music.go @@ -0,0 +1,53 @@ +package utils + +import ( + "os" + "path/filepath" + "strings" + + "gitlab.com/ChaotiCryptidz/musicutil/types" +) + +/* +def scan_for_music(src: str) -> list[File]: + files: list[File] = [] + for format in supported_formats: + for path in Path(src).rglob("*." + format): + files.append(file_from_path(path, src)) + return files +*/ + +func isValidFileExtension(filePath string) bool { + ext := strings.TrimPrefix(filepath.Ext(filePath), ".") + if ext == "mp3" { + return true + } else if ext == "flac" { + return true + } + return false +} + +func ScanForMusic(srcDir string) ([]*types.File, error) { + files := make([]*types.File, 0) + + err := filepath.Walk(srcDir, + func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if !info.IsDir() && isValidFileExtension(path) { + files = append(files, types.FileFromPath( + srcDir, + path, + )) + } + return nil + }) + + if err != nil { + return nil, err + } + + return files, nil +} diff --git a/utils/tag_cache/tag_cache.go b/utils/tag_cache/tag_cache.go new file mode 100644 index 0000000..d3e7304 --- /dev/null +++ b/utils/tag_cache/tag_cache.go @@ -0,0 +1,164 @@ +package tag_cache + +import ( + "crypto/sha1" + "encoding/hex" + "encoding/json" + "io" + "io/ioutil" + "os" + + "github.com/cespare/xxhash" + "github.com/rs/zerolog/log" + "gitlab.com/ChaotiCryptidz/musicutil/types" +) + +type CacheData struct { + FileHash uint64 `json:"file_hash"` + FileTags *types.Tags `json:"file_tags"` +} + +type TagCache struct { + // map of filename hash to file tags & file hash + cache map[string]CacheData + source_dir string + settings *tagCacheSettings +} + +func get_cache_filename(source_dir string, settings *tagCacheSettings) string { + if settings.CacheDir == "source_dir" { + return source_dir + "/.tag_cache.json" + } else { + return settings.CacheDir + "/.tag_cache.json" + } +} + +func NewTagCache(source_dir string) *TagCache { + cache := &TagCache{} + settings := getTagCacheSettings() + cache.settings = settings + cache.source_dir = source_dir + cache.cache = make(map[string]CacheData) + + if !settings.CacheEnabled { + return cache + } + + cache_filename := get_cache_filename(source_dir, settings) + + if _, err := os.Stat(cache_filename); err != nil { + file, err := os.Create(cache_filename) + if err != nil { + log.Error().Err(err).Msg("Could not create cache file") + return cache + } + file.Write([]byte("{}")) + file.Close() + return cache + } else { + file, err := os.Open(cache_filename) + if err != nil { + log.Error().Err(err).Msg("Could not open cache file") + } + + file_data, err := ioutil.ReadAll(file) + if err != nil { + panic(err) + } + var cache_data map[string]CacheData + + err = json.Unmarshal(file_data, &cache_data) + if err != nil { + panic(err) + } + cache.cache = cache_data + return cache + } +} + +func getFileNameHash(filename string) string { + filename_hash_bytes := sha1.Sum([]byte(filename)) + filename_hash := hex.EncodeToString(filename_hash_bytes[:]) + return filename_hash +} + +func getFileContentsHash(filename string) (uint64, error) { + file_handle, err := os.Open(filename) + if err != nil { + return 0, err + } + + contents_hasher := xxhash.New() + if _, err := io.Copy(contents_hasher, file_handle); err != nil { + return 0, err + } + hash := contents_hasher.Sum64() + return hash, nil +} + +func (tc *TagCache) IsCached(file *types.File) (*types.Tags, bool) { + if !tc.settings.CacheEnabled { + return nil, false + } + + filename_hash := getFileNameHash(file.Filename) + + // is not in cache at all + if _, ok := tc.cache[filename_hash]; !ok { + return nil, false + } + + hash, err := getFileContentsHash(file.JoinPathTo()) + if err != nil { + log.Error().Err(err).Msg("Could not hash file, skipping") + return nil, false + } + + // is in cache + if tc.cache[filename_hash].FileHash != hash { + return nil, false + } else { + return tc.cache[filename_hash].FileTags, true + } +} + +func (tc *TagCache) AddToCache(file *types.File, tags *types.Tags) { + if !tc.settings.CacheEnabled { + return + } + + filename_hash := getFileNameHash(file.Filename) + hash, err := getFileContentsHash(file.JoinPathTo()) + if err != nil { + panic(err) + } + + tc.cache[filename_hash] = CacheData{ + FileHash: hash, + FileTags: tags.DeepCopy(), + } +} + +func (tc *TagCache) Close() { + if !tc.settings.CacheEnabled { + return + } + + cache_filename := get_cache_filename(tc.source_dir, tc.settings) + + file, err := os.OpenFile(cache_filename, os.O_RDWR|os.O_CREATE, 0755) + if err != nil { + panic(err) + } + defer file.Close() + + data, err := json.Marshal(tc.cache) + if err != nil { + panic(err) + } + + _, err = file.Write(data) + if err != nil { + panic(err) + } +} diff --git a/utils/tag_cache/tag_cache_settings.go b/utils/tag_cache/tag_cache_settings.go new file mode 100644 index 0000000..77e697f --- /dev/null +++ b/utils/tag_cache/tag_cache_settings.go @@ -0,0 +1,17 @@ +package tag_cache + +import ( + "github.com/spf13/viper" +) + +type tagCacheSettings struct { + CacheEnabled bool + CacheDir string +} + +func getTagCacheSettings() *tagCacheSettings { + return &tagCacheSettings{ + CacheEnabled: viper.GetBool("cache"), + CacheDir: viper.GetString("cache_dir"), + } +} diff --git a/utils/tag_extractor/extractor.go b/utils/tag_extractor/extractor.go new file mode 100644 index 0000000..7d921a2 --- /dev/null +++ b/utils/tag_extractor/extractor.go @@ -0,0 +1,45 @@ +package tag_extractor + +import ( + "errors" + "path/filepath" + "strings" + + "gitlab.com/ChaotiCryptidz/musicutil/types" +) + +func tagsOrErr(tags *types.Tags) (*types.Tags, error) { + if len(tags.Title) == 0 { + return nil, errors.New("Could not extract title") + } + + if len(tags.Artist) == 0 { + return nil, errors.New("Could not extract artist") + } + + return tags, nil +} + +func ExtractTags(path string) (*types.Tags, error) { + // Do this in order of speed and preference + // Maybe add some better logic in here at some point + + file_extension := strings.TrimSuffix(filepath.Ext(path), ".") + + var tags *types.Tags + var err error = nil + + if file_extension == "mp3" || file_extension == "flac" { + tags, err = TaglibExtractor(path) + if err == nil { + return tagsOrErr(tags) + } + } + + tags, err = FFProbeTagExtractor(path) + if err == nil { + return tagsOrErr(tags) + } + + return nil, errors.New("Could not extract tags") +} diff --git a/utils/tag_extractor/ffprobe_extractor.go b/utils/tag_extractor/ffprobe_extractor.go new file mode 100644 index 0000000..be34b38 --- /dev/null +++ b/utils/tag_extractor/ffprobe_extractor.go @@ -0,0 +1,108 @@ +package tag_extractor + +import ( + "encoding/json" + "os/exec" + + "gitlab.com/ChaotiCryptidz/musicutil/types" +) + +/* +def load_tag_information_ffprobe(file: File) -> Tags: + path = file.join_path_to() + tags = Tags() + + command_args = [ + ffprobe_path, + "-v", "quiet", + "-print_format", "json", + "-show_format", + path + ] + + ffprobe_output = run_command( + command_args, capture_output=True).stdout + + data = load_json_string(ffprobe_output) + file_tags = data["format"]["tags"] + + title = None + artist = None + + try: + print(file_tags.keys()) + if "title" in file_tags.keys(): + title = file_tags["title"] + elif "TITLE" in file_tags.keys(): + title = file_tags["TITLE"] + + if "artist" in file_tags.keys(): + artist = file_tags["artist"] + elif "ARTIST" in file_tags.keys(): + artist = file_tags["ARTIST"] + except Exception as e: + print(data["format"]["tags"], file, e) + exit() + + if title is None or artist is None: + raise Exception("Could Not Load Tags Using FFprobe") + + tags.title = title + tags.artist = artist + return tags +*/ + +type FFProbeTags map[string]string + +type FFProbeFormat struct { + Tags FFProbeTags `json:"tags"` +} + +type FFProbeOutput struct { + Format FFProbeFormat `json:"format"` +} + +func FFProbeTagExtractor(path string) (*types.Tags, error) { + ffprobe_output_str, err := exec.Command( + "ffprobe", + "-v", "quiet", + "-print_format", "json", + "-show_format", + path, + ).Output() + + if err != nil { + return nil, err + } + + var ffprobe_output FFProbeOutput + err = json.Unmarshal([]byte(ffprobe_output_str), &ffprobe_output) + if err != nil { + return nil, err + } + + file_tags := ffprobe_output.Format.Tags + + tags := &types.Tags{ + Title: "", + Artist: "", + } + + if tag, ok := file_tags["title"]; ok { + tags.Title = tag + } + + if tag, ok := file_tags["TITLE"]; ok { + tags.Title = tag + } + + if tag, ok := file_tags["artist"]; ok { + tags.Artist = tag + } + + if tag, ok := file_tags["ARTIST"]; ok { + tags.Artist = tag + } + + return tags, nil +} diff --git a/utils/tag_extractor/taglib_extractor.go b/utils/tag_extractor/taglib_extractor.go new file mode 100644 index 0000000..9485048 --- /dev/null +++ b/utils/tag_extractor/taglib_extractor.go @@ -0,0 +1,25 @@ +package tag_extractor + +import ( + "os" + + "github.com/dhowden/tag" + "gitlab.com/ChaotiCryptidz/musicutil/types" +) + +func TaglibExtractor(path string) (*types.Tags, error) { + file, err := os.Open(path) + if err != nil { + return nil, err + } + + meta, err := tag.ReadFrom(file) + if err != nil { + return nil, err + } + + return &types.Tags{ + Title: meta.Title(), + Artist: meta.Artist(), + }, nil +} diff --git a/utils/transcoder/transcode_presets.go b/utils/transcoder/transcode_presets.go new file mode 100644 index 0000000..8dcd87c --- /dev/null +++ b/utils/transcoder/transcode_presets.go @@ -0,0 +1,198 @@ +package transcoder + +import ( + "fmt" +) + +type Preset struct { + Name string `json:"name"` + Config *TranscodeConfig `json:"config"` +} + +var TranscodePresets map[string][]*Preset + +func add_to_presets(category string, items []*Preset) { + if _, ok := TranscodePresets[category]; !ok { + TranscodePresets[category] = make([]*Preset, 0) + } + for _, preset := range items { + TranscodePresets[category] = append(TranscodePresets[category], preset) + } +} + +func add_mp3_presets() { + mp3_presets := make([]*Preset, 0) + + mp3_bitrates := []int{8, 16, 24, 32, 40, 48, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320} + + for _, bitrate := range mp3_bitrates { + mp3_presets = append(mp3_presets, &Preset{ + Name: fmt.Sprintf("mp3-%dk", bitrate), + Config: &TranscodeConfig{ + Encoder: "libmp3lame", + Container: "mp3", + FileExtension: "mp3", + UseBitrate: true, + Bitrate: fmt.Sprintf("%dk", bitrate), + }, + }) + } + + mp3_qualities := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} + for _, quality := range mp3_qualities { + mp3_presets = append(mp3_presets, &Preset{ + Name: fmt.Sprintf("mp3-v%d", quality), + Config: &TranscodeConfig{ + Encoder: "libmp3lame", + Container: "mp3", + FileExtension: "mp3", + UseQuality: true, + Quality: fmt.Sprintf("%d", quality), + }, + }) + } + + add_to_presets("mp3", mp3_presets) +} + +func add_opus_presets() { + opus_presets := make([]*Preset, 0) + + opus_bitrates := []int{16, 24, 32, 64, 96, 128, 256} + + for _, bitrate := range opus_bitrates { + opus_presets = append(opus_presets, &Preset{ + Name: fmt.Sprintf("opus-%dk", bitrate), + Config: &TranscodeConfig{ + Encoder: "libopus", + Container: "ogg", + FileExtension: "opus", + UseBitrate: true, + Bitrate: fmt.Sprintf("%dk", bitrate), + }, + }) + } + + add_to_presets("opus", opus_presets) +} + +func add_vorbis_presets() { + vorbis_presets := make([]*Preset, 0) + + vorbis_qualities := []int{-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + + for _, quality := range vorbis_qualities { + vorbis_presets = append(vorbis_presets, &Preset{ + Name: fmt.Sprintf("vorbis-v%d", quality), + Config: &TranscodeConfig{ + Encoder: "libvorbis", + Container: "ogg", + FileExtension: "ogg", + UseQuality: true, + Quality: fmt.Sprintf("%d", quality), + }, + }) + } + + add_to_presets("vorbis", vorbis_presets) +} + +func add_g726_presets() { + g726_presets := make([]*Preset, 0) + + g726_bitrates := []int{16, 24, 32, 40} + + for _, bitrate := range g726_bitrates { + g726_presets = append(g726_presets, &Preset{ + Name: fmt.Sprintf("g726-%dk", bitrate), + Config: &TranscodeConfig{ + Encoder: "g726", + Container: "matroska", + FileExtension: "mka", + SampleRate: "8000", + Channels: "1", + UseBitrate: true, + Bitrate: fmt.Sprintf("%dk", bitrate), + }, + }) + } + add_to_presets("g726", g726_presets) +} + +func add_speex_presets() { + speex_presets := make([]*Preset, 0) + + speex_qualities := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + + for _, quality := range speex_qualities { + speex_presets = append(speex_presets, &Preset{ + Name: fmt.Sprintf("speex-q%d", quality), + Config: &TranscodeConfig{ + Encoder: "libspeex", + Container: "ogg", + FileExtension: "ogg", + UseQuality: true, + Quality: fmt.Sprintf("%d", quality), + }, + }) + } + + add_to_presets("speex", speex_presets) +} + +func add_flac_preset() { + add_to_presets("flac", []*Preset{ + { + Name: "flac", + Config: &TranscodeConfig{ + Encoder: "flac", + Container: "flac", + FileExtension: "flac", + }, + }, + }) +} + +func add_wav_preset() { + add_to_presets("wav", []*Preset{ + { + Name: "wav", + Config: &TranscodeConfig{ + Container: "wav", + FileExtension: "wav", + }, + }, + }) +} + +func GetPresetByName(name string) (*Preset, error) { + for _, category := range TranscodePresets { + for _, preset := range category { + if preset.Name == name { + return preset, nil + } + } + } + return nil, fmt.Errorf("No preset with name %s", name) +} + +func PrintTranscodePresets() { + for cat_name, cat := range TranscodePresets { + fmt.Printf("Category %s:\n", cat_name) + for _, preset := range cat { + fmt.Printf("- %s\n", preset.Name) + } + } +} + +func init() { + TranscodePresets = make(map[string][]*Preset, 0) + + add_mp3_presets() + add_opus_presets() + add_vorbis_presets() + add_g726_presets() + add_speex_presets() + add_flac_preset() + add_wav_preset() +} diff --git a/utils/transcoder/transcoder.go b/utils/transcoder/transcoder.go new file mode 100644 index 0000000..4834fc3 --- /dev/null +++ b/utils/transcoder/transcoder.go @@ -0,0 +1,79 @@ +package transcoder + +import ( + "errors" + "fmt" + "os/exec" + "strings" + + "gitlab.com/ChaotiCryptidz/musicutil/types" +) + +type TranscodeConfig struct { + UseQuality bool `json:"use_quality"` + UseBitrate bool `json:"use_bitrate"` + Encoder string `json:"encoder"` + FileExtension string `json:"file_extension"` + Container string `json:"container"` + Bitrate string `json:"bitrate"` + Quality string `json:"quality"` + SampleRate string `json:"sample_rate"` + Channels string `json:"channels"` +} + +func isNotEmptyString(x string) bool { + if len(strings.TrimSpace(x)) == 0 { + return false + } + + return true +} + +func Transcode(file *types.File, config *TranscodeConfig, dest string) (string, error) { + command_args := make([]string, 0) + command_args = append(command_args, "-y") + command_args = append(command_args, "-hide_banner") + + command_args = append(command_args, "-i") + command_args = append(command_args, file.JoinPathTo()) + + if isNotEmptyString(config.Encoder) { + command_args = append(command_args, "-c:a") + command_args = append(command_args, config.Encoder) + } + + if isNotEmptyString(config.Container) { + command_args = append(command_args, "-f") + command_args = append(command_args, config.Container) + } + + if isNotEmptyString(config.SampleRate) { + command_args = append(command_args, "-ar") + command_args = append(command_args, config.SampleRate) + } + + if isNotEmptyString(config.Channels) { + command_args = append(command_args, "-ac") + command_args = append(command_args, config.Channels) + } + + if config.UseQuality { + command_args = append(command_args, "-q:a") + command_args = append(command_args, config.Quality) + } + + if config.UseBitrate { + command_args = append(command_args, "-b:a") + command_args = append(command_args, config.Bitrate) + } + + command_args = append(command_args, dest) + + cmd := exec.Command("ffmpeg", command_args...) + output, err := cmd.CombinedOutput() + if err != nil { + fmt.Println(output) + return string(output), errors.New("ffmpeg error") + } + return string(output), nil +}