diff --git a/src/BilibiliLiveAreaTool/Properties/PublishProfiles/FolderProfile.pubxml b/src/BilibiliLiveAreaTool/Properties/PublishProfiles/FolderProfile.pubxml
index e272a85..7a9269a 100644
--- a/src/BilibiliLiveAreaTool/Properties/PublishProfiles/FolderProfile.pubxml
+++ b/src/BilibiliLiveAreaTool/Properties/PublishProfiles/FolderProfile.pubxml
@@ -9,6 +9,10 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
bin\Release\publish\
FileSystem
net6.0
- false
+ true
+ win-x64
+ true
+ false
+ false
\ No newline at end of file
diff --git a/src/BilibiliLiveCommon/Config/CacheKeyConstant.cs b/src/BilibiliLiveCommon/Config/CacheKeyConstant.cs
index 70109fa..478ea55 100644
--- a/src/BilibiliLiveCommon/Config/CacheKeyConstant.cs
+++ b/src/BilibiliLiveCommon/Config/CacheKeyConstant.cs
@@ -5,5 +5,7 @@ public class CacheKeyConstant
public const string COOKIE_KEY = "COOKIE_KEY";
public const string LIVE_STATUS_KEY = "LIVE_STATUS_KEY";
+
+ public const string MAIL_SEND_CACHE_KEY = "MAIL_{0}_SEND_STATUS_KEY";
}
}
diff --git a/src/BilibiliLiveCommon/Services/BilibiliLiveApiService.cs b/src/BilibiliLiveCommon/Services/BilibiliLiveApiService.cs
index 44e044c..4c78daa 100644
--- a/src/BilibiliLiveCommon/Services/BilibiliLiveApiService.cs
+++ b/src/BilibiliLiveCommon/Services/BilibiliLiveApiService.cs
@@ -278,7 +278,7 @@ private async Task CheckArea(int areaId)
private async Task SlowDownOperation(string operationName)
{
int sleepMsec = new Random().Next(5000, 10000);
- _logger.LogInformation($"执行{operationName}操作完成,休眠{sleepMsec / 1000}秒(避免频繁操作)。");
+ _logger.LogDebug($"执行{operationName}操作完成,休眠{sleepMsec / 1000}秒,避免被B站频繁操作。");
await Task.Delay(sleepMsec);
}
diff --git a/src/BilibiliLiveMonitor/Configs/Node/AppSettingsNode.cs b/src/BilibiliLiveMonitor/Configs/Models/AppSettingsNode.cs
similarity index 100%
rename from src/BilibiliLiveMonitor/Configs/Node/AppSettingsNode.cs
rename to src/BilibiliLiveMonitor/Configs/Models/AppSettingsNode.cs
diff --git a/src/BilibiliLiveMonitor/Configs/Node/EmailConfigNode.cs b/src/BilibiliLiveMonitor/Configs/Models/EmailConfigNode.cs
similarity index 100%
rename from src/BilibiliLiveMonitor/Configs/Node/EmailConfigNode.cs
rename to src/BilibiliLiveMonitor/Configs/Models/EmailConfigNode.cs
diff --git a/src/BilibiliLiveMonitor/Configs/Node/LiveSettingsNode.cs b/src/BilibiliLiveMonitor/Configs/Models/LiveSettingsNode.cs
similarity index 100%
rename from src/BilibiliLiveMonitor/Configs/Node/LiveSettingsNode.cs
rename to src/BilibiliLiveMonitor/Configs/Models/LiveSettingsNode.cs
diff --git a/src/BilibiliLiveMonitor/Jobs/MonitorClientJob.cs b/src/BilibiliLiveMonitor/Jobs/MonitorClientJob.cs
index 8c21a69..42ff7df 100644
--- a/src/BilibiliLiveMonitor/Jobs/MonitorClientJob.cs
+++ b/src/BilibiliLiveMonitor/Jobs/MonitorClientJob.cs
@@ -7,6 +7,7 @@
using BilibiliLiveCommon.Model;
using BilibiliLiveCommon.Services.Interface;
using BilibiliLiveCommon.Config;
+using BilibiliLiveCommon.Utils;
namespace BilibiliLiveMonitor.Jobs
{
@@ -37,7 +38,7 @@ public async Task Execute(IJobExecutionContext context)
try
{
_logger.LogInformation($"开始查询直播间{_liveSettings.RoomId}状态....");
- if(_liveSettings.RoomId <= 0)
+ if (_liveSettings.RoomId <= 0)
{
throw new Exception("获取需要查询的直播房间号失败,请检查配置文件是否正确配置房间号。");
}
@@ -46,24 +47,19 @@ public async Task Execute(IJobExecutionContext context)
{
throw new Exception("获取直播间信息失败。");
}
+ _logger.LogInformation($"获取直播间{_liveSettings.RoomId}状态成功,当前状态:{(playInfo.is_living ? "直播中" : "停止直播")}");
RoomPlayInfo lastPlayInfo = _cache.Get(CacheKeyConstant.LIVE_STATUS_KEY);
- if(lastPlayInfo == null)
+ if (lastPlayInfo == null)
{
//第一次存缓存强制设置直播状态为1,免得第一次开启时就发送通知
playInfo.live_status = 1;
_cache.Set(CacheKeyConstant.LIVE_STATUS_KEY, playInfo);
return;
}
- if(lastPlayInfo.is_living != playInfo.is_living)
+ if (lastPlayInfo.is_living != playInfo.is_living)
{
- if (playInfo.is_living)
- {
- //开启了直播
- }
- else
- {
- //直播已关闭
- }
+ await SendEmailNotice(playInfo);
+ _cache.Set(CacheKeyConstant.LIVE_STATUS_KEY, playInfo);
}
}
catch (Exception ex)
@@ -77,11 +73,29 @@ public async Task Execute(IJobExecutionContext context)
///
///
///
- private async Task SendEmailNotice(string reason)
+ private async Task SendEmailNotice(RoomPlayInfo info)
{
try
{
- var result = await _email.Send("直播停止通知", reason);
+ _logger.LogInformation($"直播间{info.room_id}直播状态发生改变,发送通知邮件");
+ String reason = "";
+ string cacheKey = string.Format(CacheKeyConstant.MAIL_SEND_CACHE_KEY, info.live_status);
+ if (info.is_living)
+ {
+ reason = $"开播提醒:\r\n您订阅的直播间(https://live.bilibili.com/{info.room_id})开始直播啦。";
+ }
+ else
+ {
+ reason = $"关闭提醒:\r\n您订阅的直播间(https://live.bilibili.com/{info.room_id})已经停止直播。";
+ }
+ long lastSendTime = _cache.Get(cacheKey);
+ if (TimeUtil.Timestamp() - lastSendTime < 600)
+ {
+ _logger.LogWarning($"邮件发送过于频繁,忽略本次发送。{reason}");
+ return;
+ }
+ var result = await _email.Send("直播订阅通知", reason);
+ _cache.Set(cacheKey, TimeUtil.Timestamp());
if (result.Item1 != SendStatus.Success)
{
throw new Exception(result.Item2);
diff --git a/src/BilibiliLiveMonitor/Properties/PublishProfiles/FolderProfile.pubxml b/src/BilibiliLiveMonitor/Properties/PublishProfiles/FolderProfile.pubxml
index 0226252..033a89e 100644
--- a/src/BilibiliLiveMonitor/Properties/PublishProfiles/FolderProfile.pubxml
+++ b/src/BilibiliLiveMonitor/Properties/PublishProfiles/FolderProfile.pubxml
@@ -11,7 +11,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
net6.0
win-x64
true
- false
+ true
false
false
diff --git a/src/BilibiliLiveMonitor/appsettings.json b/src/BilibiliLiveMonitor/appsettings.json
index f0d028e..d65487e 100644
--- a/src/BilibiliLiveMonitor/appsettings.json
+++ b/src/BilibiliLiveMonitor/appsettings.json
@@ -7,7 +7,7 @@
}
},
"AppSettings": {
- "IntervalTime": 30, //检查间隔时间 单位:秒
+ "IntervalTime": 60, //检查间隔时间 单位:秒
"IsEnableEmailNotice": false,
"EmailConfig": {
"SmtpServer": "smtp.oncemi.com",
diff --git a/src/BilibiliLiver/BilibiliLiver.csproj b/src/BilibiliLiver/BilibiliLiver.csproj
index cb43a24..ce76584 100644
--- a/src/BilibiliLiver/BilibiliLiver.csproj
+++ b/src/BilibiliLiver/BilibiliLiver.csproj
@@ -6,7 +6,7 @@
3.0.1
3.0.1
withsalt
- 2.0.2
+ 3.0.3
B站直播工具。
@@ -28,6 +28,7 @@
+
@@ -41,6 +42,9 @@
Always
+
+ Always
+
diff --git a/src/BilibiliLiver/Config/Models/LiveSetting.cs b/src/BilibiliLiver/Config/Models/LiveSettings.cs
similarity index 76%
rename from src/BilibiliLiver/Config/Models/LiveSetting.cs
rename to src/BilibiliLiver/Config/Models/LiveSettings.cs
index 0ca51f1..5d1740f 100644
--- a/src/BilibiliLiver/Config/Models/LiveSetting.cs
+++ b/src/BilibiliLiver/Config/Models/LiveSettings.cs
@@ -1,6 +1,6 @@
namespace BilibiliLiver.Config.Models
{
- public class LiveSetting
+ public class LiveSettings
{
public int LiveAreaId { get; set; }
@@ -10,6 +10,8 @@ public class LiveSetting
public bool AutoRestart { get; set; }
- public static string Position { get { return "LiveSetting"; } }
+ public int RepushFailedExitMinutes { get; set; }
+
+ public static string Position { get { return "LiveSettings"; } }
}
}
diff --git a/src/BilibiliLiver/DependencyInjection/RegisteConfigure.cs b/src/BilibiliLiver/DependencyInjection/RegisteConfigure.cs
index 012bb23..a9ea1f3 100644
--- a/src/BilibiliLiver/DependencyInjection/RegisteConfigure.cs
+++ b/src/BilibiliLiver/DependencyInjection/RegisteConfigure.cs
@@ -8,7 +8,7 @@ public static class RegisteConfigure
{
public static IServiceCollection ConfigureSettings(this IServiceCollection services, HostBuilderContext hostContext)
{
- services.Configure(hostContext.Configuration.GetSection(LiveSetting.Position));
+ services.Configure(hostContext.Configuration.GetSection(LiveSettings.Position));
return services;
}
}
diff --git a/src/BilibiliLiver/Program.cs b/src/BilibiliLiver/Program.cs
index dd05461..574df1c 100644
--- a/src/BilibiliLiver/Program.cs
+++ b/src/BilibiliLiver/Program.cs
@@ -2,6 +2,8 @@
using BilibiliLiver.Services;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using NLog.Web;
using System;
using System.Reflection;
@@ -18,6 +20,14 @@ static void Main(string[] args)
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
+ .ConfigureLogging(logging =>
+ {
+ //移除已经注册的其他日志处理程序
+ logging.ClearProviders();
+ //设置最小的日志级别
+ logging.SetMinimumLevel(LogLevel.Trace);
+ })
+ .UseNLog()
.ConfigureServices((hostContext, services) =>
{
//配置初始化
diff --git a/src/BilibiliLiver/Properties/PublishProfiles/FolderProfile.pubxml b/src/BilibiliLiver/Properties/PublishProfiles/FolderProfile.pubxml
index 1af32ef..9f277b7 100644
--- a/src/BilibiliLiver/Properties/PublishProfiles/FolderProfile.pubxml
+++ b/src/BilibiliLiver/Properties/PublishProfiles/FolderProfile.pubxml
@@ -10,8 +10,9 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
FileSystem
net6.0
true
- linux-x64
+ win-x64
true
false
+ false
\ No newline at end of file
diff --git a/src/BilibiliLiver/Services/ConfigureService.cs b/src/BilibiliLiver/Services/ConfigureService.cs
index 5b1317b..33ee0c5 100644
--- a/src/BilibiliLiver/Services/ConfigureService.cs
+++ b/src/BilibiliLiver/Services/ConfigureService.cs
@@ -19,14 +19,14 @@ public class ConfigureService : IHostedService
private readonly IBilibiliLiveApiService _api;
private readonly IBilibiliCookieService _cookie;
private readonly IPushStreamService _push;
- private readonly LiveSetting _liveSetting;
+ private readonly LiveSettings _liveSetting;
public ConfigureService(ILogger logger
, IBilibiliAccountService account
, IBilibiliLiveApiService api
, IBilibiliCookieService cookie
, IPushStreamService push
- , IOptions liveSettingOptions)
+ , IOptions liveSettingOptions)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_account = account ?? throw new ArgumentNullException(nameof(account));
diff --git a/src/BilibiliLiver/Services/PushStreamService.cs b/src/BilibiliLiver/Services/PushStreamService.cs
index 7c72839..79eb6ef 100644
--- a/src/BilibiliLiver/Services/PushStreamService.cs
+++ b/src/BilibiliLiver/Services/PushStreamService.cs
@@ -22,7 +22,7 @@ public class PushStreamService : IPushStreamService
private readonly ILogger _logger;
private readonly IBilibiliAccountService _account;
private readonly IBilibiliLiveApiService _api;
- private readonly LiveSetting _liveSetting;
+ private readonly LiveSettings _liveSetting;
private CancellationTokenSource _tokenSource;
private Task _mainTask;
@@ -30,7 +30,7 @@ public class PushStreamService : IPushStreamService
public PushStreamService(ILogger logger
, IBilibiliAccountService account
, IBilibiliLiveApiService api
- , IOptions liveSettingOptions)
+ , IOptions liveSettingOptions)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_account = account ?? throw new ArgumentNullException(nameof(account));
@@ -143,53 +143,55 @@ public async Task StopPush()
///
private async Task InitLiveProcessStartInfo()
{
- try
+ //检查Cookie是否有效
+ UserInfo userInfo = await _account.Login();
+ if (userInfo == null || !userInfo.IsLogin)
{
- //检查Cookie是否有效
- UserInfo userInfo = await _account.Login();
- if (userInfo == null || !userInfo.IsLogin)
- {
- throw new Exception("登录失败,Cookie已失效");
- }
- //获取直播间信息
- var liveRoomInfo = await _api.GetLiveRoomInfo();
- //开启直播
- StartLiveInfo startLiveInfo = await _api.StartLive(liveRoomInfo.room_id, _liveSetting.LiveAreaId);
- string url = startLiveInfo.rtmp.addr + startLiveInfo.rtmp.code;
- if (string.IsNullOrWhiteSpace(url))
- {
- _logger.ThrowLogError("获取推流地址失败,请重试!");
- }
- _logger.LogInformation($"获取推流地址成功,推流地址:{url}");
+ throw new Exception("登录失败,Cookie已失效");
+ }
+ //获取直播间信息
+ var liveRoomInfo = await _api.GetLiveRoomInfo();
+ if (liveRoomInfo.area_v2_id != _liveSetting.LiveAreaId)
+ {
+ await _api.UpdateLiveRoomArea(liveRoomInfo.room_id, _liveSetting.LiveAreaId);
+ }
+ if (liveRoomInfo.title != _liveSetting.LiveRoomName)
+ {
+ await _api.UpdateLiveRoomName(liveRoomInfo.room_id, _liveSetting.LiveRoomName);
+ }
+ //开启直播
+ StartLiveInfo startLiveInfo = await _api.StartLive(liveRoomInfo.room_id, _liveSetting.LiveAreaId);
+ string url = startLiveInfo.rtmp.addr + startLiveInfo.rtmp.code;
+ if (string.IsNullOrWhiteSpace(url))
+ {
+ throw new Exception("获取推流地址失败,请重试!");
+ }
+ _logger.LogInformation($"获取推流地址成功,推流地址:{url}");
- string newCmd = _liveSetting.FFmpegCmd.Replace("[[URL]]", $"\"{url}\"");
- int firstNullChar = newCmd.IndexOf((char)32);
- if (firstNullChar < 0)
- {
- throw new Exception("无法获取命令执行名称,比如在命令ffmpeg -version中,无法获取ffmpeg。");
- }
- string cmdName = newCmd.Substring(0, firstNullChar);
- string cmdArgs = newCmd.Substring(firstNullChar);
- if (string.IsNullOrEmpty(cmdArgs))
- {
- throw new Exception("命令参数不能为空!");
- }
- var psi = new ProcessStartInfo
- {
- FileName = cmdName,
- Arguments = cmdArgs,
- RedirectStandardOutput = true,
- RedirectStandardError = false,
- UseShellExecute = false,
- CreateNoWindow = RuntimeInformation.IsOSPlatform(OSPlatform.Linux),
- };
- return psi;
+ string newCmd = _liveSetting.FFmpegCmd
+ .Trim('\r', '\n', ' ')
+ .Replace("[[URL]]", $"\"{url}\"");
+ int firstNullChar = newCmd.IndexOf((char)32);
+ if (firstNullChar < 0)
+ {
+ throw new Exception("无法获取命令执行名称,比如在命令ffmpeg -version中,无法获取ffmpeg。");
}
- catch (Exception ex)
+ string cmdName = newCmd.Substring(0, firstNullChar);
+ string cmdArgs = newCmd.Substring(firstNullChar);
+ if (string.IsNullOrEmpty(cmdArgs))
{
- _logger.LogError(ex, "初始化直播参数时遇到错误。");
- return null;
+ throw new Exception("命令参数不能为空!");
}
+ var psi = new ProcessStartInfo
+ {
+ FileName = cmdName,
+ Arguments = cmdArgs,
+ RedirectStandardOutput = true,
+ RedirectStandardError = false,
+ UseShellExecute = false,
+ CreateNoWindow = RuntimeInformation.IsOSPlatform(OSPlatform.Linux),
+ };
+ return psi;
}
///
@@ -198,9 +200,7 @@ private async Task InitLiveProcessStartInfo()
///
private async Task PushStream()
{
- ProcessStartInfo psi = null;
bool isAutoRestart = true;
-
while (isAutoRestart && !_tokenSource.IsCancellationRequested)
{
try
@@ -213,12 +213,8 @@ private async Task PushStream()
await Task.Delay(10000, _tokenSource.Token);
}
//start live
- psi = await InitLiveProcessStartInfo();
- if (psi == null)
- {
- throw new Exception($"初始化直播参数失败。");
- }
- _logger.LogInformation("正在初始化推流指令...");
+ ProcessStartInfo psi = await InitLiveProcessStartInfo();
+ _logger.LogInformation("推流参数初始化完成,即将开始推流...");
//启动
using (var proc = Process.Start(psi))
{
@@ -233,20 +229,13 @@ private async Task PushStream()
await Task.Delay(100);
if (!_tokenSource.IsCancellationRequested)
{
- if (isAutoRestart)
- {
- _logger.LogWarning($"FFmpeg异常退出,将在60秒后重试推流。");
- }
- else
- {
- _logger.LogWarning($"FFmpeg异常退出。");
- }
+ _logger.LogWarning($"FFmpeg异常退出。");
}
}
+ //如果开启了自动重试
if (isAutoRestart && !_tokenSource.IsCancellationRequested)
{
- _logger.LogWarning($"等待重新推流...");
- //如果开启了自动重试,那么等待60s后再次尝试
+ _logger.LogWarning($"等待60s后重新推流...");
await Task.Delay(60000, _tokenSource.Token);
}
}
@@ -257,12 +246,15 @@ private async Task PushStream()
catch (Exception ex)
{
_logger.LogError(ex, $"推流过程中发生错误,{ex.Message}");
- _logger.LogWarning($"等待60s后重新推流...");
-
- //如果开启了自动重试,那么等待60s后再次尝试
- await Task.Delay(60000, _tokenSource.Token);
+ //如果开启了自动重试
+ if (isAutoRestart && !_tokenSource.IsCancellationRequested)
+ {
+ _logger.LogWarning($"等待60s后重新推流...");
+ await Task.Delay(60000, _tokenSource.Token);
+ }
}
}
}
+
}
}
diff --git a/src/BilibiliLiver/appsettings.json b/src/BilibiliLiver/appsettings.json
index 33ddf81..13b25e6 100644
--- a/src/BilibiliLiver/appsettings.json
+++ b/src/BilibiliLiver/appsettings.json
@@ -1,5 +1,5 @@
{
- "LiveSetting": {
+ "LiveSettings": {
//直播间分类
"LiveAreaId": 369,
//直播间名称
@@ -8,7 +8,9 @@
//填写到此处时,请注意将命令中‘"’用‘\’进行转义,将推流的rtmp连接替换为[[URL]],[[URL]]不需要双引号。
//下面推流指令默认适配设备树莓派,使用USB摄像头,设备为/dev/video0
"FFmpegCmd": "ffmpeg -thread_queue_size 1024 -f v4l2 -s 1280*720 -input_format mjpeg -i \"/dev/video0\" -stream_loop -1 -i \"data/demo_music.m4a\" -vcodec h264_omx -pix_fmt yuv420p -r 30 -s 1280*720 -g 60 -b:v 10M -bufsize 10M -acodec aac -ac 2 -ar 44100 -ab 128k -f flv [[URL]]",
- //ffmpeg异常退出后,是否自动重新启动
- "AutoRestart": true
+ //ffmpeg异常退出后,是否自动重新推流
+ "AutoRestart": true,
+ //重新尝试推流失败后多久后退出,避免不断请求B站API,开启自动重试有效
+ "RepushFailedExitMinutes": 30
}
}
\ No newline at end of file
diff --git a/src/BilibiliLiver/nlog.config b/src/BilibiliLiver/nlog.config
new file mode 100644
index 0000000..d121377
--- /dev/null
+++ b/src/BilibiliLiver/nlog.config
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+