From 6f6f8cf605c362711411e0e781266a1ff90e5a44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20B=C3=B6ckelmann?= Date: Sat, 12 Apr 2025 13:54:48 +0200 Subject: [PATCH] Automatic Recognize Title --- .idea/dataSources.xml | 23 ++++++ .idea/deploymentTargetSelector.xml | 18 +++++ .idea/misc.xml | 1 - .../notevault/network/sync/SongSyncAPI.java | 4 + .../network/sync/SongSyncService.java | 20 +++++ .../network/sync/models/AIRecognizedSong.java | 40 ++++++++++ .../ui/songeditor/SongEditorDialog.java | 77 +++++++++++++++++-- .../layout/fragment_song_editor_dialog.xml | 24 ++++++ 8 files changed, 201 insertions(+), 6 deletions(-) create mode 100644 .idea/dataSources.xml create mode 100644 .idea/deploymentTargetSelector.xml create mode 100644 app/src/main/java/com/stormtales/notevault/network/sync/models/AIRecognizedSong.java diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..5c70232 --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,23 @@ + + + + + sqlite.xerial + true + org.sqlite.JDBC + jdbc:sqlite:$PROJECT_DIR$/music_database + + + + $ProjectFileDir$ + + + file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/xerial/sqlite-jdbc/3.45.1.0/sqlite-jdbc-3.45.1.0.jar + + + file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..818c0d7 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,18 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 1d038f9..7c2d85a 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/app/src/main/java/com/stormtales/notevault/network/sync/SongSyncAPI.java b/app/src/main/java/com/stormtales/notevault/network/sync/SongSyncAPI.java index be474fc..2bb30d0 100644 --- a/app/src/main/java/com/stormtales/notevault/network/sync/SongSyncAPI.java +++ b/app/src/main/java/com/stormtales/notevault/network/sync/SongSyncAPI.java @@ -38,4 +38,8 @@ public interface SongSyncAPI { @GET("/sync/songs/get/notesheet/") Call downloadNotesheet(@Query("server_filename") String server_filename); + + @Multipart + @POST("/ai/regognize/title") + Call recognizeTitle(@Part MultipartBody.Part image); } diff --git a/app/src/main/java/com/stormtales/notevault/network/sync/SongSyncService.java b/app/src/main/java/com/stormtales/notevault/network/sync/SongSyncService.java index e1cf87a..60ea902 100644 --- a/app/src/main/java/com/stormtales/notevault/network/sync/SongSyncService.java +++ b/app/src/main/java/com/stormtales/notevault/network/sync/SongSyncService.java @@ -215,6 +215,22 @@ public class SongSyncService { }); } + public void recognizeTitle(String firstNoteSheet, RecognizedSongCallback callback) { + RequestBody requestFile = RequestBody.create(MediaType.parse("image/jpeg"), new File(firstNoteSheet)); + MultipartBody.Part image = MultipartBody.Part.createFormData("image", firstNoteSheet, requestFile); + songSyncAPI.recognizeTitle(image).enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + callback.onRecognizedSong(response.body()); + } + + @Override + public void onFailure(Call call, Throwable throwable) { + Log.d("SongSyncService", "Recognition failed: " + throwable.getMessage(), throwable); + } + }); + } + public interface SyncDeletedSongsCallback { void finishSongSyncing(List remoteDeletedSongs, List localDeletedSongs); @@ -223,4 +239,8 @@ public class SongSyncService { public interface UploadNoteSheetCallback { void finishUploadNoteSheets(List uploadResponses); } + + public interface RecognizedSongCallback { + void onRecognizedSong(AIRecognizedSong aiRecognizedSong); + } } diff --git a/app/src/main/java/com/stormtales/notevault/network/sync/models/AIRecognizedSong.java b/app/src/main/java/com/stormtales/notevault/network/sync/models/AIRecognizedSong.java new file mode 100644 index 0000000..fc42783 --- /dev/null +++ b/app/src/main/java/com/stormtales/notevault/network/sync/models/AIRecognizedSong.java @@ -0,0 +1,40 @@ +package com.stormtales.notevault.network.sync.models; + +public class AIRecognizedSong { + private String title; + private int year; + private String composer; + private String description; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public int getYear() { + return year; + } + + public void setYear(int year) { + this.year = year; + } + + public String getComposer() { + return composer; + } + + public void setComposer(String composer) { + this.composer = composer; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } +} diff --git a/app/src/main/java/com/stormtales/notevault/ui/songeditor/SongEditorDialog.java b/app/src/main/java/com/stormtales/notevault/ui/songeditor/SongEditorDialog.java index b8af916..ade8b40 100644 --- a/app/src/main/java/com/stormtales/notevault/ui/songeditor/SongEditorDialog.java +++ b/app/src/main/java/com/stormtales/notevault/ui/songeditor/SongEditorDialog.java @@ -6,6 +6,7 @@ import android.content.Context; import android.net.Uri; import android.os.Bundle; import android.text.Layout; +import android.util.Log; import android.widget.Button; import android.widget.EditText; import androidx.annotation.NonNull; @@ -20,11 +21,17 @@ import com.stormtales.notevault.R; import com.stormtales.notevault.data.entities.NoteSheet; import com.stormtales.notevault.data.entities.Song; import com.stormtales.notevault.data.sync.SyncStatus; +import com.stormtales.notevault.network.sync.SongSyncService; +import com.stormtales.notevault.network.sync.models.AIRecognizedSong; import com.stormtales.notevault.ui.home.HomeViewModel; import com.stormtales.notevault.utils.NoteSheetsUtil; import com.stormtales.notevault.utils.Tupel; import org.jetbrains.annotations.NotNull; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; import java.util.ArrayList; import java.util.List; @@ -36,9 +43,12 @@ public class SongEditorDialog extends DialogFragment { private HomeViewModel homeViewModel; private Song editedSong; + private SongSyncService songSyncService; + private View progressbar; public SongEditorDialog() { // Required empty public constructor + this.songSyncService = new SongSyncService(this.getContext()); } @@ -49,21 +59,79 @@ public class SongEditorDialog extends DialogFragment { View dialogView = inflater.inflate(R.layout.fragment_song_editor_dialog, null); - dialogView.findViewById(R.id.btnCancel).setOnClickListener(v-> onCancel()); dialogView.findViewById(R.id.btnSave).setOnClickListener(v -> onSave()); + if(this.noteSheetFiles == null || noteSheetFiles.length == 0) { + dialogView.findViewById(R.id.btnAutoDetect).setEnabled(false); + } else { + progressbar = dialogView.findViewById(R.id.spinnerAutoDetect); + + } + + dialogView.findViewById(R.id.btnAutoDetect).setOnClickListener(v -> { + progressbar.setVisibility(ViewGroup.VISIBLE); + extractTitleFromFirstPage(); + }); + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); builder.setCancelable(false); builder.setView(dialogView); dialog = builder.create(); dialog.setCanceledOnTouchOutside(false); - - return dialog; } + private File createTemporaryFileForRecognition(Uri noteSheetUri) { + try { + // Hole den InputStream des Bildes + InputStream inputStream = getContext().getContentResolver().openInputStream(noteSheetUri); + if (inputStream != null) { + // Erstelle eine temporäre Datei + File tempFile = File.createTempFile("temp_note_sheet", ".jpg", getContext().getCacheDir()); + + // Inhalt des InputStreams in die temporäre Datei schreiben + try (OutputStream outputStream = new FileOutputStream(tempFile)) { + byte[] buffer = new byte[8192]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + } + + // Schließe den InputStream + inputStream.close(); + + return tempFile; + } + } catch (Exception e) { + Log.e("SongEditorDialog", "Fehler beim Erstellen der temporären Datei: " + e.getMessage()); + } + + return null; + } + private void extractTitleFromFirstPage() { + if(noteSheetFiles != null && noteSheetFiles.length > 0) { + Uri firstPageUri = noteSheetFiles[0]; + + // Temporäre Datei erstellen + File tempFile = createTemporaryFileForRecognition(firstPageUri); + if(tempFile != null) { + songSyncService.recognizeTitle(tempFile.getAbsolutePath(), new SongSyncService.RecognizedSongCallback() { + @Override + public void onRecognizedSong(AIRecognizedSong aiRecognizedSong) { + progressbar.setVisibility(ViewGroup.GONE); + ((EditText) dialog.findViewById(R.id.etTitle)).setText(aiRecognizedSong.getTitle()); + ((EditText) dialog.findViewById(R.id.etComposer)).setText(aiRecognizedSong.getComposer()); + ((EditText) dialog.findViewById(R.id.etYear)).setText(String.valueOf(aiRecognizedSong.getYear())); + ((EditText) dialog.findViewById(R.id.etGenre)).setText(aiRecognizedSong.getDescription()); + } + }); + } + } + } + @Override public void onStart() { super.onStart(); @@ -97,13 +165,12 @@ public class SongEditorDialog extends DialogFragment { } else { Song song = new Song(title, composer, genre, releaseYear); - List noteSheetList = new ArrayList<>(); Context context = this.getContext(); + List noteSheetList = new ArrayList<>(); for(Uri uri : noteSheetFiles) { Tupel result = NoteSheetsUtil.saveImageInternally(context.getContentResolver(), uri, context.getFilesDir()); noteSheetList.add(new NoteSheet(result.getValue00(), result.getValue01())); } - homeViewModel.addSong(song, noteSheetList); } diff --git a/app/src/main/res/layout/fragment_song_editor_dialog.xml b/app/src/main/res/layout/fragment_song_editor_dialog.xml index eefee44..306a975 100644 --- a/app/src/main/res/layout/fragment_song_editor_dialog.xml +++ b/app/src/main/res/layout/fragment_song_editor_dialog.xml @@ -96,6 +96,30 @@ android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="end"> + + + + +