From ded142c2011a0e835f259e0a314e24da05848d70 Mon Sep 17 00:00:00 2001 From: simon987 Date: Thu, 6 Aug 2020 20:15:35 -0400 Subject: [PATCH] Docker support, switch to pgsql, refactor, update neo4j --- Dockerfile | 3 + README.md | 6 +- pom.xml | 15 +- .../java/net/simon987/musicgraph/Main.java | 9 +- .../AutocompleteLine.java | 2 +- .../musicgraph/entities/Relation.java | 2 +- .../musicgraph/io/ICoverArtDatabase.java | 2 - .../simon987/musicgraph/io/MusicDatabase.java | 38 +-- .../io/PostgreSQLCoverArtDatabase.java | 66 ++++ .../musicgraph/io/SQLiteCoverArtDatabase.java | 94 ------ .../musicgraph/logging/LogManager.java | 2 +- .../musicgraph/webapi/ArtistController.java | 2 +- .../musicgraph/webapi/AutoCompleteData.java | 2 + .../webapi/AutocompleteController.java | 2 +- .../musicgraph/webapi/ChartController.java | 48 --- .../musicgraph/webapi/ChartOptions.java | 18 -- .../musicgraph/webapi/CoverController.java | 2 +- .../musicgraph/webapi/IChartBuilder.java | 6 - .../net/simon987/musicgraph/webapi/Index.java | 2 +- .../musicgraph/webapi/LabelController.java | 2 +- .../musicgraph/webapi/MagickChartBuilder.java | 306 ------------------ .../musicgraph/webapi/TagController.java | 2 +- 22 files changed, 116 insertions(+), 515 deletions(-) rename src/main/java/net/simon987/musicgraph/{webapi => entities}/AutocompleteLine.java (78%) create mode 100644 src/main/java/net/simon987/musicgraph/io/PostgreSQLCoverArtDatabase.java delete mode 100644 src/main/java/net/simon987/musicgraph/io/SQLiteCoverArtDatabase.java delete mode 100644 src/main/java/net/simon987/musicgraph/webapi/ChartController.java delete mode 100644 src/main/java/net/simon987/musicgraph/webapi/ChartOptions.java delete mode 100644 src/main/java/net/simon987/musicgraph/webapi/IChartBuilder.java delete mode 100644 src/main/java/net/simon987/musicgraph/webapi/MagickChartBuilder.java diff --git a/Dockerfile b/Dockerfile index 96aa6c8..476fbf9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,6 +7,9 @@ WORKDIR /app/target FROM openjdk:11-jre-slim ENV NEO4J_ADDR "localhost:7687" +ENV PG_URL "localhost:5432/musicbrainz_db" +ENV PG_USERNAME "musicbrainz" +ENV PG_PASSWORD "musicbrainz" COPY --from=build /app/target/music-graph-0.1-jar-with-dependencies.jar /app/ WORKDIR /app/ diff --git a/README.md b/README.md index afd0ef3..14b3b29 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,9 @@ # music-graph [![CodeFactor](https://www.codefactor.io/repository/github/simon987/music-graph-api/badge)](https://www.codefactor.io/repository/github/simon987/music-graph-api) -[![Build Status](https://ci.simon987.net/buildStatus/icon?job=music_graph_api)](https://ci.simon987.net/job/music_graph_api/) API for music-graph. -This is a read-only wrapper for the data in the Neo4j database, it also serves the cover art, which is stored in a SQLite database. - -This project is WIP, as such, it does not implement any kind of rate-limitting or configuration for ports and is mostly undocumented. +This is a read-only wrapper for the data in the Neo4j database, +it also serves the album cover art, which is stored in a PostgreSQL database. diff --git a/pom.xml b/pom.xml index 8d2fb25..0b21a7c 100644 --- a/pom.xml +++ b/pom.xml @@ -59,7 +59,7 @@ org.neo4j.driver neo4j-java-driver - 1.7.3 + 4.0.2 @@ -89,12 +89,14 @@ jaxb-api 2.4.0-b180830.0359 + com.sun.xml.bind jaxb-osgi 2.4.0-b180830.0438 + com.sun.xml.bind @@ -114,14 +116,15 @@ com.google.guava guava - 27.1-jre + 29.0-jre - + + - org.xerial - sqlite-jdbc - 3.27.2.1 + org.postgresql + postgresql + 42.2.14 \ No newline at end of file diff --git a/src/main/java/net/simon987/musicgraph/Main.java b/src/main/java/net/simon987/musicgraph/Main.java index 373a1ae..b684030 100644 --- a/src/main/java/net/simon987/musicgraph/Main.java +++ b/src/main/java/net/simon987/musicgraph/Main.java @@ -23,9 +23,13 @@ public class Main { ResourceConfig rc = new ResourceConfig(); + String pgUrl = System.getenv("PG_URL"); + String pgUsername = System.getenv("PG_USERNAME"); + String pgPassword = System.getenv("PG_PASSWORD"); + rc.registerInstances(new MusicDatabase()); - rc.registerInstances(new SQLiteCoverArtDatabase("covers.db")); - rc.registerInstances(new MagickChartBuilder("/dev/shm/im_chart/")); + rc.registerInstances(new PostgreSQLCoverArtDatabase(pgUrl, pgUsername, pgPassword)); +// rc.registerInstances(new MagickChartBuilder("/dev/shm/im_chart/")); rc.registerClasses(Index.class); rc.registerClasses(ArtistController.class); @@ -33,7 +37,6 @@ public class Main { rc.registerClasses(CoverController.class); rc.registerClasses(TagController.class); rc.registerClasses(ReleaseController.class); - rc.registerClasses(ChartController.class); rc.registerClasses(JacksonFeature.class); rc.registerClasses(MyExceptionMapper.class); diff --git a/src/main/java/net/simon987/musicgraph/webapi/AutocompleteLine.java b/src/main/java/net/simon987/musicgraph/entities/AutocompleteLine.java similarity index 78% rename from src/main/java/net/simon987/musicgraph/webapi/AutocompleteLine.java rename to src/main/java/net/simon987/musicgraph/entities/AutocompleteLine.java index 3c7a0ce..b5ac0e4 100644 --- a/src/main/java/net/simon987/musicgraph/webapi/AutocompleteLine.java +++ b/src/main/java/net/simon987/musicgraph/entities/AutocompleteLine.java @@ -1,4 +1,4 @@ -package net.simon987.musicgraph.webapi; +package net.simon987.musicgraph.entities; public class AutocompleteLine { public String name; diff --git a/src/main/java/net/simon987/musicgraph/entities/Relation.java b/src/main/java/net/simon987/musicgraph/entities/Relation.java index dadee2e..d59b316 100644 --- a/src/main/java/net/simon987/musicgraph/entities/Relation.java +++ b/src/main/java/net/simon987/musicgraph/entities/Relation.java @@ -2,7 +2,7 @@ package net.simon987.musicgraph.entities; public class Relation { - public float weight; + public double weight; public long source; public long target; } diff --git a/src/main/java/net/simon987/musicgraph/io/ICoverArtDatabase.java b/src/main/java/net/simon987/musicgraph/io/ICoverArtDatabase.java index 8fda6d4..a5ad8bc 100644 --- a/src/main/java/net/simon987/musicgraph/io/ICoverArtDatabase.java +++ b/src/main/java/net/simon987/musicgraph/io/ICoverArtDatabase.java @@ -11,6 +11,4 @@ public interface ICoverArtDatabase { * @throws Exception if unexpected error */ byte[] getCover(String mbid) throws Exception; - - HashMap getCovers(List mbids) throws Exception; } diff --git a/src/main/java/net/simon987/musicgraph/io/MusicDatabase.java b/src/main/java/net/simon987/musicgraph/io/MusicDatabase.java index 39020ca..04607a7 100644 --- a/src/main/java/net/simon987/musicgraph/io/MusicDatabase.java +++ b/src/main/java/net/simon987/musicgraph/io/MusicDatabase.java @@ -5,11 +5,11 @@ import com.google.common.collect.ImmutableList; import net.simon987.musicgraph.entities.*; import net.simon987.musicgraph.logging.LogManager; import net.simon987.musicgraph.webapi.AutoCompleteData; -import net.simon987.musicgraph.webapi.AutocompleteLine; +import net.simon987.musicgraph.entities.AutocompleteLine; import org.glassfish.jersey.internal.inject.AbstractBinder; -import org.neo4j.driver.v1.*; -import org.neo4j.driver.v1.types.Node; -import org.neo4j.driver.v1.types.Relationship; +import org.neo4j.driver.*; +import org.neo4j.driver.types.Node; +import org.neo4j.driver.types.Relationship; import javax.inject.Singleton; import javax.validation.constraints.Max; @@ -37,10 +37,10 @@ public class MusicDatabase extends AbstractBinder { AuthTokens.basic("neo4j", "neo4j")); } - private StatementResult query(Session session, String query, Map args) { + private Result query(Session session, String query, Map args) { long start = System.nanoTime(); - StatementResult result = session.run(query, args); + Result result = session.run(query, args); long end = System.nanoTime(); long took = (end - start) / 1000000; @@ -57,7 +57,7 @@ public class MusicDatabase extends AbstractBinder { Map params = new HashMap<>(); params.put("mbid", mbid); - StatementResult result = query(session, + Result result = query(session, "MATCH (a:Artist)-[r:IS_MEMBER_OF]-(b:Artist) " + "WHERE a.id = $mbid " + "RETURN a as artist, a {rels: collect(DISTINCT r), nodes: collect(DISTINCT b)} as rank1 " + @@ -82,7 +82,7 @@ public class MusicDatabase extends AbstractBinder { try (Session session = driver.session()) { - StatementResult result = query(session, + Result result = query(session, "MATCH (a:Artist {id: $mbid})" + "WITH a OPTIONAL MATCH (a)-[:CREDITED_FOR]->(r:Release) " + "WITH collect({id: ID(r), mbid:r.id, name:r.name, year:r.year, labels:labels(r)}) as releases, a " + @@ -163,7 +163,7 @@ public class MusicDatabase extends AbstractBinder { Map params = new HashMap<>(); params.put("mbid", mbid); - StatementResult result = query(session, + Result result = query(session, "MATCH (a:Artist) " + "WHERE a.id = $mbid " + "WITH a OPTIONAL MATCH (a)-[r:IS_RELATED_TO]-(b) " + @@ -191,7 +191,7 @@ public class MusicDatabase extends AbstractBinder { params.put("idFrom", idFrom); params.put("idTo", idTo); - StatementResult result = query(session, + Result result = query(session, "MATCH p = allShortestPaths(" + "(:Artist {id: $idFrom})-[r:IS_RELATED_TO*..10]-(:Artist {id:$idTo})) " + "WHERE ALL (rel in r WHERE rel.weight > 0.10) " + @@ -220,7 +220,7 @@ public class MusicDatabase extends AbstractBinder { Map params = new HashMap<>(); params.put("tag_id", id); - StatementResult result = query(session, + Result result = query(session, "MATCH (t:Tag)-[r:IS_TAGGED]-(a:Artist) " + "WHERE ID(t) = $tag_id " + // Is rels really necessary? @@ -250,7 +250,7 @@ public class MusicDatabase extends AbstractBinder { Map params = new HashMap<>(); params.put("label_id", id); - StatementResult result = query(session, + Result result = query(session, "MATCH (l:Label)-[]-(:Release)-[:CREDITED_FOR]-(a:Artist) " + "WHERE ID(l) = $label_id " + "RETURN l, {nodes: collect(DISTINCT a)} as rank1 " + @@ -279,7 +279,7 @@ public class MusicDatabase extends AbstractBinder { Map params = new HashMap<>(); params.put("label_id", id); - StatementResult result = query(session, + Result result = query(session, "MATCH (l:Label)-[r:IS_RELATED_TO]-(l2:Label) " + "WHERE ID(t) = $tag_id " + "RETURN {rels: collect(DISTINCT r), nodes: collect(DISTINCT t2)} as rank1 " + @@ -307,7 +307,7 @@ public class MusicDatabase extends AbstractBinder { Map params = new HashMap<>(); params.put("tag_id", id); - StatementResult result = query(session, + Result result = query(session, "MATCH (t:Tag)-[r:IS_RELATED_TO]-(t2:Tag) " + "WHERE ID(t) = $tag_id " + "RETURN {rels: collect(DISTINCT r), nodes: collect(DISTINCT t2)} as rank1 " + @@ -329,7 +329,7 @@ public class MusicDatabase extends AbstractBinder { } - private void parseRelatedResult(StatementResult result, SearchResult out) { + private void parseRelatedResult(Result result, SearchResult out) { long start = System.nanoTime(); if (result.hasNext()) { Record row = result.next(); @@ -416,7 +416,7 @@ public class MusicDatabase extends AbstractBinder { relation.source = rel.startNodeId(); relation.target = rel.endNodeId(); if (rel.containsKey("weight")) { - relation.weight = rel.get("weight").asFloat(); + relation.weight = rel.get("weight").asDouble(); } return relation; @@ -429,7 +429,7 @@ public class MusicDatabase extends AbstractBinder { try (Session session = driver.session()) { - StatementResult result = query(session, + Result result = query(session, "MATCH (release:Release {id: $mbid})-[:CREDITED_FOR]-(a:Artist) " + "OPTIONAL MATCH (release)-[r:IS_TAGGED]->(t:Tag) " + "RETURN release {name:release.name, year:release.year," + @@ -478,7 +478,7 @@ public class MusicDatabase extends AbstractBinder { AutoCompleteData data = new AutoCompleteData(); - StatementResult result = query(session, + Result result = query(session, "MATCH (a:Artist) " + "WHERE a.sortname STARTS WITH $prefix " + "RETURN a ORDER BY a.listeners DESC " + @@ -499,7 +499,7 @@ public class MusicDatabase extends AbstractBinder { } params.put("prefix", prefix.toLowerCase()); - StatementResult tagResult = query(session, + Result tagResult = query(session, "MATCH (t:Tag)-[:IS_TAGGED]-(:Artist) " + "WHERE t.name STARTS WITH $prefix " + "RETURN DISTINCT t ORDER BY t.occurrences DESC " + diff --git a/src/main/java/net/simon987/musicgraph/io/PostgreSQLCoverArtDatabase.java b/src/main/java/net/simon987/musicgraph/io/PostgreSQLCoverArtDatabase.java new file mode 100644 index 0000000..078d8a7 --- /dev/null +++ b/src/main/java/net/simon987/musicgraph/io/PostgreSQLCoverArtDatabase.java @@ -0,0 +1,66 @@ +package net.simon987.musicgraph.io; + +import net.simon987.musicgraph.logging.LogManager; +import org.glassfish.jersey.internal.inject.AbstractBinder; + +import java.sql.*; +import java.util.logging.Logger; + +public class PostgreSQLCoverArtDatabase extends AbstractBinder implements ICoverArtDatabase { + + private String url; + private String username; + private String password; + + private Connection connection; + + private static final Logger logger = LogManager.getLogger(); + + public PostgreSQLCoverArtDatabase(String url, String username, String password) { + this.url = url; + this.username = username; + this.password = password; + } + + @Override + protected void configure() { + bind(this).to(ICoverArtDatabase.class); + } + + public byte[] getCover(String mbid) throws SQLException { + + try { + setupConn(); + + PreparedStatement stmt = connection.prepareStatement("SELECT tn FROM mg.covers WHERE mbid::text=?"); + stmt.setString(1, mbid); + ResultSet rs = stmt.executeQuery(); + + if (rs.next()) { + byte[] bytes = rs.getBytes(1); + rs.close(); + return bytes; + } else { + rs.close(); + return null; + } + + } catch (SQLException e) { + logger.severe(String.format("Exception during cover art query mbid=%s ex=%s", + mbid, e.getMessage())); + throw e; + } + } + + private void setupConn() throws SQLException { + try { + if (connection == null || connection.isClosed()) { + logger.fine("Connecting to cover art DB"); + connection = DriverManager.getConnection("jdbc:postgresql://" + url, username, password); + } + } catch (SQLException e) { + logger.fine("Connecting to cover art DB"); + connection = DriverManager.getConnection("jdbc:postgresql://" + url, username, password); + } + } +} diff --git a/src/main/java/net/simon987/musicgraph/io/SQLiteCoverArtDatabase.java b/src/main/java/net/simon987/musicgraph/io/SQLiteCoverArtDatabase.java deleted file mode 100644 index 882d42e..0000000 --- a/src/main/java/net/simon987/musicgraph/io/SQLiteCoverArtDatabase.java +++ /dev/null @@ -1,94 +0,0 @@ -package net.simon987.musicgraph.io; - -import net.simon987.musicgraph.logging.LogManager; -import org.glassfish.jersey.internal.inject.AbstractBinder; - -import java.sql.*; -import java.util.HashMap; -import java.util.List; -import java.util.Properties; -import java.util.logging.Logger; - -public class SQLiteCoverArtDatabase extends AbstractBinder implements ICoverArtDatabase { - - private String dbFile; - private Connection connection; - - private static Logger logger = LogManager.getLogger(); - - public SQLiteCoverArtDatabase(String dbFile) { - this.dbFile = dbFile; - } - - @Override - protected void configure() { - bind(this).to(ICoverArtDatabase.class); - } - - public byte[] getCover(String mbid) throws SQLException { - - try { - setupConn(); - - PreparedStatement stmt = connection.prepareStatement("SELECT cover FROM covers WHERE id=?"); - stmt.setString(1, mbid); - ResultSet rs = stmt.executeQuery(); - - if (rs.next()) { - byte[] bytes = rs.getBytes(1); - rs.close(); - return bytes; - } else { - rs.close(); - return null; - } - - } catch (SQLException e) { - logger.severe(String.format("Exception during cover art query mbid=%s ex=%s", - mbid, e.getMessage())); - throw e; - } - } - - public HashMap getCovers(List mbids) throws SQLException { - - HashMap covers = new HashMap<>(mbids.size()); - - try { - setupConn(); - - PreparedStatement stmt = connection.prepareStatement( - //TODO: sqlite injection - String.format("SELECT id, cover FROM covers WHERE id IN ('%s')", - String.join("','", mbids)) - ); - ResultSet rs = stmt.executeQuery(); - - while (rs.next()) { - covers.put(rs.getString(1), rs.getBytes(2)); - } - - return covers; - - } catch (SQLException e) { - logger.severe(String.format("Exception during cover art query mbid=%s ex=%s", - mbids, e.getMessage())); - throw e; - } - } - - private void setupConn() throws SQLException { - Properties config = new Properties(); - config.setProperty("open_mode", "1"); - - try { - if (connection == null || connection.isClosed()) { - logger.fine("Connecting to SQLite cover art DB"); - connection = DriverManager.getConnection("jdbc:sqlite:" + dbFile, config); - } - } catch (SQLException e) { - logger.fine("Connecting to SQLite cover art DB"); - connection = DriverManager.getConnection("jdbc:sqlite:" + dbFile, config); - } - } -} diff --git a/src/main/java/net/simon987/musicgraph/logging/LogManager.java b/src/main/java/net/simon987/musicgraph/logging/LogManager.java index e4328cf..cc3e168 100644 --- a/src/main/java/net/simon987/musicgraph/logging/LogManager.java +++ b/src/main/java/net/simon987/musicgraph/logging/LogManager.java @@ -6,7 +6,7 @@ import java.util.logging.Logger; public class LogManager { - private static Logger logger; + private static final Logger logger; static { logger = Logger.getLogger("music-graph"); diff --git a/src/main/java/net/simon987/musicgraph/webapi/ArtistController.java b/src/main/java/net/simon987/musicgraph/webapi/ArtistController.java index 68c2a13..5f869e2 100644 --- a/src/main/java/net/simon987/musicgraph/webapi/ArtistController.java +++ b/src/main/java/net/simon987/musicgraph/webapi/ArtistController.java @@ -14,7 +14,7 @@ import java.util.logging.Logger; @Path("/artist") public class ArtistController { - private static Logger logger = LogManager.getLogger(); + private static final Logger logger = LogManager.getLogger(); @Inject private MusicDatabase db; diff --git a/src/main/java/net/simon987/musicgraph/webapi/AutoCompleteData.java b/src/main/java/net/simon987/musicgraph/webapi/AutoCompleteData.java index 421d81b..d82796a 100644 --- a/src/main/java/net/simon987/musicgraph/webapi/AutoCompleteData.java +++ b/src/main/java/net/simon987/musicgraph/webapi/AutoCompleteData.java @@ -1,5 +1,7 @@ package net.simon987.musicgraph.webapi; +import net.simon987.musicgraph.entities.AutocompleteLine; + import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/net/simon987/musicgraph/webapi/AutocompleteController.java b/src/main/java/net/simon987/musicgraph/webapi/AutocompleteController.java index de5835a..a81094a 100644 --- a/src/main/java/net/simon987/musicgraph/webapi/AutocompleteController.java +++ b/src/main/java/net/simon987/musicgraph/webapi/AutocompleteController.java @@ -14,7 +14,7 @@ import java.util.logging.Logger; @Path("/") public class AutocompleteController { - private static Logger logger = LogManager.getLogger(); + private static final Logger logger = LogManager.getLogger(); @Inject private MusicDatabase db; diff --git a/src/main/java/net/simon987/musicgraph/webapi/ChartController.java b/src/main/java/net/simon987/musicgraph/webapi/ChartController.java deleted file mode 100644 index af07229..0000000 --- a/src/main/java/net/simon987/musicgraph/webapi/ChartController.java +++ /dev/null @@ -1,48 +0,0 @@ -package net.simon987.musicgraph.webapi; - -import net.simon987.musicgraph.io.ICoverArtDatabase; -import net.simon987.musicgraph.io.MusicDatabase; - -import javax.inject.Inject; -import javax.ws.rs.GET; -import javax.ws.rs.NotFoundException; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -@Path("/chart") -public class ChartController { - - @Inject - IChartBuilder chartBuilder; - - @Inject - ICoverArtDatabase coverArtDatabase; - - @Inject - private MusicDatabase musicDatabase; - - @GET - @Produces("image/png") - public byte[] makeChart() throws Exception { - var opt = new ChartOptions(); - - throw new NotFoundException(); - -// List releases = new ArrayList<>(); -// -// opt.cols = 4; -// opt.rows = 5; -// opt.covers = coverArtDatabase.getCovers(releases); -// opt.backgroundColor = "blue"; -// opt.details = releases -// .parallelStream() // Thread-safe? -// .map(r -> musicDatabase.getReleaseDetails(r)) -// .collect(Collectors.toList()); -// -// return chartBuilder.makeChart(opt); - } -} diff --git a/src/main/java/net/simon987/musicgraph/webapi/ChartOptions.java b/src/main/java/net/simon987/musicgraph/webapi/ChartOptions.java deleted file mode 100644 index 0a5d500..0000000 --- a/src/main/java/net/simon987/musicgraph/webapi/ChartOptions.java +++ /dev/null @@ -1,18 +0,0 @@ -package net.simon987.musicgraph.webapi; - -import net.simon987.musicgraph.entities.ReleaseDetails; - -import java.util.HashMap; -import java.util.List; - -public class ChartOptions { - - public HashMap covers; - public List details; - public int rows; - public int cols; - public String backgroundColor; - public String textColor = "white"; - public String font = "Hack-Regular"; - public String borderColor = "white"; -} diff --git a/src/main/java/net/simon987/musicgraph/webapi/CoverController.java b/src/main/java/net/simon987/musicgraph/webapi/CoverController.java index 511840b..3a9b6c0 100644 --- a/src/main/java/net/simon987/musicgraph/webapi/CoverController.java +++ b/src/main/java/net/simon987/musicgraph/webapi/CoverController.java @@ -10,7 +10,7 @@ import java.util.logging.Logger; @Path("/cover/{mbid}") public class CoverController { - private static Logger logger = LogManager.getLogger(); + private static final Logger logger = LogManager.getLogger(); @Inject private ICoverArtDatabase db; diff --git a/src/main/java/net/simon987/musicgraph/webapi/IChartBuilder.java b/src/main/java/net/simon987/musicgraph/webapi/IChartBuilder.java deleted file mode 100644 index eb6b4f8..0000000 --- a/src/main/java/net/simon987/musicgraph/webapi/IChartBuilder.java +++ /dev/null @@ -1,6 +0,0 @@ -package net.simon987.musicgraph.webapi; - -public interface IChartBuilder { - - byte[] makeChart(ChartOptions options) throws Exception; -} diff --git a/src/main/java/net/simon987/musicgraph/webapi/Index.java b/src/main/java/net/simon987/musicgraph/webapi/Index.java index 8cc27c7..a9503c4 100644 --- a/src/main/java/net/simon987/musicgraph/webapi/Index.java +++ b/src/main/java/net/simon987/musicgraph/webapi/Index.java @@ -12,7 +12,7 @@ public class Index { private static final ApiInfo INFO = new ApiInfo( "music-graph-api", - "1.1", + "2.0", "/application.wadl", "https://github.com/simon987/music-graph-api" ); diff --git a/src/main/java/net/simon987/musicgraph/webapi/LabelController.java b/src/main/java/net/simon987/musicgraph/webapi/LabelController.java index 5048626..adcf505 100644 --- a/src/main/java/net/simon987/musicgraph/webapi/LabelController.java +++ b/src/main/java/net/simon987/musicgraph/webapi/LabelController.java @@ -16,7 +16,7 @@ import java.util.logging.Logger; @Path("/label") public class LabelController { - private static Logger logger = LogManager.getLogger(); + private static final Logger logger = LogManager.getLogger(); @Inject private MusicDatabase db; diff --git a/src/main/java/net/simon987/musicgraph/webapi/MagickChartBuilder.java b/src/main/java/net/simon987/musicgraph/webapi/MagickChartBuilder.java deleted file mode 100644 index 259b27d..0000000 --- a/src/main/java/net/simon987/musicgraph/webapi/MagickChartBuilder.java +++ /dev/null @@ -1,306 +0,0 @@ -package net.simon987.musicgraph.webapi; - -import net.simon987.musicgraph.entities.ReleaseDetails; -import org.glassfish.jersey.internal.inject.AbstractBinder; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.stream.Collectors; - -public class MagickChartBuilder extends AbstractBinder implements IChartBuilder { - - private String workspacePath; - private File workspace; - - private static final String[] letters = new String[]{ - "A", "B", "C", "D", "E", "F", "G", "H", "I" - }; - - @Override - protected void configure() { - bind(this).to(IChartBuilder.class); - } - - public MagickChartBuilder(String workspacePath) { - this.workspacePath = workspacePath; - this.workspace = new File(workspacePath); - } - - @Override - public byte[] makeChart(ChartOptions options) throws Exception { - - ProcessBuilder processBuilder = new ProcessBuilder(); - List releases = options.details.stream().map(o -> o.mbid).collect(Collectors.toList()); - - File chartWorkspace = makeChartWorkspace(); - - saveCoversToDisk(options, chartWorkspace); - makeGrid(options, chartWorkspace, releases); - File header = makeHeader(options, chartWorkspace); - File side = makeSide(options, chartWorkspace); - - // Side + grid - processBuilder - .directory(chartWorkspace) - .command( - "convert", - "+append", - side.toString(), - "grid.png", - "grid_with_side.png" - ) - .start() - .waitFor(); - - // Side + grid - processBuilder - .directory(chartWorkspace) - .command( - "convert", - "-append", - "-gravity", "east", - header.toString(), - "grid_with_side.png", - "final_grid.png" - ) - .start() - .waitFor(); - - makeDescription(options, chartWorkspace); - - // + description - processBuilder - .directory(chartWorkspace) - .command( - "convert", - "+append", - "-gravity", "south", - "final_grid.png", - "description.png", - "final.png" - ) - .start() - .waitFor(); - - - FileInputStream file = new FileInputStream(new File(chartWorkspace, "final.png")); - byte[] chart = file.readAllBytes(); - file.close(); - - cleanup(chartWorkspace); - - return chart; - } - - private void cleanup(File chartWorkspace) { - String[] entries = chartWorkspace.list(); - if (entries != null) { - for (String s : entries) { - File currentFile = new File(chartWorkspace, s); - currentFile.delete(); - } - } - chartWorkspace.delete(); - } - - private File makeChartWorkspace() throws IOException { - String chartId = UUID.randomUUID().toString(); - - File chartWorkspace = new File(workspacePath, chartId); - if (!chartWorkspace.mkdir()) { - throw new IOException(String.format("Could not create chart workspace: %s", chartWorkspace.getAbsolutePath())); - } - return chartWorkspace; - } - - private void saveCoversToDisk(ChartOptions options, File chartWorkspace) throws IOException { - for (ReleaseDetails releaseDetails : options.details) { - if (options.covers.containsKey(releaseDetails.mbid)) { - FileOutputStream file = new FileOutputStream(new File(chartWorkspace, releaseDetails.mbid)); - file.write(options.covers.get(releaseDetails.mbid)); - file.close(); - } - } - } - - private void makeDescription(ChartOptions options, File chartWorkspace) - throws InterruptedException, IOException { - - ProcessBuilder processBuilder = new ProcessBuilder(); - - for (int i = 0; i < options.rows; i++) { - - String desc = formatDescription(options, i); - - processBuilder - .directory(chartWorkspace) - .command( - "convert", - "-size", "620x316", - "xc:" + options.backgroundColor, - "-fill", options.textColor, - "-pointsize", "20", - "-font", options.font, - "-annotate", "+0+40", desc, - String.format("d_%d.png", i) - ) - .start() - .waitFor(); - processBuilder - .directory(chartWorkspace) - .command( - "convert", - "-append", - "d_*.png", - "description.png" - ) - .start() - .waitFor(); - } - } - - private static String formatDescription(ChartOptions options, int row) { - - StringBuilder sb = new StringBuilder(); - - for (int col = 0; col < options.cols; col++) { - - int releaseIndex = row * options.cols + col; - if (releaseIndex >= options.details.size()) { - break; - } - - ReleaseDetails release = options.details.get(releaseIndex); - - String year = release.year != 0 ? String.format("(%s)", release.year) : ""; - - if (release.name.length() + release.artist.length() < 40) { - sb.append(String.format("%s%d %s - %s %s\n", - letters[row], col+1, - release.name, release.artist, year)); - } else { - sb.append(String.format("%s%d %s -\n\t\t\t %s %s\n", - letters[row], col+1, - release.name, release.artist, year)); - } - } - return sb.toString(); - } - - private void makeGrid(ChartOptions options, File chartWorkspace, List releases) - throws InterruptedException, IOException { - - ProcessBuilder processBuilder = new ProcessBuilder(); - - List cmd = new ArrayList<>(); - cmd.add("montage"); - cmd.addAll(releases); - cmd.add("-tile"); - cmd.add(String.format("%dx%d", options.cols, options.rows)); - cmd.add("-background"); - cmd.add(options.backgroundColor); - cmd.add("-border"); - cmd.add("1"); - cmd.add("-bordercolor"); - cmd.add(options.borderColor); - cmd.add("-geometry"); - cmd.add("256x256+30+30"); - cmd.add("grid.png"); - - processBuilder - .directory(chartWorkspace) - .command(cmd) - .start() - .waitFor(); - } - - private File makeSide(ChartOptions options, File chartWorkSpace) - throws InterruptedException, IOException { - - File sideFile = new File(chartWorkSpace, "side.png"); - - ProcessBuilder processBuilder = new ProcessBuilder(); - - for (int i = 0; i < options.rows; i++) { - processBuilder - .directory(chartWorkSpace) - .command( - "convert", - "-background", options.backgroundColor, - "-gravity", "east", - "-size", "60x256", - "-fill", options.textColor, - "-pointsize", "64", - "-font", options.font, - "-strip", - String.format("label:%s", letters[i]), - String.format("s_%d.png", i + 1) - ) - .start() - .waitFor(); - } - - processBuilder - .directory(chartWorkSpace) - .command( - "montage", - "-tile", "1x", - "-background", options.backgroundColor, - "-geometry", "+0+31", - "s_*.png", - sideFile.toString() - ) - .start() - .waitFor(); - - return sideFile; - } - - private File makeHeader(ChartOptions options, File chartWorkspace) - throws InterruptedException, IOException { - - File headerFile = new File(chartWorkspace, "header.png"); - ProcessBuilder processBuilder = new ProcessBuilder(); - - // Header - for (int i = 1; i <= options.cols; i++) { - processBuilder - .directory(chartWorkspace) - .command( - "convert", - "-background", options.backgroundColor, - "-gravity", "south", - "-size", "256x80", - "-fill", options.textColor, - "-chop", "0x12", - "-pointsize", "64", - "-font", options.font, - "-strip", - String.format("label:%d", i), - String.format("h_%d.png", i) - ) - .start() - .waitFor(); - } - - processBuilder - .directory(chartWorkspace) - .command( - "montage", - "-tile", "x1", - "h_*.png", - "-background", options.backgroundColor, - "-geometry", "+31+0", - headerFile.toString() - ) - .start() - .waitFor(); - - return headerFile; - } -} diff --git a/src/main/java/net/simon987/musicgraph/webapi/TagController.java b/src/main/java/net/simon987/musicgraph/webapi/TagController.java index f098cb3..be244d1 100644 --- a/src/main/java/net/simon987/musicgraph/webapi/TagController.java +++ b/src/main/java/net/simon987/musicgraph/webapi/TagController.java @@ -16,7 +16,7 @@ import java.util.logging.Logger; @Path("/tag") public class TagController { - private static Logger logger = LogManager.getLogger(); + private static final Logger logger = LogManager.getLogger(); @Inject private MusicDatabase db;