Added logging on ORM methods. Implemented library file scanning.
This commit is contained in:
parent
b605b56221
commit
c84092b37e
@ -9,6 +9,7 @@ import org.jaudiotagger.tag.Tag as JATag
|
|||||||
import org.jaudiotagger.tag.FieldKey
|
import org.jaudiotagger.tag.FieldKey
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils
|
||||||
|
|
||||||
import static org.jaudiotagger.tag.FieldKey.*
|
import static org.jaudiotagger.tag.FieldKey.*
|
||||||
|
|
||||||
@ -23,7 +24,7 @@ public class MediaLibrary {
|
|||||||
logger.debug("Creating a MediaLibrary rooted at: {}",
|
logger.debug("Creating a MediaLibrary rooted at: {}",
|
||||||
rootDir.canonicalPath)
|
rootDir.canonicalPath)
|
||||||
|
|
||||||
this.orm = org
|
this.orm = orm
|
||||||
this.libraryRoot = rootDir }
|
this.libraryRoot = rootDir }
|
||||||
|
|
||||||
public void clean() {
|
public void clean() {
|
||||||
@ -32,33 +33,46 @@ public class MediaLibrary {
|
|||||||
orm.removeEmptyPlaylists()
|
orm.removeEmptyPlaylists()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void rescanLibrary() {
|
||||||
|
libraryRoot.eachFileRecurse { addFile(it) }
|
||||||
|
}
|
||||||
|
|
||||||
public MediaFile addFile(File f) {
|
public MediaFile addFile(File f) {
|
||||||
if (!f.exists() || !f.isFile()) {
|
if (!f.exists() || !f.isFile()) {
|
||||||
logger.debug("Ignoring non-existant file: {}", f.canonicalPath)
|
logger.info("Ignoring non-existant file: {}", f.canonicalPath)
|
||||||
return null }
|
return null }
|
||||||
|
|
||||||
def relPath = getRelativePath(libraryRoot, f)
|
def relPath = getRelativePath(libraryRoot, f)
|
||||||
MediaFile mf = orm.getMediaFileByFilePath(relPath)
|
MediaFile mf = orm.getMediaFileByFilePath(relPath)
|
||||||
|
|
||||||
if (mf) {
|
if (mf) {
|
||||||
logger.debug("Ignoring a media file I already know about: {}",
|
logger.info(
|
||||||
relPath)
|
"Ignoring a media file I already know about: {}", relPath)
|
||||||
return mf }
|
return mf }
|
||||||
|
|
||||||
// Read in the media's tags
|
// Read in the media's tags
|
||||||
mf = new MediaFile()
|
mf = new MediaFile()
|
||||||
def af = AudioFileIO.read(f)
|
def af
|
||||||
|
|
||||||
|
try { af = AudioFileIO.read(f) }
|
||||||
|
catch (Exception e) {
|
||||||
|
logger.info("Ignoring a file because I can't" +
|
||||||
|
"read the media tag info:\n\t{}",
|
||||||
|
e.localizedMessage)
|
||||||
|
return null }
|
||||||
|
|
||||||
def fileTag = af.tag
|
def fileTag = af.tag
|
||||||
|
|
||||||
mf.name = fileTag.getFirst(TITLE).trim() ?: f.name
|
mf.name = fileTag?.getFirst(TITLE)?.trim() ?: f.name
|
||||||
mf.filePath = relPath
|
mf.filePath = relPath
|
||||||
mf.comment = fileTag.getFirst(COMMENT).trim()
|
mf.comment = fileTag?.getFirst(COMMENT)?.trim()
|
||||||
|
mf.trackNumber = (fileTag?.getFirst(TRACK) ?: null) as Integer
|
||||||
|
|
||||||
def folderParts = mf.filePath.split("[\\\\/]")[1..<-1]
|
def folderParts = mf.filePath.split("[\\\\/]")[1..<-1] as LinkedList
|
||||||
|
|
||||||
// Find artist and album names (if any)
|
// Find artist and album names (if any)
|
||||||
def artistName = fileTag.getFirst(ARTIST).trim()
|
def artistName = fileTag?.getFirst(ARTIST)?.trim()
|
||||||
def albumName = fileTag.getFirst(ALBUM).trim()
|
def albumName = fileTag?.getFirst(ALBUM)?.trim()
|
||||||
|
|
||||||
if (!artistName) {
|
if (!artistName) {
|
||||||
mf.metaInfoSource = MediaFile.FILE_LOCATION
|
mf.metaInfoSource = MediaFile.FILE_LOCATION
|
||||||
@ -70,34 +84,34 @@ public class MediaLibrary {
|
|||||||
|
|
||||||
// Hash the file
|
// Hash the file
|
||||||
mf.fileHash = f.withInputStream { DigestUtils.md5Hex(it) }
|
mf.fileHash = f.withInputStream { DigestUtils.md5Hex(it) }
|
||||||
orm.create(mf)
|
|
||||||
|
|
||||||
associateWithArtistAndAlbum(mf, artistName, albumName)
|
orm.withTransaction {
|
||||||
|
orm.create(mf)
|
||||||
|
associateWithArtistAndAlbum(mf, artistName, albumName, fileTag)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void associateWithArtistAndAlbum(MediaFile mf, String artistName,
|
private void associateWithArtistAndAlbum(MediaFile mf, String artistName,
|
||||||
String albumName) {
|
String albumName, JATag fileTag) {
|
||||||
Artist artist = null
|
Artist artist = null
|
||||||
Album album = null
|
Album album = null
|
||||||
boolean newAlbumOrArtist = false
|
boolean newAlbumOrArtist = false
|
||||||
|
|
||||||
if (albumName) {
|
|
||||||
album = orm.findAlbumByName(albumName)
|
|
||||||
if (!album) {
|
|
||||||
newAlbumOrArtist = true
|
|
||||||
album = new Album(name: albumName,
|
|
||||||
year: fileTag.getFirst(YEAR) ?: null)
|
|
||||||
orm.create(album) } }
|
|
||||||
|
|
||||||
if (artistName) {
|
if (artistName) {
|
||||||
artist = orm.findArtistByName(artistName)
|
artist = orm.getArtistByName(artistName)
|
||||||
if (!artist) {
|
if (!artist) {
|
||||||
newAlbumOrArtist = true
|
newAlbumOrArtist = true
|
||||||
artist = new Artist(name: artistName)
|
artist = new Artist(name: artistName)
|
||||||
orm.create(artist) } }
|
orm.create(artist) } }
|
||||||
|
|
||||||
// TODO: need to rethink for case where another album does alrady exist
|
if (albumName) {
|
||||||
// by this name, but is already associated with a different artist.
|
album = orm.getAlbumByName(albumName)
|
||||||
|
if (!album) {
|
||||||
|
newAlbumOrArtist = true
|
||||||
|
album = new Album(name: albumName,
|
||||||
|
year: (fileTag?.getFirst(YEAR) ?: null) as Integer,
|
||||||
|
trackTotal: (fileTag?.getFirst(TRACK_TOTAL) ?: null) as Integer)
|
||||||
|
orm.create(album) } }
|
||||||
|
|
||||||
if (artist && album && newAlbumOrArtist)
|
if (artist && album && newAlbumOrArtist)
|
||||||
orm.addAlbumArtist(album.id, artist.id)
|
orm.addAlbumArtist(album.id, artist.id)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.jdbernard.wdiwtlt.db
|
package com.jdbernard.wdiwtlt.db
|
||||||
|
|
||||||
|
import java.lang.reflect.Modifier
|
||||||
import java.sql.Connection
|
import java.sql.Connection
|
||||||
import java.sql.PreparedStatement
|
import java.sql.PreparedStatement
|
||||||
import java.sql.ResultSet
|
import java.sql.ResultSet
|
||||||
@ -11,6 +12,9 @@ import groovy.sql.Sql
|
|||||||
import groovy.transform.CompileStatic
|
import groovy.transform.CompileStatic
|
||||||
import groovy.transform.TailRecursive
|
import groovy.transform.TailRecursive
|
||||||
|
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
import com.jdbernard.wdiwtlt.db.models.*
|
import com.jdbernard.wdiwtlt.db.models.*
|
||||||
|
|
||||||
public class ORM {
|
public class ORM {
|
||||||
@ -19,6 +23,7 @@ public class ORM {
|
|||||||
private Sql sql
|
private Sql sql
|
||||||
|
|
||||||
private static UPPERCASE_PATTERN = Pattern.compile(/(.)(\p{javaUpperCase})/)
|
private static UPPERCASE_PATTERN = Pattern.compile(/(.)(\p{javaUpperCase})/)
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(ORM)
|
||||||
|
|
||||||
public ORM(DataSource dataSource) {
|
public ORM(DataSource dataSource) {
|
||||||
this.dataSource = dataSource
|
this.dataSource = dataSource
|
||||||
@ -34,7 +39,8 @@ public class ORM {
|
|||||||
.append(" WHERE id = ?")
|
.append(" WHERE id = ?")
|
||||||
.toString()
|
.toString()
|
||||||
|
|
||||||
return recordToModel(sql.firstRow(query, id), modelClass) }
|
logger.debug("Selecting model.\n\tSQL: {}\n\tPARAMS: {}", query, id)
|
||||||
|
return recordToModel(sql.firstRow(query, [id]), modelClass) }
|
||||||
|
|
||||||
public def getByName(String name, Class modelClass) {
|
public def getByName(String name, Class modelClass) {
|
||||||
def query = new StringBuilder()
|
def query = new StringBuilder()
|
||||||
@ -43,7 +49,8 @@ public class ORM {
|
|||||||
.append(" WHERE name = ?")
|
.append(" WHERE name = ?")
|
||||||
.toString()
|
.toString()
|
||||||
|
|
||||||
return recordToModel(sql.firstRow(query, name), modelClass) }
|
logger.debug("Selecting model.\n\tSQL: {}\n\tPARAMS: {}", query, name)
|
||||||
|
return recordToModel(sql.firstRow(query, [name]), modelClass) }
|
||||||
|
|
||||||
public def getBy(List<String> columns, List<Object> values,
|
public def getBy(List<String> columns, List<Object> values,
|
||||||
Class modelClass) {
|
Class modelClass) {
|
||||||
@ -54,6 +61,7 @@ public class ORM {
|
|||||||
.append(columns.collect { it + " = ?" }.join(' AND '))
|
.append(columns.collect { it + " = ?" }.join(' AND '))
|
||||||
.toString()
|
.toString()
|
||||||
|
|
||||||
|
logger.debug("Selecting models.\n\tSQL: {}\n\tPARAMS: {}", query, values)
|
||||||
return sql.rows(query, values)
|
return sql.rows(query, values)
|
||||||
.collect { recordToModel(it, modelClass) } }
|
.collect { recordToModel(it, modelClass) } }
|
||||||
|
|
||||||
@ -65,7 +73,7 @@ public class ORM {
|
|||||||
def setClauses = []
|
def setClauses = []
|
||||||
def params = []
|
def params = []
|
||||||
|
|
||||||
model.class.fields
|
getInstanceFields(model.class)
|
||||||
.findAll { it.name != 'id' }
|
.findAll { it.name != 'id' }
|
||||||
.each { field ->
|
.each { field ->
|
||||||
setClauses << '"' + nameFromModel(field.name) + '"= ?'
|
setClauses << '"' + nameFromModel(field.name) + '"= ?'
|
||||||
@ -80,13 +88,14 @@ public class ORM {
|
|||||||
.append(model.id)
|
.append(model.id)
|
||||||
.toString()
|
.toString()
|
||||||
|
|
||||||
|
logger.debug("Updating model.\n\tSQL: {}\n\tPARAMS: {}", query, params)
|
||||||
return sql.executeUpdate(query, params) }
|
return sql.executeUpdate(query, params) }
|
||||||
|
|
||||||
public def create(def model) {
|
public def create(def model) {
|
||||||
def columns = []
|
def columns = []
|
||||||
def params = []
|
def params = []
|
||||||
|
|
||||||
model.class.fields
|
getInstanceFields(model.class)
|
||||||
.findAll { it.name != 'id' }
|
.findAll { it.name != 'id' }
|
||||||
.each { field ->
|
.each { field ->
|
||||||
//if (field.class.getAnnotation(Model)) // check to see if we
|
//if (field.class.getAnnotation(Model)) // check to see if we
|
||||||
@ -103,17 +112,27 @@ public class ORM {
|
|||||||
.append((1..columns.size()).collect { '?' }.join(', '))
|
.append((1..columns.size()).collect { '?' }.join(', '))
|
||||||
.append(")").toString()
|
.append(")").toString()
|
||||||
|
|
||||||
|
logger.debug("Creating model.\n\tSQL: {}\n\tPARAMS: {}", query, params)
|
||||||
model.id = sql.executeInsert(query, params)[0][0]
|
model.id = sql.executeInsert(query, params)[0][0]
|
||||||
return 1 }
|
return 1 }
|
||||||
|
|
||||||
public def delete(def model) {
|
public def delete(def model) {
|
||||||
sql.execute("DELETE FROM ${model.class.simpleName} WHERE id = ?", [model.id])
|
def query = new StringBuilder()
|
||||||
|
.append("DELETE FROM ")
|
||||||
|
.append(pluralize(nameFromModel(model.class.simpleName)))
|
||||||
|
.append("WHERE id = ?")
|
||||||
|
.toString()
|
||||||
|
|
||||||
|
logger.debug("Deleting model.\n\tSQL: {}\n\tPARAMS: {}", query, model.id)
|
||||||
|
sql.execute(query, [model.id])
|
||||||
return sql.updateCount }
|
return sql.updateCount }
|
||||||
|
|
||||||
public def associate(String linkTable, int firstId, int secondId) {
|
public def associate(String linkTable, Integer firstId, Integer secondId) {
|
||||||
return sql.executeInsert(
|
def query = "INSERT INTO $linkTable VALUES (?, ?) ON CONFLICT DO NOTHING"
|
||||||
"INSERT INTO $linkTable VALUES (?, ?) ON CONFLICT DO NOTHING",
|
def params = [firstId, secondId]
|
||||||
[firstId, secondId]) }
|
logger.debug("Creating association.\n\tSQL: {}\n\tPARAMS: {}",
|
||||||
|
query, params)
|
||||||
|
return sql.execute(query, params) }
|
||||||
|
|
||||||
public def associate(def m1, def m2) {
|
public def associate(def m1, def m2) {
|
||||||
return associate(pluralize(nameFromModel(m1.class.simpleName)) +
|
return associate(pluralize(nameFromModel(m1.class.simpleName)) +
|
||||||
@ -124,24 +143,31 @@ public class ORM {
|
|||||||
public Album getAlbumById(int id) { return getById(id, Album) }
|
public Album getAlbumById(int id) { return getById(id, Album) }
|
||||||
public Album getAlbumByName(String name) { return getByName(name, Album) }
|
public Album getAlbumByName(String name) { return getByName(name, Album) }
|
||||||
public Album getAlbumByNameAndArtistId(String name, int artistId) {
|
public Album getAlbumByNameAndArtistId(String name, int artistId) {
|
||||||
def albums = sql.rows("""\
|
def query = """\
|
||||||
SELECT al.*
|
SELECT al.*
|
||||||
FROM albums al JOIN
|
FROM albums al JOIN
|
||||||
artists_albums aa ON
|
artists_albums aa ON
|
||||||
al.id = aa.album_id AND
|
al.id = aa.album_id AND
|
||||||
aa.artist_id = ?
|
aa.artist_id = ?
|
||||||
WHERE al.name = ?""",
|
WHERE al.name = ?"""
|
||||||
[artistId, name]).collect { recordToModel(it, Album) }
|
def params = [artistId, name]
|
||||||
|
logger.debug("Selecting albums.\n\tSQL: {}\n\tPARAMS: {}",
|
||||||
|
query, params)
|
||||||
|
def albums = sql.rows(query, params)
|
||||||
|
.collect { recordToModel(it, Album) }
|
||||||
return albums ? albums[0] : null }
|
return albums ? albums[0] : null }
|
||||||
|
|
||||||
public Album getAlbumsByArtistId(int artistId) {
|
public Album getAlbumsByArtistId(int artistId) {
|
||||||
return sql.rows("""\
|
def query = """\
|
||||||
SELECT al.*
|
SELECT al.*
|
||||||
FROM albums al JOIN
|
FROM albums al JOIN
|
||||||
artists_albums aa ON
|
artists_albums aa ON
|
||||||
al.id = aa.album_id AND
|
al.id = aa.album_id AND
|
||||||
aa.artist_id = ?""",
|
aa.artist_id = ?"""
|
||||||
[artistId]).collect { recordToModel(it, Album) } }
|
|
||||||
|
logger.debug("Selecting albums.\n\tSQL: {}\n\tPARAMS: {}", query, artistId)
|
||||||
|
return sql.rows(query, [artistId])
|
||||||
|
.collect { recordToModel(it, Album) } }
|
||||||
|
|
||||||
public List<Album> removeEmptyAlbums() {
|
public List<Album> removeEmptyAlbums() {
|
||||||
throw new UnsupportedOperationException("Not yet implemented.");
|
throw new UnsupportedOperationException("Not yet implemented.");
|
||||||
@ -151,13 +177,15 @@ public class ORM {
|
|||||||
public Artist getArtistById(int id) { return getById(id, Artist) }
|
public Artist getArtistById(int id) { return getById(id, Artist) }
|
||||||
public Artist getArtistByName(String name) { return getByName(name, Artist) }
|
public Artist getArtistByName(String name) { return getByName(name, Artist) }
|
||||||
public Artist getArtistsByAlbum(int albumId) {
|
public Artist getArtistsByAlbum(int albumId) {
|
||||||
return sql.rows("""\
|
var query = """\
|
||||||
SELECT ar.*
|
SELECT ar.*
|
||||||
FROM artists ar JOIN
|
FROM artists ar JOIN
|
||||||
artists_albums aa ON
|
artists_albums aa ON
|
||||||
ar.id = aa.artist_id AND
|
ar.id = aa.artist_id AND
|
||||||
aa.album_id = ?""",
|
aa.album_id = ?"""
|
||||||
[albumId]).collect { recordToModel(it, Artist) } }
|
logger.debug("Selecting artists.\n\tSQL: {}\n\tPARAMS: {}", query, artistId)
|
||||||
|
return sql.rows(query, [albumId])
|
||||||
|
.collect { recordToModel(it, Artist) } }
|
||||||
|
|
||||||
public List<Artist> removeEmptyArtists() {
|
public List<Artist> removeEmptyArtists() {
|
||||||
throw new UnsupportedOperationException("Not yet implemented.");
|
throw new UnsupportedOperationException("Not yet implemented.");
|
||||||
@ -177,12 +205,31 @@ public class ORM {
|
|||||||
public MediaFile getMediaFileById(int id) { return getById(id, MediaFile) }
|
public MediaFile getMediaFileById(int id) { return getById(id, MediaFile) }
|
||||||
public MediaFile getMediaFileByName(String name) { return getByName(name, MediaFile) }
|
public MediaFile getMediaFileByName(String name) { return getByName(name, MediaFile) }
|
||||||
|
|
||||||
|
public MediaFile getMediaFileByFilePath(String filePath) {
|
||||||
|
def files = getBy(["file_path"], [filePath], MediaFile)
|
||||||
|
return files ? files[0] : null }
|
||||||
|
|
||||||
public def associateMediaFileWithAlbum(int mediaFileId, int albumId) {
|
public def associateMediaFileWithAlbum(int mediaFileId, int albumId) {
|
||||||
return associate("albums_media_files", albumId, mediaFileId) }
|
return associate("albums_media_files", albumId, mediaFileId) }
|
||||||
|
|
||||||
public def associateMediaFileWithArtist(int mediaFileId, int artistId) {
|
public def associateMediaFileWithArtist(int mediaFileId, int artistId) {
|
||||||
return associate("artists_media_files", artistId, mediaFileId) }
|
return associate("artists_media_files", artistId, mediaFileId) }
|
||||||
|
|
||||||
|
public def incrementPlayCount(int mediaFileId) {
|
||||||
|
def query = "UPDATE media_files SET play_count = play_count + 1 WHERE ID = ?"
|
||||||
|
def params = [mediaFileId]
|
||||||
|
|
||||||
|
logger.debug("Updating media file.\n\tSQL: {}\n\tPARAMS: {}", query, params)
|
||||||
|
sql.executeUpdate(query, params)
|
||||||
|
|
||||||
|
query = "SELECT play_count FROM media_files WHERE id = ?"
|
||||||
|
logger.debug("Selecting media file play count.\n\tSQL: {}\n\tPARAMS: {}", query, params)
|
||||||
|
return sql.firstRow(query, params)[0] }
|
||||||
|
|
||||||
|
public MediaFile incrementPlayCount(MediaFile mf) {
|
||||||
|
mf.playCount = incrementPlayCount(mf.id)
|
||||||
|
return mf }
|
||||||
|
|
||||||
/// ### Playlist-specific methods
|
/// ### Playlist-specific methods
|
||||||
public Playlist getPlaylistById(int id) { return getById(id, Playlist) }
|
public Playlist getPlaylistById(int id) { return getById(id, Playlist) }
|
||||||
public Playlist getPlaylistByName(String name) { return getByName(name, Playlist) }
|
public Playlist getPlaylistByName(String name) { return getByName(name, Playlist) }
|
||||||
@ -196,6 +243,7 @@ public class ORM {
|
|||||||
public Tag getTagByName(String name) { return getByName(name, Tag) }
|
public Tag getTagByName(String name) { return getByName(name, Tag) }
|
||||||
|
|
||||||
/// ### Utility functions
|
/// ### Utility functions
|
||||||
|
public void withTransaction(Closure c) { sql.withTransaction(c) }
|
||||||
public static String nameToModel(String name) {
|
public static String nameToModel(String name) {
|
||||||
def pts = name.toLowerCase().split('_')
|
def pts = name.toLowerCase().split('_')
|
||||||
return pts.length == 1 ? pts[0] :
|
return pts.length == 1 ? pts[0] :
|
||||||
@ -208,7 +256,7 @@ public class ORM {
|
|||||||
public static String pluralize(String name) { return name + "s" }
|
public static String pluralize(String name) { return name + "s" }
|
||||||
|
|
||||||
static def updateModel(def record, def model) {
|
static def updateModel(def record, def model) {
|
||||||
model.class.fields.each { field ->
|
getInstanceFields(model.class).each { field ->
|
||||||
field.set(model, record[nameFromModel(field.name)]) }
|
field.set(model, record[nameFromModel(field.name)]) }
|
||||||
return model }
|
return model }
|
||||||
|
|
||||||
@ -217,7 +265,7 @@ public class ORM {
|
|||||||
|
|
||||||
def model = clazz.newInstance()
|
def model = clazz.newInstance()
|
||||||
|
|
||||||
model.class.fields.each { field ->
|
getInstanceFields(model.class).each { field ->
|
||||||
field.set(model, record[nameFromModel(field.name)]) }
|
field.set(model, record[nameFromModel(field.name)]) }
|
||||||
|
|
||||||
return model }
|
return model }
|
||||||
@ -227,8 +275,11 @@ public class ORM {
|
|||||||
|
|
||||||
def record = [:]
|
def record = [:]
|
||||||
|
|
||||||
model.class.fields.each { field ->
|
getInstanceFields(model.class).each { field ->
|
||||||
record[nameFromModel(field.name)] = field.get(model) }
|
record[nameFromModel(field.name)] = field.get(model) }
|
||||||
|
|
||||||
return record }
|
return record }
|
||||||
|
|
||||||
|
static def getInstanceFields(Class clazz) {
|
||||||
|
return clazz.fields.findAll { !Modifier.isStatic(it.modifiers) } }
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user