diff --git a/importsongs/importsongs.go b/importsongs/importsongs.go index a56e9e3..1f4fd04 100644 --- a/importsongs/importsongs.go +++ b/importsongs/importsongs.go @@ -24,7 +24,13 @@ const ( func TableExists(name string, conn *pgx.Conn) bool { var exists bool - err := conn.QueryRow(context.Background(), "SELECT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = $1);", name).Scan(&exists) + err := conn.QueryRow( + context.Background(), + `SELECT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND + tablename = $1);`, + name, + ). + Scan(&exists) if err != nil { fmt.Fprintf(os.Stderr, "SELECT EXISTS failed: %v\n", err) return false @@ -33,7 +39,10 @@ func TableExists(name string, conn *pgx.Conn) bool { } func DbExists() bool { - conn, err := pgx.Connect(context.Background(), "postgres://postgres:postgres@localhost:5432/muzi") + conn, err := pgx.Connect( + context.Background(), + "postgres://postgres:postgres@localhost:5432/muzi", + ) if err != nil { return false } @@ -42,7 +51,10 @@ func DbExists() bool { } func CreateDB() error { - conn, err := pgx.Connect(context.Background(), "postgres://postgres:postgres@localhost:5432") + conn, err := pgx.Connect( + context.Background(), + "postgres://postgres:postgres@localhost:5432", + ) if err != nil { fmt.Fprintf(os.Stderr, "Cannot connect to PostgreSQL: %v\n", err) return err @@ -56,21 +68,30 @@ func CreateDB() error { return nil } -func JsonToDB(jsonFile string, platform int) { +func JsonToDB(jsonFile string, platform int) error { if !DbExists() { err := CreateDB() if err != nil { + fmt.Fprintf(os.Stderr, "Error creating muzi database: %v\n", err) panic(err) } } - conn, err := pgx.Connect(context.Background(), "postgres://postgres:postgres@localhost:5432/muzi") + conn, err := pgx.Connect( + context.Background(), + "postgres://postgres:postgres@localhost:5432/muzi", + ) if err != nil { fmt.Fprintf(os.Stderr, "Cannot connect to muzi database: %v\n", err) panic(err) } defer conn.Close(context.Background()) if !TableExists("history", conn) { - _, err = conn.Exec(context.Background(), "CREATE TABLE history ( ms_played INTEGER, timestamp TIMESTAMPTZ, song_name TEXT, artist TEXT, album_name TEXT, PRIMARY KEY (timestamp, ms_played, artist, song_name));") + _, err = conn.Exec( + context.Background(), + `CREATE TABLE history ( ms_played INTEGER, timestamp TIMESTAMPTZ, + song_name TEXT, artist TEXT, album_name TEXT, PRIMARY KEY (timestamp, + ms_played, artist, song_name));`, + ) } if err != nil { fmt.Fprintf(os.Stderr, "Cannot create history table: %v\n", err) @@ -79,7 +100,7 @@ func JsonToDB(jsonFile string, platform int) { jsonData, err := os.ReadFile(jsonFile) if err != nil { fmt.Fprintf(os.Stderr, "Cannot read %s: %v\n", jsonFile, err) - panic(err) + return err } if platform == spotify { type Track struct { @@ -111,31 +132,52 @@ func JsonToDB(jsonFile string, platform int) { err := json.Unmarshal(jsonData, &tracks) if err != nil { fmt.Fprintf(os.Stderr, "Cannot unmarshal %s: %v\n", jsonFile, err) - panic(err) + return err } for _, track := range tracks { // skip adding a song if it was only listed to for less than 20 seconds if track.Played < 20000 { continue } - _, err = conn.Exec(context.Background(), "INSERT INTO history (timestamp, song_name, artist, album_name, ms_played) VALUES ($1, $2, $3, $4, $5);", track.Timestamp, track.Name, track.Artist, track.Album, track.Played) + _, err = conn.Exec( + context.Background(), + `INSERT INTO history (timestamp, song_name, artist, album_name, + ms_played) VALUES ($1, $2, $3, $4, $5);`, + track.Timestamp, + track.Name, + track.Artist, + track.Album, + track.Played, + ) if err != nil { - fmt.Fprintf(os.Stderr, "Couldn't add track to muzi DB (%s): %v\n", (track.Artist + " - " + track.Name), err) + fmt.Fprintf( + os.Stderr, + "Couldn't add track to muzi DB (%s): %v\n", + (track.Artist + " - " + track.Name), + err, + ) } } } + return nil } -func AddDirToDB(path string, platform int) { +func AddDirToDB(path string, platform int) error { dirs, err := os.ReadDir(path) if err != nil { - panic(err) + fmt.Fprintf(os.Stderr, "Error while reading path: %s: %v\n", path, err) + return err } for _, dir := range dirs { - subPath := filepath.Join(path, dir.Name(), "Spotify Extended Streaming History") + subPath := filepath.Join( + path, + dir.Name(), + "Spotify Extended Streaming History", + ) entries, err := os.ReadDir(subPath) if err != nil { - panic(err) + fmt.Fprintf(os.Stderr, "Error while reading path: %s: %v\n", subPath, err) + return err } for _, f := range entries { jsonFileName := f.Name() @@ -149,38 +191,54 @@ func AddDirToDB(path string, platform int) { } } jsonFilePath := filepath.Join(subPath, jsonFileName) - JsonToDB(jsonFilePath, platform) + err = JsonToDB(jsonFilePath, platform) + if err != nil { + fmt.Fprintf(os.Stderr, + "Error adding json data (%s) to muzi database: %v", jsonFilePath, err) + return err + } } } + return nil } -func ImportLastFM() { +func ImportLastFM(username string, apiKey string) error { if !DbExists() { err := CreateDB() if err != nil { + fmt.Fprintf(os.Stderr, "Error creating muzi database: %v\n", err) panic(err) } } - conn, err := pgx.Connect(context.Background(), "postgres://postgres:postgres@localhost:5432/muzi") + conn, err := pgx.Connect( + context.Background(), + "postgres://postgres:postgres@localhost:5432/muzi", + ) if err != nil { fmt.Fprintf(os.Stderr, "Cannot connect to muzi database: %v\n", err) panic(err) } defer conn.Close(context.Background()) if !TableExists("history", conn) { - _, err = conn.Exec(context.Background(), "CREATE TABLE history ( ms_played INTEGER, timestamp TIMESTAMPTZ, song_name TEXT, artist TEXT, album_name TEXT, PRIMARY KEY (timestamp, ms_played, artist, song_name));") + _, err = conn.Exec( + context.Background(), + `CREATE TABLE history ( ms_played INTEGER, timestamp TIMESTAMPTZ, + song_name TEXT, artist TEXT, album_name TEXT, PRIMARY KEY (timestamp, + ms_played, artist, song_name));`, + ) } if err != nil { fmt.Fprintf(os.Stderr, "Cannot create history table: %v\n", err) panic(err) } - username := "" - apiKey := "" - - resp, err := http.Get("https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=" + username + "&api_key=" + apiKey + "&format=json&limit=1") + resp, err := http.Get( + "https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=" + + username + "&api_key=" + apiKey + "&format=json&limit=1", + ) if err != nil { - panic(err) + fmt.Fprintf(os.Stderr, "Error getting LastFM http response: %v\n", err) + return err } type Response struct { Recenttracks struct { @@ -229,53 +287,96 @@ func ImportLastFM() { } for i := 1; i <= totalPages; i++ { - resp, err := http.Get("https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=" + username + "&api_key=" + apiKey + "&format=json&limit=100&page=" + strconv.Itoa(i)) + resp, err := http.Get( + "https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=" + + username + "&api_key=" + apiKey + "&format=json&limit=100&page=" + + strconv.Itoa( + i, + ), + ) if err != nil { - panic(err) + fmt.Fprintf(os.Stderr, "Error getting LastFM http response: %v\n", err) + return err } json.NewDecoder(resp.Body).Decode(&data) for j := range data.Recenttracks.Track { if data.Recenttracks.Track[j].Attr.Nowplaying == "true" { continue } - unixTime, err := strconv.ParseInt(data.Recenttracks.Track[j].Date.Uts, 10, 64) + unixTime, err := strconv.ParseInt( + data.Recenttracks.Track[j].Date.Uts, + 10, + 64, + ) if err != nil { - panic(err) + fmt.Fprintf(os.Stderr, "Error parsing string for int: %v\n", err) + return err } ts := time.Unix(unixTime, 0) - _, err = conn.Exec(context.Background(), "INSERT INTO history (timestamp, song_name, artist, album_name, ms_played) VALUES ($1, $2, $3, $4, $5);", ts, data.Recenttracks.Track[j].Name, data.Recenttracks.Track[j].Artist.Text, data.Recenttracks.Track[j].Album.Text, 0) + _, err = conn.Exec( + context.Background(), + `INSERT INTO history (timestamp, song_name, artist, album_name, + ms_played) VALUES ($1, $2, $3, $4, $5);`, + ts, + data.Recenttracks.Track[j].Name, + data.Recenttracks.Track[j].Artist.Text, + data.Recenttracks.Track[j].Album.Text, + 0, + ) if err != nil { - fmt.Fprintf(os.Stderr, "Couldn't add track to muzi DB (%s): %v\n", (data.Recenttracks.Track[j].Artist.Text + " - " + data.Recenttracks.Track[j].Name), err) + fmt.Fprintf( + os.Stderr, + "Couldn't add track to muzi DB (%s): %v\n", + (data.Recenttracks.Track[j].Artist.Text + " - " + + data.Recenttracks.Track[j].Name), err) } } } + return nil } -func ImportSpotify() { +func ImportSpotify() error { path := filepath.Join(".", "imports", "spotify", "zip") targetBase := filepath.Join(".", "imports", "spotify", "extracted") entries, err := os.ReadDir(path) if err != nil { - panic(err) + fmt.Fprintf(os.Stderr, "Error reading path: %s: %v\n", path, err) + return err } for _, f := range entries { _, err := zip.OpenReader(filepath.Join(path, f.Name())) - if err == nil { - fileName := f.Name() - fileFullPath := filepath.Join(path, fileName) - fileBaseName := fileName[:(strings.LastIndex(fileName, "."))] - targetDirFullPath := filepath.Join(targetBase, fileBaseName) + if err != nil { + fmt.Fprintf(os.Stderr, "Error opening zip: %s: %v\n", + filepath.Join(path, f.Name()), err) + continue + } + fileName := f.Name() + fileFullPath := filepath.Join(path, fileName) + fileBaseName := fileName[:(strings.LastIndex(fileName, "."))] + targetDirFullPath := filepath.Join(targetBase, fileBaseName) - Extract(fileFullPath, targetDirFullPath) + err = Extract(fileFullPath, targetDirFullPath) + if err != nil { + fmt.Fprintf(os.Stderr, "Error extracting %s to %s: %v\n", + fileFullPath, targetDirFullPath, err) + return err } } - AddDirToDB(targetBase, spotify) + err = AddDirToDB(targetBase, spotify) + if err != nil { + fmt.Fprintf(os.Stderr, + "Error adding directory of data (%s) to muzi database: %v\n", + targetBase, err) + return err + } + return nil } -func Extract(path string, target string) { +func Extract(path string, target string) error { archive, err := zip.OpenReader(path) if err != nil { - panic(err) + fmt.Fprintf(os.Stderr, "Error opening zip: %s: %v\n", path, err) + return err } defer archive.Close() @@ -286,9 +387,13 @@ func Extract(path string, target string) { filePath := filepath.Join(target, f.Name) fmt.Println("extracting:", filePath) - if !strings.HasPrefix(filePath, filepath.Clean(target)+string(os.PathSeparator)) { - fmt.Println("Invalid file path") - return + if !strings.HasPrefix( + filePath, + filepath.Clean(target)+string(os.PathSeparator), + ) { + err = fmt.Errorf("Invalid file path: %s", filePath) + fmt.Fprintf(os.Stderr, "%v\n", err) + return err } if f.FileInfo().IsDir() { fmt.Println("Creating Directory", filePath) @@ -296,20 +401,36 @@ func Extract(path string, target string) { continue } if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil { - panic(err) + fmt.Fprintf(os.Stderr, "Error making directory: %s: %v\n", + filepath.Dir(filePath), err) + return err } - fileToExtract, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) + fileToExtract, err := os.OpenFile( + filePath, + os.O_WRONLY|os.O_CREATE|os.O_TRUNC, + f.Mode(), + ) if err != nil { - panic(err) + fmt.Fprintf(os.Stderr, "Error opening file: %s: %v\n", filePath, err) + return err } extractedFile, err := f.Open() if err != nil { - panic(err) + fmt.Fprintf(os.Stderr, "Error opening file: %s: %v\n", f.Name, err) + return err } if _, err := io.Copy(fileToExtract, extractedFile); err != nil { - panic(err) + fmt.Fprintf( + os.Stderr, + "Error while copying file: %s to: %s: %v\n", + fileToExtract.Name(), + extractedFile, + err, + ) + return err } fileToExtract.Close() extractedFile.Close() } + return nil } diff --git a/main.go b/main.go index 8194b65..f2ab8c8 100644 --- a/main.go +++ b/main.go @@ -3,9 +3,11 @@ package main import ( "errors" "fmt" + "os" + "path/filepath" + "muzi/importsongs" "muzi/web" - "os" ) func dbCheck() error { @@ -20,7 +22,6 @@ func dbCheck() error { } func dirCheck(path string) error { - _, err := os.Stat(path) if err != nil { if errors.Is(err, os.ErrNotExist) { @@ -35,32 +36,48 @@ func dirCheck(path string) error { } func main() { - dirImports := "./imports/" + dirImports := filepath.Join(".", "imports") - dirSpotify := "./imports/spotify/" - dirSpotifyZip := "./imports/spotify/zip/" - dirSpotifyExt := "./imports/spotify/extracted/" + dirSpotify := filepath.Join(".", "imports", "spotify") + dirSpotifyZip := filepath.Join(".", "imports", "spotify", "zip") + dirSpotifyExt := filepath.Join(".", "imports", "spotify", "extracted") + fmt.Printf("Checking if directory %s exists...\n", dirImports) err := dirCheck(dirImports) if err != nil { return } + fmt.Printf("Checking if directory %s exists...\n", dirSpotify) err = dirCheck(dirSpotify) if err != nil { return } + fmt.Printf("Checking if directory %s exists...\n", dirSpotifyZip) err = dirCheck(dirSpotifyZip) if err != nil { return } + fmt.Printf("Checking if directory %s exists...\n", dirSpotifyExt) err = dirCheck(dirSpotifyExt) if err != nil { return } + fmt.Println("Checking if muzi database exists...") err = dbCheck() if err != nil { return } + username := "" + apiKey := "" + fmt.Printf("Importing LastFM data for %s", username) + err = importsongs.ImportLastFM(username, apiKey) + if err != nil { + return + } + err = importsongs.ImportSpotify() + if err != nil { + return + } web.Start() }