X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fnet%2Fpterodactylus%2Fsonitus%2Fdata%2Fsink%2FIcecast2Sink.java;h=f98fba54c7d087f42b7fbe9bbd137dcbbfc68d02;hb=633a841142f978235ed9f745b6ba16c278963e62;hp=3d414134063aea730afeed95fa25cb3150527114;hpb=ee261ec959322c6d1969aff67319559ba1480859;p=sonitus.git diff --git a/src/main/java/net/pterodactylus/sonitus/data/sink/Icecast2Sink.java b/src/main/java/net/pterodactylus/sonitus/data/sink/Icecast2Sink.java index 3d41413..f98fba5 100644 --- a/src/main/java/net/pterodactylus/sonitus/data/sink/Icecast2Sink.java +++ b/src/main/java/net/pterodactylus/sonitus/data/sink/Icecast2Sink.java @@ -17,40 +17,32 @@ package net.pterodactylus.sonitus.data.sink; -import static com.google.common.base.Preconditions.*; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.Socket; import java.net.URLEncoder; -import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -import net.pterodactylus.sonitus.data.ConnectException; -import net.pterodactylus.sonitus.data.Connection; -import net.pterodactylus.sonitus.data.Format; +import net.pterodactylus.sonitus.data.AbstractFilter; +import net.pterodactylus.sonitus.data.Controller; import net.pterodactylus.sonitus.data.Metadata; -import net.pterodactylus.sonitus.data.Sink; -import net.pterodactylus.sonitus.data.Source; import net.pterodactylus.sonitus.io.InputStreamDrainer; -import com.google.common.base.Function; -import com.google.common.base.Joiner; -import com.google.common.base.Optional; -import com.google.common.collect.FluentIterable; import com.google.common.io.BaseEncoding; import com.google.common.io.Closeables; /** - * {@link Sink} implementation that delivers all incoming data to an Icecast2 - * server. + * {@link net.pterodactylus.sonitus.data.Sink} implementation that delivers all + * incoming data to an Icecast2 server. * * @author David ‘Bombe’ Roden */ -public class Icecast2Sink implements Sink { +public class Icecast2Sink extends AbstractFilter { /** The logger. */ private static final Logger logger = Logger.getLogger(Icecast2Sink.class.getName()); @@ -79,12 +71,14 @@ public class Icecast2Sink implements Sink { /** Whether to publish the server. */ private final boolean publishServer; - /** The connected source. */ - private Source source; + /** The output stream to the server. */ + private OutputStream socketOutputStream; /** * Creates a new Icecast2 sink. * + * @param eventBus + * The event bus * @param server * The hostname of the server * @param port @@ -104,6 +98,7 @@ public class Icecast2Sink implements Sink { * not publish it */ public Icecast2Sink(String server, int port, String password, String mountPoint, String serverName, String serverDescription, String genre, boolean publishServer) { + super(String.format("icecast://%s:%d/%s", server, port, mountPoint)); this.server = server; this.port = port; this.password = password; @@ -115,97 +110,89 @@ public class Icecast2Sink implements Sink { } // - // SINK METHODS + // FILTER METHODS // @Override - public void connect(Source source) throws ConnectException { - checkNotNull(source, "source must not be null"); - - this.source = source; - try { - logger.info(String.format("Icecast2Sink: Connecting to %s:%d...", server, port)); - final Socket socket = new Socket(server, port); - logger.info("Icecast2Sink: Connected."); - final OutputStream socketOutputStream = socket.getOutputStream(); - final InputStream socketInputStream = socket.getInputStream(); - - sendLine(socketOutputStream, String.format("SOURCE /%s ICE/1.0", mountPoint)); - sendLine(socketOutputStream, String.format("Authorization: Basic %s", generatePassword(password))); - sendLine(socketOutputStream, String.format("Content-Type: %s", getContentType(source.format()))); - sendLine(socketOutputStream, String.format("ICE-Name: %s", serverName)); - sendLine(socketOutputStream, String.format("ICE-Description: %s", serverDescription)); - sendLine(socketOutputStream, String.format("ICE-Genre: %s", genre)); - sendLine(socketOutputStream, String.format("ICE-Public: %d", publishServer ? 1 : 0)); - sendLine(socketOutputStream, ""); - socketOutputStream.flush(); - - new Thread(new InputStreamDrainer(socketInputStream)).start(); - new Thread(new Connection(source) { - - private long counter; - - @Override - protected int bufferSize() { - return 4096; - } - - @Override - protected void feed(byte[] buffer) throws IOException { - socketOutputStream.write(buffer); - socketOutputStream.flush(); - counter += buffer.length; - logger.finest(String.format("Wrote %d Bytes.", counter)); - } + public List> controllers() { + return Collections.emptyList(); + } - @Override - protected void finish() throws IOException { - Closeables.close(socketOutputStream, true); - Closeables.close(socket, true); - } - }).start(); + @Override + public void open(Metadata metadata) throws IOException { + logger.info(String.format("Connecting to %s:%d...", server, port)); + Socket socket = new Socket(server, port); + logger.info("Connected."); + socketOutputStream = socket.getOutputStream(); + InputStream socketInputStream = socket.getInputStream(); + + sendLine(socketOutputStream, String.format("SOURCE /%s ICE/1.0", mountPoint)); + sendLine(socketOutputStream, String.format("Authorization: Basic %s", generatePassword(password))); + sendLine(socketOutputStream, String.format("Content-Type: %s", getContentType(metadata))); + sendLine(socketOutputStream, String.format("ICE-Name: %s", serverName)); + sendLine(socketOutputStream, String.format("ICE-Description: %s", serverDescription)); + sendLine(socketOutputStream, String.format("ICE-Genre: %s", genre)); + sendLine(socketOutputStream, String.format("ICE-Public: %d", publishServer ? 1 : 0)); + sendLine(socketOutputStream, ""); + socketOutputStream.flush(); + + new Thread(new InputStreamDrainer(socketInputStream)).start(); + + metadataUpdated(metadata); + } - metadataUpdated(); - } catch (IOException ioe1) { - throw new ConnectException(ioe1); + @Override + public void close() { + try { + Closeables.close(socketOutputStream, true); + } catch (IOException e) { + /* will never throw. */ } } @Override - public void metadataUpdated() { - Metadata metadata = source.metadata(); - String metadataString = String.format("%s (%s)", Joiner.on(" - ").skipNulls().join(FluentIterable.from(Arrays.asList(metadata.artist(), metadata.name())).transform(new Function, Object>() { + public void metadataUpdated(final Metadata metadata) { + super.metadataUpdated(metadata); + new Thread(new Runnable() { @Override - public Object apply(Optional input) { - return input.orNull(); - } - })), "Sonitus"); - logger.info(String.format("Updating metadata to %s", metadataString)); + public void run() { + String metadataString = String.format("%s (%s)", metadata.title(), "Sonitus"); + logger.info(String.format("Updating metadata to %s", metadataString)); + + Socket socket = null; + OutputStream socketOutputStream = null; + try { + socket = new Socket(server, port); + socketOutputStream = socket.getOutputStream(); + + sendLine(socketOutputStream, String.format("GET /admin/metadata?pass=%s&mode=updinfo&mount=/%s&song=%s HTTP/1.0", password, mountPoint, URLEncoder.encode(metadataString, "UTF-8"))); + sendLine(socketOutputStream, String.format("Authorization: Basic %s", generatePassword(password))); + sendLine(socketOutputStream, String.format("User-Agent: Mozilla/Sonitus")); + sendLine(socketOutputStream, ""); + socketOutputStream.flush(); - Socket socket = null; - OutputStream socketOutputStream = null; - try { - socket = new Socket(server, port); - socketOutputStream = socket.getOutputStream(); - - sendLine(socketOutputStream, String.format("GET /admin/metadata?pass=%s&mode=updinfo&mount=/%s&song=%s HTTP/1.0", password, mountPoint, URLEncoder.encode(metadataString, "UTF-8"))); - sendLine(socketOutputStream, String.format("Authorization: Basic %s", generatePassword(password))); - sendLine(socketOutputStream, String.format("User-Agent: Mozilla/Sonitus")); - sendLine(socketOutputStream, ""); - socketOutputStream.flush(); - - new InputStreamDrainer(socket.getInputStream()).run(); - } catch (IOException ioe1) { - logger.log(Level.WARNING, "Could not update metadata!", ioe1); - } finally { - try { - Closeables.close(socketOutputStream, true); - Closeables.close(socket, true); - } catch (IOException ioe1) { - /* ignore, will not happen. */ + new InputStreamDrainer(socket.getInputStream()).run(); + } catch (IOException ioe1) { + logger.log(Level.WARNING, "Could not update metadata!", ioe1); + } finally { + try { + Closeables.close(socketOutputStream, true); + if (socket != null) { + socket.close(); + } + } catch (IOException ioe1) { + /* ignore. */ + } + } } - } + }).start(); + } + + @Override + public void process(byte[] buffer) throws IOException { + socketOutputStream.write(buffer); + socketOutputStream.flush(); } // @@ -220,7 +207,7 @@ public class Icecast2Sink implements Sink { * The output stream to send the line to * @param line * The line to send - * @throws IOException + * @throws java.io.IOException * if an I/O error occurs */ private static void sendLine(OutputStream outputStream, String line) throws IOException { @@ -234,7 +221,7 @@ public class Icecast2Sink implements Sink { * @param password * The password to encode * @return The encoded password - * @throws UnsupportedEncodingException + * @throws java.io.UnsupportedEncodingException * if the UTF-8 encoding is not supported (which can never happen) */ private static String generatePassword(String password) throws UnsupportedEncodingException { @@ -242,15 +229,15 @@ public class Icecast2Sink implements Sink { } /** - * Returns a MIME type for the given format. Currently only Vorbis, MP3, and - * PCM formats are recognized. + * Returns a MIME type for the given metadata. Currently only Vorbis, MP3, PCM, + * Ogg Vorbis, Opus, and FLAC formats are recognized. * - * @param format - * The format to get a MIME type for - * @return The MIME type of the format + * @param metadata + * The metadata to get a MIME type for + * @return The MIME type of the metadata */ - private static String getContentType(Format format) { - String encoding = format.encoding(); + private static String getContentType(Metadata metadata) { + String encoding = metadata.encoding(); if ("Vorbis".equalsIgnoreCase(encoding)) { return "audio/ogg"; } @@ -260,6 +247,15 @@ public class Icecast2Sink implements Sink { if ("PCM".equalsIgnoreCase(encoding)) { return "audio/vnd.wave"; } + if ("Vorbis".equalsIgnoreCase(encoding)) { + return "application/ogg"; + } + if ("Opus".equalsIgnoreCase(encoding)) { + return "audio/ogg; codecs=opus"; + } + if ("FLAC".equalsIgnoreCase(encoding)) { + return "audio/flac"; + } return "application/octet-stream"; }