Updated CLI to handle DB sync, UUID as record ids, more details after a library scan.
This commit is contained in:
parent
d6edd3f11d
commit
bc21abc24b
@ -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] }
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user