progress
This commit is contained in:
parent
b9bebe7dd3
commit
059b8ff976
4 changed files with 234 additions and 51 deletions
|
@ -14,6 +14,12 @@
|
||||||
"CSVFile": "gleise_zahlen.csv",
|
"CSVFile": "gleise_zahlen.csv",
|
||||||
"Default_Variants": ["hoch"],
|
"Default_Variants": ["hoch"],
|
||||||
"Default_Language": "dt"
|
"Default_Language": "dt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Module",
|
||||||
|
"Path": "module",
|
||||||
|
"CSVFile": "module_dt.csv",
|
||||||
|
"Default_Language": "dt"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
9
go.mod
9
go.mod
|
@ -2,9 +2,12 @@ module main
|
||||||
|
|
||||||
go 1.24.1
|
go 1.24.1
|
||||||
|
|
||||||
require github.com/posener/complete v1.2.3
|
require (
|
||||||
|
github.com/peterh/liner v1.2.2
|
||||||
|
golang.org/x/text v0.23.0
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
github.com/mattn/go-runewidth v0.0.3 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.0.0 // indirect
|
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1 // indirect
|
||||||
)
|
)
|
||||||
|
|
24
go.sum
24
go.sum
|
@ -1,16 +1,8 @@
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
github.com/peterh/liner v1.2.2 h1:aJ4AOodmL+JxOZZEL2u9iJf8omNRpqHc/EbrK+3mAXw=
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/peterh/liner v1.2.2/go.mod h1:xFwJyiKIXJZUKItq5dGHZSTBRAuG/CpeNpWLyiNRNwI=
|
||||||
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1 h1:kwrAHlwJ0DUBZwQ238v+Uod/3eZ8B2K5rYsUHBQvzmI=
|
||||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||||
github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
|
|
||||||
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
|
|
246
main.go
246
main.go
|
@ -7,22 +7,26 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/peterh/liner"
|
||||||
|
"golang.org/x/text/unicode/norm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config represents the structure of the config.json file.
|
// Config represents the structure of the config.json file.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
BaseDir string `json:"Base_Dir"`
|
BaseDir string `json:"Base_Dir"`
|
||||||
Modules []Module `json:"Modules"`
|
Modules []Module `json:"Modules"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Module represents a module entry in the config.json file.
|
// Module represents a module entry in the config.json file.
|
||||||
type Module struct {
|
type Module struct {
|
||||||
Name string `json:"Name"`
|
Name string `json:"Name"`
|
||||||
Path string `json:"Path"`
|
Path string `json:"Path"`
|
||||||
CSVFile string `json:"CSVFile"`
|
CSVFile string `json:"CSVFile"`
|
||||||
DefaultLanguage string `json:"Default_Language"`
|
DefaultLanguage string `json:"Default_Language"`
|
||||||
DefaultVariants []string `json:"Default_Variants"`
|
DefaultVariants []string `json:"Default_Variants"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -40,39 +44,217 @@ func main() {
|
||||||
log.Fatalf("Error parsing JSON: %v", err)
|
log.Fatalf("Error parsing JSON: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print the display texts of all modules as a comma-separated list
|
fmt.Println("Blechelse - waiting for your orders")
|
||||||
fmt.Println("Display Texts of Modules:")
|
interactiveMode(config)
|
||||||
for _, module := range config.Modules {
|
}
|
||||||
displayTexts := []string{}
|
|
||||||
csvFilePath := config.BaseDir + "/" + module.CSVFile
|
|
||||||
|
|
||||||
// Open and read the CSV file
|
// Interactive mode with tab autocomplete and playing audio
|
||||||
csvFile, err := os.Open(csvFilePath)
|
func interactiveMode(config Config) {
|
||||||
|
line := liner.NewLiner()
|
||||||
|
defer line.Close()
|
||||||
|
|
||||||
|
line.SetCtrlCAborts(true)
|
||||||
|
displayTexts := collectAllDisplayTexts(config)
|
||||||
|
|
||||||
|
// Setting up the autocomplete feature
|
||||||
|
line.SetCompleter(func(line string) (c []string) {
|
||||||
|
// Split the input line into parts based on commas
|
||||||
|
words := strings.Split(line, ",")
|
||||||
|
lastWord := words[len(words)-1]
|
||||||
|
|
||||||
|
// Autocomplete only the last word
|
||||||
|
for _, text := range displayTexts {
|
||||||
|
if strings.HasPrefix(strings.ToLower(text), strings.ToLower(lastWord)) {
|
||||||
|
c = append(c, text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
})
|
||||||
|
|
||||||
|
var currentInput []string // Store entered words
|
||||||
|
for {
|
||||||
|
// Print the current input prompt above the line
|
||||||
|
printPrompt(currentInput)
|
||||||
|
|
||||||
|
// Read user input
|
||||||
|
inputText, err := line.Prompt("Enter text (or type 'exit' to quit, 'play' to trigger playback): ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error opening CSV file %s: %v", csvFilePath, err)
|
fmt.Println("Error reading input:", err)
|
||||||
}
|
continue
|
||||||
defer csvFile.Close()
|
|
||||||
|
|
||||||
reader := csv.NewReader(csvFile)
|
|
||||||
records, err := reader.ReadAll()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error reading CSV file %s: %v", csvFilePath, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore the first line
|
inputText = strings.TrimSpace(inputText)
|
||||||
if len(records) > 1 {
|
|
||||||
records = records[1:]
|
// Exit condition
|
||||||
|
if inputText == "exit" {
|
||||||
|
fmt.Println("Goodbye!")
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, record := range records {
|
// If "play" is typed, trigger audio playback
|
||||||
displayTexts = append(displayTexts, record[1]) // Assuming the display text is in the second column (index 1)
|
if inputText == "play" {
|
||||||
|
if len(currentInput) > 0 {
|
||||||
|
// Concatenate the current input for display and play each part individually
|
||||||
|
concatenatedPrompt := strings.Join(currentInput, " ")
|
||||||
|
fmt.Printf("Playing: %s\n", concatenatedPrompt)
|
||||||
|
// Play each part individually in the order they were added
|
||||||
|
autocompleteAndPlay(config, currentInput)
|
||||||
|
currentInput = nil // Clear the input after playing
|
||||||
|
} else {
|
||||||
|
fmt.Println("No input to play.")
|
||||||
|
}
|
||||||
|
} else if len(inputText) > 0 {
|
||||||
|
// If it's not "play", it's a part of the input, so add to the current input
|
||||||
|
currentInput = append(currentInput, inputText)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("- Module Name: %s (%s)\n", module.Name, commaSeparated(displayTexts))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to join a slice of strings with commas
|
// Print the current accumulated prompt above the input line
|
||||||
func commaSeparated(slice []string) string {
|
func printPrompt(currentInput []string) {
|
||||||
return fmt.Sprintf("[%s]", strings.Join(slice, ", "))
|
fmt.Println("Current prompt:")
|
||||||
|
for i, word := range currentInput {
|
||||||
|
fmt.Printf("%d. %s\n", i+1, word)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect all display texts for autocomplete
|
||||||
|
func collectAllDisplayTexts(config Config) []string {
|
||||||
|
var displayTexts []string
|
||||||
|
for _, module := range config.Modules {
|
||||||
|
// Unpack both return values (displayTexts and fileNames)
|
||||||
|
moduleDisplayTexts, _ := readDisplayTextsAndFileNames(config.BaseDir + "/" + module.CSVFile)
|
||||||
|
displayTexts = append(displayTexts, moduleDisplayTexts...)
|
||||||
|
}
|
||||||
|
return displayTexts
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to collect all display texts and play matching files
|
||||||
|
func autocompleteAndPlay(config Config, inputText []string) {
|
||||||
|
var matchingFiles []string // Store matching files in the order of inputText
|
||||||
|
var moduleNames []string // Store corresponding module names for each matched file
|
||||||
|
|
||||||
|
// Normalize each input word (e.g., convert umlauts to standard form)
|
||||||
|
for _, part := range inputText {
|
||||||
|
normalizedInputText := norm.NFC.String(part)
|
||||||
|
|
||||||
|
// Collect matching filenames from the second column (display text) and first column (filenames)
|
||||||
|
for _, module := range config.Modules {
|
||||||
|
displayTexts, fileNames := readDisplayTextsAndFileNames(config.BaseDir + "/" + module.CSVFile)
|
||||||
|
for i, text := range displayTexts {
|
||||||
|
// Normalize display text before comparing and remove any trailing spaces
|
||||||
|
normalizedText := norm.NFC.String(strings.TrimSpace(text))
|
||||||
|
|
||||||
|
// Explicitly compare both normalized strings (case-sensitive)
|
||||||
|
if normalizedText == normalizedInputText {
|
||||||
|
matchingFiles = append(matchingFiles, fileNames[i])
|
||||||
|
moduleNames = append(moduleNames, module.Name) // Track the module that matched
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no matching filenames found, prompt again
|
||||||
|
if len(matchingFiles) == 0 {
|
||||||
|
fmt.Println("No matching display text found.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugging: print matched files and their corresponding modules
|
||||||
|
fmt.Println("Matched files:")
|
||||||
|
for i, fileName := range matchingFiles {
|
||||||
|
fmt.Printf(" - File: %s, Module: %s\n", fileName, moduleNames[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play matching audio files in the exact order they were entered
|
||||||
|
for i, fileName := range matchingFiles {
|
||||||
|
// Get the module name associated with this matched file
|
||||||
|
moduleName := moduleNames[i]
|
||||||
|
|
||||||
|
// Construct the correct audio path using the module information
|
||||||
|
audioPath := constructAudioPath(config, fileName, moduleName)
|
||||||
|
|
||||||
|
// Debugging: print the constructed audio path
|
||||||
|
fmt.Printf("Attempting to play audio from path: %s\n", audioPath)
|
||||||
|
|
||||||
|
// Check if the file exists before trying to play it
|
||||||
|
if _, err := os.Stat(audioPath); os.IsNotExist(err) {
|
||||||
|
fmt.Printf("File does not exist: %s\n", audioPath)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play the audio file
|
||||||
|
err := playAudioFiles(audioPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error playing audio: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to construct the audio file path with language and variant considerations
|
||||||
|
func constructAudioPath(config Config, fileName, moduleName string) string {
|
||||||
|
var audioPath string
|
||||||
|
var languagePart, variantPart string
|
||||||
|
|
||||||
|
// Find the module for which we need to construct the path
|
||||||
|
var selectedModule Module
|
||||||
|
for _, module := range config.Modules {
|
||||||
|
if module.Name == moduleName {
|
||||||
|
selectedModule = module
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a language is set in the selected module, use it
|
||||||
|
if len(selectedModule.DefaultLanguage) > 0 {
|
||||||
|
languagePart = selectedModule.DefaultLanguage + "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a variant is set in the selected module, use it
|
||||||
|
if len(selectedModule.DefaultVariants) > 0 {
|
||||||
|
variantPart = selectedModule.DefaultVariants[0] + "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the path
|
||||||
|
audioPath = fmt.Sprintf("%s/%s%s/%s%s", config.BaseDir, languagePart, selectedModule.Path, variantPart, fileName)
|
||||||
|
return audioPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to read display texts (second column) and filenames (first column) from a CSV file
|
||||||
|
func readDisplayTextsAndFileNames(filePath string) ([]string, []string) {
|
||||||
|
file, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error opening CSV file: %v", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
reader := csv.NewReader(file)
|
||||||
|
records, err := reader.ReadAll()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error reading CSV records: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var displayTexts, fileNames []string
|
||||||
|
for _, record := range records {
|
||||||
|
if len(record) > 1 {
|
||||||
|
fileNames = append(fileNames, record[0]) // First column is the filename
|
||||||
|
displayTexts = append(displayTexts, record[1]) // Second column is the display text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return displayTexts, fileNames
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to play audio files based on the file name
|
||||||
|
func playAudioFiles(filePath string) error {
|
||||||
|
// Debugging: print the file path being played
|
||||||
|
fmt.Printf("Playing audio from: %s\n", filePath)
|
||||||
|
|
||||||
|
cmd := exec.Command("ffplay", "-nodisp", "-autoexit", filePath)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error playing audio file: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue