mirror of
https://github.com/simon987/music-graph-api.git
synced 2025-04-19 18:06:42 +00:00
Chart builder setup, release details endpoint
This commit is contained in:
parent
606a95279e
commit
98aae25a72
6
pom.xml
6
pom.xml
@ -122,11 +122,5 @@
|
|||||||
<artifactId>sqlite-jdbc</artifactId>
|
<artifactId>sqlite-jdbc</artifactId>
|
||||||
<version>3.27.2.1</version>
|
<version>3.27.2.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.jetbrains</groupId>
|
|
||||||
<artifactId>annotations</artifactId>
|
|
||||||
<version>17.0.0</version>
|
|
||||||
<scope>compile</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
@ -2,9 +2,7 @@ package net.simon987.musicgraph;
|
|||||||
|
|
||||||
import net.simon987.musicgraph.io.*;
|
import net.simon987.musicgraph.io.*;
|
||||||
import net.simon987.musicgraph.logging.LogManager;
|
import net.simon987.musicgraph.logging.LogManager;
|
||||||
import net.simon987.musicgraph.webapi.ArtistController;
|
import net.simon987.musicgraph.webapi.*;
|
||||||
import net.simon987.musicgraph.webapi.CoverController;
|
|
||||||
import net.simon987.musicgraph.webapi.Index;
|
|
||||||
import org.glassfish.grizzly.http.server.HttpServer;
|
import org.glassfish.grizzly.http.server.HttpServer;
|
||||||
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
|
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
|
||||||
import org.glassfish.jersey.jackson.JacksonFeature;
|
import org.glassfish.jersey.jackson.JacksonFeature;
|
||||||
@ -18,7 +16,6 @@ public class Main {
|
|||||||
private final static Logger logger = LogManager.getLogger();
|
private final static Logger logger = LogManager.getLogger();
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
||||||
startHttpServer();
|
startHttpServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,12 +25,16 @@ public class Main {
|
|||||||
|
|
||||||
rc.registerInstances(new MusicDatabase());
|
rc.registerInstances(new MusicDatabase());
|
||||||
rc.registerInstances(new SQLiteCoverArtDatabase("covers.db"));
|
rc.registerInstances(new SQLiteCoverArtDatabase("covers.db"));
|
||||||
|
rc.registerInstances(new MagickChartBuilder("/dev/shm/im_chart/"));
|
||||||
|
|
||||||
rc.registerClasses(Index.class);
|
rc.registerClasses(Index.class);
|
||||||
rc.registerClasses(ArtistController.class);
|
rc.registerClasses(ArtistController.class);
|
||||||
rc.registerClasses(CoverController.class);
|
rc.registerClasses(CoverController.class);
|
||||||
|
rc.registerClasses(ChartController.class);
|
||||||
rc.registerClasses(JacksonFeature.class);
|
rc.registerClasses(JacksonFeature.class);
|
||||||
|
|
||||||
|
rc.registerClasses(MyExceptionMapper.class);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(
|
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(
|
||||||
new URI("http://localhost:3030/"),
|
new URI("http://localhost:3030/"),
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
package net.simon987.musicgraph.entities;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ReleaseDetails {
|
||||||
|
|
||||||
|
public String name;
|
||||||
|
public String mbid;
|
||||||
|
public String artist;
|
||||||
|
public long year;
|
||||||
|
public List<Tag> tags;
|
||||||
|
|
||||||
|
public ReleaseDetails() {
|
||||||
|
this.tags = new ArrayList<>();
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,8 @@
|
|||||||
package net.simon987.musicgraph.io;
|
package net.simon987.musicgraph.io;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public interface ICoverArtDatabase {
|
public interface ICoverArtDatabase {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -8,4 +11,6 @@ public interface ICoverArtDatabase {
|
|||||||
* @throws Exception if unexpected error
|
* @throws Exception if unexpected error
|
||||||
*/
|
*/
|
||||||
byte[] getCover(String mbid) throws Exception;
|
byte[] getCover(String mbid) throws Exception;
|
||||||
|
|
||||||
|
HashMap<String, byte[]> getCovers(List<String> mbids) throws Exception;
|
||||||
}
|
}
|
||||||
|
@ -155,4 +155,49 @@ public class MusicDatabase extends AbstractBinder {
|
|||||||
return relation;
|
return relation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ReleaseDetails getReleaseDetails(String mbid) {
|
||||||
|
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("mbid", mbid);
|
||||||
|
|
||||||
|
try (Session session = driver.session()) {
|
||||||
|
|
||||||
|
StatementResult result = query(session,
|
||||||
|
"MATCH (release:Release {id: $mbid})-[:CREDITED_FOR]-(a:Artist)\n" +
|
||||||
|
"OPTIONAL MATCH (release)-[r:IS_TAGGED]->(t:Tag)\n" +
|
||||||
|
"RETURN release {name:release.name, year:release.year," +
|
||||||
|
"tags:collect({weight:r.weight, name:t.name}), artist: a.name}\n" +
|
||||||
|
"LIMIT 1",
|
||||||
|
params);
|
||||||
|
|
||||||
|
ReleaseDetails details = new ReleaseDetails();
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
if (result.hasNext()) {
|
||||||
|
Map<String, Object> map = result.next().get("release").asMap();
|
||||||
|
|
||||||
|
details.mbid = mbid;
|
||||||
|
details.name = (String) map.get("name");
|
||||||
|
details.artist = (String) map.get("artist");
|
||||||
|
details.year = (long) map.get("year");
|
||||||
|
details.tags.addAll(
|
||||||
|
((List<Map>) map.get("tags"))
|
||||||
|
.stream()
|
||||||
|
.filter(x -> x.get("name") != null)
|
||||||
|
.map(x -> new Tag(
|
||||||
|
(String) x.get("name"),
|
||||||
|
(Long) x.get("weight")
|
||||||
|
))
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return details;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@ import net.simon987.musicgraph.logging.LogManager;
|
|||||||
import org.glassfish.jersey.internal.inject.AbstractBinder;
|
import org.glassfish.jersey.internal.inject.AbstractBinder;
|
||||||
|
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public class SQLiteCoverArtDatabase extends AbstractBinder implements ICoverArtDatabase {
|
public class SQLiteCoverArtDatabase extends AbstractBinder implements ICoverArtDatabase {
|
||||||
@ -47,6 +49,33 @@ public class SQLiteCoverArtDatabase extends AbstractBinder implements ICoverArtD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HashMap<String, byte[]> getCovers(List<String> mbids) throws SQLException {
|
||||||
|
|
||||||
|
HashMap<String, byte[]> 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 {
|
private void setupConn() throws SQLException {
|
||||||
try {
|
try {
|
||||||
if (connection == null || connection.isClosed()) {
|
if (connection == null || connection.isClosed()) {
|
||||||
|
@ -9,7 +9,7 @@ import java.util.logging.LogRecord;
|
|||||||
|
|
||||||
public class MusicGraphFormatter extends Formatter {
|
public class MusicGraphFormatter extends Formatter {
|
||||||
|
|
||||||
private static final DateFormat dateFormat = new SimpleDateFormat("MM-dd HH:mm:ss");
|
private static final DateFormat dateFormat = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String format(LogRecord record) {
|
public String format(LogRecord record) {
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
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<String> 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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package net.simon987.musicgraph.webapi;
|
||||||
|
|
||||||
|
import net.simon987.musicgraph.entities.ReleaseDetails;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ChartOptions {
|
||||||
|
|
||||||
|
public HashMap<String, byte[]> covers;
|
||||||
|
public List<ReleaseDetails> details;
|
||||||
|
public int rows;
|
||||||
|
public int cols;
|
||||||
|
public String backgroundColor;
|
||||||
|
public String textColor = "white";
|
||||||
|
public String font = "Hack-Regular";
|
||||||
|
public String borderColor = "white";
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package net.simon987.musicgraph.webapi;
|
||||||
|
|
||||||
|
public interface IChartBuilder {
|
||||||
|
|
||||||
|
byte[] makeChart(ChartOptions options) throws Exception;
|
||||||
|
}
|
@ -0,0 +1,306 @@
|
|||||||
|
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<String> 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<String> releases)
|
||||||
|
throws InterruptedException, IOException {
|
||||||
|
|
||||||
|
ProcessBuilder processBuilder = new ProcessBuilder();
|
||||||
|
|
||||||
|
List<String> 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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package net.simon987.musicgraph.webapi;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.ext.ExceptionMapper;
|
||||||
|
import javax.ws.rs.ext.Provider;
|
||||||
|
|
||||||
|
import org.glassfish.grizzly.utils.Exceptions;
|
||||||
|
|
||||||
|
@Provider
|
||||||
|
public class MyExceptionMapper implements ExceptionMapper<Throwable> {
|
||||||
|
@Override
|
||||||
|
public Response toResponse(Throwable ex) {
|
||||||
|
|
||||||
|
//TODO: logger
|
||||||
|
|
||||||
|
return Response.status(500)
|
||||||
|
.entity(Exceptions.getStackTraceAsString(ex))
|
||||||
|
.type("text/plain")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user