From dd6ad0fe221580cdd0760dac75af8bfdacf64817 Mon Sep 17 00:00:00 2001 From: Jared K Date: Thu, 17 Mar 2022 00:56:10 -0700 Subject: [PATCH] Allow subdirectories for media --- go.mod | 2 +- js/main.js | 40 +++++----------- main.go | 136 ++++++++++++++++++++++++++++++++--------------------- 3 files changed, 95 insertions(+), 83 deletions(-) diff --git a/go.mod b/go.mod index b382da5..672e24b 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/UpDownLeftDie/obs-random-videos/v2 -go 1.17 +go 1.18 require github.com/sparkdemcisin81/promptui v1.0.0 diff --git a/js/main.js b/js/main.js index 4386e4f..2f250aa 100644 --- a/js/main.js +++ b/js/main.js @@ -1,22 +1,18 @@ // @ts-check -const mediaFolder = /** @type {string} */ ("http:\\\\absolute\\{{ .MediaFolder }}"); const initMediaFiles = /** @type {string[]} */ (["{{ StringsJoin .MediaFiles "\", \"" }}"]); -const transitionVideo = /** @type {string} */("{{ .TransitionVideo }}"); +const transitionVideoPath = /** @type {string} */("{{ .TransitionVideo }}"); const playOnlyOne = /** @type {boolean} */ ({{ .PlayOnlyOne }}); const loopFirstVideo = /** @type {boolean} */ ({{ .LoopFirstVideo }}); -const transitionVideoPath = /** @type {string} */ ( - `${mediaFolder}${transitionVideo}` -); - let isTransition = true; /** * shuffleArr takes in an array and returns a new array in random order - * @param {any[]} a any array to be shuffled + * @param {any[]} originalArray any array to be shuffled * @return {any[]} shuffled array */ -function shuffleArr(a) { - var j, x, i; +function shuffleArr(originalArray) { + let a = [...originalArray] + let j, x, i; for (i = a.length - 1; i > 0; i--) { j = Math.floor(Math.random() * (i + 1)); x = a[i]; @@ -26,17 +22,6 @@ function shuffleArr(a) { return a; } -/** - * prependFolderToFiles simply adds a folder path to a list of files and returns - * the new array - * @param {string} folder folder path, must end with a trailing slash - * @param {string[]} files array of file names - * @returns {string[]} new array with full path to files - */ -function prependFolderToFiles(folder, files) { - return files.map((file) => `${folder}${file}`); -} - /** * storePlaylistState takes in a playlist and stores it into localstorage * @param {string[]} state @@ -52,10 +37,7 @@ function storePlaylistState(state) { * @returns {string[]} a new playlist */ function getNewPlaylist() { - const playlist = prependFolderToFiles( - mediaFolder, - shuffleArr(initMediaFiles), - ); + const playlist = shuffleArr(initMediaFiles); storePlaylistState(playlist); return playlist; } @@ -69,9 +51,9 @@ function getPlaylist() { try { playlist = JSON.parse(localStorage.getItem('playlist')); } catch { - console.log('playlist empty!'); + console.log('playlist doesn\'t exist yet!'); } - if (!playlist || playlist.length === 0) { + if (!playlist || playlist.length === 0 || typeof playlist.pop === 'undefined') { playlist = getNewPlaylist(); } return playlist; @@ -99,7 +81,7 @@ function getNextPlaylistItem() { let mediaItem = playlist.pop(); // check if we played this mediaItem last run - console.log({ lastPlayed: localStorage.getItem('lastPlayed'), mediaItem }); + console.log({ lastPlayed: localStorage.getItem('lastPlayed'), mediaItem, wasLastPlayed: localStorage.getItem('lastPlayed') === mediaItem }); if (localStorage.getItem('lastPlayed') === mediaItem) { // moves the repeated item to the end so its not skipped entirely storePlaylistState([mediaItem].concat(playlist)); @@ -124,7 +106,7 @@ function playNext(player, nextPlayer) { } let video = localStorage.getItem('lastPlayed'); - if (!loopFirstVideo && (!transitionVideo || !isTransition)) { + if (!loopFirstVideo && (!transitionVideoPath || !isTransition)) { video = getNextPlaylistItem(); console.log(`next video: ${video}`); } @@ -135,7 +117,7 @@ function playNext(player, nextPlayer) { nextPlayer.style['z-index'] = 0; nextPlayer.style['opacity'] = '0'; - if (transitionVideo && transitionVideo !== '' && isTransition) { + if (transitionVideoPath && transitionVideoPath !== '' && isTransition) { video = transitionVideoPath; isTransition = false; } else { diff --git a/main.go b/main.go index 62f0628..89a0fa9 100644 --- a/main.go +++ b/main.go @@ -19,7 +19,7 @@ import ( ) //go:embed template.gohtml -var templateHtml string +var templateHTML string //go:embed js/main.js var mainScript string @@ -27,6 +27,7 @@ var mainScript string //go:embed js/body.js var bodyScript string +// UserAnswers holds config answers from comand line prompt ui type UserAnswers struct { MediaFolder string MediaFiles []string @@ -36,6 +37,7 @@ type UserAnswers struct { TransitionVideo string } +// Scripts stores javascript scripts that are later injected into templateHTML type Scripts struct { MainScript string BodyScript string @@ -46,38 +48,31 @@ var scripts = Scripts{ BodyScript: bodyScript, } -var outputHtmlName = "obs-random-videos.html" -var audioFileExts = []string{".mp3", ".ogg", ".aac"} -var videoFileExts = []string{".mp4", ".webm", ".mpeg4", ".m4v", ".mov"} -var mediaFileExts = append(audioFileExts, videoFileExts...) -var promptDelay = 100 * time.Millisecond // helps with race conditionsin promptui +var ( + outputHTMLName = "obs-random-videos.html" + audioFileExts = []string{".mp3", ".ogg", ".aac"} + videoFileExts = []string{".mp4", ".webm", ".mpeg4", ".m4v", ".mov"} + // imagesFileExts = []string{".png", ".jpg", ".jpeg", ".gif", ".webp"} + // mediaFileExts = append(append(audioFileExts, videoFileExts...), imagesFileExts...) + mediaFileExts = append(audioFileExts, videoFileExts...) + promptDelay = 100 * time.Millisecond // helps with race conditionsin promptui +) func main() { - currentDir, err := filepath.Abs(filepath.Dir(os.Args[0])) + mainDir, err := filepath.Abs(filepath.Dir(os.Args[0])) if err != nil { log.Fatalf("Failed to get current directory path: %v", err) } - separator := string(os.PathSeparator) - currentDirHTML := currentDir + separator - outputHtmlName = currentDirHTML + outputHtmlName - if runtime.GOOS == "windows" { - separatorEscaped := strings.Repeat(separator, 2) - currentDirHTML = strings.Replace(currentDirHTML, separator, separatorEscaped, -1) - } - files, err := os.ReadDir(currentDir) - if err != nil { - log.Fatalf("Failed to read current directory: %v", err) - } - mediaFiles := filterFiles(files, mediaFileExts) + mediaFiles := getMediaFiles(mainDir) if len(mediaFiles) < 1 { - fmt.Printf("No media files found in: %s", currentDir) + fmt.Printf("No media files found in: %s", mainDir) fmt.Print("\n\nPress enter to exit...") input := bufio.NewScanner(os.Stdin) input.Scan() return } - answers, err := askQuestions(currentDirHTML, mediaFiles) + answers, err := askQuestions(mediaFiles, mainDir) if err != nil { log.Fatalf("Something went wrong getting user input: %v", err) } @@ -85,31 +80,59 @@ func main() { answers.MediaFiles = removeTransitionVideo(answers.TransitionVideo, answers.MediaFiles) } - templateHtml = "\n" + templateHtml - var outputHtml bytes.Buffer - t := template.Must(template.New("html").Parse(templateHtml)) - err = t.Execute(&outputHtml, scripts) + templateHTML = "\n" + templateHTML + var outputHTML bytes.Buffer + t := template.Must(template.New("HTML").Parse(templateHTML)) + err = t.Execute(&outputHTML, scripts) if err != nil { log.Fatalf("Failed compiling template: %v", err) } - t = template.Must(template.New("html").Funcs(template.FuncMap{"StringsJoin": strings.Join}).Parse(outputHtml.String())) - outputHtml.Reset() - err = t.Execute(&outputHtml, answers) + t = template.Must(template.New("HTML").Funcs(template.FuncMap{"StringsJoin": strings.Join}).Parse(outputHTML.String())) + outputHTML.Reset() + err = t.Execute(&outputHTML, answers) if err != nil { log.Fatalf("Failed compiling template final: %v", err) } - outputHtmlFile, err := os.Create(outputHtmlName) + outputHTMLFile, err := os.Create(outputHTMLName) if err != nil { log.Fatalf("Failed create output file: %v", err) } - outputHtmlFile.WriteString(outputHtml.String()) - outputHtmlFile.Close() + outputHTMLFile.WriteString(outputHTML.String()) + outputHTMLFile.Close() os.Exit(0) } -func askQuestions(currentDir string, mediaFiles []string) (UserAnswers, error) { +func getMediaFiles(currentDir string) []string { + mediaFiles := []string{} + filepath.WalkDir(currentDir, func(path string, file fs.DirEntry, err error) error { + if err != nil { + return err + } + if !file.IsDir() { + if isValidFileType(file) { + // TODO: move this fix to end of program before injection + // Then clean up getTransitionVideo and show the subpath in the file selector + fixedFilePath := fixFilePath(path) + mediaFiles = append(mediaFiles, fixedFilePath) + } + } + return nil + }) + return mediaFiles +} + +func fixFilePath(filePath string) string { + separator := string(os.PathSeparator) + newFilePath := fmt.Sprintf("http:%sabsolute%s%s", separator+separator, separator, filePath) + if runtime.GOOS == "windows" { + separatorEscaped := strings.Repeat(separator, 2) + return strings.Replace(newFilePath, separator, separatorEscaped, -1) + } + return newFilePath +} + +func askQuestions(mediaFiles []string, mainDir string) (UserAnswers, error) { answers := UserAnswers{ - MediaFolder: currentDir, MediaFiles: mediaFiles, PlayOnlyOne: false, LoopFirstVideo: false, @@ -117,15 +140,14 @@ func askQuestions(currentDir string, mediaFiles []string) (UserAnswers, error) { TransitionVideo: "", } - answers.PlayOnlyOne = ShowQuestion("Do you only want to play one video? (The first random video will play once and then stop)", false) + answers.PlayOnlyOne = showQuestion("Do you only want to play one video? (The first random video will play once and then stop)", false) if !answers.PlayOnlyOne { - answers.LoopFirstVideo = ShowQuestion("Do you want to loop the first video?", false) - answers.HaveTransitionVideo = ShowQuestion("Do have a transition video? (This video plays after every other video)", false) + answers.LoopFirstVideo = showQuestion("Do you want to loop the first video?", false) + answers.HaveTransitionVideo = showQuestion("Do have a transition video? (This video plays after every other video)", false) if answers.HaveTransitionVideo { err := errors.New("") - answers.TransitionVideo, err = GetTransitionVideo(mediaFiles) + answers.TransitionVideo, err = getTransitionVideo(mediaFiles, mainDir) if err != nil { - fmt.Printf("Prompt failed getting transition video: %v", err) return answers, err } } @@ -144,22 +166,24 @@ func removeTransitionVideo(transitionVideo string, mediaFiles []string) []string return files } -func filterFiles(files []fs.DirEntry, fileExts []string) []string { - filteredFiles := []string{} - for _, f := range files { - for _, ext := range fileExts { - if strings.HasSuffix(f.Name(), ext) { - filteredFiles = append(filteredFiles, f.Name()) - } +func isValidFileType(file fs.DirEntry) bool { + fileExts := mediaFileExts + for _, ext := range fileExts { + if strings.HasSuffix(file.Name(), ext) { + return true } } - return filteredFiles + return false } -func GetTransitionVideo(mediaFiles []string) (string, error) { +func getTransitionVideo(mediaFiles []string, mainDir string) (string, error) { items := []string{} - for _, f := range mediaFiles { - items = append(items, f) + // Strip full filepath when displaying options to users + for _, filePath := range mediaFiles { + fileParts := strings.Split(filePath, string(os.PathSeparator)) + fmt.Printf("%v", fileParts) + fileName := fileParts[len(fileParts)-1] + items = append(items, fileName) } items = append(items, "CANCEL") prompt := promptui.Select{ @@ -172,12 +196,18 @@ func GetTransitionVideo(mediaFiles []string) (string, error) { return "", err } if strings.ToLower(result) != "cancel" { - return result, nil + // Need to match file name with full path again + for _, filePath := range mediaFiles { + if strings.Contains(filePath, result) { + return filePath, nil + } + } + log.Fatalf("Error occurred when trying to get transition video path for: %s", result) } return "", nil } -func ShowQuestion(message string, defaultValueFlag bool) bool { +func showQuestion(message string, defaultValueFlag bool) bool { defaultValue := "y" if !defaultValueFlag { defaultValue = "n" @@ -190,7 +220,7 @@ func ShowQuestion(message string, defaultValueFlag bool) bool { return nil } } - return errors.New(fmt.Sprintf("Number should be one of the values %v", allowedValues)) + return fmt.Errorf("number should be one of the values %v", allowedValues) } prompt := promptui.Prompt{ @@ -202,7 +232,7 @@ func ShowQuestion(message string, defaultValueFlag bool) bool { result, err := prompt.Run() if err != nil { time.Sleep(promptDelay) - return ShowQuestion(message, defaultValueFlag) + return showQuestion(message, defaultValueFlag) } result = strings.ToLower(result)