Compare commits
8 Commits
4c9c10b013
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
32b56b56d7 | ||
|
540eedba94 | ||
|
3f0337af50 | ||
|
c0e7e2fcb5 | ||
|
787e7e22ce | ||
|
77343481d5 | ||
|
6b6811cb7a | ||
|
e3dbe80669 |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/target
|
||||
dependency-reduced-pom.xml
|
42
Build.bat
42
Build.bat
@@ -1,24 +1,40 @@
|
||||
@echo off
|
||||
echo === Building RadioDJ-TunaBridge ===
|
||||
echo.
|
||||
echo === 🚀 Building RadioDJ-TunaBridge with Java 17 ===
|
||||
echo.
|
||||
|
||||
REM Clean previous build
|
||||
REM Manually set Java 17 location
|
||||
set "JAVA_HOME=C:\java_runtime\17"
|
||||
set "PATH=%JAVA_HOME%\bin;%PATH%"
|
||||
|
||||
REM Step 1: Clean project
|
||||
echo 🧹 Cleaning...
|
||||
mvn clean
|
||||
|
||||
REM Compile and package the JAR
|
||||
mvn package
|
||||
|
||||
IF %ERRORLEVEL% NEQ 0 (
|
||||
echo ❌ Build failed.
|
||||
echo ❌ Maven clean 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!
|
||||
REM Step 2: Compile and package
|
||||
echo 🔨 Compiling...
|
||||
mvn package
|
||||
IF %ERRORLEVEL% NEQ 0 (
|
||||
echo ❌ Maven build failed.
|
||||
pause
|
||||
exit /b %ERRORLEVEL%
|
||||
)
|
||||
|
||||
REM Step 3: Copy JAR to top level
|
||||
set "TARGET_JAR=target\radiodj-tuna-bridge-1.0-SNAPSHOT.jar"
|
||||
set "OUTPUT_JAR=radiodj-tuna-bridge.jar"
|
||||
|
||||
IF EXIST "%TARGET_JAR%" (
|
||||
copy "%TARGET_JAR%" "%OUTPUT_JAR%" >nul
|
||||
echo ✅ JAR ready: %OUTPUT_JAR%
|
||||
) ELSE (
|
||||
echo ❌ No JAR found in target folder.
|
||||
)
|
||||
|
||||
echo.
|
||||
pause
|
94
pom.xml
94
pom.xml
@@ -1,42 +1,102 @@
|
||||
<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
|
||||
<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>
|
||||
<groupId>com.minster586</groupId>
|
||||
<artifactId>radiodj-tuna-bridge</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<version>1.0.1-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>RadioDJ Tuna Bridge</name>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<javafx.version>20</javafx.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- Jackson for XML & JSON processing -->
|
||||
<!-- JavaFX modules -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-xml</artifactId>
|
||||
<version>2.17.0</version>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-controls</artifactId>
|
||||
<version>${javafx.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-graphics</artifactId>
|
||||
<version>${javafx.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-base</artifactId>
|
||||
<version>${javafx.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- SnakeYAML for config.yml parsing -->
|
||||
<!-- YAML parsing -->
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>2.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Dorkbox SystemTray for Windows notifications -->
|
||||
<!-- XML support -->
|
||||
<dependency>
|
||||
<groupId>com.dorkbox</groupId>
|
||||
<artifactId>SystemTray</artifactId>
|
||||
<version>3.17</version>
|
||||
<groupId>xerces</groupId>
|
||||
<artifactId>xercesImpl</artifactId>
|
||||
<version>2.12.2</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Compiler plugin -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.11.0</version>
|
||||
</plugin>
|
||||
|
||||
<!-- Jar plugin for manifest -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.2.2</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>com.minster586.radiodjtuna.MainApp</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- Shade plugin to bundle everything -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>com.minster586.radiodjtuna.MainApp</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@@ -3,9 +3,21 @@ package com.minster586.radiodjtuna;
|
||||
public class Config {
|
||||
private String radioDjApiUrl;
|
||||
private String albumArtDirectory;
|
||||
private int updateIntervalSeconds;
|
||||
private String fallbackArtFilename;
|
||||
private int updateIntervalSeconds;
|
||||
|
||||
// ✅ Required no-arg constructor for SnakeYAML
|
||||
public Config() {}
|
||||
|
||||
// ✅ Full constructor for manual creation
|
||||
public Config(String radioDjApiUrl, String albumArtDirectory, String fallbackArtFilename, int updateIntervalSeconds) {
|
||||
this.radioDjApiUrl = radioDjApiUrl;
|
||||
this.albumArtDirectory = albumArtDirectory;
|
||||
this.fallbackArtFilename = fallbackArtFilename;
|
||||
this.updateIntervalSeconds = updateIntervalSeconds;
|
||||
}
|
||||
|
||||
// ✅ Getters and Setters
|
||||
public String getRadioDjApiUrl() {
|
||||
return radioDjApiUrl;
|
||||
}
|
||||
@@ -22,14 +34,6 @@ public class Config {
|
||||
this.albumArtDirectory = albumArtDirectory;
|
||||
}
|
||||
|
||||
public int getUpdateIntervalSeconds() {
|
||||
return updateIntervalSeconds;
|
||||
}
|
||||
|
||||
public void setUpdateIntervalSeconds(int updateIntervalSeconds) {
|
||||
this.updateIntervalSeconds = updateIntervalSeconds;
|
||||
}
|
||||
|
||||
public String getFallbackArtFilename() {
|
||||
return fallbackArtFilename;
|
||||
}
|
||||
@@ -37,4 +41,12 @@ public class Config {
|
||||
public void setFallbackArtFilename(String fallbackArtFilename) {
|
||||
this.fallbackArtFilename = fallbackArtFilename;
|
||||
}
|
||||
|
||||
public int getUpdateIntervalSeconds() {
|
||||
return updateIntervalSeconds;
|
||||
}
|
||||
|
||||
public void setUpdateIntervalSeconds(int updateIntervalSeconds) {
|
||||
this.updateIntervalSeconds = updateIntervalSeconds;
|
||||
}
|
||||
}
|
@@ -1,14 +1,21 @@
|
||||
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.FileInputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Scanner;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.LoaderOptions;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.constructor.Constructor;
|
||||
import org.yaml.snakeyaml.representer.Representer;
|
||||
import org.yaml.snakeyaml.nodes.Tag;
|
||||
|
||||
public class ConfigManager {
|
||||
|
||||
@@ -19,56 +26,90 @@ public class ConfigManager {
|
||||
}
|
||||
|
||||
public Config loadOrCreateConfig() {
|
||||
File configFile = configPath.toFile();
|
||||
if (!configFile.exists()) {
|
||||
System.out.println("⚙️ Creating new config.yml...");
|
||||
Config newConfig = promptForConfig();
|
||||
saveConfig(newConfig);
|
||||
return newConfig;
|
||||
File file = configPath.toFile();
|
||||
if (file.exists()) {
|
||||
try (FileInputStream in = new FileInputStream(file)) {
|
||||
Yaml yaml = new Yaml(new Constructor(Config.class, new LoaderOptions()));
|
||||
return yaml.load(in);
|
||||
} catch (Exception e) {
|
||||
System.err.println(" Failed to load config.yml: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
System.out.println(" Creating new config.yml...");
|
||||
Config config = promptUserConfig();
|
||||
saveConfig(config);
|
||||
convertFallbackToPngIfNecessary(config);
|
||||
return config;
|
||||
}
|
||||
|
||||
private Config promptForConfig() {
|
||||
private Config promptUserConfig() {
|
||||
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 RadioDJ API URL (e.g., http://localhost:1234/playing): ");
|
||||
String apiUrl = scanner.nextLine().trim();
|
||||
|
||||
System.out.print("📁 Enter full path to RadioDJ album art folder: ");
|
||||
config.setAlbumArtDirectory(scanner.nextLine().trim());
|
||||
System.out.print(" Enter path to album art folder: ");
|
||||
String artDir = scanner.nextLine().trim();
|
||||
|
||||
System.out.print("⏱️ Enter update interval (seconds): ");
|
||||
config.setUpdateIntervalSeconds(Integer.parseInt(scanner.nextLine().trim()));
|
||||
System.out.print(" Enter filename of fallback album art (e.g., fallback.jpg): ");
|
||||
String fallback = scanner.nextLine().trim();
|
||||
|
||||
System.out.print("🖼️ Enter fallback album art filename (e.g. fallback-art.jpg): ");
|
||||
config.setFallbackArtFilename(scanner.nextLine().trim());
|
||||
int interval = promptForInterval(scanner);
|
||||
|
||||
return config;
|
||||
return new Config(apiUrl, artDir, fallback, interval);
|
||||
}
|
||||
|
||||
private int promptForInterval(Scanner scanner) {
|
||||
while (true) {
|
||||
System.out.print(" Enter metadata update interval (seconds): ");
|
||||
String input = scanner.nextLine().trim();
|
||||
if (!input.isEmpty()) {
|
||||
try {
|
||||
return Integer.parseInt(input);
|
||||
} catch (NumberFormatException e) {
|
||||
System.out.println(" Please enter a valid number.");
|
||||
}
|
||||
} else {
|
||||
System.out.println(" This field is required.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
options.setPrettyFlow(true);
|
||||
|
||||
Representer representer = new Representer(options);
|
||||
representer.addClassTag(Config.class, Tag.MAP); // 👈 Prevents !!class tag
|
||||
|
||||
Yaml yaml = new Yaml(representer, options);
|
||||
yaml.dump(config, writer);
|
||||
System.out.println("💾 Config saved to " + configPath);
|
||||
} catch (Exception e) {
|
||||
System.err.println("❌ Failed to save config: " + e.getMessage());
|
||||
System.out.println(" Config saved: " + configPath.getFileName());
|
||||
} catch (IOException e) {
|
||||
System.err.println(" Failed to save config.yml: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void convertFallbackToPngIfNecessary(Config config) {
|
||||
String fallback = config.getFallbackArtFilename();
|
||||
if (fallback.toLowerCase().endsWith(".jpg")) {
|
||||
File jpgFile = new File(config.getAlbumArtDirectory(), fallback);
|
||||
File pngFile = new File(config.getAlbumArtDirectory(), fallback.replaceAll("(?i)\\.jpg$", ".png"));
|
||||
|
||||
try {
|
||||
BufferedImage image = ImageIO.read(jpgFile);
|
||||
if (image != null) {
|
||||
ImageIO.write(image, "png", pngFile);
|
||||
System.out.println(" Converted fallback image to PNG: " + pngFile.getName());
|
||||
config.setFallbackArtFilename(pngFile.getName());
|
||||
saveConfig(config); // Resave with updated filename
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println(" Failed to convert fallback image: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,48 +3,77 @@ package com.minster586.radiodjtuna;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Scanner;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
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...");
|
||||
System.out.println(" RadioDJ Tuna Bridge starting up...");
|
||||
|
||||
initializePaths();
|
||||
ensureOutputFolder();
|
||||
|
||||
// Load or create config
|
||||
ConfigManager configManager = new ConfigManager(configFile);
|
||||
config = configManager.loadOrCreateConfig();
|
||||
Config config = configManager.loadOrCreateConfig();
|
||||
|
||||
// Set up metadata cache manager
|
||||
cacheManager = new MetadataCacheManager(cacheFile);
|
||||
lastPlayed = cacheManager.loadCache();
|
||||
MetadataCacheManager cacheManager = new MetadataCacheManager(cacheFile);
|
||||
final TrackInfo[] lastPlayed = { cacheManager.loadCache() };
|
||||
|
||||
if (lastPlayed != null) {
|
||||
System.out.println("🗃️ Loaded cached track: " + lastPlayed.getTitle());
|
||||
} else {
|
||||
System.out.println("📁 No metadata cache found yet.");
|
||||
RadioDjApiClient apiClient = new RadioDjApiClient(config.getRadioDjApiUrl());
|
||||
OutputManager outputManager = new OutputManager(
|
||||
outputDir,
|
||||
config.getAlbumArtDirectory(),
|
||||
config.getFallbackArtFilename()
|
||||
);
|
||||
|
||||
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
|
||||
Runnable pollingTask = () -> {
|
||||
try {
|
||||
TrackInfo current = apiClient.fetchNowPlaying();
|
||||
if (current == null || current.getTitle().isBlank()) {
|
||||
System.out.println(" No track data received.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (lastPlayed[0] == null || !current.equals(lastPlayed[0])) {
|
||||
System.out.println(" New track detected: " + current.getTitle());
|
||||
outputManager.writeOutputFiles(current);
|
||||
cacheManager.saveCache(current);
|
||||
lastPlayed[0] = current;
|
||||
} else {
|
||||
// System.out.println(" No change — current track: " + current.getTitle());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println(" Failed to fetch or process now playing data: " + e.getMessage());
|
||||
}
|
||||
};
|
||||
|
||||
int interval = Math.max(config.getUpdateIntervalSeconds(), 3);
|
||||
scheduler.scheduleAtFixedRate(pollingTask, 0, interval, TimeUnit.SECONDS);
|
||||
|
||||
System.out.println(" Press 0 + Enter to stop the app...");
|
||||
|
||||
try (Scanner scanner = new Scanner(System.in)) {
|
||||
while (true) {
|
||||
String input = scanner.nextLine().trim();
|
||||
if ("0".equals(input)) {
|
||||
System.out.println(" Shutdown requested. Closing...");
|
||||
scheduler.shutdownNow();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println(" Error reading input: " + e.getMessage());
|
||||
}
|
||||
|
||||
// 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.");
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
private static void initializePaths() {
|
||||
@@ -59,9 +88,9 @@ public class MainApp {
|
||||
if (!outFolder.exists()) {
|
||||
boolean created = outFolder.mkdirs();
|
||||
if (created) {
|
||||
System.out.println("📁 Created output folder: " + outputDir);
|
||||
System.out.println(" Created output folder: " + outputDir);
|
||||
} else {
|
||||
System.err.println("❌ Failed to create output folder. Check permissions.");
|
||||
System.err.println(" Failed to create output folder. Check permissions.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -39,7 +39,7 @@ public class MetadataCacheManager {
|
||||
return new TrackInfo(title, artist, album, albumArt);
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("⚠️ Failed to load last-played.xml: " + e.getMessage());
|
||||
System.err.println(" Failed to load last-played.xml: " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,7 @@ public class MetadataCacheManager {
|
||||
transformer.transform(input, output);
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("❌ Failed to save last-played.xml: " + e.getMessage());
|
||||
System.err.println(" Failed to save last-played.xml: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -3,6 +3,9 @@ package com.minster586.radiodjtuna;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
public class OutputManager {
|
||||
|
||||
@@ -21,9 +24,14 @@ public class OutputManager {
|
||||
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());
|
||||
System.err.println(" Failed writing metadata text files: " + e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
convertAlbumArtToPng(track.getAlbumArt());
|
||||
} catch (Exception e) {
|
||||
System.err.println(" Failed converting album art: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,45 +39,57 @@ public class OutputManager {
|
||||
Path file = outputDir.resolve(fileName);
|
||||
Files.writeString(file, content != null ? content : "", StandardCharsets.UTF_8,
|
||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
System.out.println(" Wrote file: " + file.getFileName());
|
||||
}
|
||||
|
||||
private void copyAlbumArt(String albumArtFileName) {
|
||||
Path destination = outputDir.resolve("album-art.jpg");
|
||||
private void convertAlbumArtToPng(String albumArtFileName) {
|
||||
Path destination = outputDir.resolve("album-art.png");
|
||||
|
||||
if (albumArtFileName == null || albumArtFileName.isBlank()) {
|
||||
System.out.println("ℹ️ No album art in tag, using fallback.");
|
||||
copyFallback(destination);
|
||||
System.out.println(" No album art in tag, using fallback.");
|
||||
convertFallbackToPng(destination);
|
||||
return;
|
||||
}
|
||||
|
||||
Path sourceFile = albumArtSourceFolder.resolve(albumArtFileName);
|
||||
|
||||
if (!Files.exists(sourceFile)) {
|
||||
System.out.println("⚠️ Album art not found: " + sourceFile.getFileName() + ", using fallback.");
|
||||
copyFallback(destination);
|
||||
System.out.println(" Album art not found: " + sourceFile.getFileName() + ", using fallback.");
|
||||
convertFallbackToPng(destination);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Files.copy(sourceFile, destination, StandardCopyOption.REPLACE_EXISTING);
|
||||
System.out.println("🎨 Album art updated: " + albumArtFileName);
|
||||
BufferedImage image = ImageIO.read(sourceFile.toFile());
|
||||
if (image != null) {
|
||||
ImageIO.write(image, "png", destination.toFile());
|
||||
System.out.println(" Album art converted to PNG: " + destination.getFileName());
|
||||
} else {
|
||||
System.out.println(" Could not read album art image, using fallback.");
|
||||
convertFallbackToPng(destination);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println("❌ Failed copying album art: " + e.getMessage());
|
||||
copyFallback(destination);
|
||||
System.err.println(" Failed converting album art: " + e.getMessage());
|
||||
convertFallbackToPng(destination);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyFallback(Path destination) {
|
||||
private void convertFallbackToPng(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());
|
||||
if (!Files.exists(fallbackSource)) {
|
||||
System.err.println(" Fallback art file is missing: " + fallbackSource);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
BufferedImage image = ImageIO.read(fallbackSource.toFile());
|
||||
if (image != null) {
|
||||
ImageIO.write(image, "png", destination.toFile());
|
||||
System.out.println(" Fallback album art converted to PNG.");
|
||||
} else {
|
||||
System.err.println(" Could not read fallback image.");
|
||||
}
|
||||
} else {
|
||||
System.err.println("❌ Fallback art file is missing: " + fallbackSource);
|
||||
} catch (IOException e) {
|
||||
System.err.println(" Failed to convert fallback art: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user