Bugfix stream editor
This commit is contained in:
2023-08-01 15:35:11 +01:00
parent 82dc5615c2
commit 7ca87a6181
20 changed files with 561 additions and 93 deletions

View File

@ -1,18 +1,20 @@
using Newtonsoft.Json;
using Nostr.Client.Messages;
namespace NostrStreamer.ApiModel;
public class Account
{
[JsonProperty("event")]
public NostrEvent? Event { get; init; }
public PatchEvent? Event { get; init; }
[JsonProperty("endpoints")]
public List<AccountEndpoint> Endpoints { get; init; } = new();
[JsonProperty("balance")]
public long Balance { get; init; }
[JsonProperty("tos")]
public AccountTos Tos { get; init; }
}
public class AccountEndpoint
@ -42,15 +44,11 @@ public class EndpointCost
public string Unit { get; init; } = null!;
}
[Obsolete("Use EndpointCost")]
public class AccountQuota
public class AccountTos
{
[JsonProperty("rate")]
public double Rate { get; init; }
[JsonProperty("accepted")]
public bool Accepted { get; init; }
[JsonProperty("unit")]
public string Unit { get; init; } = null!;
[JsonProperty("remaining")]
public long Remaining { get; init; }
[JsonProperty("link")]
public Uri Link { get; init; } = null!;
}

View File

@ -0,0 +1,9 @@
using Newtonsoft.Json;
namespace NostrStreamer.ApiModel;
public class PatchAccount
{
[JsonProperty("accept_tos")]
public bool AcceptTos { get; init; }
}

View File

@ -38,6 +38,8 @@ public class Config
public LndConfig Lnd { get; init; } = null!;
public S3BlobConfig DvrStore { get; init; } = null!;
public DateTime TosDate { get; init; }
}
public class LndConfig

View File

@ -43,16 +43,16 @@ public class NostrController : Controller
.AsNoTracking()
.ToListAsync();
var latestEvent = await _db.Streams
.AsNoTracking()
.Where(a => a.User.PubKey == user.PubKey)
.OrderByDescending(a => a.Starts)
.Select(a => a.Event)
.FirstOrDefaultAsync();
var account = new Account
{
Event = !string.IsNullOrEmpty(latestEvent) ? JsonConvert.DeserializeObject<NostrEvent>(latestEvent, NostrSerializer.Settings) : null,
Event = new PatchEvent()
{
Title = user.Title ?? "",
Summary = user.Summary ?? "",
Image = user.Image ?? "",
ContentWarning = user.ContentWarning,
Tags = user.SplitTags()
},
Endpoints = endpoints.Select(a => new AccountEndpoint
{
Name = a.Name,
@ -65,7 +65,12 @@ public class NostrController : Controller
Rate = a.Cost / 1000d
}
}).ToList(),
Balance = (long)Math.Floor(user.Balance / 1000m)
Balance = (long)Math.Floor(user.Balance / 1000m),
Tos = new()
{
Accepted = user.TosAccepted >= _config.TosDate,
Link = new Uri(_config.ApiHost, "/tos")
}
};
return Content(JsonConvert.SerializeObject(account, NostrSerializer.Settings), "application/json");
@ -77,8 +82,17 @@ public class NostrController : Controller
var pubkey = GetPubKey();
if (string.IsNullOrEmpty(pubkey)) return Unauthorized();
var streamManager = await _streamManagerFactory.ForCurrentStream(pubkey);
await streamManager.PatchEvent(req.Title, req.Summary, req.Image, req.Tags, req.ContentWarning);
await _userService.UpdateStreamInfo(pubkey, req);
try
{
var streamManager = await _streamManagerFactory.ForCurrentStream(pubkey);
await streamManager.UpdateEvent();
}
catch
{
//ignore
}
return Accepted();
}
@ -95,6 +109,23 @@ public class NostrController : Controller
});
}
[HttpPatch("account")]
public async Task<IActionResult> PatchAccount([FromBody] PatchAccount patch)
{
var user = await GetUser();
if (user == default)
{
return NotFound();
}
if (patch.AcceptTos)
{
await _userService.AcceptTos(user.PubKey);
}
return Accepted();
}
private async Task<User?> GetUser()
{
var pk = GetPubKey();

View File

@ -14,6 +14,9 @@ public class UserConfiguration : IEntityTypeConfiguration<User>
builder.Property(a => a.Balance)
.IsRequired();
builder.Property(a => a.TosAccepted)
.IsRequired();
builder.Property(a => a.Version)
.IsRowVersion();

View File

@ -39,6 +39,11 @@ public class User
/// </summary>
public string? ContentWarning { get; set; }
/// <summary>
/// Date when user accepted TOS
/// </summary>
public DateTime? TosAccepted { get; init; }
/// <summary>
/// Concurrency token
/// </summary>

View File

@ -44,6 +44,12 @@ public static class Extensions
ForcePathStyle = true
});
}
public static string[] SplitTags(this User user)
{
return !string.IsNullOrEmpty(user.Tags) ?
user.Tags.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) : Array.Empty<string>();
}
}
public class Variant

View File

@ -0,0 +1,314 @@
// <auto-generated />
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using NostrStreamer.Database;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace NostrStreamer.Migrations
{
[DbContext(typeof(StreamerContext))]
[Migration("20230801110903_TOS")]
partial class TOS
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.8")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("NostrStreamer.Database.IngestEndpoint", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("App")
.IsRequired()
.HasColumnType("text");
b.Property<List<string>>("Capabilities")
.IsRequired()
.HasColumnType("text[]");
b.Property<int>("Cost")
.HasColumnType("integer");
b.Property<string>("Forward")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("App")
.IsUnique();
b.ToTable("Endpoints");
});
modelBuilder.Entity("NostrStreamer.Database.Payment", b =>
{
b.Property<string>("PaymentHash")
.HasColumnType("text");
b.Property<decimal>("Amount")
.HasColumnType("numeric(20,0)");
b.Property<DateTime>("Created")
.HasColumnType("timestamp with time zone");
b.Property<string>("Invoice")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("IsPaid")
.HasColumnType("boolean");
b.Property<string>("Nostr")
.HasColumnType("text");
b.Property<string>("PubKey")
.IsRequired()
.HasColumnType("text");
b.Property<int>("Type")
.HasColumnType("integer");
b.HasKey("PaymentHash");
b.HasIndex("PubKey");
b.ToTable("Payments");
});
modelBuilder.Entity("NostrStreamer.Database.User", b =>
{
b.Property<string>("PubKey")
.HasColumnType("text");
b.Property<long>("Balance")
.HasColumnType("bigint");
b.Property<string>("ContentWarning")
.HasColumnType("text");
b.Property<string>("Image")
.HasColumnType("text");
b.Property<string>("StreamKey")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Summary")
.HasColumnType("text");
b.Property<string>("Tags")
.HasColumnType("text");
b.Property<string>("Title")
.HasColumnType("text");
b.Property<DateTime?>("TosAccepted")
.IsRequired()
.HasColumnType("timestamp with time zone");
b.Property<uint>("Version")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("xid")
.HasColumnName("xmin");
b.HasKey("PubKey");
b.ToTable("Users");
});
modelBuilder.Entity("NostrStreamer.Database.UserStream", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("EdgeIp")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("EndpointId")
.HasColumnType("uuid");
b.Property<DateTime?>("Ends")
.HasColumnType("timestamp with time zone");
b.Property<string>("Event")
.IsRequired()
.HasColumnType("text");
b.Property<string>("ForwardClientId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PubKey")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Recording")
.HasColumnType("text");
b.Property<DateTime>("Starts")
.HasColumnType("timestamp with time zone");
b.Property<int>("State")
.HasColumnType("integer");
b.Property<string>("StreamId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("EndpointId");
b.HasIndex("PubKey");
b.ToTable("Streams");
});
modelBuilder.Entity("NostrStreamer.Database.UserStreamGuest", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("PubKey")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Relay")
.HasColumnType("text");
b.Property<string>("Role")
.HasColumnType("text");
b.Property<string>("Sig")
.HasColumnType("text");
b.Property<Guid>("StreamId")
.HasColumnType("uuid");
b.Property<decimal>("ZapSplit")
.HasColumnType("numeric");
b.HasKey("Id");
b.HasIndex("StreamId", "PubKey")
.IsUnique();
b.ToTable("Guests");
});
modelBuilder.Entity("NostrStreamer.Database.UserStreamRecording", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<double>("Duration")
.HasColumnType("double precision");
b.Property<DateTime>("Timestamp")
.HasColumnType("timestamp with time zone");
b.Property<string>("Url")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserStreamId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserStreamId");
b.ToTable("Recordings");
});
modelBuilder.Entity("NostrStreamer.Database.Payment", b =>
{
b.HasOne("NostrStreamer.Database.User", "User")
.WithMany("Payments")
.HasForeignKey("PubKey")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("NostrStreamer.Database.UserStream", b =>
{
b.HasOne("NostrStreamer.Database.IngestEndpoint", "Endpoint")
.WithMany()
.HasForeignKey("EndpointId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("NostrStreamer.Database.User", "User")
.WithMany("Streams")
.HasForeignKey("PubKey")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Endpoint");
b.Navigation("User");
});
modelBuilder.Entity("NostrStreamer.Database.UserStreamGuest", b =>
{
b.HasOne("NostrStreamer.Database.UserStream", "Stream")
.WithMany("Guests")
.HasForeignKey("StreamId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Stream");
});
modelBuilder.Entity("NostrStreamer.Database.UserStreamRecording", b =>
{
b.HasOne("NostrStreamer.Database.UserStream", "Stream")
.WithMany()
.HasForeignKey("UserStreamId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Stream");
});
modelBuilder.Entity("NostrStreamer.Database.User", b =>
{
b.Navigation("Payments");
b.Navigation("Streams");
});
modelBuilder.Entity("NostrStreamer.Database.UserStream", b =>
{
b.Navigation("Guests");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NostrStreamer.Migrations
{
/// <inheritdoc />
public partial class TOS : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
name: "TosAccepted",
table: "Users",
type: "timestamp with time zone",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "TosAccepted",
table: "Users");
}
}
}

View File

@ -118,6 +118,10 @@ namespace NostrStreamer.Migrations
b.Property<string>("Title")
.HasColumnType("text");
b.Property<DateTime?>("TosAccepted")
.IsRequired()
.HasColumnType("timestamp with time zone");
b.Property<uint>("Version")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
@ -282,7 +286,7 @@ namespace NostrStreamer.Migrations
modelBuilder.Entity("NostrStreamer.Database.UserStreamRecording", b =>
{
b.HasOne("NostrStreamer.Database.UserStream", "Stream")
.WithMany("Recordings")
.WithMany()
.HasForeignKey("UserStreamId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
@ -300,8 +304,6 @@ namespace NostrStreamer.Migrations
modelBuilder.Entity("NostrStreamer.Database.UserStream", b =>
{
b.Navigation("Guests");
b.Navigation("Recordings");
});
#pragma warning restore 612, 618
}

View File

@ -48,8 +48,4 @@
<PackageReference Include="Nostr.Client" Version="1.4.2" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.4" />
</ItemGroup>
<ItemGroup>
<Folder Include="Services\Abstract\" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,63 @@
@page
@using NostrStreamer
@model NostrStreamer.Pages.Tos
@inject Config Config
@{
Layout = null;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>api.zap.stream Terms of Service</title>
</head>
<body>
<h1>api.zap.stream Terms of Service</h1>
<p>
<strong>Effective Date:</strong> @Config.TosDate.ToString("u")</p>
<p>
Welcome to zap.stream! By using our streaming service, you agree to the following Terms of Service ("Terms").
Please read them carefully.
</p>
<ol>
<li>
User Accounts: You are responsible for maintaining the security of your account and any activities that occur
under it.
</li>
<li>
User Conduct: You agree not to violate any laws, infringe upon others' rights, or engage in fraudulent or
harmful activities while using our service.
</li>
<li>
Prohibited Content: You may not stream or upload copyrighted content unless you have obtained the necessary
rights or permissions.
</li>
<li>
Termination: We reserve the right to suspend or terminate your access to the service if you violate these
Terms.
</li>
<li>
Disclaimer: The zap.stream service is provided "as is" without any warranties.
</li>
<li>
Limitation of Liability: We shall not be liable for any damages arising from your use of the service.
</li>
<li>
Governing Law: These Terms shall be governed by and construed in accordance with the applicable local laws,
regulations, and jurisdiction where the service is accessed and used.
</li>
<li>
Modifications: We may update these Terms from time to time, and your continued use of the service constitutes
acceptance of the modified Terms.
</li>
<li>
Entire Agreement: These Terms constitute the entire agreement between you and zap.stream.
</li>
</ol>
<p>If you have any questions or concerns, please contact us at <a href="mailto:info@zap.stream">info@zap.stream</a>.</p>
<p>By using zap.stream, you agree to abide by these Terms of Service. Enjoy your streaming experience!</p>
</body>
</html>

View File

@ -0,0 +1,11 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace NostrStreamer.Pages;
public class Tos : PageModel
{
public void OnGet()
{
}
}

View File

@ -23,6 +23,7 @@ internal static class Program
services.AddCors();
services.AddMemoryCache();
services.AddHttpClient();
services.AddRazorPages();
services.AddControllers().AddNewtonsoftJson();
services.AddSingleton(config);
@ -75,6 +76,7 @@ internal static class Program
app.UseCors(o => o.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin());
app.UseAuthorization();
app.MapRazorPages();
app.MapControllers();
await app.RunAsync();

View File

@ -61,8 +61,7 @@ public class StreamEventBuilder
tags.Add(new("recording", new Uri(_config.DataHost, $"recording/{stream.Id}.m3u8").ToString()));
}
foreach (var tag in !string.IsNullOrEmpty(user.Tags) ?
user.Tags.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) : Array.Empty<string>())
foreach (var tag in user.SplitTags())
{
tags.Add(new("t", tag));
}

View File

@ -10,6 +10,12 @@ public interface IStreamManager
/// <returns></returns>
UserStream GetStream();
/// <summary>
/// Test if streaming is allowed for this user, otherwise throw
/// </summary>
/// <exception cref="Exception">Throws if cant stream</exception>
void TestCanStream();
/// <summary>
/// Stream ingress check on srs-edge
/// </summary>
@ -35,17 +41,6 @@ public interface IStreamManager
/// <returns></returns>
Task ConsumeQuota(double duration);
/// <summary>
/// Update stream details
/// </summary>
/// <param name="title"></param>
/// <param name="summary"></param>
/// <param name="image"></param>
/// <param name="tags"></param>
/// <param name="contentWarning"></param>
/// <returns></returns>
Task PatchEvent(string? title, string? summary, string? image, string[]? tags, string? contentWarning);
/// <summary>
/// Update viewer count
/// </summary>

View File

@ -1,4 +1,3 @@
using System.Text.RegularExpressions;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Nostr.Client.Json;
@ -14,15 +13,16 @@ public class NostrStreamManager : IStreamManager
private readonly StreamEventBuilder _eventBuilder;
private readonly IDvrStore _dvrStore;
private readonly ThumbnailService _thumbnailService;
private readonly Config _config;
public NostrStreamManager(ILogger<NostrStreamManager> logger, StreamManagerContext context,
StreamEventBuilder eventBuilder, IDvrStore dvrStore, ThumbnailService thumbnailService)
public NostrStreamManager(ILogger<NostrStreamManager> logger, StreamManagerContext context, IServiceProvider serviceProvider)
{
_logger = logger;
_context = context;
_eventBuilder = eventBuilder;
_dvrStore = dvrStore;
_thumbnailService = thumbnailService;
_eventBuilder = serviceProvider.GetRequiredService<StreamEventBuilder>();
_dvrStore = serviceProvider.GetRequiredService<IDvrStore>();
_thumbnailService = serviceProvider.GetRequiredService<ThumbnailService>();
_config = serviceProvider.GetRequiredService<Config>();
}
public UserStream GetStream()
@ -30,13 +30,22 @@ public class NostrStreamManager : IStreamManager
return _context.UserStream;
}
public Task<List<string>> OnForward()
public void TestCanStream()
{
if (_context.User.Balance <= 0)
{
throw new LowBalanceException("User balance empty");
}
if (_context.User.TosAccepted == null || _context.User.TosAccepted < _config.TosDate)
{
throw new Exception("TOS not accepted");
}
}
public Task<List<string>> OnForward()
{
TestCanStream();
return Task.FromResult(new List<string>
{
$"rtmp://127.0.0.1:1935/{_context.UserStream.Endpoint.App}/{_context.User.StreamKey}?vhost={_context.UserStream.Endpoint.Forward}"
@ -46,12 +55,7 @@ public class NostrStreamManager : IStreamManager
public async Task StreamStarted()
{
_logger.LogInformation("Stream started for: {pubkey}", _context.User.PubKey);
if (_context.User.Balance <= 0)
{
throw new Exception("User balance empty");
}
TestCanStream();
await UpdateStreamState(UserStreamState.Live);
#pragma warning disable CS4014
@ -95,31 +99,6 @@ public class NostrStreamManager : IStreamManager
}
}
public async Task PatchEvent(string? title, string? summary, string? image, string[]? tags, string? contentWarning)
{
var user = _context.User;
await _context.Db.Users
.Where(a => a.PubKey == _context.User.PubKey)
.ExecuteUpdateAsync(o => o.SetProperty(v => v.Title, title)
.SetProperty(v => v.Summary, summary)
.SetProperty(v => v.Image, image)
.SetProperty(v => v.Tags, tags != null ? string.Join(",", tags) : null)
.SetProperty(v => v.ContentWarning, contentWarning));
user.Title = title;
user.Summary = summary;
user.Image = image;
user.Tags = tags != null ? string.Join(",", tags) : null;
user.ContentWarning = contentWarning;
var ev = _eventBuilder.CreateStreamEvent(user, _context.UserStream);
await _context.Db.Streams.Where(a => a.Id == _context.UserStream.Id)
.ExecuteUpdateAsync(o => o.SetProperty(v => v.Event, JsonConvert.SerializeObject(ev, NostrSerializer.Settings)));
_eventBuilder.BroadcastEvent(ev);
}
public async Task AddGuest(string pubkey, string role, decimal zapSplit)
{
_context.Db.Guests.Add(new()

View File

@ -2,7 +2,6 @@ using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Nostr.Client.Json;
using NostrStreamer.Database;
using NostrStreamer.Services.Dvr;
namespace NostrStreamer.Services.StreamManager;
@ -12,18 +11,16 @@ public class StreamManagerFactory
private readonly ILoggerFactory _loggerFactory;
private readonly StreamEventBuilder _eventBuilder;
private readonly IServiceProvider _serviceProvider;
private readonly IDvrStore _dvrStore;
private readonly ThumbnailService _thumbnailService;
private readonly Config _config;
public StreamManagerFactory(StreamerContext db, ILoggerFactory loggerFactory, StreamEventBuilder eventBuilder,
IServiceProvider serviceProvider, IDvrStore dvrStore, ThumbnailService thumbnailService)
IServiceProvider serviceProvider, Config config)
{
_db = db;
_loggerFactory = loggerFactory;
_eventBuilder = eventBuilder;
_serviceProvider = serviceProvider;
_dvrStore = dvrStore;
_thumbnailService = thumbnailService;
_config = config;
}
public async Task<IStreamManager> CreateStream(StreamInfo info)
@ -47,7 +44,12 @@ public class StreamManagerFactory
if (user.Balance <= 0)
{
throw new Exception("Cannot start stream with empty balance");
throw new LowBalanceException("Cannot start stream with empty balance");
}
if (user.TosAccepted == null || user.TosAccepted < _config.TosDate)
{
throw new Exception("TOS not accepted");
}
var stream = new UserStream
@ -82,7 +84,7 @@ public class StreamManagerFactory
EdgeApi = new SrsApi(_serviceProvider.GetRequiredService<HttpClient>(), new Uri($"http://{stream.EdgeIp}:1985"))
};
return new NostrStreamManager(_loggerFactory.CreateLogger<NostrStreamManager>(), ctx, _eventBuilder, _dvrStore, _thumbnailService);
return new NostrStreamManager(_loggerFactory.CreateLogger<NostrStreamManager>(), ctx, _serviceProvider);
}
public async Task<IStreamManager> ForStream(Guid id)
@ -102,7 +104,7 @@ public class StreamManagerFactory
EdgeApi = new SrsApi(_serviceProvider.GetRequiredService<HttpClient>(), new Uri($"http://{stream.EdgeIp}:1985"))
};
return new NostrStreamManager(_loggerFactory.CreateLogger<NostrStreamManager>(), ctx, _eventBuilder, _dvrStore, _thumbnailService);
return new NostrStreamManager(_loggerFactory.CreateLogger<NostrStreamManager>(), ctx, _serviceProvider);
}
public async Task<IStreamManager> ForCurrentStream(string pubkey)
@ -122,7 +124,7 @@ public class StreamManagerFactory
EdgeApi = new SrsApi(_serviceProvider.GetRequiredService<HttpClient>(), new Uri($"http://{stream.EdgeIp}:1985"))
};
return new NostrStreamManager(_loggerFactory.CreateLogger<NostrStreamManager>(), ctx, _eventBuilder, _dvrStore, _thumbnailService);
return new NostrStreamManager(_loggerFactory.CreateLogger<NostrStreamManager>(), ctx, _serviceProvider);
}
public async Task<IStreamManager> ForStream(StreamInfo info)
@ -150,6 +152,6 @@ public class StreamManagerFactory
EdgeApi = new SrsApi(_serviceProvider.GetRequiredService<HttpClient>(), new Uri($"http://{stream.EdgeIp}:1985"))
};
return new NostrStreamManager(_loggerFactory.CreateLogger<NostrStreamManager>(), ctx, _eventBuilder, _dvrStore, _thumbnailService);
return new NostrStreamManager(_loggerFactory.CreateLogger<NostrStreamManager>(), ctx, _serviceProvider);
}
}

View File

@ -1,5 +1,6 @@
using Microsoft.EntityFrameworkCore;
using Nostr.Client.Utils;
using NostrStreamer.ApiModel;
using NostrStreamer.Database;
namespace NostrStreamer.Services;
@ -69,4 +70,23 @@ public class UserService
return await _db.Users.AsNoTracking()
.SingleOrDefaultAsync(a => a.PubKey.Equals(pubkey));
}
public async Task AcceptTos(string pubkey)
{
var change = await _db.Users.Where(a => a.PubKey.Equals(pubkey))
.ExecuteUpdateAsync(o => o.SetProperty(v => v.TosAccepted, DateTime.UtcNow));
if (change != 1) throw new Exception($"Failed to accept TOS, {change} rows updated.");
}
public async Task UpdateStreamInfo(string pubkey, PatchEvent req)
{
await _db.Users
.Where(a => a.PubKey == pubkey)
.ExecuteUpdateAsync(o => o.SetProperty(v => v.Title, req.Title)
.SetProperty(v => v.Summary, req.Summary)
.SetProperty(v => v.Image, req.Image)
.SetProperty(v => v.Tags, req.Tags.Length > 0 ? string.Join(",", req.Tags) : null)
.SetProperty(v => v.ContentWarning, req.ContentWarning));
}
}

View File

@ -17,6 +17,7 @@
"SrsApiHost": "http://localhost:9002",
"DataHost": "http://localhost:5295/api/playlist/",
"ApiHost": "http://localhost:5295",
"TosDate": "2023-07-06",
"Relays": [
"ws://localhost:8081"
],