staggering apple music scrobbling, checking for recent scrobbles
This commit is contained in:
parent
5f7a8ec4fa
commit
10f30750b6
@ -27,7 +27,7 @@ public class AppleTimeline : Timeline<AppleMusicCurrentlyPlayingContext>
|
||||
{
|
||||
Recent.AddRange(items.Select(x =>
|
||||
TimelineItem<AppleMusicCurrentlyPlayingContext>.From(x, DateTime.UtcNow)));
|
||||
Recent.ForEach(x => x.Item.Scrobbled = true);
|
||||
Recent.ForEach(x => x.Item.ScrobbleIgnored = true);
|
||||
return newItems;
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ using IF.Lastfm.Core.Api;
|
||||
using IF.Lastfm.Core.Objects;
|
||||
using IF.Lastfm.Core.Scrobblers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Selector.AppleMusic.Watcher;
|
||||
|
||||
namespace Selector.AppleMusic.Consumer;
|
||||
|
||||
@ -41,24 +42,56 @@ public class AppleMusicScrobbler :
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendCached()
|
||||
{
|
||||
Logger.LogInformation("Sending any cached Apple Music scrobbles");
|
||||
var response = await _scrobbler.SendCachedScrobblesAsync();
|
||||
if (response.Success)
|
||||
{
|
||||
Logger.LogInformation("Sent [{}] cached Apple Music scrobbles", response.AcceptedCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogError(response.Exception, "Failed to send cached Apple Music scrobbles");
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task ProcessEvent(AppleListeningChangeEventArgs e)
|
||||
{
|
||||
await _lock.WaitAsync(CancelToken);
|
||||
|
||||
try
|
||||
{
|
||||
var lastScrobbled = e.Timeline.LastOrDefault(t => t.Item.Scrobbled);
|
||||
var toScrobble = e.Timeline.Where(e => !e.Item.Scrobbled).ToList();
|
||||
await SendCached();
|
||||
|
||||
Logger.LogInformation("Sending any cached Apple Music scrobbles");
|
||||
var response = await _scrobbler.SendCachedScrobblesAsync();
|
||||
if (response.Success)
|
||||
var lastScrobbled = e.Timeline.LastOrDefault(t => !t.Item.ToScrobble);
|
||||
var hourAgo = DateTimeOffset.UtcNow - TimeSpan.FromHours(1);
|
||||
var unScrobbled = e.Timeline
|
||||
.Where(i =>
|
||||
i.Item.ToScrobble
|
||||
&& i.Item.FirstSeen < hourAgo)
|
||||
.ToList();
|
||||
|
||||
var lastThreeHours = DateTimeOffset.UtcNow - TimeSpan.FromHours(4);
|
||||
var alreadyProcessed = e.Timeline
|
||||
.Where(x =>
|
||||
!x.Item.ToScrobble
|
||||
&& x.Item.FirstSeen > lastThreeHours
|
||||
&& x.Item.FirstSeen < hourAgo)
|
||||
.Select(x => x.Item.Track.Id)
|
||||
.ToArray();
|
||||
var toScrobble = new List<TimelineItem<AppleMusicCurrentlyPlayingContext>>();
|
||||
foreach (var u in unScrobbled)
|
||||
{
|
||||
Logger.LogInformation("Sent [{}] cached Apple Music scrobbles", response.AcceptedCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogError(response.Exception, "Failed to send cached Apple Music scrobbles");
|
||||
if (!alreadyProcessed.Contains(u.Item.Track.Id))
|
||||
{
|
||||
toScrobble.Add(u);
|
||||
}
|
||||
else
|
||||
{
|
||||
u.Item.ScrobbleIgnored = true;
|
||||
Logger.LogInformation("Ignored [{}], been scrobbled in the last three hours", u.Item.Track);
|
||||
}
|
||||
}
|
||||
|
||||
if (!toScrobble.Any()) return;
|
||||
@ -103,11 +136,11 @@ public class AppleMusicScrobbler :
|
||||
|
||||
if (scrobbleResponse.Success)
|
||||
{
|
||||
Logger.LogInformation("Sent [{}] Apple Music scrobbles", response.AcceptedCount);
|
||||
Logger.LogInformation("Sent [{}] Apple Music scrobbles", scrobbleResponse.AcceptedCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogError(response.Exception, "Failed to send Apple Music scrobbles, ignored [{}]",
|
||||
Logger.LogError(scrobbleResponse.Exception, "Failed to send Apple Music scrobbles, ignored [{}]",
|
||||
string.Join(", ", scrobbleResponse.Ignored));
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ public class TrackAttributes
|
||||
|
||||
//TODO: Artwork
|
||||
public required Artwork Artwork { get; set; }
|
||||
public required string ComposerName { get; set; }
|
||||
public string? ComposerName { get; set; }
|
||||
public required string Url { get; set; }
|
||||
public required PlayParams PlayParams { get; set; }
|
||||
public int DiscNumber { get; set; }
|
||||
|
@ -7,6 +7,9 @@ public class AppleMusicCurrentlyPlayingContext
|
||||
public DateTime FirstSeen { get; set; }
|
||||
public required Track Track { get; set; }
|
||||
public bool Scrobbled { get; set; }
|
||||
public bool ScrobbleIgnored { get; set; }
|
||||
|
||||
public bool ToScrobble => !Scrobbled && !ScrobbleIgnored;
|
||||
}
|
||||
|
||||
public class AppleMusicCurrentlyPlayingContextComparer : IEqualityComparer<AppleMusicCurrentlyPlayingContext>
|
||||
|
@ -113,6 +113,7 @@ public class AppleMusicPlayerWatcher : BaseWatcher, IAppleMusicPlayerWatcher
|
||||
Track = Live.Track,
|
||||
FirstSeen = Live.FirstSeen,
|
||||
Scrobbled = Live.Scrobbled,
|
||||
ScrobbleIgnored = Live.ScrobbleIgnored,
|
||||
};
|
||||
}
|
||||
else
|
||||
@ -124,6 +125,7 @@ public class AppleMusicPlayerWatcher : BaseWatcher, IAppleMusicPlayerWatcher
|
||||
Track = recentlyPlayedTracks.Data.First(),
|
||||
FirstSeen = DateTime.UtcNow,
|
||||
Scrobbled = false,
|
||||
ScrobbleIgnored = false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -71,8 +71,8 @@ namespace Selector.CLI
|
||||
foreach (var dbWatcher in db.Watcher
|
||||
.Include(w => w.User)
|
||||
.Where(w =>
|
||||
// ((w.Type == WatcherType.SpotifyPlayer || w.Type == WatcherType.SpotifyPlaylist) &&
|
||||
// !string.IsNullOrWhiteSpace(w.User.SpotifyRefreshToken)) ||
|
||||
((w.Type == WatcherType.SpotifyPlayer || w.Type == WatcherType.SpotifyPlaylist) &&
|
||||
!string.IsNullOrWhiteSpace(w.User.SpotifyRefreshToken)) ||
|
||||
(w.Type == WatcherType.AppleMusicPlayer && w.User.AppleMusicLinked)
|
||||
))
|
||||
{
|
||||
@ -149,17 +149,17 @@ namespace Selector.CLI
|
||||
|
||||
if (userEventFirerFactory is not null) consumers.Add(await userEventFirerFactory.GetApple());
|
||||
|
||||
// if (dbWatcher.User.LastFmConnected() && !string.IsNullOrWhiteSpace(dbWatcher.User.LastFmPassword))
|
||||
// {
|
||||
// var scrobbler = await scrobblerFactory.Get();
|
||||
// await scrobbler.Auth(dbWatcher.User.LastFmUsername, dbWatcher.User.LastFmPassword);
|
||||
// consumers.Add(scrobbler);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// logger.LogDebug("[{username}] No Last.fm username/password, skipping scrobbler",
|
||||
// dbWatcher.User.UserName);
|
||||
// }
|
||||
if (dbWatcher.User.LastFmConnected() && !string.IsNullOrWhiteSpace(dbWatcher.User.LastFmPassword))
|
||||
{
|
||||
var scrobbler = await scrobblerFactory.Get();
|
||||
await scrobbler.Auth(dbWatcher.User.LastFmUsername, dbWatcher.User.LastFmPassword);
|
||||
consumers.Add(scrobbler);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogDebug("[{username}] No Last.fm username/password, skipping scrobbler",
|
||||
dbWatcher.User.UserName);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -12,12 +12,12 @@
|
||||
// "PlaylistUri": "spotify:playlist:4o5IArXmDeJByESaUJoEFS",
|
||||
// "pollperiod": 2000
|
||||
// },
|
||||
{
|
||||
"type": "player",
|
||||
"lastfmusername": "sarsoo",
|
||||
"pollperiod": 2000,
|
||||
"consumers": [ "audiofeaturescache", "cachewriter", "publisher", "playcounter", "mappingpersister" ]
|
||||
}
|
||||
// {
|
||||
// "type": "player",
|
||||
// "lastfmusername": "sarsoo",
|
||||
// "pollperiod": 2000,
|
||||
// "consumers": [ "audiofeaturescache", "cachewriter", "publisher", "playcounter", "mappingpersister" ]
|
||||
// }
|
||||
]
|
||||
},
|
||||
"Database": {
|
||||
|
@ -9,6 +9,7 @@ public class AppleMusicListen : Listen, IUserListen
|
||||
public string TrackId { get; set; }
|
||||
public string Isrc { get; set; }
|
||||
public bool IsScrobbled { get; set; }
|
||||
public bool ScrobbleIgnored { get; set; }
|
||||
|
||||
public string UserId { get; set; }
|
||||
public ApplicationUser User { get; set; }
|
||||
@ -20,6 +21,7 @@ public class AppleMusicListen : Listen, IUserListen
|
||||
TrackId = track.Track.Id,
|
||||
Isrc = track.Track.Attributes.Isrc,
|
||||
IsScrobbled = track.Scrobbled,
|
||||
ScrobbleIgnored = track.ScrobbleIgnored,
|
||||
|
||||
TrackName = track.Track.Attributes.Name,
|
||||
AlbumName = track.Track.Attributes.AlbumName,
|
||||
|
567
Selector.Model/Migrations/20250412091852_scrobble_ignored_column.Designer.cs
generated
Normal file
567
Selector.Model/Migrations/20250412091852_scrobble_ignored_column.Designer.cs
generated
Normal file
@ -0,0 +1,567 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using Selector.Model;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Selector.Model.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20250412091852_scrobble_ignored_column")]
|
||||
partial class scrobble_ignored_column
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasDefaultSchema("selector")
|
||||
.HasAnnotation("Npgsql:CollationDefinition:case_insensitive", "en-u-ks-primary,en-u-ks-primary,icu,False")
|
||||
.HasAnnotation("ProductVersion", "9.0.3")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles", "selector");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = "00c64c0a-3387-4933-9575-83443fa9092b",
|
||||
Name = "Admin",
|
||||
NormalizedName = "ADMIN"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", "selector");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims", "selector");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins", "selector");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles", "selector");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens", "selector");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Selector.Model.AlbumLastfmSpotifyMapping", b =>
|
||||
{
|
||||
b.Property<string>("SpotifyUri")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("LastfmAlbumName")
|
||||
.HasColumnType("text")
|
||||
.UseCollation("case_insensitive");
|
||||
|
||||
b.Property<string>("LastfmArtistName")
|
||||
.HasColumnType("text")
|
||||
.UseCollation("case_insensitive");
|
||||
|
||||
b.HasKey("SpotifyUri");
|
||||
|
||||
b.ToTable("AlbumMapping", "spotify");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Selector.Model.AppleMusicListen", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("AlbumName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.UseCollation("case_insensitive");
|
||||
|
||||
b.Property<string>("ArtistName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.UseCollation("case_insensitive");
|
||||
|
||||
b.Property<bool>("IsScrobbled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("Isrc")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<bool>("ScrobbleIgnored")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTime>("Timestamp")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("TrackId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("TrackName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.UseCollation("case_insensitive");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AppleMusicListen", "apple");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Selector.Model.ApplicationUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("AppleMusicKey")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("AppleMusicLastRefresh")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<bool>("AppleMusicLinked")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("LastFmPassword")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("LastFmUsername")
|
||||
.HasColumnType("text")
|
||||
.UseCollation("case_insensitive");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("SaveScrobbles")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("SpotifyAccessToken")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<bool>("SpotifyIsLinked")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTime>("SpotifyLastRefresh")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("SpotifyRefreshToken")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("SpotifyTokenExpiry")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers", "selector");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Selector.Model.ArtistLastfmSpotifyMapping", b =>
|
||||
{
|
||||
b.Property<string>("SpotifyUri")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("LastfmArtistName")
|
||||
.HasColumnType("text")
|
||||
.UseCollation("case_insensitive");
|
||||
|
||||
b.HasKey("SpotifyUri");
|
||||
|
||||
b.ToTable("ArtistMapping", "spotify");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Selector.Model.SpotifyListen", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("AlbumName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.UseCollation("case_insensitive");
|
||||
|
||||
b.Property<string>("ArtistName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.UseCollation("case_insensitive");
|
||||
|
||||
b.Property<int?>("PlayedDuration")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("Timestamp")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("TrackName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.UseCollation("case_insensitive");
|
||||
|
||||
b.Property<string>("TrackUri")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("SpotifyListen", "spotify");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Selector.Model.TrackLastfmSpotifyMapping", b =>
|
||||
{
|
||||
b.Property<string>("SpotifyUri")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("LastfmArtistName")
|
||||
.HasColumnType("text")
|
||||
.UseCollation("case_insensitive");
|
||||
|
||||
b.Property<string>("LastfmTrackName")
|
||||
.HasColumnType("text")
|
||||
.UseCollation("case_insensitive");
|
||||
|
||||
b.HasKey("SpotifyUri");
|
||||
|
||||
b.ToTable("TrackMapping", "spotify");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Selector.Model.UserScrobble", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("AlbumArtistName")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("AlbumName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.UseCollation("case_insensitive");
|
||||
|
||||
b.Property<string>("ArtistName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.UseCollation("case_insensitive");
|
||||
|
||||
b.Property<DateTime>("Timestamp")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("TrackName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.UseCollation("case_insensitive");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("Scrobble", "lastfm");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Selector.Model.Watcher", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("Watcher", "selector");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Selector.Model.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Selector.Model.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Selector.Model.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Selector.Model.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Selector.Model.AppleMusicListen", b =>
|
||||
{
|
||||
b.HasOne("Selector.Model.ApplicationUser", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Selector.Model.SpotifyListen", b =>
|
||||
{
|
||||
b.HasOne("Selector.Model.ApplicationUser", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Selector.Model.UserScrobble", b =>
|
||||
{
|
||||
b.HasOne("Selector.Model.ApplicationUser", "User")
|
||||
.WithMany("Scrobbles")
|
||||
.HasForeignKey("UserId");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Selector.Model.Watcher", b =>
|
||||
{
|
||||
b.HasOne("Selector.Model.ApplicationUser", "User")
|
||||
.WithMany("Watchers")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Selector.Model.ApplicationUser", b =>
|
||||
{
|
||||
b.Navigation("Scrobbles");
|
||||
|
||||
b.Navigation("Watchers");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Selector.Model.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class scrobble_ignored_column : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "ScrobbleIgnored",
|
||||
schema: "apple",
|
||||
table: "AppleMusicListen",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ScrobbleIgnored",
|
||||
schema: "apple",
|
||||
table: "AppleMusicListen");
|
||||
}
|
||||
}
|
||||
}
|
@ -206,6 +206,9 @@ namespace Selector.Model.Migrations
|
||||
b.Property<string>("Isrc")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<bool>("ScrobbleIgnored")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTime>("Timestamp")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user