Задача такая: каждые 5 секунд проверять твич на наличие стрима. Если стрим есть - запустить дампер.
Загрузка списка каналов:
private void LoadList(string filePath)
{
JArray jArray = JArray.Parse(File.ReadAllText(filePath));
foreach (JObject j in jArray)
{
string channelName = j.Value<string>("channelName");
StreamItem streamItem = new StreamItem(channelName);
streamItem.CopiesCount = j.Value<int>("copiesCount");
if (streamItem.CopiesCount > 3)
{
streamItem.CopiesCount = 3;
}
else if (streamItem.CopiesCount < 0)
{
streamItem.CopiesCount = 0;
}
streamItem.IsImportant = j.Value<bool>("important");
streamItem.TimerRemaining = config.CheckingIntervalInactive;
AddStreamToListView(streamItem);
}
}
private void AddStreamToListView(StreamItem streamItem)
{
ListViewItem item = new ListViewItem(streamItem.ChannelName);
item.SubItems.Add(timerCheck.Enabled ? streamItem.TimerRemaining.ToString() : "Отключен!");
item.SubItems.Add(streamItem.DumpingFilePath);
string sizeString = streamItem.DumpingFileSize >= 0L ? streamItem.DumpingFileSize.ToString() : null;
item.SubItems.Add(sizeString);
item.SubItems.Add(streamItem.CopiesCount.ToString());
item.SubItems.Add(string.Empty);
item.SubItems.Add(string.Empty);
item.Tag = streamItem;
listViewStreams.Items.Add(item);
}
private void ResetItem(StreamItem streamItem)
{
streamItem.DumpingFilePath = null;
streamItem.DumpingFileSize = -1L;
streamItem.DateServer = DateTime.MinValue;
streamItem.DateLocal = DateTime.MinValue;
streamItem.TimerRemaining = config.CheckingIntervalInactive;
int id = FindStreamItemInListView(streamItem, listViewStreams);
if (id >= 0)
{
listViewStreams.Items[id].SubItems[COLUMN_ID_FILEPATH].Text = null;
listViewStreams.Items[id].SubItems[COLUMN_ID_FILESIZE].Text = null;
listViewStreams.Items[id].SubItems[COLUMN_ID_DATE].Text = null;
listViewStreams.Items[id].SubItems[COLUMN_ID_PLAYLIST_URL] = null;
}
}
private void CheckItem(int itemIndex)
{
StreamItem streamItem = (StreamItem)listViewStreams.Items[itemIndex].Tag;
if (!streamItem.IsChecking)
{
streamItem.IsChecking = true;
listViewStreams.Items[itemIndex].SubItems[COLUMN_ID_TIMER].Text = "Запуск проверки...";
MakeThread(streamItem).Start(SynchronizationContext.Current);
}
}
private void CheckAllItems()
{
for (int i = 0; i < listViewStreams.Items.Count; i++)
{
CheckItem(i);
}
}
private void timerCheck_Tick(object sender, EventArgs e)
{
for (int i = 0; i < listViewStreams.Items.Count; i++)
{
StreamItem streamItem = (StreamItem)listViewStreams.Items[i].Tag;
if (!streamItem.IsChecking)
{
streamItem.TimerRemaining--;
listViewStreams.Items[i].SubItems[COLUMN_ID_TIMER].Text = streamItem.TimerRemaining.ToString();
if (streamItem.TimerRemaining <= 0)
{
streamItem.IsChecking = true;
listViewStreams.Items[i].SubItems[COLUMN_ID_TIMER].Text = "Запуск проверки...";
streamItem.TimerRemaining = streamItem.IsStreamActive ? config.CheckingIntervalActive : config.CheckingIntervalInactive;
MakeThread(streamItem).Start(SynchronizationContext.Current);
}
}
}
}
Запуск потока и обработка событий:
private Thread MakeThread(StreamItem streamItem)
{
ThreadChecker threadChecker = new ThreadChecker(streamItem);
threadChecker.CheckingStarted += OnCheckingStarted;
threadChecker.NewLiveDetected += OnLiveDetected;
threadChecker.DumpingStarted += OnDumpingStarted;
threadChecker.FileSizeChanged += OnFileSizeChanged;
threadChecker.DumpingHalted += OnDumpingHalted;
threadChecker.TitleChanged += OnTitleChanged;
threadChecker.Completed += OnThreadCheckCompleted;
threadChecker.LogAdding += OnLogAdding;
return new Thread(threadChecker.Work);
}
private void OnCheckingStarted(object sender)
{
StreamItem streamItem = (sender as ThreadChecker).StreamItem;
int id = FindStreamItemInListView(streamItem, listViewStreams);
if (id >= 0)
{
listViewStreams.Items[id].SubItems[COLUMN_ID_TIMER].Text = "Проверка...";
}
}
private void OnLiveDetected(object sender)
{
StreamItem streamItem = (sender as ThreadChecker).StreamItem;
notifyIcon1.BalloonTipTitle = "СТРИИИИИИМ!!!!!!!";
notifyIcon1.BalloonTipText = $"Канал {streamItem.ChannelName} начал трансляцию!";
notifyIcon1.BalloonTipIcon = ToolTipIcon.Info;
notifyIcon1.ShowBalloonTip(5000);
AddToLog(streamItem.ChannelName, "Новая трансляция!");
}
private void OnDumpingStarted(object sender, int errorCode)
{
StreamItem streamItem = (sender as ThreadChecker).StreamItem;
int id = FindStreamItemInListView(streamItem, listViewStreams);
if (id >= 0)
{
if (errorCode == 200)
{
listViewStreams.Items[id].SubItems[COLUMN_ID_FILEPATH].Text = streamItem.DumpingFilePath;
listViewStreams.Items[id].SubItems[COLUMN_ID_FILESIZE].Text = "0";
listViewStreams.Items[id].SubItems[COLUMN_ID_DATE].Text = streamItem.DateLocal.ToString();
listViewStreams.Items[id].SubItems[COLUMN_ID_PLAYLIST_URL].Text = streamItem.PlaylistUrl;
}
else
{
notifyIcon1.BalloonTipTitle = streamItem.ChannelName;
notifyIcon1.BalloonTipText = "Ошибка доступа к плейлисту!";
notifyIcon1.BalloonTipIcon = ToolTipIcon.Error;
notifyIcon1.ShowBalloonTip(5000);
AddToLog(streamItem.ChannelName, $"Ошибка доступа к плейлисту! Код ошибки: {errorCode}");
ResetItem(streamItem);
}
}
}
private void OnFileSizeChanged(object sender, long oldFileSize)
{
StreamItem streamItem = (sender as ThreadChecker).StreamItem;
int id = FindStreamItemInListView(streamItem, listViewStreams);
if (id >= 0)
{
listViewStreams.Items[id].SubItems[COLUMN_ID_FILESIZE].Text = FormatSize(streamItem.DumpingFileSize);
}
}
private void OnDumpingHalted(object sender)
{
StreamItem streamItem = (sender as ThreadChecker).StreamItem;
int id = FindStreamItemInListView(streamItem, listViewStreams);
if (id >= 0)
{
notifyIcon1.BalloonTipTitle = streamItem.ChannelName;
notifyIcon1.BalloonTipText = "ЗАВИСЛО!!!";
notifyIcon1.BalloonTipIcon = ToolTipIcon.Error;
notifyIcon1.ShowBalloonTip(5000);
AddToLog(streamItem.ChannelName, "Зависло нафиг!");
ResetItem(streamItem);
MakeThread(streamItem).Start(SynchronizationContext.Current);
}
}
private void OnTitleChanged(object sender)
{
StreamItem streamItem = (sender as ThreadChecker).StreamItem;
notifyIcon1.BalloonTipTitle = streamItem.ChannelName;
notifyIcon1.BalloonTipText = $"Изменилось название: {streamItem.Title}";
notifyIcon1.BalloonTipIcon = ToolTipIcon.Info;
notifyIcon1.ShowBalloonTip(5000);
AddToLog(streamItem.ChannelName, $"Новое название стрима: {streamItem.Title}");
}
private void OnThreadCheckCompleted(object sender, int errorCode)
{
StreamItem streamItem = (sender as ThreadChecker).StreamItem;
int id = FindStreamItemInListView(streamItem, listViewStreams);
if (id >= 0) //это срабатывает
{
if (errorCode == 200)
{
listViewStreams.Items[id].SubItems[COLUMN_ID_FILEPATH].Text = streamItem.DumpingFilePath;
listViewStreams.Items[id].SubItems[COLUMN_ID_PLAYLIST_URL].Text = streamItem.PlaylistUrl;
streamItem.TimerRemaining = config.CheckingIntervalActive;
}
else
{
listViewStreams.Items[id].SubItems[COLUMN_ID_FILEPATH].Text =
errorCode != TwitchApi.ERROR_USER_OFFLINE ?
$"Ошибка! {TwitchApi.ErrorCodeToString(errorCode)}, {(sender as ThreadChecker).LastErrorMessage}" : null;
listViewStreams.Items[id].SubItems[COLUMN_ID_FILESIZE].Text = null;
listViewStreams.Items[id].SubItems[COLUMN_ID_DATE].Text = null;
listViewStreams.Items[id].SubItems[COLUMN_ID_PLAYLIST_URL].Text = null;
if (errorCode == TwitchApi.ERROR_USER_NOT_FOUND)
{
streamItem.TimerRemaining = (int)numericUpDownTimerIntervalActive.Maximum;
}
}
listViewStreams.Items[id].SubItems[COLUMN_ID_TIMER].Text =
timerCheck.Enabled ? streamItem.TimerRemaining.ToString() : "Отключен!"; //а это уже нет
}
streamItem.IsChecking = false; //и это, видимо, тоже нет.
}
private void OnLogAdding(object sender, string logText)
{
AddToLog((sender as ThreadChecker).StreamItem.ChannelName, logText);
}
Проблема в том, что всё нормально работает. Но бывает, что у меня на пару секунд отваливается интернет. Соединение как-бы держится, но ничего не передаётся. В результате, в одном из потоков метод OnThreadCheckCompleted
не выполняется до конца и во втором столбе ListView
постоянно висит надпись “Проверка”. Если попытаться обратиться к этому объекту в Tag
, то
private void miEditChannelToolStripMenuItem_Click(object sender, EventArgs e)
{
int id = listViewStreams.SelectedIndex(); //тут NullPointerException
if (id >= 0)
{
StreamItem streamItem = (StreamItem)listViewStreams.Items[id].Tag;
FormAdding formAdding = new FormAdding(streamItem);
if (formAdding.ShowDialog() == DialogResult.OK)
{
listViewStreams.Items[id].Tag = formAdding.StreamItem;
listViewStreams.Items[id].SubItems[COLUMN_ID_COPIES_COUNT].Text =
formAdding.StreamItem.CopiesCount.ToString();
}
}
}
public static int SelectedIndex(this ListView listView) //в отдельном классе
{
return listView.Items.Count > 0 && listView.SelectedIndices.Count > 0 ? listView.SelectedIndices[0] : -1;
}
код выполняется и уже после этого возникает NullPointerException
. Но как такое может быть?