commit
dfb9b68340
17 changed files with 771 additions and 0 deletions
@ -0,0 +1,3 @@ |
|||
/.idea/ |
|||
/build/ |
|||
/.gradle/ |
@ -0,0 +1,13 @@ |
|||
# FLAC Freezer Fixer |
|||
Fix the metadata Deezer failed to fix. Now in Kotlin. |
|||
|
|||
## Building |
|||
```bash |
|||
./gradlew shadowJar |
|||
``` |
|||
The jar file can then be found in `build/libs/`. |
|||
|
|||
## Usage |
|||
```bash |
|||
java -jar FLACFreezerFixer.jar <directory> [--dry-run] |
|||
``` |
@ -0,0 +1,34 @@ |
|||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile |
|||
|
|||
buildscript { |
|||
repositories { |
|||
gradlePluginPortal() |
|||
} |
|||
} |
|||
|
|||
plugins { |
|||
kotlin("jvm") version "1.6.10" |
|||
application |
|||
|
|||
id("com.github.johnrengelman.shadow") version "7.1.2" |
|||
id("java") |
|||
} |
|||
|
|||
group = "net.toomuchram" |
|||
version = "1.0-SNAPSHOT" |
|||
|
|||
repositories { |
|||
mavenCentral() |
|||
} |
|||
|
|||
dependencies { |
|||
implementation("org:jaudiotagger:2.0.3") |
|||
} |
|||
|
|||
tasks.withType<KotlinCompile> { |
|||
kotlinOptions.jvmTarget = "1.8" |
|||
} |
|||
|
|||
application { |
|||
mainClass.set("MainKt") |
|||
} |
@ -0,0 +1 @@ |
|||
kotlin.code.style=official |
Binary file not shown.
@ -0,0 +1,5 @@ |
|||
distributionBase=GRADLE_USER_HOME |
|||
distributionPath=wrapper/dists |
|||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip |
|||
zipStoreBase=GRADLE_USER_HOME |
|||
zipStorePath=wrapper/dists |
@ -0,0 +1,185 @@ |
|||
#!/usr/bin/env sh |
|||
|
|||
# |
|||
# Copyright 2015 the original author or authors. |
|||
# |
|||
# Licensed under the Apache License, Version 2.0 (the "License"); |
|||
# you may not use this file except in compliance with the License. |
|||
# You may obtain a copy of the License at |
|||
# |
|||
# https://www.apache.org/licenses/LICENSE-2.0 |
|||
# |
|||
# Unless required by applicable law or agreed to in writing, software |
|||
# distributed under the License is distributed on an "AS IS" BASIS, |
|||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
# See the License for the specific language governing permissions and |
|||
# limitations under the License. |
|||
# |
|||
|
|||
############################################################################## |
|||
## |
|||
## Gradle start up script for UN*X |
|||
## |
|||
############################################################################## |
|||
|
|||
# Attempt to set APP_HOME |
|||
# Resolve links: $0 may be a link |
|||
PRG="$0" |
|||
# Need this for relative symlinks. |
|||
while [ -h "$PRG" ] ; do |
|||
ls=`ls -ld "$PRG"` |
|||
link=`expr "$ls" : '.*-> \(.*\)$'` |
|||
if expr "$link" : '/.*' > /dev/null; then |
|||
PRG="$link" |
|||
else |
|||
PRG=`dirname "$PRG"`"/$link" |
|||
fi |
|||
done |
|||
SAVED="`pwd`" |
|||
cd "`dirname \"$PRG\"`/" >/dev/null |
|||
APP_HOME="`pwd -P`" |
|||
cd "$SAVED" >/dev/null |
|||
|
|||
APP_NAME="Gradle" |
|||
APP_BASE_NAME=`basename "$0"` |
|||
|
|||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. |
|||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' |
|||
|
|||
# Use the maximum available, or set MAX_FD != -1 to use that value. |
|||
MAX_FD="maximum" |
|||
|
|||
warn () { |
|||
echo "$*" |
|||
} |
|||
|
|||
die () { |
|||
echo |
|||
echo "$*" |
|||
echo |
|||
exit 1 |
|||
} |
|||
|
|||
# OS specific support (must be 'true' or 'false'). |
|||
cygwin=false |
|||
msys=false |
|||
darwin=false |
|||
nonstop=false |
|||
case "`uname`" in |
|||
CYGWIN* ) |
|||
cygwin=true |
|||
;; |
|||
Darwin* ) |
|||
darwin=true |
|||
;; |
|||
MSYS* | MINGW* ) |
|||
msys=true |
|||
;; |
|||
NONSTOP* ) |
|||
nonstop=true |
|||
;; |
|||
esac |
|||
|
|||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar |
|||
|
|||
|
|||
# Determine the Java command to use to start the JVM. |
|||
if [ -n "$JAVA_HOME" ] ; then |
|||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then |
|||
# IBM's JDK on AIX uses strange locations for the executables |
|||
JAVACMD="$JAVA_HOME/jre/sh/java" |
|||
else |
|||
JAVACMD="$JAVA_HOME/bin/java" |
|||
fi |
|||
if [ ! -x "$JAVACMD" ] ; then |
|||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME |
|||
|
|||
Please set the JAVA_HOME variable in your environment to match the |
|||
location of your Java installation." |
|||
fi |
|||
else |
|||
JAVACMD="java" |
|||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. |
|||
|
|||
Please set the JAVA_HOME variable in your environment to match the |
|||
location of your Java installation." |
|||
fi |
|||
|
|||
# Increase the maximum file descriptors if we can. |
|||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then |
|||
MAX_FD_LIMIT=`ulimit -H -n` |
|||
if [ $? -eq 0 ] ; then |
|||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then |
|||
MAX_FD="$MAX_FD_LIMIT" |
|||
fi |
|||
ulimit -n $MAX_FD |
|||
if [ $? -ne 0 ] ; then |
|||
warn "Could not set maximum file descriptor limit: $MAX_FD" |
|||
fi |
|||
else |
|||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" |
|||
fi |
|||
fi |
|||
|
|||
# For Darwin, add options to specify how the application appears in the dock |
|||
if $darwin; then |
|||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" |
|||
fi |
|||
|
|||
# For Cygwin or MSYS, switch paths to Windows format before running java |
|||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then |
|||
APP_HOME=`cygpath --path --mixed "$APP_HOME"` |
|||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` |
|||
|
|||
JAVACMD=`cygpath --unix "$JAVACMD"` |
|||
|
|||
# We build the pattern for arguments to be converted via cygpath |
|||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` |
|||
SEP="" |
|||
for dir in $ROOTDIRSRAW ; do |
|||
ROOTDIRS="$ROOTDIRS$SEP$dir" |
|||
SEP="|" |
|||
done |
|||
OURCYGPATTERN="(^($ROOTDIRS))" |
|||
# Add a user-defined pattern to the cygpath arguments |
|||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then |
|||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" |
|||
fi |
|||
# Now convert the arguments - kludge to limit ourselves to /bin/sh |
|||
i=0 |
|||
for arg in "$@" ; do |
|||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` |
|||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option |
|||
|
|||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition |
|||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` |
|||
else |
|||
eval `echo args$i`="\"$arg\"" |
|||
fi |
|||
i=`expr $i + 1` |
|||
done |
|||
case $i in |
|||
0) set -- ;; |
|||
1) set -- "$args0" ;; |
|||
2) set -- "$args0" "$args1" ;; |
|||
3) set -- "$args0" "$args1" "$args2" ;; |
|||
4) set -- "$args0" "$args1" "$args2" "$args3" ;; |
|||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; |
|||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; |
|||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; |
|||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; |
|||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; |
|||
esac |
|||
fi |
|||
|
|||
# Escape application args |
|||
save () { |
|||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done |
|||
echo " " |
|||
} |
|||
APP_ARGS=`save "$@"` |
|||
|
|||
# Collect all arguments for the java command, following the shell quoting and substitution rules |
|||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" |
|||
|
|||
exec "$JAVACMD" "$@" |
@ -0,0 +1,89 @@ |
|||
@rem |
|||
@rem Copyright 2015 the original author or authors. |
|||
@rem |
|||
@rem Licensed under the Apache License, Version 2.0 (the "License"); |
|||
@rem you may not use this file except in compliance with the License. |
|||
@rem You may obtain a copy of the License at |
|||
@rem |
|||
@rem https://www.apache.org/licenses/LICENSE-2.0 |
|||
@rem |
|||
@rem Unless required by applicable law or agreed to in writing, software |
|||
@rem distributed under the License is distributed on an "AS IS" BASIS, |
|||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
@rem See the License for the specific language governing permissions and |
|||
@rem limitations under the License. |
|||
@rem |
|||
|
|||
@if "%DEBUG%" == "" @echo off |
|||
@rem ########################################################################## |
|||
@rem |
|||
@rem Gradle startup script for Windows |
|||
@rem |
|||
@rem ########################################################################## |
|||
|
|||
@rem Set local scope for the variables with windows NT shell |
|||
if "%OS%"=="Windows_NT" setlocal |
|||
|
|||
set DIRNAME=%~dp0 |
|||
if "%DIRNAME%" == "" set DIRNAME=. |
|||
set APP_BASE_NAME=%~n0 |
|||
set APP_HOME=%DIRNAME% |
|||
|
|||
@rem Resolve any "." and ".." in APP_HOME to make it shorter. |
|||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi |
|||
|
|||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. |
|||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" |
|||
|
|||
@rem Find java.exe |
|||
if defined JAVA_HOME goto findJavaFromJavaHome |
|||
|
|||
set JAVA_EXE=java.exe |
|||
%JAVA_EXE% -version >NUL 2>&1 |
|||
if "%ERRORLEVEL%" == "0" goto execute |
|||
|
|||
echo. |
|||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. |
|||
echo. |
|||
echo Please set the JAVA_HOME variable in your environment to match the |
|||
echo location of your Java installation. |
|||
|
|||
goto fail |
|||
|
|||
:findJavaFromJavaHome |
|||
set JAVA_HOME=%JAVA_HOME:"=% |
|||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe |
|||
|
|||
if exist "%JAVA_EXE%" goto execute |
|||
|
|||
echo. |
|||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% |
|||
echo. |
|||
echo Please set the JAVA_HOME variable in your environment to match the |
|||
echo location of your Java installation. |
|||
|
|||
goto fail |
|||
|
|||
:execute |
|||
@rem Setup the command line |
|||
|
|||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar |
|||
|
|||
|
|||
@rem Execute Gradle |
|||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* |
|||
|
|||
:end |
|||
@rem End local scope for the variables with windows NT shell |
|||
if "%ERRORLEVEL%"=="0" goto mainEnd |
|||
|
|||
:fail |
|||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of |
|||
rem the _cmd.exe /c_ return code! |
|||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 |
|||
exit /b 1 |
|||
|
|||
:mainEnd |
|||
if "%OS%"=="Windows_NT" endlocal |
|||
|
|||
:omega |
@ -0,0 +1,3 @@ |
|||
|
|||
rootProject.name = "FLACFreezerFixer" |
|||
|
@ -0,0 +1,6 @@ |
|||
import flacfixer.TUI |
|||
|
|||
fun main(args: Array<String>) { |
|||
val tui = TUI(args) |
|||
tui.initialise() |
|||
} |
@ -0,0 +1,221 @@ |
|||
package flacfixer |
|||
|
|||
import flacfixer.fixes.* |
|||
import org.jaudiotagger.audio.exceptions.CannotReadException |
|||
import org.jaudiotagger.tag.FieldKey |
|||
import java.io.File |
|||
import java.io.FileNotFoundException |
|||
import java.util.logging.Level |
|||
import java.util.logging.Logger |
|||
import kotlin.system.exitProcess |
|||
|
|||
|
|||
class TUI (private val args: Array<String>) { |
|||
private var yesToAutomaticFix = false |
|||
|
|||
fun initialise() { |
|||
|
|||
setLogLevel() |
|||
|
|||
println("Welcome to FLAC Freezer fixer!") |
|||
if (args.isEmpty()) { |
|||
println("Syntax: java -jar flacfreezerfixer.jar <directory>") |
|||
exitProcess(0) |
|||
} |
|||
|
|||
val dryRun = args.contains("--dry-run") |
|||
|
|||
println("Working directory: ${args[0]}") |
|||
if (dryRun) { |
|||
println("DRY RUN") |
|||
} |
|||
|
|||
mainLoop(args[0], dryRun) |
|||
} |
|||
|
|||
private fun mainLoop(dir: String, dry: Boolean) { |
|||
while (true) { |
|||
println("=======") |
|||
println("0: List all files") |
|||
println("1: Fix duplicate artists") |
|||
println("2: Fix album artist folder") |
|||
println("3: Replace in tags") |
|||
println("4: Fix artist in filename") |
|||
println("Everything else: exit") |
|||
|
|||
print("> ") |
|||
|
|||
when (readln().trim()) { |
|||
"0" -> { |
|||
listAllFiles(dir) |
|||
} |
|||
"1" -> { |
|||
fixDuplicateArtists(dir, dry) |
|||
} |
|||
"2" -> { |
|||
fixAlbumArtistFolder(dir, dry) |
|||
} |
|||
"3" -> { |
|||
replaceInTags(dir, dry) |
|||
} |
|||
"4" -> { |
|||
fixArtistFilename(dir, dry) |
|||
} |
|||
else -> { |
|||
exitProcess(0) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* FIXES |
|||
*/ |
|||
|
|||
private fun listAllFiles(dir: String) { |
|||
File(dir).walkTopDown().forEach { |
|||
if (it.extension == "flac" || it.extension == "mp3") |
|||
println(it.absolutePath) |
|||
} |
|||
|
|||
println("Done!") |
|||
} |
|||
|
|||
private fun fixDuplicateArtists(dir: String, dry: Boolean) { |
|||
fixDirectory(dir) { file -> |
|||
return@fixDirectory runFix(ArtistFix(file, FieldKey.ARTIST), dry) |
|||
} |
|||
} |
|||
|
|||
private fun fixAlbumArtistFolder(dir: String, dry: Boolean) { |
|||
fixDirectory(dir) { file -> |
|||
return@fixDirectory runFix( |
|||
FolderFix(file), |
|||
dry |
|||
) |
|||
} |
|||
println("You should probably delete empty directories") |
|||
println("Sample command: find $dir -empty -type d -delete") |
|||
} |
|||
|
|||
private fun replaceInTags(dir: String, dry: Boolean) { |
|||
print("What should be replaced? ") |
|||
val targetString = readln() |
|||
print("What should it be replaced with? ") |
|||
val replaceString = readln() |
|||
|
|||
val fieldKey: FieldKey |
|||
do { |
|||
print("Should we look in [1] artist, [2] album artist or [3] song title? ") |
|||
when (readln()) { |
|||
"1" -> { |
|||
fieldKey = FieldKey.ARTIST |
|||
break |
|||
} |
|||
"2" -> { |
|||
fieldKey = FieldKey.ALBUM_ARTIST |
|||
break |
|||
} |
|||
"3" -> { |
|||
fieldKey = FieldKey.ALBUM_ARTIST |
|||
break |
|||
} |
|||
else -> { |
|||
println("Please input 1, 2 or 3") |
|||
continue |
|||
} |
|||
} |
|||
} while (true) |
|||
|
|||
fixDirectory(dir) { file -> |
|||
return@fixDirectory runFix( |
|||
ReplaceFix(file, fieldKey, targetString, replaceString), |
|||
dry |
|||
) |
|||
} |
|||
} |
|||
|
|||
private fun fixArtistFilename(dir: String, dry: Boolean) { |
|||
fixDirectory(dir) { file -> |
|||
return@fixDirectory runFix(FilenameFix(file), dry) |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Fix all files in a directory |
|||
* @param dir The directory to run in |
|||
* @param function The fix function, which is given a file and returns if fixDirectory should keep going |
|||
*/ |
|||
private fun fixDirectory(dir:String, function: (file: File) -> Boolean) { |
|||
yesToAutomaticFix = false |
|||
for (it in File(dir).walkTopDown()) { |
|||
if (it.extension == "flac" || it.extension == "mp3") { |
|||
try { |
|||
if (!function(it)) { |
|||
break |
|||
} |
|||
} catch (e: CannotReadException) { |
|||
println("Could not open file: " + it.absolutePath) |
|||
} catch (e: FileNotFoundException) { |
|||
println("Could not open file (probably due to Unicode characters): ${it.absolutePath}") |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
/** |
|||
* Run the specified fix, in the same way every time |
|||
* @param fix The fix to be ran, which is an implementation of the interface Fix |
|||
* @param dry Do a dry run |
|||
* @return Boolean indicating whether to continue on or break the loop |
|||
*/ |
|||
private fun runFix(fix: Fix, dry: Boolean): Boolean { |
|||
if (fix.isFixNeeded()) { |
|||
println("----") |
|||
println("File: " + fix.file.absolutePath) |
|||
println(fix.getFixReason()) |
|||
print("Fix? [(y)es/(m)anually/(N)o] ") |
|||
val line = if (yesToAutomaticFix) { |
|||
print("\n") |
|||
"a" |
|||
} else { |
|||
readln().trim() |
|||
} |
|||
|
|||
if (line == "q") { |
|||
return false |
|||
} |
|||
|
|||
if (line == "All") { |
|||
yesToAutomaticFix = true |
|||
} |
|||
|
|||
if (line == "y" || line == "Y" || yesToAutomaticFix) { |
|||
println("Automatically fixing...") |
|||
val newValue = fix.automaticFix() |
|||
println("New value: $newValue") |
|||
if (!dry) |
|||
fix.manualFix(newValue) |
|||
} |
|||
if (line == "m" || line == "M") { |
|||
println("Please enter a new value here: ") |
|||
val newValue = readln() |
|||
println("New value: $newValue") |
|||
if (!dry) |
|||
fix.manualFix(newValue) |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
|
|||
private fun setLogLevel() { |
|||
val targetLevel = Level.WARNING |
|||
val root: Logger = Logger.getLogger("") |
|||
root.level = targetLevel |
|||
for (handler in root.handlers) { |
|||
handler.level = targetLevel |
|||
} |
|||
println("level set: " + targetLevel.name) |
|||
} |
|||
} |
@ -0,0 +1,58 @@ |
|||
package flacfixer.fixes |
|||
|
|||
import org.jaudiotagger.audio.AudioFile |
|||
import org.jaudiotagger.audio.AudioFileIO |
|||
import org.jaudiotagger.tag.FieldKey |
|||
import java.io.File |
|||
|
|||
class ArtistFix(override val file: File, override val field: FieldKey) : Fix{ |
|||
|
|||
override val audioFile: AudioFile = AudioFileIO.read(file) |
|||
|
|||
private val ARTIST_WHITELIST = listOf("D-block & S-te-fan", "Jeckyll & Hyde", "Streiks & Kratchs", "Josh & Wesz", "Degos & Re-Done", "Harris & Ford", "Dimitri Vegas & Like Mike") |
|||
|
|||
override fun getFixReason(): String { |
|||
return "Suspicious ${field.name}: " + audioFile.tag.getFirst(field) |
|||
} |
|||
|
|||
override fun isFixNeeded(): Boolean { |
|||
val artists = audioFile.tag.getFirst(field) |
|||
|
|||
|
|||
val artistArray = artists.split(", ") |
|||
artistArray.forEachIndexed { index, artist -> |
|||
if (artist.contains(" and ") || artist.contains("feat") || artist.contains("/") || (artist.contains(" & ") && !ARTIST_WHITELIST.contains(artist))) { |
|||
return true |
|||
} |
|||
|
|||
artistArray.forEachIndexed { index2, artist2 -> |
|||
if ((artist.contains(artist2) || artist2.contains(artist)) && index != index2) |
|||
return true |
|||
} |
|||
} |
|||
|
|||
return false |
|||
} |
|||
|
|||
override fun automaticFix(): String { |
|||
val artists = audioFile.tag.getFirst(field).replace("/", ", ") |
|||
|
|||
val artistArray = artists.split(", ") |
|||
|
|||
val newArtists = mutableListOf<String>() |
|||
artistArray.forEach { artist -> |
|||
var result = true |
|||
for (x in newArtists) { |
|||
if (x.contains(artist)) { |
|||
result = false |
|||
} |
|||
} |
|||
|
|||
if (result) { |
|||
newArtists.add(artist) |
|||
} |
|||
} |
|||
|
|||
return newArtists.joinToString(", ") |
|||
} |
|||
} |
@ -0,0 +1,42 @@ |
|||
package flacfixer.fixes |
|||
|
|||
import org.jaudiotagger.audio.AudioFile |
|||
import org.jaudiotagger.audio.AudioFileIO |
|||
import org.jaudiotagger.tag.FieldKey |
|||
import java.io.File |
|||
|
|||
class FilenameFix(override val file: File) : Fix { |
|||
override val field: FieldKey = FieldKey.ARTIST |
|||
override val audioFile: AudioFile = AudioFileIO.read(file) |
|||
|
|||
|
|||
override fun getFixReason(): String { |
|||
return "Artist in filename is not the same as in the metadata (${audioFile.tag.getFirst(field)})" |
|||
} |
|||
|
|||
override fun isFixNeeded(): Boolean { |
|||
val artist = audioFile.tag.getFirst(field) |
|||
val filename = file.nameWithoutExtension |
|||
val split = filename.split(" - ") |
|||
if (split.count() < 2) { |
|||
return false |
|||
} |
|||
val filenameArtist = split[0] |
|||
return artist != filenameArtist |
|||
} |
|||
|
|||
override fun automaticFix(): String { |
|||
return audioFile.tag.getFirst(field) |
|||
} |
|||
|
|||
override fun manualFix(newValue: String) { |
|||
val filename = file.name |
|||
val split = filename.split(" - ") |
|||
|
|||
|
|||
val newFilename = "$newValue - ${split.subList(1, split.count()).joinToString(" - ")}" |
|||
val newFile = File(file.parent + "/" + newFilename) |
|||
|
|||
file.renameTo(newFile) |
|||
} |
|||
} |
@ -0,0 +1,27 @@ |
|||
package flacfixer.fixes |
|||
|
|||
import org.jaudiotagger.audio.AudioFile |
|||
import org.jaudiotagger.audio.exceptions.CannotWriteException |
|||
import org.jaudiotagger.tag.FieldKey |
|||
import java.io.File |
|||
|
|||
interface Fix { |
|||
val file: File |
|||
val field: FieldKey |
|||
|
|||
val audioFile: AudioFile |
|||
|
|||
fun getFixReason(): String |
|||
fun isFixNeeded(): Boolean |
|||
|
|||
fun automaticFix(): String |
|||
fun manualFix(newValue: String) { |
|||
audioFile.tag.deleteField(field) |
|||
audioFile.tag.setField(field, newValue) |
|||
try { |
|||
audioFile.commit() |
|||
} catch (e: CannotWriteException) { |
|||
println("ERROR: Could not write file!") |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,51 @@ |
|||
package flacfixer.fixes |
|||
|
|||
import org.jaudiotagger.audio.AudioFile |
|||
import org.jaudiotagger.audio.AudioFileIO |
|||
import org.jaudiotagger.tag.FieldKey |
|||
import java.io.File |
|||
import java.nio.file.Files |
|||
|
|||
class FolderFix(override val file: File) : Fix { |
|||
|
|||
override val audioFile: AudioFile = AudioFileIO.read(file) |
|||
override val field: FieldKey = FieldKey.ALBUM_ARTIST |
|||
|
|||
override fun getFixReason(): String { |
|||
val directory = audioFile.file.parentFile.parent.split("/").last() |
|||
val tags = audioFile.tag.getFirst(field) |
|||
return "Parent directory $directory is not the same as ${field.name} $tags" |
|||
} |
|||
|
|||
override fun isFixNeeded(): Boolean { |
|||
val directory = audioFile.file.parentFile.parent.split("/").last() |
|||
val tags = audioFile.tag.getFirst(field) |
|||
if (tags == "") { |
|||
println("Field is EMPTY for file " + audioFile.file.absolutePath) |
|||
return false |
|||
} |
|||
return directory != tags |
|||
} |
|||
|
|||
override fun automaticFix(): String { |
|||
return audioFile.tag.getFirst(field) |
|||
} |
|||
|
|||
override fun manualFix(newValue: String) { |
|||
// Check if the directory already exists |
|||
val parentParent = audioFile.file.parentFile.parentFile.parentFile |
|||
val album = audioFile.file.parent.split("/").last() |
|||
val newDirectory = parentParent.resolve("$newValue/$album") |
|||
// If not, create it |
|||
if (!newDirectory.exists()) { |
|||
newDirectory.mkdirs() |
|||
} |
|||
|
|||
Files.move( |
|||
audioFile.file.toPath(), |
|||
newDirectory.resolve( |
|||
file.absolutePath.split("/").last() |
|||
).toPath() |
|||
) |
|||
} |
|||
} |
@ -0,0 +1,31 @@ |
|||
package flacfixer.fixes |
|||
|
|||
import org.jaudiotagger.audio.AudioFile |
|||
import org.jaudiotagger.audio.AudioFileIO |
|||
import org.jaudiotagger.tag.FieldKey |
|||
import java.io.File |
|||
|
|||
class ReplaceFix( |
|||
override val file: File, |
|||
override val field: FieldKey, |
|||
private val targetString: String, |
|||
private val replaceString: String |
|||
) : Fix { |
|||
|
|||
override val audioFile: AudioFile = AudioFileIO.read(file) |
|||
|
|||
override fun getFixReason(): String { |
|||
val tags = audioFile.tag.getFirst(field) |
|||
return field.name + " ($tags) includes $targetString" |
|||
} |
|||
|
|||
override fun isFixNeeded(): Boolean { |
|||
val tags = audioFile.tag.getFirst(field) |
|||
return tags.contains(targetString) |
|||
} |
|||
|
|||
override fun automaticFix(): String { |
|||
val original = audioFile.tag.getFirst(field) |
|||
return original.replace(targetString, replaceString) |
|||
} |
|||
} |
@ -0,0 +1,2 @@ |
|||
handlers= java.util.logging.ConsoleHandler |
|||
org.jaudiotagger.level = WARNING |
Loading…
Reference in new issue