Refactored CLI selection logic into a helper class.

This commit is contained in:
Jonathan Bernard 2016-03-10 10:47:26 -06:00
parent 1e03063400
commit b0532f6733

View File

@ -83,6 +83,10 @@ Configuration:
new ANSI().eraseLine(Erase.All).cursorPrevLine().eraseLine(Erase.All) new ANSI().eraseLine(Erase.All).cursorPrevLine().eraseLine(Erase.All)
.cursorPrevLine().eraseLine(Erase.All).toString() .cursorPrevLine().eraseLine(Erase.All).toString()
public final static modelClass = [
'album': Album, 'artist': Artist, 'bookmark': Bookmark,
'mediaFile': MediaFile, 'playlist': Playlist, 'tag': Tag ]
private int displayWidth = 79 private int displayWidth = 79
private long msgTimeout private long msgTimeout
private ScrollText currentlyPlaying = new ScrollText( private ScrollText currentlyPlaying = new ScrollText(
@ -93,7 +97,7 @@ Configuration:
private SimpleDateFormat sdf = new SimpleDateFormat('EEE-HH-SSS') private SimpleDateFormat sdf = new SimpleDateFormat('EEE-HH-SSS')
/// Current play queue and selection data /// Current play queue and selection data
def selection = [:] Selection currentSelection = new Selection()
Playlist playQueue = library.save(new Playlist( Playlist playQueue = library.save(new Playlist(
name: "CLI Queue ${sdf.format(new Date())}")) name: "CLI Queue ${sdf.format(new Date())}"))
Bookmark playBookmark Bookmark playBookmark
@ -246,10 +250,10 @@ Configuration:
logger.debug("command: $command") logger.debug("command: $command")
switch(command?.toLowerCase()) { switch(command?.toLowerCase()) {
case 'scan': return scanMediaLibrary() case 'scan': return scanMediaLibrary()
case 'select': return processSelect(line) case 'select': return processSelect(line, currentSelection)
case 'list': return processList(line) case 'list': return processList(line, currentSelection)
case 'add': return processAdd(line) case 'add': return processAdd(line)
case 'enque': return processEnque(line) case 'enqueue': return processEnqueue(line)
case 'tag': return processTag(line) case 'tag': return processTag(line)
case 'clear': return processClear(line) case 'clear': return processClear(line)
case 'play': return line.size() == 0 ? case 'play': return line.size() == 0 ?
@ -296,10 +300,12 @@ Configuration:
dismissMsgDate = new Date(new Date().time + msgTimeout) dismissMsgDate = new Date(new Date().time + msgTimeout)
return library } return library }
private def processSelect(LinkedList line) { private def processSelect(LinkedList line, def sel = null) {
String option = line.poll() String option = line.poll()
boolean current = option == "current" boolean current = option == "current"
if (!sel) sel = new Selection()
def items def items
if (current) { if (current) {
if (!curMediaFile) { if (!curMediaFile) {
setErr "No media is currently playing." setErr "No media is currently playing."
@ -308,18 +314,21 @@ Configuration:
option = line.poll() option = line.poll()
switch (option) { switch (option) {
case 'album': case 'album':
return select(Album, getExactlyOne(Album, sel.album = ensureExactlyOne(
library.getAlbumsWhere({ mediaFileId: curMediaFile.id}))) library.getAlbumsWhere({ mediaFileId: curMediaFile.id}))
return sel
case 'artist': case 'artist':
return selectOneMatch(library.getArtistsWhere({ sel.artist = ensureExactlyOne(
mediaFileId: curMediaFile.id})) library.getArtistsWhere({ mediaFileId: curMediaFile.id}))
return sel
case 'playlist': case 'playlist':
return selectOneMatch(playQueue) sel.playlist = playQueue; return sel
case 'file': case 'file':
return selectOneMatch(curMediaFile) sel.mediaFile = curMediaFile; return sel
case 'tags': case 'tags':
return select(tags: library.getTagsWhere({ sel.tags = library.getTagsWhere({
mediaFileId: curMediaFile.id})) mediaFileId: curMediaFile.id})
return sel
default: default:
setErr("Unrecognized option to ${promptStyle}select " + setErr("Unrecognized option to ${promptStyle}select " +
"current${errorStyle}.") "current${errorStyle}.")
@ -328,16 +337,15 @@ Configuration:
} }
switch (option) { switch (option) {
case 'album': return selectOneMatch( case 'file': option = 'mediaFile'
library.getByIdOrName(Album, line.join(' '))) case 'album': case 'artist': case 'playlist':
case 'artist': return selectOneMatch( sel[option] = ensureExactlyOne(
library.getByIdOrName(Artist, line.join(' '))) library.getByIdOrName(modelClass[option], line.join(' ')))
case 'playlist': return selectOneMatch( return sel
library.getByIdOrName(Playlist, line.join(' '))) case 'tags':
case 'file': return selectOneMatch( sel.tags = line.collect { library.getByIdOrName(Tag, it) }
library.getByIdOrName(MediaFile, line.join(' '))) .findAll().flatten()
case 'tags': return select(tags: return sel
line.map { library.getByIdOrName(Tag, it) }.filter().flatten())
default: default:
setErr("Unrecognized option to ${promptStyle}select${errorStyle}") setErr("Unrecognized option to ${promptStyle}select${errorStyle}")
@ -390,7 +398,7 @@ Configuration:
switch(option) { switch(option) {
case 'queue': return library.removeAllFromPlaylist(playQueue.id) case 'queue': return library.removeAllFromPlaylist(playQueue.id)
case 'selected playlist': case 'selected playlist':
if (!selection.playlist) { if (!currentSelection.playlist) {
printLongMessage("No playlist currently selected.") printLongMessage("No playlist currently selected.")
return null } return null }
return library.removeAllFromPlaylist(selected.playlist.id) return library.removeAllFromPlaylist(selected.playlist.id)
@ -407,16 +415,19 @@ Configuration:
return null } return null }
return library.removeAllFromPlaylist(playlist.id) return library.removeAllFromPlaylist(playlist.id)
case 'selection': case 'selection':
currentSelection = new Selection()
break
default: default:
printLongMessage("Unrecognized option to the ${promptStyle}" + printLongMessage("Unrecognized option to the ${promptStyle}" +
"add${normalStyle} command. Use ${promptStyle}help clear" + "clear${normalStyle} command. Use ${promptStyle}help clear" +
"${normalStyle} to see a list of valid options.") "${normalStyle} to see a list of valid options.")
return null return null
} }
} }
private def processList(LinkedList options) { private def processList(LinkedList options, def selection) {
logger.debug("Listing things. Options: $options") logger.debug("Listing things. Options: $options")
if (!selection) selection = new Selection()
def option = options.poll() def option = options.poll()
boolean all = option == 'all' boolean all = option == 'all'
if (all) option = options.poll() if (all) option = options.poll()
@ -424,6 +435,7 @@ Configuration:
logger.debug("Option: $option") logger.debug("Option: $option")
def list def list
switch(option) { switch(option) {
case 'albums': case 'albums':
if (all) list = library.getAlbums() if (all) list = library.getAlbums()
@ -435,7 +447,7 @@ Configuration:
String albumMatch = options?.join(" ")?.trim() String albumMatch = options?.join(" ")?.trim()
if (albumMatch) list = list.findAll { it.name =~ albumMatch } if (albumMatch) list = list.findAll { it.name =~ albumMatch }
printLongMessage(makeList(Album, list, all, printLongMessage(makeList(selection, Album, list, all,
{ "${it.id}: ${it}" })) { "${it.id}: ${it}" }))
break break
@ -449,19 +461,20 @@ Configuration:
String artistMatch = options?.join(" ")?.trim() String artistMatch = options?.join(" ")?.trim()
if (artistMatch) list = list.findAll { it.name =~ artistMatch } if (artistMatch) list = list.findAll { it.name =~ artistMatch }
printLongMessage(makeList(Artist, list, all, printLongMessage(makeList(selection, Artist, list, all,
{ "${it.id}: ${it.name}" })) { "${it.id}: ${it.name}" }))
break break
case 'files': case 'files':
case 'selection': case 'selection':
if (all) list = library.getMediaFiles() if (all) list = library.getMediaFiles()
else list = getSelectedMediaFiles() else list = selection.selectedFiles
if (selection.album) list = list.sort { it.trackNumber }
String mediaFileMatch = options?.join(" ")?.trim() String mediaFileMatch = options?.join(" ")?.trim()
if (mediaFileMatch) list = list.findAll { if (mediaFileMatch) list = list.findAll {
it.name =~ mediaFileMatch } it.name =~ mediaFileMatch }
printLongMessage(makeList(MediaFile, list, all printLongMessage(makeList(selection, MediaFile, list, all,
{ "${it.id}: ${it.trackNumber} - ${it.name}" })) { "${it.id}: ${it.trackNumber} - ${it.name}" }))
break break
@ -473,7 +486,7 @@ Configuration:
String bookmarkMatch = options?.join(" ")?.trim() String bookmarkMatch = options?.join(" ")?.trim()
if (boolmarkMatch) if (boolmarkMatch)
list = list.findAll { it.name =~ bookmarkMatch } list = list.findAll { it.name =~ bookmarkMatch }
printLongMessage(makeList(Bookmark, list, all, printLongMessage(makeList(selection, Bookmark, list, all,
{ "${it.id}: ${it.name} ${it.userCreated ? '' : ' (auto)'}" })) { "${it.id}: ${it.name} ${it.userCreated ? '' : ' (auto)'}" }))
break break
@ -487,7 +500,7 @@ Configuration:
String playlistMatch = options?.join(" ")?.trim() String playlistMatch = options?.join(" ")?.trim()
if (playlistMatch) if (playlistMatch)
list = list.findAll { it.name =~ playlistMatch } list = list.findAll { it.name =~ playlistMatch }
printLongMessage(makeList(Playlist, list, all, printLongMessage(makeList(selection, Playlist, list, all,
{ "${it.id}: ${it.name} ${it.userCreated ? '' : ' (auto)'}" })) { "${it.id}: ${it.name} ${it.userCreated ? '' : ' (auto)'}" }))
break break
@ -500,7 +513,7 @@ Configuration:
String tagMatch = options?.join(" ")?.trim() String tagMatch = options?.join(" ")?.trim()
if (tagMatch) list = list.findAll { it.name =~ tagMatch } if (tagMatch) list = list.findAll { it.name =~ tagMatch }
printLongMessage(makeList(Tag, list, all, printLongMessage(makeList(selection, Tag, list, all,
{ "${it.id}: ${it.name}" })) { "${it.id}: ${it.name}" }))
break break
@ -511,19 +524,16 @@ Configuration:
return null return null
} }
resetStatus()
return list return list
} }
public def unselect(Class modelClass) {
String key = uncapitalize(modelClass.simpleName)
this.selection[key] = null }
public def ensureExactlyOne(def matches) { public def ensureExactlyOne(def matches) {
if (!matches) { if (!matches) {
setErr("Nothing matches."); setErr("Nothing matches.");
return null } return null }
String englishName = toEnglish(modelClass.simpleName) String englishName = toEnglish(matches[0].class.simpleName)
if (matches.size() > 1) { if (matches.size() > 1) {
setErr("Multiple ${englishName}s match: " + setErr("Multiple ${englishName}s match: " +
matches.collect { "${it.id}: ${it.name}" }.join(', ')) matches.collect { "${it.id}: ${it.name}" }.join(', '))
@ -534,11 +544,6 @@ Configuration:
public def getExactlyOne(Class modelClass, def criteria) { public def getExactlyOne(Class modelClass, def criteria) {
return ensureExactlyOne(library.getByIdOrName(modelClass, criteria)) } return ensureExactlyOne(library.getByIdOrName(modelClass, criteria)) }
public def selectOneMatch(def matches) {
def match = ensureExactlyOne(matches)
if (match) selection[uncapitalize(match.class.simpleName)] = match
return match }
private void drawLeader(afterOutput = false) { private void drawLeader(afterOutput = false) {
String leader = beforeLeader + getLeader() + String leader = beforeLeader + getLeader() +
@ -581,16 +586,16 @@ Configuration:
status.text = errorStyle + errMsg status.text = errorStyle + errMsg
dismissMsgDate = new Date(new Date().time + msgTimeout) } dismissMsgDate = new Date(new Date().time + msgTimeout) }
private String makeList(Class modelClass, def items, private String makeList(Selection selection, Class modelClass,
boolean listAll = false, Closure toString = null) { def items, boolean listAll = false, Closure toString = null) {
def result = new StringBuilder() def result = new StringBuilder()
.append("--------------------\n${modelClass.simpleName}") .append("--------------------\n${modelClass.simpleName}s")
if (!listAll && (selection.playlist || selection.artist || if (!listAll && (selection.playlist || selection.artist ||
selection.mediaFile)) selection.mediaFile))
result.append("\n(for selection: ") result.append("\n(for selection: ")
.append(describeSelection()) .append(selection.toString())
.append(normalStyle) .append(normalStyle)
.append(")") .append(")")
@ -600,49 +605,8 @@ Configuration:
return result.toString() } return result.toString() }
private String describeSelection() {
StringBuilder s = new StringBuilder()
if (selection.playlist) s.append(playlistStyle)
.append(selection.playlist)
.append(normalStyle)
.append(": ")
if (selection.artist) s.append(artistStyle)
.append(selection.artist)
.append(normalStyle)
.append(" / ")
if (selection.album) s.append(albumStyle)
.append(selection.album)
.append(normalStyle)
.append(" / ")
if (selection.mediaFile) s.append(fileStyle)
.append(selection.mediaFile)
.append(normalStyle)
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,
tags: selection?.tags) }
private String select(Map s) {
['artist', 'album', 'playlist', 'file', 'tags'].each {
this.selection[it] = s[it] }
resetStatus()
return selection
}
private String resetStatus() { private String resetStatus() {
String s = describeSelection() String s = currentSelection.toString()
if (s.size() == 0) status.text = "No current media selections." if (s.size() == 0) status.text = "No current media selections."
else status.text = s else status.text = s
@ -657,4 +621,62 @@ Configuration:
private static String toEnglish(String modelName) { private static String toEnglish(String modelName) {
return UPPERCASE_PATTERN.matcher(modelName). return UPPERCASE_PATTERN.matcher(modelName).
replaceAll(/$1 $2/).toLowerCase() } replaceAll(/$1 $2/).toLowerCase() }
public class Selection {
Album album
Artist artist
MediaFile mediaFile
Playlist playlist
List<Tag> tags
public def unselect(Class modelClass) {
String key = uncapitalize(modelClass.simpleName)
def value = this[key]
this[key] = null
return value }
public Selection select(Map s) {
['artist', 'album', 'playlist', 'file', 'tags'].each {
this[it] = s[it] }
return this
}
public def select(def value) {
if (value) this[uncapitalize(match.class.simpleName)] = value
return value }
public List<MediaFile> getSelectedFiles() {
return library.getMediaFilesWhere(
playlistId: playlist?.id,
artistId: artist?.id,
albumId: album?.id,
tags: tags) }
public String toString() {
StringBuilder s = new StringBuilder()
if (playlist) s.append(playlistStyle)
.append(playlist)
.append(normalStyle)
.append(": ")
if (artist) s.append(artistStyle)
.append(artist)
.append(normalStyle)
.append(" / ")
if (album) s.append(albumStyle)
.append(album)
.append(normalStyle)
.append(" / ")
if (mediaFile) s.append(fileStyle)
.append(mediaFile)
.append(normalStyle)
return s.toString()
}
}
} }