Automatic Recognize Title

This commit is contained in:
Sebastian Böckelmann 2025-04-12 13:54:48 +02:00
parent 648bd66ba4
commit 6f6f8cf605
8 changed files with 201 additions and 6 deletions

23
.idea/dataSources.xml Normal file
View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="music_database" uuid="eb23f694-6586-450b-8f6f-a75731d36b96">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/music_database</jdbc-url>
<jdbc-additional-properties>
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
</jdbc-additional-properties>
<working-dir>$ProjectFileDir$</working-dir>
<libraries>
<library>
<url>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</url>
</library>
<library>
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar</url>
</library>
</libraries>
</data-source>
</component>
</project>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-04-10T18:07:32.446414465Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=R52N50NLGRT" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
</selectionStates>
</component>
</project>

View File

@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="FrameworkDetectionExcludesConfiguration">

View File

@ -38,4 +38,8 @@ public interface SongSyncAPI {
@GET("/sync/songs/get/notesheet/")
Call<ResponseBody> downloadNotesheet(@Query("server_filename") String server_filename);
@Multipart
@POST("/ai/regognize/title")
Call<AIRecognizedSong> recognizeTitle(@Part MultipartBody.Part image);
}

View File

@ -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<AIRecognizedSong>() {
@Override
public void onResponse(Call<AIRecognizedSong> call, Response<AIRecognizedSong> response) {
callback.onRecognizedSong(response.body());
}
@Override
public void onFailure(Call<AIRecognizedSong> call, Throwable throwable) {
Log.d("SongSyncService", "Recognition failed: " + throwable.getMessage(), throwable);
}
});
}
public interface SyncDeletedSongsCallback {
void finishSongSyncing(List<String> remoteDeletedSongs, List<Integer> localDeletedSongs);
@ -223,4 +239,8 @@ public class SongSyncService {
public interface UploadNoteSheetCallback {
void finishUploadNoteSheets(List<UploadResponse> uploadResponses);
}
public interface RecognizedSongCallback {
void onRecognizedSong(AIRecognizedSong aiRecognizedSong);
}
}

View File

@ -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;
}
}

View File

@ -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<NoteSheet> noteSheetList = new ArrayList<>();
Context context = this.getContext();
List<NoteSheet> noteSheetList = new ArrayList<>();
for(Uri uri : noteSheetFiles) {
Tupel<String, String> result = NoteSheetsUtil.saveImageInternally(context.getContentResolver(), uri, context.getFilesDir());
noteSheetList.add(new NoteSheet(result.getValue00(), result.getValue01()));
}
homeViewModel.addSong(song, noteSheetList);
}

View File

@ -96,6 +96,30 @@
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="end">
<!-- ProgressBar als Spinner -->
<ProgressBar
android:id="@+id/spinnerAutoDetect"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="16dp"
android:baselineAligned="false"
android:visibility="gone"
android:indeterminate="true" />
<TextView
android:id="@+id/btnAutoDetect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Automatische Erkennung"
android:textColor="@color/light_blue_600"
android:paddingEnd="16dp"
android:clickable="true"
android:focusable="true"
android:textSize="16sp"
android:layout_gravity="center_vertical"
android:fontFamily="sans-serif-medium" />
<Button
android:id="@+id/btnCancel"