diff --git a/cli/src/main/groovy/com/jdbernard/wdiwtlt/cli/CommandLineInterface.groovy b/cli/src/main/groovy/com/jdbernard/wdiwtlt/cli/CommandLineInterface.groovy index ae94158..af42da7 100644 --- a/cli/src/main/groovy/com/jdbernard/wdiwtlt/cli/CommandLineInterface.groovy +++ b/cli/src/main/groovy/com/jdbernard/wdiwtlt/cli/CommandLineInterface.groovy @@ -10,6 +10,7 @@ import com.zaxxer.hikari.HikariConfig import com.zaxxer.hikari.HikariDataSource import org.docopt.Docopt import jline.console.ConsoleReader +import java.text.SimpleDateFormat import java.util.regex.Matcher import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -83,8 +84,11 @@ Configuration: text: "No media currently playing.") private ScrollText status = new ScrollText(maxWidth: displayWidth) private Date dismissMsgDate = new Date() + private SimpleDateFormat sdf = new SimpleDateFormat('EEE-HH-SSS') def selection = [:] + def currentPlaylist = library.save(new Playlist( + "CLI Queue ${sdf.format(new Date())}")) public static void main(String[] args) { @@ -117,7 +121,8 @@ Configuration: File libRoot = new File( opts["--library-root"] ?: givenCfg[ConfigWrapper.LIBRARY_DIR_KEY] ?: - wdiwtltDefaultConfig.libraryRootPath) + wdiwtltDefaultConfig.libraryRootPath ?: + "no libRoot configured") if (libRoot && (!libRoot.exists() || !libRoot.isDirectory())) exitErr("Library root does not exist or is not a directory: " + @@ -181,10 +186,11 @@ Configuration: } void setupTextStyles() { - titleStyle = new ANSI().color(Colors.GREEN, Colors.DEFAULT, false).toString() + titleStyle = new ANSI().color(Colors.WHITE, Colors.DEFAULT, false).toString() normalStyle = new ANSI().resetText().toString() promptStyle = new ANSI().color(Colors.YELLOW, Colors.DEFAULT, true).toString() statusStyle = new ANSI().color(Colors.CYAN, Colors.DEFAULT, false).toString() + playlistStyle = new ANSI().color(Colors.GREEN, Colors.DEFAULT, false).toString() artistStyle = new ANSI().color(Colors.RED, Colors.DEFAULT, false).toString() albumStyle = new ANSI().color(Colors.BLUE, Colors.DEFAULT, false).toString() fileStyle = new ANSI().color(Colors.GREEN, Colors.DEFAULT, false).toString() @@ -230,9 +236,15 @@ Configuration: String command = line.poll() logger.debug("command: $command") switch(command?.toLowerCase()) { - case 'album': return selectAlbum(line) - case 'artist': return selectArtist(line) - case 'scan': return scanMediaLibrary() + case 'album': return selectAlbum(line) + case 'artist': return selectArtist(line) + case 'playlist': return selectPlaylist(line) + case 'current': return selectCurrent(line) + case 'scan': return scanMediaLibrary() + case 'new': return processNew(line) + case 'add': return processAdd(line) + case 'tag': return tagMediaFiles(line) + case 'split': return processSplit(line) case 'list': String nextArg = line.poll() @@ -271,6 +283,33 @@ Configuration: } + private def processNew(LinkedList line) { + def option = line.poll() + switch(option) { + case 'playlist': + case 'bookmark': + default: + printLongMessage("Unrecognized option to the ${promptStyle}" + + "new${normalStyle} command. Use ${promptStyle}help new" + + "${normalStyle} to see a list of valid options.") + return null + } + } + + private def processAdd(LinkedList line) { + // Add takes the form of + def options = line.poll() + switch(option) { + case 'playlist': + case 'bookmark': + default: + printLongMessage("Unrecognized option to the ${promptStyle}" + + "add${normalStyle} command. Use ${promptStyle}help add" + + "${normalStyle} to see a list of valid options.") + return null + } + } + private def processList(LinkedList options, boolean all) { logger.debug("Listing albums. Options: $options") def option = options.poll() @@ -288,7 +327,8 @@ Configuration: String albumMatch = options?.join(" ")?.trim() if (albumMatch) list = list.findAll { it.name =~ albumMatch } - printLongMessage(makeAlbumList(list, all)) + printLongMessage(makeList(Album, list, all, + { "${it.id}: ${it}" })) break case 'artists': @@ -301,21 +341,20 @@ Configuration: String artistMatch = options?.join(" ")?.trim() if (artistMatch) list = list.findAll { it.name =~ artistMatch } - printLongMessage(makeArtistList(list, all)) + printLongMessage(makeList(Artist, list, all, + { "${it.id}: ${it.name}" })) break case 'files': + case 'selection': if (all) list = library.getMediaFiles() - else if (selection.mediaFile) list = [selection.mediaFile] - else list = library.getMediaFilesWhere( - playlistId: selection.playlist?.id, - artistId: selection.artist?.id, - albumId: selection.album?.id) + else list = getSelectedMediaFiles() String mediaFileMatch = options?.join(" ")?.trim() if (mediaFileMatch) list = list.findAll { it.name =~ mediaFileMatch } - printLongMessage(makeMediaFileList(list, all)) + printLongMessage(makeList(MediaFile, list, all + { "${it.id}: ${it.trackNumber} - ${it.name}" })) break case 'bookmarks': @@ -326,12 +365,12 @@ Configuration: String bookmarkMatch = options?.join(" ")?.trim() if (boolmarkMatch) list = list.findAll { it.name =~ bookmarkMatch } - printLongMessage(makeBookmarkList(list, all)) + printLongMessage(makeList(Bookmark, list, all, + { "${it.id}: ${it.name} ${it.userCreated ? '' : ' (auto)'}" })) break case 'playlists': if (all) list = library.getPlaylists() - else if (selection.playlist) list = [selection.playlist] else list = library.getPlaylistsWhere( artistId: selection?.artist?.id, albumId: selection?.album?.id, @@ -340,7 +379,21 @@ Configuration: String playlistMatch = options?.join(" ")?.trim() if (playlistMatch) list = list.findAll { it.name =~ playlistMatch } - printLongMessage(makePlaylistList(list, all)) + printLongMessage(makeList(Playlist, list, all, + { "${it.id}: ${it.name} ${it.userCreated ? '' : ' (auto)'}" })) + break + + case 'tags': + if (all) list = library.getTags() + else list = library.getTagsWhere( + playlistId: selection?.playlist?.id, + artistId: selection?.artist?.id, + albumId: selection?.album?.id) + + String tagMatch = options?.join(" ")?.trim() + if (tagMatch) list = list.findAll { it.name =~ tagMatch } + printLongMessage(makeList(Tag, list, all, + { "${it.id}: ${it.name}" })) break default: @@ -382,10 +435,29 @@ Configuration: setErr("Multiple artists match '$input': " + match.collect { "${it.id}: ${it.name}" }.join(", ")) return null } + selection.artist = match[0] resetStatus() return match[0] } + public Playlist selectPlaylist(LinkedList input) { + String criteria = input.join(" ") + + if (!criteria) { selection.playlist = null; resetStatus(); return null } + // currentPlaylist = library.save( + // new Playlist(name: "CLI Queue ${sdf.format(new Date())}")) } + + def match = library.getByIdOrName(Playlist, criteria) + + if (!match) { setErr("No playlist matches: '$input'."); return null } + else if (match.size() > 1) { + setErr("Multiple playlists match '$input': " + + match.collect { "${it.id}: ${it.name}" }.join(", ")) + return null } + selection.playlist = match[0] + resetStatus() + return match[0] } + public MediaLibrary scanMediaLibrary() { status.text = "Scanning media library..." library.rescanLibrary() @@ -435,47 +507,21 @@ Configuration: status.text = errorStyle + errMsg dismissMsgDate = new Date(new Date().time + msgTimeout) } - private String makeAlbumList(def albums, boolean listAll) { + private String makeList(Class modelClass, def items, + boolean listAll = false, Closure toString = null) { + def result = new StringBuilder() - .append("--------------------\nAlbums") + .append("--------------------\n${modelClass.simpleName}") if (!listAll && (selection.playlist || selection.artist || selection.mediaFile)) result.append("\n(for selection: ") .append(describeSelection()) + .append(normalStyle) + .append(")") result.append(":\n\n") - result.append(albums.collect { "${it.id}: ${it}" }.join("\n")) - .append("\n") - - return result.toString() } - - private String makeArtistList(def artists, boolean listAll) { - def result = new StringBuilder() - .append("--------------------\nArists") - - if (!listAll && (selection.playlist || selection.artist || - selection.mediaFile)) - result.append("\n(for selection: ") - .append(describeSelection()) - - result.append(":\n\n") - result.append(artists.collect { "${it.id}: ${it.name}" }.join("\n")) - .append("\n") - - return result.toString() } - - private String makeMediaFileList(def mediaFiles, boolean listAll) { - def result = new StringBuilder() - .append("--------------------\nMedia Files") - - if (!listAll && (selection.playlist || selection.artist || - selection.mediaFile)) - result.append("\n(for selection: ") - .append(describeSelection()) - - result.append(":\n\n") - result.append(mediaFiles.collect { "${it.id}: ${it.trackNumber} - ${it.name}" }.join("\n")) + result.append(items.collect( toString ?: { it.toString() }).join("\n")) .append("\n") return result.toString() } @@ -505,6 +551,14 @@ Configuration: return s.toString() } + private getSelectedMediaFiles() { + if (selection.mediaFile) return selection.mediaFile + + return list = library.getMediaFilesWhere( + playlistId: selection?.playlist?.id, + artistId: selection?.artist?.id, + albumId: selection?.album?.id) } + private String resetStatus() { String s = describeSelection()