From 4e93c857b5c3339043ba2a815a05fa5f50f6a24a Mon Sep 17 00:00:00 2001 From: Chigozirim Igweamaka Date: Thu, 9 May 2024 20:48:49 +0100 Subject: [PATCH] Make 'wav' module separating it from 'utils'. --- utils/dbClient.go | 216 +++++++++++++++++++----------------------- utils/utils.go | 17 ++++ wav/convert.go | 41 ++++++++ {utils => wav}/wav.go | 2 +- 4 files changed, 159 insertions(+), 117 deletions(-) create mode 100644 utils/utils.go create mode 100644 wav/convert.go rename {utils => wav}/wav.go (99%) diff --git a/utils/dbClient.go b/utils/dbClient.go index 6e191a9..b506a41 100644 --- a/utils/dbClient.go +++ b/utils/dbClient.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "song-recognition/models" + "strings" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" @@ -111,7 +112,7 @@ func (db *DbClient) GetTables(addresses []uint32) (map[uint32][]models.Table, er table := models.Table{ AnchorTimeMs: uint32(itemMap["anchorTimeMs"].(int64)), - SongID: itemMap["songID"].(string), + SongID: uint32(itemMap["songID"].(int64)), } docTables = append(docTables, table) } @@ -122,7 +123,7 @@ func (db *DbClient) GetTables(addresses []uint32) (map[uint32][]models.Table, er } func (db *DbClient) TotalSongs() (int, error) { - existingSongsCollection := db.client.Database("song-recognition").Collection("existing-songs") + existingSongsCollection := db.client.Database("song-recognition").Collection("songs") total, err := existingSongsCollection.CountDocuments(context.Background(), bson.D{}) if err != nil { return 0, err @@ -131,29 +132,7 @@ func (db *DbClient) TotalSongs() (int, error) { return int(total), nil } -func (db *DbClient) SongExists(songTitle, songArtist, ytID string) (bool, error) { - existingSongsCollection := db.client.Database("song-recognition").Collection("existing-songs") - - key := fmt.Sprintf("%s - %s", songTitle, songArtist) - var filter bson.M - - if len(ytID) == 0 { - filter = bson.M{"_id": key} - } else { - filter = bson.M{"ytID": ytID} - } - - var result bson.M - if err := existingSongsCollection.FindOne(context.Background(), filter).Decode(&result); err == nil { - return true, nil - } else if err != mongo.ErrNoDocuments { - return false, fmt.Errorf("failed to retrieve registered songs: %v", err) - } - - return false, nil -} - -func (db *DbClient) RegisterSong(songTitle, songArtist, ytID string) error { +func (db *DbClient) RegisterSong(songTitle, songArtist, ytID string) (uint32, error) { existingSongsCollection := db.client.Database("song-recognition").Collection("songs") // Create a compound unique index on ytID and key, if it doesn't already exist @@ -163,105 +142,110 @@ func (db *DbClient) RegisterSong(songTitle, songArtist, ytID string) error { } _, err := existingSongsCollection.Indexes().CreateOne(context.Background(), indexModel) if err != nil { - return fmt.Errorf("failed to create unique index: %v", err) + return 0, fmt.Errorf("failed to create unique index: %v", err) } // Attempt to insert the song with ytID and key - key := fmt.Sprintf("%s - %s", songTitle, songArtist) - _, err = existingSongsCollection.InsertOne(context.Background(), bson.M{"_id": key, "ytID": ytID}) + songID := GenerateUniqueID() + key := GenerateSongKey(songTitle, songArtist) + _, err = existingSongsCollection.InsertOne(context.Background(), bson.M{"_id": songID, "key": key, "ytID": ytID}) if err != nil { if mongo.IsDuplicateKeyError(err) { - return fmt.Errorf("song with ytID or key already exists: %v", err) + return 0, fmt.Errorf("song with ytID or key already exists: %v", err) } else { - return fmt.Errorf("failed to register song: %v", err) + return 0, fmt.Errorf("failed to register song: %v", err) } } + return songID, nil +} + +type Song struct { + Title string + Artist string + YouTubeID string +} + +func (db *DbClient) GetSongByID(songID uint32) (Song, error) { + songsCollection := db.client.Database("song-recognition").Collection("songs") + + var song bson.M + + filter := bson.M{"_id": songID} + + err := songsCollection.FindOne(context.Background(), filter).Decode(&song) + if err != nil { + if err == mongo.ErrNoDocuments { + return Song{}, fmt.Errorf("song not found") + } + return Song{}, fmt.Errorf("failed to retrieve song: %v", err) + } + + ytID := song["ytID"].(string) + title := strings.Split(song["key"].(string), "---")[0] + artist := strings.Split(song["key"].(string), "---")[1] + + songInstance := Song{title, artist, ytID} + + return songInstance, nil +} + +func (db *DbClient) GetSongByYTID(ytID string) (Song, error) { + songsCollection := db.client.Database("song-recognition").Collection("songs") + + var song bson.M + + filter := bson.M{"ytID": ytID} + + err := songsCollection.FindOne(context.Background(), filter).Decode(&song) + if err != nil { + if err == mongo.ErrNoDocuments { + return Song{}, fmt.Errorf("song not found") + } + return Song{}, fmt.Errorf("failed to retrieve song: %v", err) + } + + title := strings.Split(song["key"].(string), "---")[0] + artist := strings.Split(song["key"].(string), "---")[1] + + songInstance := Song{title, artist, song["ytID"].(string)} + + return songInstance, nil +} + +func (db *DbClient) GetSongByKey(key string) (Song, error) { + songsCollection := db.client.Database("song-recognition").Collection("songs") + + var song bson.M + + filter := bson.M{"key": key} + + err := songsCollection.FindOne(context.Background(), filter).Decode(&song) + if err != nil { + if err == mongo.ErrNoDocuments { + return Song{}, fmt.Errorf("song not found") + } + return Song{}, fmt.Errorf("failed to retrieve song: %v", err) + } + + ytID := song["ytID"].(string) + title := strings.Split(song["key"].(string), "---")[0] + artist := strings.Split(song["key"].(string), "---")[1] + + songInstance := Song{title, artist, ytID} + + return songInstance, nil +} + +func (db *DbClient) DeleteSongByID(songID uint32) error { + songsCollection := db.client.Database("song-recognition").Collection("songs") + + filter := bson.M{"_id": songID} + + _, err := songsCollection.DeleteOne(context.Background(), filter) + if err != nil { + return fmt.Errorf("failed to delete song: %v", err) + } + return nil } - -func (db *DbClient) InsertChunkTag(chunkfgp int64, chunkTag interface{}) error { - chunksCollection := db.client.Database("song-recognition").Collection("chunks") - - filter := bson.M{"fingerprint": chunkfgp} - - var result bson.M - err := chunksCollection.FindOne(context.Background(), filter).Decode(&result) - if err == nil { - // If the fingerprint already exists, append the chunkTag to the existing list - // fmt.Println("DUPLICATE FINGERPRINT: ", chunkfgp) - update := bson.M{"$push": bson.M{"chunkTags": chunkTag}} - _, err := chunksCollection.UpdateOne(context.Background(), filter, update) - if err != nil { - return fmt.Errorf("failed to update chunkTags: %v", err) - } - return nil - } else if err != mongo.ErrNoDocuments { - return err - } - - // If the document doesn't exist, insert a new document - _, err = chunksCollection.InsertOne(context.Background(), bson.M{"fingerprint": chunkfgp, "chunkTags": []interface{}{chunkTag}}) - if err != nil { - return fmt.Errorf("failed to insert chunk tag: %v", err) - } - - return nil -} - -func (db *DbClient) GetChunkTags(chunkfgp int64) ([]primitive.M, error) { - chunksCollection := db.client.Database("song-recognition").Collection("chunks") - - filter := bson.M{"fingerprint": chunkfgp} - result := bson.M{} - err := chunksCollection.FindOne(context.Background(), filter).Decode(&result) - - if err != nil { - if err == mongo.ErrNoDocuments { - return nil, nil - } - return nil, fmt.Errorf("failed to retrieve chunk tag: %w", err) - } - - var listOfChunkTags []primitive.M - for _, data := range result["chunkTags"].(primitive.A) { - listOfChunkTags = append(listOfChunkTags, data.(primitive.M)) - } - - return listOfChunkTags, nil -} - -func (db *DbClient) GetChunkTagForSong(songTitle, songArtist string) (bson.M, error) { - chunksCollection := db.client.Database("song-recognition").Collection("chunks") - - filter := bson.M{ - "chunkTags": bson.M{ - "$elemMatch": bson.M{ - "songtitle": songTitle, - "songartist": songArtist, - }, - }, - } - - var result bson.M - if err := chunksCollection.FindOne(context.Background(), filter).Decode(&result); err != nil { - if err == mongo.ErrNoDocuments { - return nil, nil - } - return nil, fmt.Errorf("failed to find chunk: %v", err) - } - - var chunkTag map[string]interface{} - for _, chunk := range result["chunkTags"].(primitive.A) { - chunkMap, ok := chunk.(primitive.M) - if !ok { - continue - } - if chunkMap["songtitle"] == songTitle && chunkMap["songartist"] == songArtist { - chunkTag = chunkMap - break - } - } - - return chunkTag, nil -} diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..e80b384 --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,17 @@ +package utils + +import ( + "math/rand" + "time" +) + +func GenerateUniqueID() uint32 { + rand.Seed(time.Now().UnixNano()) + randomNumber := rand.Uint32() + + return randomNumber +} + +func GenerateSongKey(songTitle, songArtist string) string { + return songTitle + "---" + songArtist +} diff --git a/wav/convert.go b/wav/convert.go new file mode 100644 index 0000000..fb72fda --- /dev/null +++ b/wav/convert.go @@ -0,0 +1,41 @@ +package wav + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" +) + +func ConvertToWAV(inputFilePath string, channels int) (wavFilePath string, errr error) { + _, err := os.Stat(inputFilePath) + if err != nil { + return "", fmt.Errorf("input file does not exist: %v", err) + } + + if channels != 1 || channels != 2 { + channels = 1 + } + + fileExt := filepath.Ext(inputFilePath) + outputFile := strings.TrimSuffix(inputFilePath, fileExt) + ".wav" + + // Execute FFmpeg command to convert to WAV format with one channel (mono) + cmd := exec.Command( + "ffmpeg", + "-y", // Automatically overwrite if file exists + "-i", inputFilePath, + "-c", "pcm_s16le", // Output PCM signed 16-bit little-endian audio + "-ar", "44100", + "-ac", fmt.Sprint(channels), + outputFile, + ) + + output, err := cmd.CombinedOutput() + if err != nil { + return "", fmt.Errorf("failed to convert to WAV: %v, output %v", err, string(output)) + } + + return outputFile, nil +} diff --git a/utils/wav.go b/wav/wav.go similarity index 99% rename from utils/wav.go rename to wav/wav.go index 857efcd..8b99bbc 100644 --- a/utils/wav.go +++ b/wav/wav.go @@ -1,4 +1,4 @@ -package utils +package wav import ( "bytes"