package main import ( "dmxconnect/hardware" "fmt" "github.com/rs/zerolog/log" "os" "path/filepath" "time" "github.com/wailsapp/wails/v2/pkg/runtime" "gopkg.in/yaml.v2" ) const ( projectsDirectory = "projects" // The directory were are stored all the projects avatarsDirectory = "frontend/public" // The directory were are stored all the avatars projectExtension = ".dmxproj" // The extension of a DMX Connect project ) // GetProjects gets all the projects in the projects directory func (a *App) GetProjects() ([]ProjectMetaData, error) { projects := []ProjectMetaData{} f, err := os.Open(projectsDirectory) if err != nil { log.Err(err).Str("file", "project").Msg("unable to open the projects directory") return nil, fmt.Errorf("unable to open the projects directory: %v", err) } log.Trace().Str("file", "project").Str("projectsDirectory", projectsDirectory).Msg("projects directory opened") files, err := f.Readdir(0) if err != nil { log.Err(err).Str("file", "project").Msg("unable to read the projects directory") return nil, fmt.Errorf("unable to read the projects directory: %v", err) } log.Trace().Str("file", "project").Any("projectsFiles", files).Msg("project files got") for _, fileInfo := range files { // Open the file and get the show name fileData, err := os.ReadFile(filepath.Join(projectsDirectory, fileInfo.Name())) if err != nil { log.Warn().Str("file", "project").Str("projectFile", fileInfo.Name()).Msg("unable to open the project file") continue } log.Trace().Str("file", "project").Str("projectFile", fileInfo.Name()).Any("fileData", fileData).Msg("project file read") projectObject := ProjectInfo{} err = yaml.Unmarshal(fileData, &projectObject) if err != nil { log.Warn().Str("file", "project").Str("projectFile", fileInfo.Name()).Msg("project has invalid format") continue } log.Trace().Str("file", "project").Str("projectFile", fileInfo.Name()).Msg("project file unmarshalled") // Add the SaveFile property projects = append(projects, ProjectMetaData{ Name: projectObject.ShowInfo.Name, Save: fileInfo.Name(), }) } log.Info().Str("file", "project").Any("projectsList", projects).Msg("got the projects list") return projects, nil } // CreateProject creates a new blank project func (a *App) CreateProject() ShowInfo { date := time.Now() a.projectSave = "" a.projectInfo.ShowInfo = ShowInfo{ Name: "My new show", Date: fmt.Sprintf("%04d-%02d-%02dT%02d:%02d", date.Year(), date.Month(), date.Day(), date.Hour(), date.Minute()), Avatar: "appicon.png", Comments: "Write your comments here", } log.Info().Str("file", "project").Any("showInfo", a.projectInfo.ShowInfo).Msg("project has been created") return a.projectInfo.ShowInfo } // GetProjectInfo returns the information of the saved project func (a *App) GetProjectInfo(projectFile string) (ProjectInfo, error) { // Open the project file projectPath := filepath.Join(projectsDirectory, projectFile) log.Trace().Str("file", "project").Str("projectPath", projectPath).Msg("project path is created") content, err := os.ReadFile(projectPath) if err != nil { log.Err(err).Str("file", "project").Str("projectFile", projectFile).Msg("Unable to read the project file") return ProjectInfo{}, fmt.Errorf("unable to read the project file: %v", err) } log.Trace().Str("file", "project").Str("projectPath", projectPath).Msg("project file read") a.projectInfo = ProjectInfo{} err = yaml.Unmarshal(content, &a.projectInfo) if err != nil { log.Err(err).Str("file", "project").Str("projectFile", projectFile).Msg("Unable to get the project information") return ProjectInfo{}, fmt.Errorf("unable to get the project information: %v", err) } log.Trace().Str("file", "project").Str("projectPath", projectPath).Msg("project information got") // Load it into the app a.projectSave = projectFile // Return the show information log.Info().Str("file", "project").Any("projectInfo", a.projectInfo).Msg("got the project information") return a.projectInfo, nil } // ChooseAvatarPath opens a filedialog to choose the show avatar func (a *App) ChooseAvatarPath() (string, error) { // Open the file dialog box filePath, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{ Title: "Choose your show avatar", Filters: []runtime.FileFilter{ { DisplayName: "Images", Pattern: "*.png;*.jpg;*.jpeg", }, }, }) if err != nil { log.Err(err).Str("file", "project").Msg("unable to open the avatar dialog") return "", err } log.Debug().Str("file", "project").Msg("avatar dialog is opened") // Copy the avatar to the application avatars path avatarPath := filepath.Join(avatarsDirectory, filepath.Base(filePath)) log.Trace().Str("file", "project").Str("avatarPath", avatarPath).Msg("avatar path is created") _, err = copy(filePath, avatarPath) if err != nil { log.Err(err).Str("file", "project").Str("avatarsDirectory", avatarsDirectory).Str("fileBase", filepath.Base(filePath)).Msg("unable to copy the avatar file") return "", err } log.Info().Str("file", "project").Str("avatarFileName", filepath.Base(filePath)).Msg("got the new avatar file") return filepath.Base(filePath), nil } // UpdateShowInfo updates the show information func (a *App) UpdateShowInfo(showInfo ShowInfo) { a.projectInfo.ShowInfo = showInfo log.Info().Str("file", "project").Any("showInfo", showInfo).Msg("show information was updated") } // SaveProject saves the project func (a *App) SaveProject() (string, error) { // If there is no save file, create a new one with the show name if a.projectSave == "" { date := time.Now() a.projectSave = fmt.Sprintf("%04d%02d%02d%02d%02d%02d%s", date.Year(), date.Month(), date.Day(), date.Hour(), date.Minute(), date.Second(), projectExtension) log.Debug().Str("file", "project").Str("newProjectSave", a.projectSave).Msg("projectSave is null, getting a new one") } data, err := yaml.Marshal(a.projectInfo) if err != nil { log.Err(err).Str("file", "project").Any("projectInfo", a.projectInfo).Msg("unable to format the project information") return "", err } log.Trace().Str("file", "project").Any("projectInfo", a.projectInfo).Msg("projectInfo has been marshalled") // Create the project directory if not exists err = os.MkdirAll(projectsDirectory, os.ModePerm) if err != nil { log.Err(err).Str("file", "project").Str("projectsDirectory", projectsDirectory).Msg("unable to create the projects directory") return "", err } log.Trace().Str("file", "project").Str("projectsDirectory", projectsDirectory).Msg("projects directory has been created") err = os.WriteFile(filepath.Join(projectsDirectory, a.projectSave), data, os.ModePerm) if err != nil { log.Err(err).Str("file", "project").Str("projectsDirectory", projectsDirectory).Str("projectSave", a.projectSave).Msg("unable to save the project") return "", err } log.Info().Str("file", "project").Str("projectFileName", a.projectSave).Msg("project has been saved") return a.projectSave, nil } // ShowInfo defines the information of the show type ShowInfo struct { Name string `yaml:"name"` Date string `yaml:"date"` Avatar string `yaml:"avatar"` Comments string `yaml:"comments"` } // ProjectMetaData defines all the minimum information for a lighting project type ProjectMetaData struct { Name string // Show name Save string // The save file of the project } // ProjectInfo defines all the information for a lighting project type ProjectInfo struct { ShowInfo ShowInfo `yaml:"show"` // Show information PeripheralsInfo map[string]hardware.PeripheralInfo `yaml:"peripherals"` // Peripherals information }