From 11e13f144ed8af50738a4ee4f310774e8247376e Mon Sep 17 00:00:00 2001 From: Chigozirim Igweamaka Date: Sun, 4 Aug 2024 22:27:14 +0100 Subject: [PATCH] Separate database operations into a dedicated package --- cmdHandlers.go | 3 +- shazam/shazam.go | 3 +- shazam/shazamInit.go | 3 +- socketHandlers.go | 5 +- spotify/downloader.go | 15 +-- spotify/utils.go | 6 +- utils/dbClient.go | 225 ------------------------------------------ 7 files changed, 20 insertions(+), 240 deletions(-) delete mode 100644 utils/dbClient.go diff --git a/cmdHandlers.go b/cmdHandlers.go index df43683..71e64d0 100644 --- a/cmdHandlers.go +++ b/cmdHandlers.go @@ -10,6 +10,7 @@ import ( "net/http" "os" "path/filepath" + "song-recognition/db" "song-recognition/shazam" "song-recognition/spotify" "song-recognition/utils" @@ -195,7 +196,7 @@ func erase(songsDir string) { ctx := context.Background() // wipe db - dbClient, err := utils.NewDbClient() + dbClient, err := db.NewDBClient() if err != nil { msg := fmt.Sprintf("Error creating DB client: %v\n", err) logger.ErrorContext(ctx, msg, slog.Any("error", err)) diff --git a/shazam/shazam.go b/shazam/shazam.go index 12e7d3a..a3e5f68 100644 --- a/shazam/shazam.go +++ b/shazam/shazam.go @@ -3,6 +3,7 @@ package shazam import ( "fmt" "math" + "song-recognition/db" "song-recognition/utils" "sort" "time" @@ -35,7 +36,7 @@ func FindMatches(audioSamples []float64, audioDuration float64, sampleRate int) addresses = append(addresses, address) } - db, err := utils.NewDbClient() + db, err := db.NewDBClient() if err != nil { return nil, time.Since(startTime), err } diff --git a/shazam/shazamInit.go b/shazam/shazamInit.go index 75655be..f162663 100644 --- a/shazam/shazamInit.go +++ b/shazam/shazamInit.go @@ -2,6 +2,7 @@ package shazam import ( "fmt" + "song-recognition/db" "song-recognition/models" "song-recognition/utils" "sort" @@ -30,7 +31,7 @@ func Search(audioSamples []float64, audioDuration float64, sampleRate int) ([]Ma addresses = append(addresses, address) } - db, err := utils.NewDbClient() + db, err := db.NewDBClient() if err != nil { return nil, err } diff --git a/socketHandlers.go b/socketHandlers.go index 66f3105..75ce0c6 100644 --- a/socketHandlers.go +++ b/socketHandlers.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "log/slog" + "song-recognition/db" "song-recognition/models" "song-recognition/shazam" "song-recognition/spotify" @@ -32,7 +33,7 @@ func handleTotalSongs(socket socketio.Conn) { logger := utils.GetLogger() ctx := context.Background() - db, err := utils.NewDbClient() + db, err := db.NewDBClient() if err != nil { err := xerrors.New(err) logger.ErrorContext(ctx, "error connecting to DB", slog.Any("error", err)) @@ -130,7 +131,7 @@ func handleSongDownload(socket socketio.Conn, spotifyURL string) { } // check if track already exist - db, err := utils.NewDbClient() + db, err := db.NewDBClient() if err != nil { fmt.Errorf("Log - error connecting to DB: %d", err) } diff --git a/spotify/downloader.go b/spotify/downloader.go index fac7471..5601909 100644 --- a/spotify/downloader.go +++ b/spotify/downloader.go @@ -10,6 +10,7 @@ import ( "os/exec" "path/filepath" "runtime" + "song-recognition/db" "song-recognition/shazam" "song-recognition/utils" "song-recognition/wav" @@ -88,7 +89,7 @@ func dlTrack(tracks []Track, path string) (int, error) { ctx := context.Background() - db, err := utils.NewDbClient() + db, err := db.NewDBClient() if err != nil { return 0, err } @@ -269,11 +270,11 @@ func addTags(file string, track Track) error { } func ProcessAndSaveSong(songFilePath, songTitle, songArtist, ytID string) error { - db, err := utils.NewDbClient() + dbclient, err := db.NewDBClient() if err != nil { return err } - defer db.Close() + defer dbclient.Close() wavFilePath, err := wav.ConvertToWAV(songFilePath, 1) if err != nil { @@ -295,7 +296,7 @@ func ProcessAndSaveSong(songFilePath, songTitle, songArtist, ytID string) error return fmt.Errorf("error creating spectrogram: %v", err) } - songID, err := db.RegisterSong(songTitle, songArtist, ytID) + songID, err := dbclient.RegisterSong(songTitle, songArtist, ytID) if err != nil { return err } @@ -303,13 +304,13 @@ func ProcessAndSaveSong(songFilePath, songTitle, songArtist, ytID string) error peaks := shazam.ExtractPeaks(spectro, wavInfo.Duration) fingerprints := shazam.Fingerprint(peaks, songID) - err = db.StoreFingerprints(fingerprints) + err = dbclient.StoreFingerprints(fingerprints) if err != nil { - db.DeleteSongByID(songID) + dbclient.DeleteSongByID(songID) return fmt.Errorf("error to storing fingerpring: %v", err) } - fmt.Println("Fingerprints saved in MongoDB successfully") + fmt.Printf("Fingerprint for %v by %v saved in DB successfully\n", songTitle, songArtist) return nil } diff --git a/spotify/utils.go b/spotify/utils.go index e5a21dd..e9ff497 100644 --- a/spotify/utils.go +++ b/spotify/utils.go @@ -8,7 +8,7 @@ import ( "os/exec" "path/filepath" "runtime" - "song-recognition/utils" + "song-recognition/db" "strings" ) @@ -40,7 +40,7 @@ func GetFileSize(file string) (int64, error) { } func SongKeyExists(key string) (bool, error) { - db, err := utils.NewDbClient() + db, err := db.NewDBClient() if err != nil { return false, err } @@ -55,7 +55,7 @@ func SongKeyExists(key string) (bool, error) { } func YtIDExists(ytID string) (bool, error) { - db, err := utils.NewDbClient() + db, err := db.NewDBClient() if err != nil { return false, err } diff --git a/utils/dbClient.go b/utils/dbClient.go deleted file mode 100644 index 0dde6ed..0000000 --- a/utils/dbClient.go +++ /dev/null @@ -1,225 +0,0 @@ -package utils - -import ( - "context" - "errors" - "fmt" - "song-recognition/models" - "strings" - - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/bson/primitive" - "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" -) - -// godotenv.Load(".env") - -var ( - dbUsername = GetEnv("DB_USER") - dbPassword = GetEnv("DB_PASS") - dbName = GetEnv("DB_NAME") - dbHost = GetEnv("DB_HOST") - dbPort = GetEnv("DB_PORT") - - dbUri = "mongodb://" + dbUsername + ":" + dbPassword + "@" + dbHost + ":" + dbPort + "/" + dbName -) - -// DbClient represents a MongoDB client -type DbClient struct { - client *mongo.Client -} - -// NewDbClient creates a new instance of DbClient -func NewDbClient() (*DbClient, error) { - if dbUsername == "" || dbPassword == "" { - dbUri = "mongodb://localhost:27017" - } - - clientOptions := options.Client().ApplyURI(dbUri) - client, err := mongo.Connect(context.Background(), clientOptions) - if err != nil { - return nil, fmt.Errorf("error connecting to MongoDB: %d", err) - } - return &DbClient{client: client}, nil -} - -// Close closes the underlying MongoDB client -func (db *DbClient) Close() error { - if db.client != nil { - return db.client.Disconnect(context.Background()) - } - return nil -} - -func (db *DbClient) StoreFingerprints(fingerprints map[uint32]models.Couple) error { - collection := db.client.Database("song-recognition").Collection("fingerprints") - - for address, couple := range fingerprints { - filter := bson.M{"_id": address} - update := bson.M{ - "$push": bson.M{ - "couples": bson.M{ - "anchorTimeMs": couple.AnchorTimeMs, - "songID": couple.SongID, - }, - }, - } - opts := options.Update().SetUpsert(true) - - _, err := collection.UpdateOne(context.Background(), filter, update, opts) - if err != nil { - return fmt.Errorf("error upserting document: %s", err) - } - } - - return nil -} - -func (db *DbClient) GetCouples(addresses []uint32) (map[uint32][]models.Couple, error) { - collection := db.client.Database("song-recognition").Collection("fingerprints") - - couples := make(map[uint32][]models.Couple) - - for _, address := range addresses { - // Find the document corresponding to the address - var result bson.M - err := collection.FindOne(context.Background(), bson.M{"_id": address}).Decode(&result) - if err != nil { - if err == mongo.ErrNoDocuments { - continue - } - return nil, fmt.Errorf("error retrieving document for address %d: %s", address, err) - } - - // Extract couples from the document and append them to the couples map - var docCouples []models.Couple - couplesList, ok := result["couples"].(primitive.A) - if !ok { - return nil, fmt.Errorf("couples field in document for address %d is not valid", address) - } - - for _, item := range couplesList { - itemMap, ok := item.(primitive.M) - if !ok { - return nil, fmt.Errorf("invalid couple format in document for address %d", address) - } - - couple := models.Couple{ - AnchorTimeMs: uint32(itemMap["anchorTimeMs"].(int64)), - SongID: uint32(itemMap["songID"].(int64)), - } - docCouples = append(docCouples, couple) - } - couples[address] = docCouples - } - - return couples, nil -} - -func (db *DbClient) TotalSongs() (int, error) { - existingSongsCollection := db.client.Database("song-recognition").Collection("songs") - total, err := existingSongsCollection.CountDocuments(context.Background(), bson.D{}) - if err != nil { - return 0, err - } - - return int(total), nil -} - -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 - indexModel := mongo.IndexModel{ - Keys: bson.D{{"ytID", 1}, {"key", 1}}, - Options: options.Index().SetUnique(true), - } - _, err := existingSongsCollection.Indexes().CreateOne(context.Background(), indexModel) - if err != nil { - return 0, fmt.Errorf("failed to create unique index: %v", err) - } - - // Attempt to insert the song with ytID and key - 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 0, fmt.Errorf("song with ytID or key already exists: %v", err) - } else { - return 0, fmt.Errorf("failed to register song: %v", err) - } - } - - return songID, nil -} - -type Song struct { - Title string - Artist string - YouTubeID string -} - -const FILTER_KEYS = "_id | ytID | key" - -func (db *DbClient) GetSong(filterKey string, value interface{}) (s Song, songExists bool, e error) { - if !strings.Contains(FILTER_KEYS, filterKey) { - return Song{}, false, errors.New("invalid filter key") - } - - songsCollection := db.client.Database("song-recognition").Collection("songs") - var song bson.M - - filter := bson.M{filterKey: value} - - err := songsCollection.FindOne(context.Background(), filter).Decode(&song) - if err != nil { - if err == mongo.ErrNoDocuments { - return Song{}, false, nil - } - return Song{}, false, 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, true, nil -} - -func (db *DbClient) GetSongByID(songID uint32) (Song, bool, error) { - return db.GetSong("_id", songID) -} - -func (db *DbClient) GetSongByYTID(ytID string) (Song, bool, error) { - return db.GetSong("ytID", ytID) -} - -func (db *DbClient) GetSongByKey(key string) (Song, bool, error) { - return db.GetSong("key", key) -} - -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) DeleteCollection(collectionName string) error { - collection := db.client.Database("song-recognition").Collection(collectionName) - err := collection.Drop(context.Background()) - if err != nil { - return fmt.Errorf("error deleting collection: %v", err) - } - return nil -}