Skip to content

Commit

Permalink
possible fix for update process removing wrong plays
Browse files Browse the repository at this point in the history
  • Loading branch information
th0mk committed Dec 2, 2024
1 parent af91e5a commit 696155c
Showing 1 changed file with 45 additions and 30 deletions.
75 changes: 45 additions & 30 deletions src/FMBot.Persistence/Repositories/PlayRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ public static class PlayRepository
{
public record PlayUpdate(List<UserPlay> NewPlays, List<UserPlay> RemovedPlays);

public static async Task<PlayUpdate> InsertLatestPlays(IEnumerable<RecentTrack> recentTracks, int userId, NpgsqlConnection connection)
public static async Task<PlayUpdate> InsertLatestPlays(IEnumerable<RecentTrack> recentTracks, int userId,
NpgsqlConnection connection)
{
var plays = recentTracks
var lastPlays = recentTracks
.Where(w => !w.NowPlaying &&
w.TimePlayed.HasValue)
.Select(s => new UserPlay
Expand All @@ -31,56 +32,60 @@ public static async Task<PlayUpdate> InsertLatestPlays(IEnumerable<RecentTrack>
UserId = userId
}).ToList();

var existingPlays = await GetAllUserPlays(userId, connection, plays.Count + 250);
var existingPlays = await GetAllUserPlays(userId, connection, lastPlays.Count + 250);
existingPlays = existingPlays.Where(w => w.PlaySource == PlaySource.LastFm).ToList();

var firstExistingPlay = existingPlays.MinBy(o => o.TimePlayed);

if (firstExistingPlay != null)
{
plays = plays
lastPlays = lastPlays
.Where(w => w.TimePlayed >= firstExistingPlay.TimePlayed)
.ToList();
}

var addedPlays = new List<UserPlay>();
foreach (var newPlay in plays)
foreach (var newPlay in lastPlays)
{
if (existingPlays.All(a => a.TimePlayed != newPlay.TimePlayed))
{
addedPlays.Add(newPlay);
}
}

var firstNewPlay = plays.MinBy(o => o.TimePlayed);
var firstNewPlay = lastPlays.MinBy(o => o.TimePlayed);

var removedPlays = new List<UserPlay>();
if (firstNewPlay != null)
{
foreach (var existingPlay in existingPlays.Where(w => w.TimePlayed >= firstNewPlay.TimePlayed))
{
if (plays.All(a => a.TimePlayed != existingPlay.TimePlayed))
if (lastPlays.All(a => a.TimePlayed != existingPlay.TimePlayed))
{
removedPlays.Add(existingPlay);
}
}

if (removedPlays.Any())
{
Log.Information("Found {removedPlaysCount} time series plays to remove for {userId}", removedPlays.Count, userId);
Log.Information("Found {removedPlaysCount} time series plays to remove for {userId}",
removedPlays.Count, userId);
await RemoveSpecificPlays(removedPlays, connection);
}
}

if (addedPlays.Any())
{
Log.Information("Inserting {addedPlaysCount} new time series plays for user {userId}", addedPlays.Count, userId);
Log.Information("Inserting {addedPlaysCount} new time series plays for user {userId}", addedPlays.Count,
userId);
await InsertTimeSeriesPlays(addedPlays, connection);
}

return new PlayUpdate(addedPlays, removedPlays);
}

public static async Task ReplaceAllPlays(IReadOnlyList<UserPlay> playsToInsert, int userId, NpgsqlConnection connection)
public static async Task ReplaceAllPlays(IReadOnlyList<UserPlay> playsToInsert, int userId,
NpgsqlConnection connection)
{
await RemoveAllCurrentLastFmPlays(userId, connection);

Expand All @@ -91,7 +96,8 @@ public static async Task ReplaceAllPlays(IReadOnlyList<UserPlay> playsToInsert,
private static async Task RemoveAllCurrentLastFmPlays(int userId, NpgsqlConnection connection)
{
await using var deletePlays = new NpgsqlCommand("DELETE FROM public.user_plays " +
"WHERE user_id = @userId AND (play_source IS NULL OR play_source = 0);", connection);
"WHERE user_id = @userId AND (play_source IS NULL OR play_source = 0);",
connection);

deletePlays.Parameters.AddWithValue("userId", userId);

Expand Down Expand Up @@ -171,7 +177,8 @@ public static async Task<ulong> InsertTimeSeriesPlays(IEnumerable<UserPlay> play
return await copyHelper.SaveAllAsync(connection, plays);
}

public static async Task<ICollection<UserPlay>> GetAllUserPlays(int userId, NpgsqlConnection connection, int limit = 99999999)
public static async Task<ICollection<UserPlay>> GetAllUserPlays(int userId, NpgsqlConnection connection,
int limit = 99999999)
{
const string sql = "SELECT * FROM public.user_plays WHERE user_id = @userId " +
"ORDER BY time_played DESC LIMIT @limit";
Expand All @@ -190,27 +197,31 @@ private static string GetUserPlaysSqlString(string initialSql, DataSource dataSo

sql += dataSource switch
{
DataSource.LastFm => " FROM public.user_plays WHERE user_id = @userId AND artist_name IS NOT NULL AND play_source = 0 ",
DataSource.FullImportThenLastFm => " FROM public.user_plays WHERE user_id = @userId AND artist_name IS NOT NULL AND ( " +
"(play_source = 1 OR play_source = 2) OR " +
"(play_source = 0 AND time_played >= ( " +
"SELECT MAX(time_played) FROM public.user_plays WHERE user_id = @userId AND (play_source = 1 OR play_source = 2) " +
")) OR " +
"(play_source = 0 AND time_played <= ( " +
"SELECT MIN(time_played) FROM public.user_plays WHERE user_id = @userId AND (play_source = 1 OR play_source = 2) " +
"))) ",
DataSource.ImportThenFullLastFm => " FROM public.user_plays WHERE user_id = @userId AND artist_name IS NOT NULL AND ( " +
"play_source = 0 OR " +
"((play_source = 1 OR play_source = 2) AND time_played < ( " +
"SELECT MIN(time_played) FROM public.user_plays WHERE user_id = @userId AND play_source = 0 " +
"))) ",
DataSource.LastFm =>
" FROM public.user_plays WHERE user_id = @userId AND artist_name IS NOT NULL AND play_source = 0 ",
DataSource.FullImportThenLastFm =>
" FROM public.user_plays WHERE user_id = @userId AND artist_name IS NOT NULL AND ( " +
"(play_source = 1 OR play_source = 2) OR " +
"(play_source = 0 AND time_played >= ( " +
"SELECT MAX(time_played) FROM public.user_plays WHERE user_id = @userId AND (play_source = 1 OR play_source = 2) " +
")) OR " +
"(play_source = 0 AND time_played <= ( " +
"SELECT MIN(time_played) FROM public.user_plays WHERE user_id = @userId AND (play_source = 1 OR play_source = 2) " +
"))) ",
DataSource.ImportThenFullLastFm =>
" FROM public.user_plays WHERE user_id = @userId AND artist_name IS NOT NULL AND ( " +
"play_source = 0 OR " +
"((play_source = 1 OR play_source = 2) AND time_played < ( " +
"SELECT MIN(time_played) FROM public.user_plays WHERE user_id = @userId AND play_source = 0 " +
"))) ",
_ => " FROM public.user_plays WHERE user_id = @userId "
};

if (start.HasValue)
{
sql += " AND time_played >= @start ";
}

if (end.HasValue)
{
sql += " AND time_played <= @end ";
Expand All @@ -224,7 +235,8 @@ private static string GetUserPlaysSqlString(string initialSql, DataSource dataSo
return sql;
}

public static async Task<ICollection<UserPlay>> GetUserPlays(int userId, NpgsqlConnection connection, DataSource dataSource, int limit = 9999999, DateTime? start = null, DateTime? end = null)
public static async Task<ICollection<UserPlay>> GetUserPlays(int userId, NpgsqlConnection connection,
DataSource dataSource, int limit = 9999999, DateTime? start = null, DateTime? end = null)
{
var sql = GetUserPlaysSqlString("SELECT * ", dataSource, start, end);

Expand All @@ -238,7 +250,8 @@ public static async Task<ICollection<UserPlay>> GetUserPlays(int userId, NpgsqlC
})).ToList();
}

public static async Task<int> GetUserPlayCount(int userId, NpgsqlConnection connection, DataSource dataSource, DateTime? start = null, DateTime? end = null)
public static async Task<int> GetUserPlayCount(int userId, NpgsqlConnection connection, DataSource dataSource,
DateTime? start = null, DateTime? end = null)
{
var sql = GetUserPlaysSqlString("SELECT COUNT(*) ", dataSource, start, end);

Expand All @@ -252,7 +265,8 @@ public static async Task<int> GetUserPlayCount(int userId, NpgsqlConnection conn
});
}

public static async Task<ICollection<UserPlay>> GetUserPlaysWithinTimeRange(int userId, NpgsqlConnection connection, DateTime start, DateTime? end = null)
public static async Task<ICollection<UserPlay>> GetUserPlaysWithinTimeRange(int userId, NpgsqlConnection connection,
DateTime start, DateTime? end = null)
{
end ??= DateTime.UtcNow;

Expand All @@ -270,7 +284,8 @@ public static async Task<ICollection<UserPlay>> GetUserPlaysWithinTimeRange(int

public static async Task SetDefaultSourceForPlays(int userId, NpgsqlConnection connection)
{
const string sql = "UPDATE public.user_plays SET play_source = 0 WHERE user_id = @userId AND play_source IS null";
const string sql =
"UPDATE public.user_plays SET play_source = 0 WHERE user_id = @userId AND play_source IS null";

await connection.ExecuteAsync(sql, new
{
Expand Down

0 comments on commit 696155c

Please sign in to comment.