Nadeko will now join invites in PM, new music code
Music does not work atm. Thx to @Bloodskilled for the new code, just need to clean it up a bit more
This commit is contained in:
parent
ce24c6e783
commit
168f553807
@ -1,4 +1,6 @@
|
|||||||
using System;
|
// Thanks to @Bloodskilled for providing most of the music code from his BooBot
|
||||||
|
// check out his server https://discord.gg/0aMlLYi2e2V7h2Kr
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@ -14,6 +16,7 @@ using System.Threading;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Discord.Legacy;
|
using Discord.Legacy;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
namespace NadekoBot.Modules {
|
namespace NadekoBot.Modules {
|
||||||
class Music : DiscordModule {
|
class Music : DiscordModule {
|
||||||
@ -203,134 +206,427 @@ namespace NadekoBot.Modules {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//new stuff
|
enum StreamTaskState {
|
||||||
class AudioStreamer {
|
Queued,
|
||||||
string sourceUrl; Channel statusTextChannel;
|
Playing,
|
||||||
int totalSourceBytes;
|
Completed
|
||||||
public bool NetworkDone { get; private set; }
|
|
||||||
public int BytesSentToTranscoder { get; private set; }
|
|
||||||
public Stream PCMOutput { get; private set; }
|
|
||||||
CancellationTokenSource tokenSource1 = new CancellationTokenSource();
|
|
||||||
CancellationTokenSource tokenSource2 = new CancellationTokenSource();
|
|
||||||
Task transcoderTask; Task outputTask;
|
|
||||||
public AudioStreamer(string streamUrl, Channel statusTextChannel = null) {
|
|
||||||
sourceUrl = streamUrl;
|
|
||||||
this.statusTextChannel = statusTextChannel;
|
|
||||||
}
|
}
|
||||||
public void Start() {
|
|
||||||
Task.Run(async () => {
|
class StreamRequest {
|
||||||
var bufferingStream = GetBufferingStream(sourceUrl);
|
static readonly string[] validFormats = { ".ogg", ".wav", ".mp3", ".webm", ".aac", ".mp4", ".flac" };
|
||||||
Console.WriteLine("Buffering video..."); // Wait for some data to arrive
|
|
||||||
while (bufferingStream.Length < 1000 || NetworkDone)
|
readonly DiscordClient client;
|
||||||
await Task.Delay(500);
|
public readonly Server Server;
|
||||||
Console.WriteLine("buf done");
|
public readonly Channel Channel;
|
||||||
Stream input, pcmOutput;
|
public Channel VoiceChannel;
|
||||||
var ffmpegProcess = GetTranscoderStreams(out input, out pcmOutput);
|
public readonly User User;
|
||||||
PCMOutput = new DualStream(); // Keep pumping network stuff into the transcoder
|
public readonly string RequestText;
|
||||||
transcoderTask = Task.Run(() => TranscoderFunc(bufferingStream, input, tokenSource1.Token));
|
|
||||||
// Keep pumping transcoder output into the PCMOutput stream
|
const string DefaultTitle = "<??>";
|
||||||
outputTask = Task.Run(() => OutputFunc(pcmOutput, PCMOutput, tokenSource2.Token));
|
public string Title = DefaultTitle;
|
||||||
// Wait until network stuff is all done
|
public TimeSpan Length = TimeSpan.FromSeconds(0);
|
||||||
while (!NetworkDone) await Task.Delay(500);
|
public string FileName;
|
||||||
// Then wait until we sent everything to the transcoder
|
|
||||||
while (BytesSentToTranscoder < totalSourceBytes) await Task.Delay(500);
|
bool linkResolved;
|
||||||
// Then wait some more until it did everything and kill it
|
public string StreamUrl;
|
||||||
await Task.Delay(5000);
|
public bool NetworkDone;
|
||||||
|
public long TotalSourceBytes;
|
||||||
|
|
||||||
|
Stream bufferingStream;
|
||||||
|
StreamTask streamTask;
|
||||||
|
|
||||||
|
public StreamTaskState State => streamTask?.State ?? StreamTaskState.Queued;
|
||||||
|
|
||||||
|
|
||||||
|
public StreamRequest(DiscordClient client, MessageEventArgs e, string text) {
|
||||||
|
this.client = client;
|
||||||
|
Server = e.Server;
|
||||||
|
Channel = e.Channel;
|
||||||
|
User = e.User;
|
||||||
|
RequestText = text.Trim();
|
||||||
|
|
||||||
|
FileName = "unresolved_" + Uri.EscapeUriString(RequestText);
|
||||||
|
|
||||||
|
Task.Run(() => {
|
||||||
try {
|
try {
|
||||||
tokenSource1.Cancel();
|
ResolveLink();
|
||||||
tokenSource2.Cancel();
|
} catch (Exception ex) {
|
||||||
Console.WriteLine("Killing transcoder...");
|
Console.WriteLine("Exception in ResolveLink: " + ex);
|
||||||
ffmpegProcess.Kill();
|
}
|
||||||
} catch { }
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
async Task TranscoderFunc(Stream sourceStream, Stream targetStream, CancellationToken cancellationToken) {
|
|
||||||
byte[] buffer = new byte[0x4000];
|
void ResolveLink() {
|
||||||
while (!NetworkDone && !cancellationToken.IsCancellationRequested) {
|
var url = RequestText;
|
||||||
|
|
||||||
|
if (url.IndexOf("soundcloud", StringComparison.OrdinalIgnoreCase) != -1) {
|
||||||
|
var track = Services.SoundcloudService.GetTrackStreamUrl(url, out Title, out StreamUrl);
|
||||||
|
Length = TimeSpan.FromMilliseconds(track.Duration);
|
||||||
|
Title = track.Title;
|
||||||
|
FileName = Uri.EscapeUriString(Title) + ".mp3";
|
||||||
|
|
||||||
|
StartBuffering();
|
||||||
|
linkResolved = true;
|
||||||
|
} else if (url.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) != -1 || url.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) != -1) {
|
||||||
|
try {
|
||||||
|
var infos = DownloadUrlResolver
|
||||||
|
.GetDownloadUrls(url.Trim())
|
||||||
|
.Where(i => i.AudioType != AudioType.Unknown)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
if (infos.Length == 0)
|
||||||
|
throw new Exception("Could not load any video elements");
|
||||||
|
|
||||||
|
var info = infos
|
||||||
|
.GroupBy(x => x.AudioBitrate) // Create groups for audio bitrates
|
||||||
|
.OrderByDescending(x => x.Key) // Group with max bitrate first
|
||||||
|
.Take(1) // Only take one group
|
||||||
|
.SelectMany(x => x) // Unpack group container again
|
||||||
|
.OrderBy(x => x.Resolution) // take vid with smallest resolution
|
||||||
|
.First(); // First one
|
||||||
|
|
||||||
|
StreamUrl = info.DownloadUrl;
|
||||||
|
Title = info.Title;
|
||||||
|
FileName = Uri.EscapeUriString(Title) + ".mp4";
|
||||||
|
|
||||||
|
StartBuffering();
|
||||||
|
linkResolved = true;
|
||||||
|
} catch (Exception) {
|
||||||
|
// Send a message to the guy that queued that
|
||||||
|
Channel.SendMessage(":warning: " + User.Mention + " Cannot load youtube url: `This video is not available in your country` or the url is corrupted somehow...");
|
||||||
|
Console.WriteLine("Cannot parse youtube url: " + url);
|
||||||
|
Cancel();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Is it a direct link oO ??
|
||||||
|
var format = validFormats.FirstOrDefault(f => url.EndsWith(f));
|
||||||
|
if (format == null) {
|
||||||
|
Console.WriteLine("Direct link: \"" + url + "\" does not end with a valid extension");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamUrl = url;
|
||||||
|
Title = url;
|
||||||
|
|
||||||
|
StartBuffering();
|
||||||
|
linkResolved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void StartBuffering() {
|
||||||
|
var folder = "StreamBuffers";
|
||||||
|
Directory.CreateDirectory(folder);
|
||||||
|
var fullPath = Path.Combine(folder, FileName);
|
||||||
|
|
||||||
|
FileStream fileStream;
|
||||||
|
FileStream readStream;
|
||||||
|
try {
|
||||||
|
if (File.Exists(fullPath) && new FileInfo(fullPath).Length > 1024 * 2) {
|
||||||
|
NetworkDone = true;
|
||||||
|
TotalSourceBytes = new FileInfo(fullPath).Length;
|
||||||
|
bufferingStream = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||||
|
|
||||||
|
if (Length.TotalSeconds < double.Epsilon)
|
||||||
|
Length = GetFileLength(fullPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open a new file to stream into
|
||||||
|
fileStream = new FileStream(fullPath, FileMode.Create, FileAccess.Write, FileShare.Read);
|
||||||
|
bufferingStream = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Console.WriteLine("Exception while creating or opening stream buffers: " + ex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Task.Run(() => {
|
||||||
|
AutoResetDelay fileLengthCheckDelay = new AutoResetDelay(500);
|
||||||
|
int byteCounter = 0;
|
||||||
|
bool fileLengthDetermined = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
var webClient = new WebClient();
|
||||||
|
var networkStream = webClient.OpenRead(StreamUrl);
|
||||||
|
|
||||||
|
if (networkStream == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
byte[] buffer = new byte[0x1000];
|
||||||
|
while (true) {
|
||||||
|
int read = networkStream.Read(buffer, 0, buffer.Length);
|
||||||
|
if (read <= 0)
|
||||||
|
break;
|
||||||
|
byteCounter += read;
|
||||||
|
TotalSourceBytes += read;
|
||||||
|
fileStream.Write(buffer, 0, read);
|
||||||
|
|
||||||
|
if (TotalSourceBytes > 1024 * 2 && Length.TotalSeconds < 0.1 && fileLengthCheckDelay.IsReady) {
|
||||||
|
Length = GetFileLength(fullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Console.WriteLine("Exception while buffering (network->file): " + ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
fileStream.Close();
|
||||||
|
NetworkDone = true;
|
||||||
|
Console.WriteLine("net: done. ({0} read)", byteCounter);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Start() {
|
||||||
|
if (State != StreamTaskState.Queued)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Stopwatch resolveTimer = Stopwatch.StartNew();
|
||||||
|
|
||||||
|
if (!linkResolved || bufferingStream == null)
|
||||||
|
Channel.SendMessage($":musical_note: Resolving link...\r\n:warning: `Keep in mind that other people can 'steal' the bot by just starting a stream command in their own server...`\r\n");
|
||||||
|
|
||||||
|
while (resolveTimer.ElapsedMilliseconds < 8000) {
|
||||||
|
if (bufferingStream != null)
|
||||||
|
break;
|
||||||
|
Thread.Sleep(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bufferingStream == null) {
|
||||||
|
Console.WriteLine("Buffering stream was not set! Can't play track!");
|
||||||
|
streamTask = new StreamTask(client, this, null);
|
||||||
|
streamTask.CancelStreaming();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
streamTask = new StreamTask(client, this, bufferingStream);
|
||||||
|
|
||||||
|
VoiceChannel = GetVoiceChannelForUser(User);
|
||||||
|
if (VoiceChannel == null) {
|
||||||
|
Channel.SendMessage($":warning: {User.Mention} `I can't find you in any voice channel. Join one, then try again...`");
|
||||||
|
streamTask.CancelStreaming(); // just to set the state to done
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go!
|
||||||
|
streamTask.StartStreaming();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Cancel() {
|
||||||
|
if (State == StreamTaskState.Completed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (streamTask == null)
|
||||||
|
streamTask = new StreamTask(client, this, bufferingStream);
|
||||||
|
streamTask.CancelStreaming();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Channel GetVoiceChannelForUser(User user) {
|
||||||
|
return client.Servers.SelectMany(s => s.VoiceChannels).FirstOrDefault(c => c.Users.Any(u => u.Id == user.Id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetFormattedTitle() {
|
||||||
|
if (Length.TotalSeconds < double.Epsilon)
|
||||||
|
Length = GetFileLength(FileName);
|
||||||
|
|
||||||
|
if (Title != DefaultTitle)
|
||||||
|
return $"**{Title.Replace('*', '°')}** *({Length.ToString()})*";
|
||||||
|
|
||||||
|
// put into <> when it contains a domain
|
||||||
|
if (StreamUrl == null)
|
||||||
|
return "<" + RequestText + ">";
|
||||||
|
if (StreamUrl.Contains("http:") || StreamUrl.Contains("https:"))
|
||||||
|
return "<" + StreamUrl.Trim() + ">";
|
||||||
|
return StreamUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TimeSpan GetFileLength(string fileName) {
|
||||||
|
try {
|
||||||
|
var startInfo = new ProcessStartInfo("ffprobe", $"-i \"{fileName}\" -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1");
|
||||||
|
startInfo.RedirectStandardOutput = true;
|
||||||
|
startInfo.RedirectStandardError = true;
|
||||||
|
startInfo.CreateNoWindow = true;
|
||||||
|
startInfo.UseShellExecute = false;
|
||||||
|
using (var process = Process.Start(startInfo)) {
|
||||||
|
var lengthLine = process.StandardOutput.ReadLine();
|
||||||
|
double result;
|
||||||
|
if (double.TryParse(lengthLine, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out result)) {
|
||||||
|
int integerPart = (int)Math.Round(result);
|
||||||
|
return TimeSpan.FromSeconds(integerPart);
|
||||||
|
} else
|
||||||
|
return TimeSpan.Zero;
|
||||||
|
}
|
||||||
|
} catch (Exception) {
|
||||||
|
Console.WriteLine("Exception while determining file play-time");
|
||||||
|
return TimeSpan.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class TranscodingTask {
|
||||||
|
readonly StreamRequest streamRequest;
|
||||||
|
readonly Stream bufferingStream;
|
||||||
|
|
||||||
|
public long BytesSentToTranscoder { get; private set; }
|
||||||
|
public DualStream PCMOutput { get; private set; }
|
||||||
|
public long ReadyBytesLeft => PCMOutput?.writePos - PCMOutput?.readPos ?? 0;
|
||||||
|
|
||||||
|
readonly CancellationTokenSource tokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
|
Task transcoderTask;
|
||||||
|
Task outputTask;
|
||||||
|
|
||||||
|
public TranscodingTask(StreamRequest streamRequest, Stream bufferingStream) {
|
||||||
|
this.streamRequest = streamRequest;
|
||||||
|
this.bufferingStream = bufferingStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start() {
|
||||||
|
Task.Run(async () => {
|
||||||
|
// Wait for some data to arrive
|
||||||
|
while (true) {
|
||||||
|
if (streamRequest.NetworkDone)
|
||||||
|
break;
|
||||||
|
if (bufferingStream.Length > 1024 * 3)
|
||||||
|
break;
|
||||||
|
await Task.Delay(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream input, pcmOutput;
|
||||||
|
var ffmpegProcess = GetTranscoderStreams(out input, out pcmOutput);
|
||||||
|
|
||||||
|
PCMOutput = new DualStream();
|
||||||
|
|
||||||
|
// Keep pumping network stuff into the transcoder
|
||||||
|
transcoderTask = Task.Run(() => TranscoderFunc(bufferingStream, input, tokenSource.Token), tokenSource.Token);
|
||||||
|
|
||||||
|
// Keep pumping transcoder output into the PCMOutput stream
|
||||||
|
outputTask = Task.Run(() => OutputFunc(pcmOutput, PCMOutput, tokenSource.Token), tokenSource.Token);
|
||||||
|
|
||||||
|
// Wait until network stuff is all done
|
||||||
|
while (!streamRequest.NetworkDone)
|
||||||
|
await Task.Delay(200);
|
||||||
|
|
||||||
|
// Then wait until we sent everything to the transcoder
|
||||||
|
while (BytesSentToTranscoder < streamRequest.TotalSourceBytes)
|
||||||
|
await Task.Delay(200);
|
||||||
|
|
||||||
|
// Then wait some more until it did everything and kill it
|
||||||
|
await Task.Delay(5000);
|
||||||
|
|
||||||
|
try {
|
||||||
|
tokenSource.Cancel();
|
||||||
|
bufferingStream.Close();
|
||||||
|
|
||||||
|
Console.WriteLine("Killing transcoder...");
|
||||||
|
ffmpegProcess.Kill();
|
||||||
|
} catch {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task TranscoderFunc(Stream sourceStream, Stream transcoderInput, CancellationToken cancellationToken) {
|
||||||
|
try {
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
while (true) {
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
break;
|
||||||
|
if (BytesSentToTranscoder >= streamRequest.TotalSourceBytes)
|
||||||
|
break;
|
||||||
|
|
||||||
// When there is new stuff available on the network we want to get it instantly
|
// When there is new stuff available on the network we want to get it instantly
|
||||||
int available = totalSourceBytes - BytesSentToTranscoder;
|
long available = streamRequest.TotalSourceBytes - BytesSentToTranscoder;
|
||||||
if (available > 0) {
|
|
||||||
int read = await sourceStream.ReadAsync(buffer, 0, Math.Min(available, buffer.Length), cancellationToken);
|
double availableRingSpace = PCMOutput.Length / (double)PCMOutput.Capacity;
|
||||||
if (read > 0) { targetStream.Write(buffer, 0, read);
|
|
||||||
|
// How much data is in the final output buffer?
|
||||||
|
// We dont want to transcode too much in advance
|
||||||
|
if (available > 0 && availableRingSpace < 1) {
|
||||||
|
int read = await sourceStream.ReadAsync(buffer, 0, (int)Math.Min(available, buffer.LongLength), cancellationToken);
|
||||||
|
if (read > 0) {
|
||||||
|
// Write to transcoder
|
||||||
|
transcoderInput.Write(buffer, 0, read);
|
||||||
BytesSentToTranscoder += read;
|
BytesSentToTranscoder += read;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// We have enough data transcoded already. Stall a bit so we dont do too much!
|
||||||
|
await Task.Delay(100);
|
||||||
}
|
}
|
||||||
else await Task.Delay(1);
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Console.WriteLine(ex.ToString());
|
||||||
}
|
}
|
||||||
Console.WriteLine("TranscoderFunc stopped");
|
Console.WriteLine("TranscoderFunc stopped");
|
||||||
}
|
}
|
||||||
async Task OutputFunc(Stream sourceStream, Stream targetStream, CancellationToken cancellationToken) {
|
|
||||||
byte[] buffer = new byte[0x4000];
|
static async Task OutputFunc(Stream sourceStream, DualStream targetBuffer, CancellationToken cancellationToken) {
|
||||||
|
try {
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
while (!cancellationToken.IsCancellationRequested) {
|
while (!cancellationToken.IsCancellationRequested) {
|
||||||
// When there is new stuff available on the network we want to get it instantly
|
// When there is new stuff available on the network we want to get it instantly
|
||||||
int read = await sourceStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken);
|
int read = await sourceStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken);
|
||||||
if (read > 0) targetStream.Write(buffer, 0, read);
|
if (read > 0) {
|
||||||
|
targetBuffer.Write(buffer, 0, read);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Console.WriteLine(ex.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
Console.WriteLine("OutputFunc stopped");
|
Console.WriteLine("OutputFunc stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Process GetTranscoderStreams(out Stream input, out Stream pcmOutput) {
|
internal static Process GetTranscoderStreams(out Stream input, out Stream pcmOutput) {
|
||||||
Process p = Process.Start(new ProcessStartInfo {
|
Process p = null;
|
||||||
|
Exception ex = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
p = Process.Start(new ProcessStartInfo {
|
||||||
FileName = "ffmpeg",
|
FileName = "ffmpeg",
|
||||||
Arguments = "-i pipe:0 -f s16le -ar 48000 -ac 2 pipe:1",
|
Arguments = "-i pipe:0 -f s16le -ar 48000 -ac 2 pipe:1",
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
|
RedirectStandardError = false,
|
||||||
RedirectStandardOutput = true,
|
RedirectStandardOutput = true,
|
||||||
RedirectStandardInput = true,
|
RedirectStandardInput = true,
|
||||||
CreateNoWindow = true,
|
CreateNoWindow = true,
|
||||||
});
|
});
|
||||||
|
} catch (Exception exInner) {
|
||||||
|
ex = exInner;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p == null || ex != null) {
|
||||||
|
input = null;
|
||||||
|
pcmOutput = null;
|
||||||
|
Console.WriteLine("Could not start ffmpeg: " + (ex?.Message ?? "<no exception>"));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
pcmOutput = p.StandardOutput.BaseStream;
|
pcmOutput = p.StandardOutput.BaseStream;
|
||||||
input = p.StandardInput.BaseStream;
|
input = p.StandardInput.BaseStream;
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
Stream GetBufferingStream(string streamUrl) {
|
|
||||||
var memoryStream = new DualStream();
|
|
||||||
Task.Run(() => {
|
|
||||||
int byteCounter = 0;
|
|
||||||
try {
|
|
||||||
var webClient = new WebClient();
|
|
||||||
var networkStream = webClient.OpenRead(streamUrl);
|
|
||||||
if (networkStream == null) return;
|
|
||||||
byte[] buffer = new byte[0x1000];
|
|
||||||
while (true) {
|
|
||||||
int read = networkStream.Read(buffer, 0, buffer.Length);
|
|
||||||
if (read <= 0) break;
|
|
||||||
byteCounter += read;
|
|
||||||
totalSourceBytes += read;
|
|
||||||
memoryStream.Write(buffer, 0, read);
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Console.WriteLine("Exception while reading network stream: " + ex);
|
|
||||||
}
|
|
||||||
NetworkDone = true; Console.WriteLine("net: done. ({0} read)", byteCounter);
|
|
||||||
});
|
|
||||||
return memoryStream;
|
|
||||||
}
|
|
||||||
async void Write(string message) {
|
|
||||||
Console.WriteLine(message);
|
|
||||||
}
|
|
||||||
public void Cancel() {
|
public void Cancel() {
|
||||||
tokenSource1.Cancel();
|
tokenSource.Cancel();
|
||||||
tokenSource2.Cancel();
|
BytesSentToTranscoder = streamRequest.TotalSourceBytes;
|
||||||
NetworkDone = true;
|
|
||||||
BytesSentToTranscoder = totalSourceBytes;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public class DualStream : MemoryStream {
|
public class DualStream : MemoryStream {
|
||||||
long readPosition;
|
public long readPos;
|
||||||
long writePosition;
|
public long writePos;
|
||||||
public override int Read(byte[] buffer, int offset, int count) {
|
public override int Read(byte[] buffer, int offset, int count) {
|
||||||
int read;
|
int read;
|
||||||
lock (this) {
|
lock (this) {
|
||||||
Position = readPosition;
|
Position = readPos;
|
||||||
read = base.Read(buffer, offset, count);
|
read = base.Read(buffer, offset, count);
|
||||||
readPosition = Position;
|
readPos = Position;
|
||||||
}
|
}
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
public override void Write(byte[] buffer, int offset, int count) {
|
public override void Write(byte[] buffer, int offset, int count) {
|
||||||
lock (this) {
|
lock (this) {
|
||||||
Position = writePosition;
|
Position = writePos;
|
||||||
base.Write(buffer, offset, count);
|
base.Write(buffer, offset, count);
|
||||||
writePosition = Position;
|
writePos = Position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,8 +99,12 @@ namespace NadekoBot
|
|||||||
static bool repliedRecently = false;
|
static bool repliedRecently = false;
|
||||||
private static async void Client_MessageReceived(object sender, MessageEventArgs e) {
|
private static async void Client_MessageReceived(object sender, MessageEventArgs e) {
|
||||||
if (e.Server != null) return;
|
if (e.Server != null) return;
|
||||||
|
try {
|
||||||
|
(await client.GetInvite(e.Message.Text))?.Accept();
|
||||||
|
} catch (Exception) { }
|
||||||
|
|
||||||
if (repliedRecently = !repliedRecently) {
|
if (repliedRecently = !repliedRecently) {
|
||||||
await e.Send("You can type `-h` or `-help` or `@MyName help` in any of the channels I am in and I will send you a message with my commands.\n Or you can find out what i do here: https://github.com/Kwoth/NadekoBot\nIf you don't want me on your server, you can simply ban me ;(");
|
await e.Send("You can type `-h` or `-help` or `@MyName help` in any of the channels I am in and I will send you a message with my commands.\n Or you can find out what i do here: https://github.com/Kwoth/NadekoBot\nYou can also just send me an invite link to a server and I will join it.\nIf you don't want me on your server, you can simply ban me ;(");
|
||||||
Timer t = new Timer();
|
Timer t = new Timer();
|
||||||
t.Interval = 2000;
|
t.Interval = 2000;
|
||||||
t.Start();
|
t.Start();
|
||||||
|
Loading…
Reference in New Issue
Block a user