Updated CLI to handle DB sync, UUID as record ids, more details after a library scan.

This commit is contained in:
Jonathan Bernard 2016-03-30 01:13:53 -05:00
parent d6edd3f11d
commit bc21abc24b

View File

@ -36,6 +36,8 @@ Usage:
wdiwtlt [options]
wdiwtlt --version
wdiwtlt --help
wdiwtlt --sync
wdiwtlt --sync-only
Options:
-c --config <config-file>
@ -55,6 +57,22 @@ Options:
HikariConfig constructor.
(see https://github.com/brettwooldridge/HikariCP#configuration-knobs-baby)
-R --remote-database-config <config-file>
For use when performing a sync: the path to a data source configuration
file for the remote database configuration data. This is processed in
the same manner as the local database configuration file.
--sync-pull
When performing a sync, copy data present on the remote database and
missing locally into the local database.
--sync-push
When performing a sync, copy data missing on the remote database and
present in the local database into the remote database.
Configuration:
"""
@ -185,12 +203,72 @@ Configuration:
// Create our database instance
def dbapi
try {
logger.debug('Using datasource config:\n\t{}', hcfg.dataSourceProperties)
def hds = new HikariDataSource(hcfg)
dbapi = new DbApi(hds) }
catch (Exception e) {
logger.error(
"Could not establish a connection to the database:\n\t{}\n",
e.localizedMessage, e)
exitErr("Could not establish a connection to the database:\n\t" +
e.localizedMessage) }
// Look to see if we've been asked to sync with a remote DB
if (opts['--sync'] || opts['--sync-only'] ||
givenCfg['sync']?.toLowerCase() == 'true') {
// Get the remote database
HikariConfig remoteHcfg
File remoteDbCfgFile = new File(
opts["--remote-database-config"] ?:
givenCfg['sync.database.config.file'] ?:
"database.remote.properties")
// Try to load from the given DB config file
if (remoteDbCfgFile && remoteDbCfgFile.exists() &&
remoteDbCfgFile.isFile()) {
Properties props = new Properties()
try { remoteDbCfgFile.withInputStream { props.load(it) } }
catch (Exception e) { props.clear() }
if (props) remoteHcfg = new HikariConfig(props) }
// Look to see if database connection properties are given in the CLI
// config file.
if (givenCfg && givenCfg["sync.database.config.dataSourceClassName"]) {
Properties props = new Properties()
props.putAll(givenCfg
.findAll { it.key.startsWith("sync.database.config.") }
.collectEntries { [it.key[21..-1], it.value] } )
if (props) remoteHcfg = new HikariConfig(props) }
def remoteDbApi
try {
logger.debug('Using remote datasource config:\n\t{}',
remoteHcfg.dataSourceProperties)
def hds = new HikariDataSource(remoteHcfg)
remoteDbApi = new DbApi(hds) }
catch (Exception e) {
logger.error(
"Could not establish a connection to the remote database:\n\t{}\n",
e.localizedMessage, e)
exitErr("Could not establish a connection to the remote database:\n\t" +
e.localizedMessage) }
// Sync the databases
try {
println 'Syncing database...'
dbapi.syncWith(remoteDbApi,
opts['--sync-pull'] || givenCfg['sync.pull']?.toLowerCase() == 'true',
opts['--sync-push'] || givenCfg['sync.push']?.toLowerCase() == 'true')
println 'Sync completed!'
if (opts['--sync-only']) System.exit(0) }
catch (Exception e) {
logger.error( "Unable to sync with remote database:\n\t{}\n",
e.localizedMessage, e)
exitErr("Unable to sync with remote database:\n\t" +
e.localizedMessage) } }
// Try to discover the VLC native libraries
def vlcj
try {
@ -204,6 +282,8 @@ Configuration:
try {
def cliInst = new CommandLineInterface(new MediaLibrary(dbapi, libRoot),
vlcj, System.in, System.out, givenCfg)
if (opts['--sync'] || givenCfg['sync']?.toLowerCase() == 'true')
cliInst.consoleReadBuffer.add('scan')
cliInst.repl() }
catch (Exception e) { e.printStackTrace(); exitErr(e.localizedMessage) }
finally { if (vlcj) vlcj.release() }
@ -234,7 +314,7 @@ Configuration:
playBookmark.playlistId = playQueue.id
playBookmark.playIndex = 0
playBookmark.mediaFileId = 0
playBookmark.mediaFileId = null
this.vlcj = vlcj
@ -367,10 +447,18 @@ Configuration:
}
public MediaLibrary scanMediaLibrary() {
status.text = "Scanning media library..."
msg "Scanning media library..."
drawLeader()
def counts = library.rescanLibrary()
msg("Scanned ${counts.total} files. Added ${counts.new} and " +
"ignored ${counts.ignored} files.")
String scanResults = "\n\n--------------------\nScan complete:\n\t${counts.total} files total."
if (counts.new) scanResults += "\n\t${counts.new} new files added."
if (counts.ignored) scanResults += "\n\t${counts.ignored} files ignored."
if (counts.absent)
scanResults += "\n\t${counts.absent} files in the database but not stored locally."
printLongMessage(scanResults)
resetStatus()
return library }
private String processList(String options, def selection) {
@ -380,7 +468,8 @@ Configuration:
else if (options != 'selection') selection = select(options, selection)
if (!selection) err "Nothing selected."
else return printLongMessage(makeList(selection, { "${it.id}: ${it} " })) }
else return printLongMessage(makeList(selection,
{ "${it.id.toString()[0..<6]}: ${it} " })) }
private List<Model> processSelect(String options, List<Model> selection) {
currentSelection = select(options, selection)
@ -423,6 +512,9 @@ Configuration:
return selection.collectMany { library.getWhere(modelClass,
[(idKeyFor(selectionClass)): it.id]) }.findAll()
case ~/absent files/:
return library.getMediaFilesWhere(presentLocally: false)
case ~/files tagged( as){0,1}((\s[^\s]+)+)/:
selectedTags = Matcher.lastMatcher[0][2].split(/\s/)
.collect { it?.trim() }.findAll()
@ -832,6 +924,11 @@ ${cmdStyle}select selected { album | artist | file | playlist | tag }${normalSty
This example would select all files that are tagged as *either*
instrumental or orchestral
${cmdStyle}select absent files${normalStyle}
Select al media files which have entries in the database but are not
actually present locally on disk.
${cmdStyle}select files tagged as <tag-name>...${normalStyle}
Select all media files tagged with the given tags. If multiple tags are
@ -1083,6 +1180,7 @@ Selecting files:
select files tagged as <tag>... and not as <tag>...
select playing { albums | artists | files | playlists | tags }
select queued { albums | artists | files | playlists | tags }
select absent files
list selection
list <selection-criteria>
@ -1197,8 +1295,8 @@ Library Management:
if (!matches) err "Nothing matches."
String englishName = toEnglish(matches[0].class)
if (matches.size() > 1) err "Multiple ${englishName}s match: " +
matches.collect { "${it.id}: ${it.name}" }.join(', ')
if (matches.size() > 1) err "Multiple ${englishName}s match:\n\t" +
matches.collect { "${it.id}: ${it.name}" }.join('\n\t')
return matches[0] }