Added logging on ORM methods. Implemented library file scanning.

This commit is contained in:
Jonathan Bernard 2016-02-09 15:28:53 -06:00
parent b605b56221
commit c84092b37e
2 changed files with 110 additions and 45 deletions

View File

@ -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)

View File

@ -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) } }
} }