Compare commits
2 Commits
500db0bbe3
...
4c9c10b013
Author | SHA1 | Date | |
---|---|---|---|
|
4c9c10b013 | ||
|
91a53ec119 |
24
Build.bat
Normal file
24
Build.bat
Normal file
@@ -0,0 +1,24 @@
|
||||
@echo off
|
||||
echo === Building RadioDJ-TunaBridge ===
|
||||
|
||||
REM Clean previous build
|
||||
mvn clean
|
||||
|
||||
REM Compile and package the JAR
|
||||
mvn package
|
||||
|
||||
IF %ERRORLEVEL% NEQ 0 (
|
||||
echo ❌ Build failed.
|
||||
pause
|
||||
exit /b %ERRORLEVEL%
|
||||
)
|
||||
|
||||
REM Copy final JAR to top-level folder (optional)
|
||||
IF EXIST target\radiodj-tuna-bridge-1.0-SNAPSHOT.jar (
|
||||
copy target\radiodj-tuna-bridge-1.0-SNAPSHOT.jar radiodj-tuna-bridge.jar >nul
|
||||
echo ✅ Build complete. Output: radiodj-tuna-bridge.jar
|
||||
) ELSE (
|
||||
echo ❌ JAR not found!
|
||||
)
|
||||
|
||||
pause
|
40
README.md
40
README.md
@@ -1,2 +1,40 @@
|
||||
# RadioDJ-Tuna
|
||||
# 🎧 RadioDJ Tuna Bridge
|
||||
|
||||
A lightweight Java utility that pulls "Now Playing" info from RadioDJ's REST XML API, and outputs it as `.txt` files and album art for tools like Tuna (OBS plugin).
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Features
|
||||
|
||||
- ✅ Pulls title / artist / album / album art via REST
|
||||
- 🛑 Only updates files if the song actually changed
|
||||
- 🖼️ Copies matching album art, or uses fallback image
|
||||
- 💾 Creates & reads from `config.yml` and `last-played.xml`
|
||||
- 🪄 Fully customizable update interval
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Usage
|
||||
|
||||
1. **First Run:**
|
||||
- Launch the app with `java -jar radiodj-tuna-bridge.jar`
|
||||
- You'll be prompted to enter your API URL, art folder, fallback filename, and polling interval
|
||||
|
||||
2. **Outputs:**
|
||||
- Stored in `tuna-output/` folder:
|
||||
- `track-title.txt`
|
||||
- `track-artist.txt`
|
||||
- `track-album.txt`
|
||||
- `album-art.jpg` (copied from your RadioDJ art folder)
|
||||
|
||||
3. **Reconfigure:**
|
||||
- Edit the `config.yml` file to change settings at any time
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Build Instructions
|
||||
|
||||
```bash
|
||||
git clone https://github.com/you/radiodj-tuna-bridge.git
|
||||
cd radiodj-tuna-bridge
|
||||
build.bat
|
42
pom.xml
Normal file
42
pom.xml
Normal file
@@ -0,0 +1,42 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
||||
http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>radiodj-tuna-bridge</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- Jackson for XML & JSON processing -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-xml</artifactId>
|
||||
<version>2.17.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- SnakeYAML for config.yml parsing -->
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>2.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Dorkbox SystemTray for Windows notifications -->
|
||||
<dependency>
|
||||
<groupId>com.dorkbox</groupId>
|
||||
<artifactId>SystemTray</artifactId>
|
||||
<version>3.17</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
7
run.bat
Normal file
7
run.bat
Normal file
@@ -0,0 +1,7 @@
|
||||
@echo off
|
||||
echo === Running RadioDJ Tuna Bridge ===
|
||||
|
||||
REM Run the JAR (assumes you built it already)
|
||||
java -jar radiodj-tuna-bridge.jar
|
||||
|
||||
pause
|
40
src/main/java/com/minster586/radiodjtuna/Config.java
Normal file
40
src/main/java/com/minster586/radiodjtuna/Config.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package com.minster586.radiodjtuna;
|
||||
|
||||
public class Config {
|
||||
private String radioDjApiUrl;
|
||||
private String albumArtDirectory;
|
||||
private int updateIntervalSeconds;
|
||||
private String fallbackArtFilename;
|
||||
|
||||
public String getRadioDjApiUrl() {
|
||||
return radioDjApiUrl;
|
||||
}
|
||||
|
||||
public void setRadioDjApiUrl(String radioDjApiUrl) {
|
||||
this.radioDjApiUrl = radioDjApiUrl;
|
||||
}
|
||||
|
||||
public String getAlbumArtDirectory() {
|
||||
return albumArtDirectory;
|
||||
}
|
||||
|
||||
public void setAlbumArtDirectory(String albumArtDirectory) {
|
||||
this.albumArtDirectory = albumArtDirectory;
|
||||
}
|
||||
|
||||
public int getUpdateIntervalSeconds() {
|
||||
return updateIntervalSeconds;
|
||||
}
|
||||
|
||||
public void setUpdateIntervalSeconds(int updateIntervalSeconds) {
|
||||
this.updateIntervalSeconds = updateIntervalSeconds;
|
||||
}
|
||||
|
||||
public String getFallbackArtFilename() {
|
||||
return fallbackArtFilename;
|
||||
}
|
||||
|
||||
public void setFallbackArtFilename(String fallbackArtFilename) {
|
||||
this.fallbackArtFilename = fallbackArtFilename;
|
||||
}
|
||||
}
|
74
src/main/java/com/minster586/radiodjtuna/ConfigManager.java
Normal file
74
src/main/java/com/minster586/radiodjtuna/ConfigManager.java
Normal file
@@ -0,0 +1,74 @@
|
||||
package com.minster586.radiodjtuna;
|
||||
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.Constructor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Scanner;
|
||||
|
||||
public class ConfigManager {
|
||||
|
||||
private final Path configPath;
|
||||
|
||||
public ConfigManager(Path configPath) {
|
||||
this.configPath = configPath;
|
||||
}
|
||||
|
||||
public Config loadOrCreateConfig() {
|
||||
File configFile = configPath.toFile();
|
||||
if (!configFile.exists()) {
|
||||
System.out.println("⚙️ Creating new config.yml...");
|
||||
Config newConfig = promptForConfig();
|
||||
saveConfig(newConfig);
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
try (InputStream input = java.nio.file.Files.newInputStream(configPath)) {
|
||||
Yaml yaml = new Yaml(new Constructor(Config.class));
|
||||
Config loaded = yaml.load(input);
|
||||
System.out.println("✅ Config loaded successfully.");
|
||||
return loaded;
|
||||
} catch (Exception e) {
|
||||
System.err.println("❌ Failed to load config: " + e.getMessage());
|
||||
System.exit(1);
|
||||
return null; // unreachable, but satisfies compiler
|
||||
}
|
||||
}
|
||||
|
||||
private Config promptForConfig() {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
Config config = new Config();
|
||||
|
||||
System.out.print("🔗 Enter RadioDJ API URL (e.g. http://localhost:1234/playing): ");
|
||||
config.setRadioDjApiUrl(scanner.nextLine().trim());
|
||||
|
||||
System.out.print("📁 Enter full path to RadioDJ album art folder: ");
|
||||
config.setAlbumArtDirectory(scanner.nextLine().trim());
|
||||
|
||||
System.out.print("⏱️ Enter update interval (seconds): ");
|
||||
config.setUpdateIntervalSeconds(Integer.parseInt(scanner.nextLine().trim()));
|
||||
|
||||
System.out.print("🖼️ Enter fallback album art filename (e.g. fallback-art.jpg): ");
|
||||
config.setFallbackArtFilename(scanner.nextLine().trim());
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
private void saveConfig(Config config) {
|
||||
try (FileWriter writer = new FileWriter(configPath.toFile())) {
|
||||
DumperOptions options = new DumperOptions();
|
||||
options.setIndent(2);
|
||||
options.setPrettyFlow(true);
|
||||
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
Yaml yaml = new Yaml(options);
|
||||
yaml.dump(config, writer);
|
||||
System.out.println("💾 Config saved to " + configPath);
|
||||
} catch (Exception e) {
|
||||
System.err.println("❌ Failed to save config: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
68
src/main/java/com/minster586/radiodjtuna/MainApp.java
Normal file
68
src/main/java/com/minster586/radiodjtuna/MainApp.java
Normal file
@@ -0,0 +1,68 @@
|
||||
package com.minster586.radiodjtuna;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class MainApp {
|
||||
|
||||
// Base paths determined at runtime
|
||||
private static Path jarDir;
|
||||
private static Path outputDir;
|
||||
private static Path configFile;
|
||||
private static Path cacheFile;
|
||||
|
||||
// Core components
|
||||
private static Config config;
|
||||
private static MetadataCacheManager cacheManager;
|
||||
private static TrackInfo lastPlayed;
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("🔊 RadioDJ Tuna Bridge starting up...");
|
||||
|
||||
initializePaths();
|
||||
ensureOutputFolder();
|
||||
|
||||
// Load or create config
|
||||
ConfigManager configManager = new ConfigManager(configFile);
|
||||
config = configManager.loadOrCreateConfig();
|
||||
|
||||
// Set up metadata cache manager
|
||||
cacheManager = new MetadataCacheManager(cacheFile);
|
||||
lastPlayed = cacheManager.loadCache();
|
||||
|
||||
if (lastPlayed != null) {
|
||||
System.out.println("🗃️ Loaded cached track: " + lastPlayed.getTitle());
|
||||
} else {
|
||||
System.out.println("📁 No metadata cache found yet.");
|
||||
}
|
||||
|
||||
// TODO: Poll RadioDJ API using config.getRadioDjApiUrl()
|
||||
// TODO: Compare result to lastPlayed using lastPlayed.equals()
|
||||
// TODO: If different, write new metadata to output folder
|
||||
// TODO: Save new metadata to last-played.xml
|
||||
// TODO: Handle album art copy or fallback
|
||||
// TODO: Show toast if API is unreachable
|
||||
|
||||
System.out.println("✅ System initialized. Ready for polling phase.");
|
||||
}
|
||||
|
||||
private static void initializePaths() {
|
||||
jarDir = Paths.get("").toAbsolutePath();
|
||||
outputDir = jarDir.resolve("tuna-output");
|
||||
configFile = jarDir.resolve("config.yml");
|
||||
cacheFile = jarDir.resolve("last-played.xml");
|
||||
}
|
||||
|
||||
private static void ensureOutputFolder() {
|
||||
File outFolder = outputDir.toFile();
|
||||
if (!outFolder.exists()) {
|
||||
boolean created = outFolder.mkdirs();
|
||||
if (created) {
|
||||
System.out.println("📁 Created output folder: " + outputDir);
|
||||
} else {
|
||||
System.err.println("❌ Failed to create output folder. Check permissions.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
package com.minster586.radiodjtuna;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.transform.*;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
|
||||
import org.w3c.dom.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class MetadataCacheManager {
|
||||
private final Path cachePath;
|
||||
|
||||
public MetadataCacheManager(Path cachePath) {
|
||||
this.cachePath = cachePath;
|
||||
}
|
||||
|
||||
public TrackInfo loadCache() {
|
||||
File file = cachePath.toFile();
|
||||
if (!file.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
Document doc = builder.parse(file);
|
||||
|
||||
Element root = doc.getDocumentElement();
|
||||
|
||||
String title = getText(root, "Title");
|
||||
String artist = getText(root, "Artist");
|
||||
String album = getText(root, "Album");
|
||||
String albumArt = getText(root, "AlbumArt");
|
||||
|
||||
return new TrackInfo(title, artist, album, albumArt);
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("⚠️ Failed to load last-played.xml: " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void saveCache(TrackInfo track) {
|
||||
try {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
Document doc = builder.newDocument();
|
||||
|
||||
Element root = doc.createElement("LastPlayed");
|
||||
doc.appendChild(root);
|
||||
|
||||
addText(doc, root, "Title", track.getTitle());
|
||||
addText(doc, root, "Artist", track.getArtist());
|
||||
addText(doc, root, "Album", track.getAlbum());
|
||||
addText(doc, root, "AlbumArt", track.getAlbumArt());
|
||||
|
||||
Transformer transformer = TransformerFactory.newInstance().newTransformer();
|
||||
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
|
||||
Result output = new StreamResult(cachePath.toFile());
|
||||
Source input = new DOMSource(doc);
|
||||
transformer.transform(input, output);
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("❌ Failed to save last-played.xml: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private String getText(Element root, String tag) {
|
||||
NodeList list = root.getElementsByTagName(tag);
|
||||
return (list.getLength() > 0) ? list.item(0).getTextContent() : "";
|
||||
}
|
||||
|
||||
private void addText(Document doc, Element parent, String tag, String value) {
|
||||
Element node = doc.createElement(tag);
|
||||
node.appendChild(doc.createTextNode(value != null ? value : ""));
|
||||
parent.appendChild(node);
|
||||
}
|
||||
}
|
75
src/main/java/com/minster586/radiodjtuna/OutputManager.java
Normal file
75
src/main/java/com/minster586/radiodjtuna/OutputManager.java
Normal file
@@ -0,0 +1,75 @@
|
||||
package com.minster586.radiodjtuna;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.*;
|
||||
|
||||
public class OutputManager {
|
||||
|
||||
private final Path outputDir;
|
||||
private final Path albumArtSourceFolder;
|
||||
private final String fallbackArtFilename;
|
||||
|
||||
public OutputManager(Path outputDir, String albumArtSourceDir, String fallbackArtFilename) {
|
||||
this.outputDir = outputDir;
|
||||
this.albumArtSourceFolder = Paths.get(albumArtSourceDir);
|
||||
this.fallbackArtFilename = fallbackArtFilename;
|
||||
}
|
||||
|
||||
public void writeOutputFiles(TrackInfo track) {
|
||||
try {
|
||||
writeTextFile("track-title.txt", track.getTitle());
|
||||
writeTextFile("track-artist.txt", track.getArtist());
|
||||
writeTextFile("track-album.txt", track.getAlbum());
|
||||
copyAlbumArt(track.getAlbumArt());
|
||||
} catch (IOException e) {
|
||||
System.err.println("❌ Failed writing output files: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void writeTextFile(String fileName, String content) throws IOException {
|
||||
Path file = outputDir.resolve(fileName);
|
||||
Files.writeString(file, content != null ? content : "", StandardCharsets.UTF_8,
|
||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
}
|
||||
|
||||
private void copyAlbumArt(String albumArtFileName) {
|
||||
Path destination = outputDir.resolve("album-art.jpg");
|
||||
|
||||
if (albumArtFileName == null || albumArtFileName.isBlank()) {
|
||||
System.out.println("ℹ️ No album art in tag, using fallback.");
|
||||
copyFallback(destination);
|
||||
return;
|
||||
}
|
||||
|
||||
Path sourceFile = albumArtSourceFolder.resolve(albumArtFileName);
|
||||
|
||||
if (!Files.exists(sourceFile)) {
|
||||
System.out.println("⚠️ Album art not found: " + sourceFile.getFileName() + ", using fallback.");
|
||||
copyFallback(destination);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Files.copy(sourceFile, destination, StandardCopyOption.REPLACE_EXISTING);
|
||||
System.out.println("🎨 Album art updated: " + albumArtFileName);
|
||||
} catch (IOException e) {
|
||||
System.err.println("❌ Failed copying album art: " + e.getMessage());
|
||||
copyFallback(destination);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyFallback(Path destination) {
|
||||
Path fallbackSource = outputDir.resolve(fallbackArtFilename);
|
||||
if (Files.exists(fallbackSource)) {
|
||||
try {
|
||||
Files.copy(fallbackSource, destination, StandardCopyOption.REPLACE_EXISTING);
|
||||
System.out.println("🖼️ Fallback album art used.");
|
||||
} catch (IOException e) {
|
||||
System.err.println("❌ Failed to copy fallback art: " + e.getMessage());
|
||||
}
|
||||
} else {
|
||||
System.err.println("❌ Fallback art file is missing: " + fallbackSource);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
package com.minster586.radiodjtuna;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
public class RadioDjApiClient {
|
||||
private final String apiUrl;
|
||||
|
||||
public RadioDjApiClient(String apiUrl) {
|
||||
this.apiUrl = apiUrl;
|
||||
}
|
||||
|
||||
public TrackInfo fetchNowPlaying() throws Exception {
|
||||
URL url = new URL(apiUrl);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setConnectTimeout(3000); // 3s timeout
|
||||
connection.setReadTimeout(3000);
|
||||
|
||||
try (InputStream in = connection.getInputStream()) {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
Document doc = builder.parse(in);
|
||||
Element root = doc.getDocumentElement();
|
||||
|
||||
String title = getText(root, "Title");
|
||||
String artist = getText(root, "Artist");
|
||||
String album = getText(root, "Album");
|
||||
String albumArt = getText(root, "AlbumArt");
|
||||
|
||||
return new TrackInfo(title, artist, album, albumArt);
|
||||
}
|
||||
}
|
||||
|
||||
private String getText(Element root, String tag) {
|
||||
return root.getElementsByTagName(tag).getLength() > 0
|
||||
? root.getElementsByTagName(tag).item(0).getTextContent()
|
||||
: "";
|
||||
}
|
||||
}
|
63
src/main/java/com/minster586/radiodjtuna/TrackInfo.java
Normal file
63
src/main/java/com/minster586/radiodjtuna/TrackInfo.java
Normal file
@@ -0,0 +1,63 @@
|
||||
package com.minster586.radiodjtuna;
|
||||
|
||||
public class TrackInfo {
|
||||
private String title;
|
||||
private String artist;
|
||||
private String album;
|
||||
private String albumArt;
|
||||
|
||||
public TrackInfo() {
|
||||
// Default constructor for XML deserialization
|
||||
}
|
||||
|
||||
public TrackInfo(String title, String artist, String album, String albumArt) {
|
||||
this.title = title;
|
||||
this.artist = artist;
|
||||
this.album = album;
|
||||
this.albumArt = albumArt;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getArtist() {
|
||||
return artist;
|
||||
}
|
||||
|
||||
public void setArtist(String artist) {
|
||||
this.artist = artist;
|
||||
}
|
||||
|
||||
public String getAlbum() {
|
||||
return album;
|
||||
}
|
||||
|
||||
public void setAlbum(String album) {
|
||||
this.album = album;
|
||||
}
|
||||
|
||||
public String getAlbumArt() {
|
||||
return albumArt;
|
||||
}
|
||||
|
||||
public void setAlbumArt(String albumArt) {
|
||||
this.albumArt = albumArt;
|
||||
}
|
||||
|
||||
public boolean equals(TrackInfo other) {
|
||||
if (other == null) return false;
|
||||
return safeEquals(this.title, other.title)
|
||||
&& safeEquals(this.artist, other.artist)
|
||||
&& safeEquals(this.album, other.album)
|
||||
&& safeEquals(this.albumArt, other.albumArt);
|
||||
}
|
||||
|
||||
private boolean safeEquals(String a, String b) {
|
||||
return (a == null && b == null) || (a != null && a.equals(b));
|
||||
}
|
||||
}
|
BIN
target/classes/com/minster586/radiodjtuna/Config.class
Normal file
BIN
target/classes/com/minster586/radiodjtuna/Config.class
Normal file
Binary file not shown.
BIN
target/classes/com/minster586/radiodjtuna/ConfigManager.class
Normal file
BIN
target/classes/com/minster586/radiodjtuna/ConfigManager.class
Normal file
Binary file not shown.
BIN
target/classes/com/minster586/radiodjtuna/MainApp.class
Normal file
BIN
target/classes/com/minster586/radiodjtuna/MainApp.class
Normal file
Binary file not shown.
Binary file not shown.
BIN
target/classes/com/minster586/radiodjtuna/OutputManager.class
Normal file
BIN
target/classes/com/minster586/radiodjtuna/OutputManager.class
Normal file
Binary file not shown.
BIN
target/classes/com/minster586/radiodjtuna/RadioDjApiClient.class
Normal file
BIN
target/classes/com/minster586/radiodjtuna/RadioDjApiClient.class
Normal file
Binary file not shown.
BIN
target/classes/com/minster586/radiodjtuna/TrackInfo.class
Normal file
BIN
target/classes/com/minster586/radiodjtuna/TrackInfo.class
Normal file
Binary file not shown.
Reference in New Issue
Block a user