PageRenderTime 66ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 1ms

/clsStatMonger.cs

https://github.com/joshgoodwin/TubeGuardian
C# | 2441 lines | 2093 code | 209 blank | 139 comment | 315 complexity | 63a32ae9f00946f368d52ed21ced7ab7 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using Google.GData.YouTube;
  6. using Google.GData.Extensions;
  7. using Google.YouTube;
  8. using System.Threading;
  9. using System.IO;
  10. using System.Xml;
  11. using System.Runtime.InteropServices;
  12. using System.Timers;
  13. using System.Net;
  14. using System.Net.Sockets;
  15. using System.Web;
  16. using mshtml;
  17. using System.Text.RegularExpressions;
  18. namespace TubeGuardian
  19. {
  20. // Hack-a-licious http 1.1 request using tcp sockets
  21. public class GetSocket
  22. {
  23. private static Socket ConnectSocket(string server, int port)
  24. {
  25. Socket s = null;
  26. IPHostEntry hostEntry = null;
  27. // Get host related information.
  28. hostEntry = Dns.GetHostEntry(server);
  29. // Loop through the AddressList to obtain the supported AddressFamily. This is to avoid
  30. // an exception that occurs when the host IP Address is not compatible with the address family
  31. // (typical in the IPv6 case).
  32. foreach (IPAddress address in hostEntry.AddressList)
  33. {
  34. IPEndPoint ipe = new IPEndPoint(address, port);
  35. Socket tempSocket =
  36. new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
  37. tempSocket.Connect(ipe);
  38. if (tempSocket.Connected)
  39. {
  40. s = tempSocket;
  41. break;
  42. }
  43. else
  44. {
  45. continue;
  46. }
  47. }
  48. return s;
  49. }
  50. // This method requests the home page content for the specified server.
  51. public static string SocketSendReceive(string server, int port, string headers, string data)
  52. {
  53. Byte[] bytesData = Encoding.ASCII.GetBytes(data);
  54. headers = headers + "\nContent-Length: " + bytesData.Length;
  55. string request = headers + "\n\n" + data;
  56. Byte[] bytesSent = Encoding.ASCII.GetBytes(request);
  57. Byte[] bytesReceived = new Byte[256];
  58. // Create a socket connection with the specified server and port.
  59. Socket s = ConnectSocket(server, port);
  60. if (s == null)
  61. return ("Connection failed");
  62. // Send request to the server.
  63. s.Send(bytesSent, bytesSent.Length, 0);
  64. // Receive the server home page content.
  65. int bytes = 0;
  66. string page = "";
  67. bytes = s.Receive(bytesReceived, bytesReceived.Length, 0);
  68. page = page + Encoding.ASCII.GetString(bytesReceived, 0, bytes);
  69. return page;
  70. }
  71. }
  72. /* Bot-ish class that contantly maintains an updated dictionary of clsDataPoint objects */
  73. public class clsStatMonger
  74. {
  75. // private data members
  76. private List<clsCredentials> _accounts = new List<clsCredentials>();
  77. private List<clsVideoFeedReader> _feed_readers = new List<clsVideoFeedReader>();
  78. private List<clsVideoEntry> _initial_dataset = new List<clsVideoEntry>();
  79. private List<clsVideoEntry> _current_dataset = new List<clsVideoEntry>();
  80. private Dictionary<string, List<clsDataPoint>> _historical_data = new Dictionary<string, List<clsDataPoint>>();
  81. private System.Timers.Timer _update_timer;
  82. private clsFileLogger _file_logger = null;
  83. private string _dev_key = string.Empty;
  84. private string _app_name = string.Empty;
  85. private clsSettings _settings;
  86. // constructor
  87. public clsStatMonger(string DeveloperKey, string ApplicationName, List<clsCredentials> Credentials, clsSettings settings)
  88. {
  89. _file_logger = new clsFileLogger("CollectorData.log");
  90. _settings = settings;
  91. _settings.OnAccountAdded += new clsSettings.AccountAddedHandler(_settings_OnAccountAdded);
  92. _settings.OnAccountRemoved += new clsSettings.AccountRemovedHandler(_settings_OnAccountRemoved);
  93. _dev_key = DeveloperKey;
  94. _app_name = ApplicationName;
  95. this.Enabled = false;
  96. _update_timer = new System.Timers.Timer();
  97. _update_timer.Enabled = false;
  98. _update_timer.Interval = _settings.Collect_Interval * 1000 * 60;
  99. _update_timer.Elapsed += new ElapsedEventHandler(_update_timer_Elapsed);
  100. _accounts = Credentials;
  101. foreach (clsCredentials c in _accounts)
  102. {
  103. clsVideoFeedReader new_feed = new clsVideoFeedReader(DeveloperKey, ApplicationName, c.Username);
  104. if (c.Password != string.Empty && c.Password != "-")
  105. new_feed.SetCredentials(c.Username, c.Password);
  106. new_feed.OnEntryFetched += new clsVideoFeedReader.EntryFetchedHandler(new_feed_OnEntryFetched);
  107. new_feed.OnStatusChange += new clsVideoFeedReader.StatusChangeHandler(new_feed_OnStatusChange);
  108. _feed_readers.Add(new_feed);
  109. }
  110. }
  111. public clsStatMonger(string DataFile)
  112. {
  113. LoadDataFile(DataFile);
  114. }
  115. void _settings_OnAccountRemoved(object sender, clsCredentials Account)
  116. {
  117. RemoveAccount(Account);
  118. }
  119. void _settings_OnAccountAdded(object sender, clsCredentials Account)
  120. {
  121. AddAccount(Account);
  122. }
  123. // public methods
  124. public void Enable()
  125. {
  126. if (this.Enabled)
  127. return;
  128. this.Enabled = true;
  129. _update_videos();
  130. _update_timer.Interval = _settings.Collect_Interval;
  131. _update_timer.Enabled = true;
  132. }
  133. public void Disable()
  134. {
  135. this.Enabled = false;
  136. _update_timer.Enabled = false;
  137. }
  138. public void Update()
  139. {
  140. _update_videos();
  141. }
  142. public void AddAccount(clsCredentials Account)
  143. {
  144. foreach (clsVideoFeedReader r in _feed_readers)
  145. if (r.Username == Account.Username)
  146. return;
  147. clsVideoFeedReader new_feed = new clsVideoFeedReader(_dev_key, _app_name, Account.Username);
  148. if (Account.Password != "-")
  149. new_feed.SetCredentials(Account.Username, Account.Password);
  150. new_feed.OnEntryFetched += new clsVideoFeedReader.EntryFetchedHandler(new_feed_OnEntryFetched);
  151. new_feed.OnStatusChange += new clsVideoFeedReader.StatusChangeHandler(new_feed_OnStatusChange);
  152. new_feed.OnException += new clsVideoFeedReader.ExceptionHandler(new_feed_OnException);
  153. _feed_readers.Add(new_feed);
  154. }
  155. public void RemoveAccount(clsCredentials Account)
  156. {
  157. int i = 0;
  158. while (i < _feed_readers.Count)
  159. {
  160. if (_feed_readers[i].Username == Account.Username)
  161. {
  162. _feed_readers[i].Dispose();
  163. _feed_readers.RemoveAt(i);
  164. }
  165. else
  166. i++;
  167. }
  168. }
  169. public void AddDataset(List<clsVideoEntry> InitialDataset, Dictionary<string, List<clsDataPoint>> HistoricalData)
  170. {
  171. List<clsDataPoint> new_datapoints = new List<clsDataPoint>();
  172. int i = 0;
  173. while (i<InitialDataset.Count)
  174. {
  175. clsVideoEntry new_entry = InitialDataset[i];
  176. i++;
  177. if (new_entry == null)
  178. continue;
  179. clsVideoEntry old_entry = _GetEntryByIdFromList(_initial_dataset, new_entry.VideoID);
  180. if (old_entry == null)
  181. {
  182. _initial_dataset.Add(new_entry);
  183. try { _historical_data.Add(new_entry.VideoID, new List<clsDataPoint>()); }
  184. catch { }
  185. continue;
  186. }
  187. List<clsDataPoint> new_dps = _compare_entities(new_entry, old_entry);
  188. if (new_entry.Time < old_entry.Time)
  189. old_entry = new_entry;
  190. foreach (clsDataPoint dp in new_dps)
  191. _historical_data[new_entry.VideoID].Add(new clsDataPoint(dp));
  192. }
  193. foreach (KeyValuePair<string, List<clsDataPoint>> kvp in HistoricalDataPoints)
  194. {
  195. if (!_historical_data.ContainsKey(kvp.Key))
  196. {
  197. _historical_data.Add(kvp.Key, new List<clsDataPoint>(kvp.Value));
  198. continue;
  199. }
  200. _historical_data[kvp.Key].AddRange(new List<clsDataPoint>(kvp.Value));
  201. }
  202. _sort_historical_data();
  203. }
  204. public void LoadDataFile(string LogFileContents)
  205. {
  206. List<clsVideoEntry> init = new List<clsVideoEntry>();
  207. Dictionary<string, List<clsDataPoint>> hist = new Dictionary<string, List<clsDataPoint>>();
  208. string log = LogFileContents;
  209. Regex r = new Regex("init : .+?{(.+?)}.+?{(.+?)}.+?{(.+?)}.+?{(.+?)}.+?{(.+?)}.+?{(.+?)}.+?{(.+?)}.+?{(.+?)}.+?{(.+?)}");
  210. MatchCollection matches = r.Matches(log);
  211. foreach (Match m in matches)
  212. {
  213. Google.GData.YouTube.YouTubeEntry entry = new Google.GData.YouTube.YouTubeEntry();
  214. entry.VideoId = m.Groups[3].Value;
  215. Google.GData.Extensions.FeedLink feedlink = new Google.GData.Extensions.FeedLink();
  216. feedlink.CountHint = int.Parse(m.Groups[8].Value);
  217. entry.Comments = new Google.GData.Extensions.Comments();
  218. entry.Comments.FeedLink = feedlink;
  219. entry.Statistics = new Google.GData.YouTube.Statistics();
  220. entry.Statistics.ViewCount = m.Groups[7].Value;
  221. entry.Statistics.FavoriteCount = m.Groups[9].Value;
  222. entry.Rating = new Google.GData.Extensions.Rating();
  223. entry.Rating.NumRaters = int.Parse(m.Groups[5].Value);
  224. entry.Rating.Average = double.Parse(m.Groups[6].Value);
  225. entry.Title = new Google.GData.Client.AtomTextConstruct(Google.GData.Client.AtomTextConstructElementType.Title, m.Groups[4].Value);
  226. clsVideoEntry new_e = new clsVideoEntry(entry);
  227. new_e.Time = DateTime.Parse(m.Groups[1].Value + " " + m.Groups[2].Value);
  228. clsVideoEntry old_entry = _GetEntryByIdFromList(init, new_e.VideoID);
  229. if ( old_entry == null)
  230. init.Add(new_e);
  231. else
  232. {
  233. List<clsDataPoint> new_dps = _compare_entities(new_e, old_entry);
  234. if (new_e.Time < old_entry.Time)
  235. old_entry = new_e;
  236. foreach (clsDataPoint dp in new_dps)
  237. {
  238. if (!hist.ContainsKey(new_e.VideoID))
  239. hist.Add(new_e.VideoID, new List<clsDataPoint>());
  240. hist[new_e.VideoID].Add(dp);
  241. }
  242. }
  243. }
  244. r = new Regex("upd : d{(.*)},t{(.*)},vId{(.*),(.*)},old{(.*)},new{(.*)}");
  245. matches = r.Matches(log);
  246. foreach (Match m in matches)
  247. {
  248. string v = m.Groups[3].Value;
  249. string f = m.Groups[4].Value;
  250. double Iv = double.Parse(m.Groups[5].Value);
  251. double Nv = double.Parse(m.Groups[6].Value);
  252. if (!hist.ContainsKey(v))
  253. hist.Add(v, new List<clsDataPoint>());
  254. clsDataPointField field = new clsDataPointField();
  255. switch (f)
  256. {
  257. case "VIEWS":
  258. field.Field = clsDataPointField.VideoDataFields.VIEWS;
  259. break;
  260. case "RATERS":
  261. field.Field = clsDataPointField.VideoDataFields.RATERS;
  262. break;
  263. case "AVERAGE_RATING":
  264. field.Field = clsDataPointField.VideoDataFields.AVERAGE_RATING;
  265. break;
  266. case "COMMENT_COUNT":
  267. field.Field = clsDataPointField.VideoDataFields.COMMENT_COUNT;
  268. break;
  269. case "FAVORITED_COUNT":
  270. field.Field = clsDataPointField.VideoDataFields.FAVORITED_COUNT;
  271. break;
  272. }
  273. clsDataPoint new_dp = new clsDataPoint(Iv, Nv, field, v);
  274. new_dp.Time = DateTime.Parse(m.Groups[1].Value + " " + m.Groups[2].Value);
  275. hist[v].Add(new_dp);
  276. }
  277. _initial_dataset = init;
  278. _historical_data = hist;
  279. _sort_historical_data();
  280. }
  281. private void _sort_historical_data()
  282. {
  283. int i = 0;
  284. while (i < _historical_data.Count)
  285. {
  286. List<clsDataPoint> current_dps = _historical_data.ElementAt(i).Value;
  287. i++;
  288. current_dps.Sort(delegate(clsDataPoint d1, clsDataPoint d2)
  289. { return d1.Time.CompareTo(d2.Time); }
  290. );
  291. //current_dps = _remove_duplicates(current_dps);
  292. }
  293. }
  294. private static List<clsDataPoint> _remove_duplicates(List<clsDataPoint> input)
  295. {
  296. List<clsDataPoint> unique = new List<clsDataPoint>();
  297. foreach (clsDataPoint dp in input)
  298. if (unique.FindIndex(delegate(clsDataPoint d) { return ((dp.VideoID.Equals(d.VideoID)) && (dp.Time.Ticks.Equals(d.Time.Ticks)) && (dp.Field.Field.Equals(d.Field.Field)) && (dp.New.Equals(d.New)) && (dp.Old.Equals(d.Old))); }) == -1)
  299. unique.Add(dp);
  300. return unique;
  301. }
  302. private static List<clsDataPoint> _compare_entities(clsVideoEntry e_adding, clsVideoEntry e_init)
  303. {
  304. clsDataPoint new_dp = null;
  305. List<clsDataPoint> ret = new List<clsDataPoint>();
  306. if (e_adding.Time < e_init.Time)
  307. {
  308. if (e_adding.ViewsCount != e_adding.ViewsCount)
  309. {
  310. new_dp = new clsDataPoint((double)e_adding.ViewsCount, (double)e_init.ViewsCount, new clsDataPointField(clsDataPointField.VideoDataFields.VIEWS), e_init.VideoID);
  311. new_dp.Time = e_init.Time;
  312. ret.Add(new_dp);
  313. }
  314. if (e_adding.ViewsCount != e_adding.ViewsCount)
  315. {
  316. new_dp = new clsDataPoint((double)e_adding.Raters, (double)e_init.Raters, new clsDataPointField(clsDataPointField.VideoDataFields.RATERS), e_init.VideoID);
  317. new_dp.Time = e_init.Time;
  318. ret.Add(new_dp);
  319. }
  320. if (e_adding.ViewsCount != e_adding.ViewsCount)
  321. {
  322. new_dp = new clsDataPoint((double)e_adding.FavoritedCount, (double)e_init.FavoritedCount, new clsDataPointField(clsDataPointField.VideoDataFields.FAVORITED_COUNT), e_init.VideoID);
  323. new_dp.Time = e_init.Time;
  324. ret.Add(new_dp);
  325. }
  326. if (e_adding.ViewsCount != e_adding.ViewsCount)
  327. {
  328. new_dp = new clsDataPoint((double)e_adding.CommentCount, (double)e_init.CommentCount, new clsDataPointField(clsDataPointField.VideoDataFields.COMMENT_COUNT), e_init.VideoID);
  329. new_dp.Time = e_init.Time;
  330. ret.Add(new_dp);
  331. }
  332. if (e_adding.ViewsCount != e_adding.ViewsCount)
  333. {
  334. new_dp = new clsDataPoint((double)e_adding.AverageRating, (double)e_init.AverageRating, new clsDataPointField(clsDataPointField.VideoDataFields.AVERAGE_RATING), e_init.VideoID);
  335. new_dp.Time = e_init.Time;
  336. ret.Add(new_dp);
  337. }
  338. e_init = e_adding;
  339. }
  340. else
  341. {
  342. if (e_init.ViewsCount != e_init.ViewsCount)
  343. {
  344. new_dp = new clsDataPoint((double)e_init.ViewsCount, (double)e_adding.ViewsCount, new clsDataPointField(clsDataPointField.VideoDataFields.VIEWS), e_adding.VideoID);
  345. new_dp.Time = e_adding.Time;
  346. ret.Add(new_dp);
  347. }
  348. if (e_init.ViewsCount != e_init.ViewsCount)
  349. {
  350. new_dp = new clsDataPoint((double)e_init.Raters, (double)e_adding.Raters, new clsDataPointField(clsDataPointField.VideoDataFields.RATERS), e_adding.VideoID);
  351. new_dp.Time = e_adding.Time;
  352. ret.Add(new_dp);
  353. }
  354. if (e_init.ViewsCount != e_init.ViewsCount)
  355. {
  356. new_dp = new clsDataPoint((double)e_init.FavoritedCount, (double)e_adding.FavoritedCount, new clsDataPointField(clsDataPointField.VideoDataFields.FAVORITED_COUNT), e_adding.VideoID);
  357. new_dp.Time = e_adding.Time;
  358. ret.Add(new_dp);
  359. }
  360. if (e_init.ViewsCount != e_init.ViewsCount)
  361. {
  362. new_dp = new clsDataPoint((double)e_init.CommentCount, (double)e_adding.CommentCount, new clsDataPointField(clsDataPointField.VideoDataFields.COMMENT_COUNT), e_adding.VideoID);
  363. new_dp.Time = e_adding.Time;
  364. ret.Add(new_dp);
  365. }
  366. if (e_init.ViewsCount != e_init.ViewsCount)
  367. {
  368. new_dp = new clsDataPoint((double)e_init.AverageRating, (double)e_adding.AverageRating, new clsDataPointField(clsDataPointField.VideoDataFields.AVERAGE_RATING), e_adding.VideoID);
  369. new_dp.Time = e_adding.Time;
  370. ret.Add(new_dp);
  371. }
  372. }
  373. return ret;
  374. }
  375. private YouTubeEntry _new_youtube_entry(string title, double views, double raters, double favs, double avgR, double comments)
  376. {
  377. YouTubeEntry e = new YouTubeEntry();
  378. e.Rating = new Rating();
  379. e.Comments.FeedLink = new FeedLink();
  380. e.Statistics = new Statistics();
  381. e.Title = new Google.GData.Client.AtomTextConstruct();
  382. e.Statistics.FavoriteCount = favs.ToString();
  383. e.Statistics.ViewCount = views.ToString();
  384. e.Rating.Average = avgR;
  385. e.Rating.NumRaters = (int)raters;
  386. e.Comments.FeedLink.CountHint = (int)comments;
  387. e.Title.Text = title;
  388. return e;
  389. }
  390. // public events
  391. public delegate void EntryAddedEventHandler(object Sender, clsVideoEntry Entry);
  392. public event EntryAddedEventHandler OnEntryAdded;
  393. private void _entry_added(clsVideoEntry Entry)
  394. {
  395. if (_file_logger != null)
  396. _file_logger.appendFile(Entry.ToString());
  397. if (OnEntryAdded != null)
  398. OnEntryAdded(this, Entry);
  399. }
  400. public delegate void EntryUpdatedEventHandler(object Sender, clsDataPoint DataPoint, clsVideoEntry Entry);
  401. public event EntryUpdatedEventHandler OnEntryUpdated;
  402. private void _entry_updated(clsDataPoint DataPoint, clsVideoEntry Entry)
  403. {
  404. if (_file_logger != null)
  405. _file_logger.appendFile(DataPoint.ToString());
  406. if (OnEntryUpdated != null)
  407. OnEntryUpdated(this, DataPoint, Entry);
  408. }
  409. public delegate void FeedReaderUpdatedEventHandler(object Sender, clsVideoFeedReader.enumFeedReaderState status);
  410. public event FeedReaderUpdatedEventHandler OnFeedReaderUpdated;
  411. private void _feed_reader_updated(object sender, clsVideoFeedReader.enumFeedReaderState status)
  412. {
  413. if (OnFeedReaderUpdated != null)
  414. OnFeedReaderUpdated(sender, status);
  415. }
  416. public delegate void FeedReaderExceptionEventHandler(object Sender, string exception);
  417. public event FeedReaderExceptionEventHandler OnFeedReaderException;
  418. private void _feed_reader_exception(object sender, string exception)
  419. {
  420. if (OnFeedReaderException != null)
  421. OnFeedReaderException(sender, exception);
  422. }
  423. // private methods
  424. private void _update_timer_Elapsed(object sender, ElapsedEventArgs e)
  425. {
  426. if (this.Enabled == false)
  427. return;
  428. _update_timer.Enabled = false;
  429. _update_timer.Interval = _settings.Collect_Interval * 1000 * 60;
  430. _update_timer.Enabled = true;
  431. _update_videos();
  432. }
  433. private void new_feed_OnStatusChange(object Sender, clsVideoFeedReader.enumFeedReaderState NewState)
  434. {
  435. _feed_reader_updated(Sender, NewState);
  436. }
  437. private void new_feed_OnEntryFetched(object Sender, clsVideoEntry Entry)
  438. {
  439. clsVideoEntry initial_entry;
  440. if ((initial_entry = _GetEntryByIdFromList(_initial_dataset, Entry.VideoID)) == null)
  441. {
  442. _initial_dataset.Add(Entry);
  443. _current_dataset.Add(Entry);
  444. _entry_added(Entry);
  445. }
  446. else
  447. {
  448. clsVideoEntry CurrentEntry = _GetEntryByIdFromList(_current_dataset, Entry.VideoID);
  449. if (CurrentEntry == null)
  450. {
  451. _current_dataset.Add(Entry);
  452. _compare_entries(initial_entry, Entry);
  453. }
  454. else
  455. {
  456. _current_dataset.Remove(CurrentEntry);
  457. _current_dataset.Add(Entry);
  458. _compare_entries(CurrentEntry, Entry);
  459. }
  460. }
  461. }
  462. private void new_feed_OnException(object Sender, Exception e)
  463. {
  464. _feed_reader_exception(Sender, e.Message);
  465. }
  466. private void _compare_entries(clsVideoEntry OldEntry, clsVideoEntry NewEntry)
  467. {
  468. if (OldEntry.VideoID != NewEntry.VideoID)
  469. return;
  470. _compare_stat(OldEntry.AverageRating, NewEntry.AverageRating, clsDataPointField.VideoDataFields.AVERAGE_RATING, NewEntry);
  471. _compare_stat(OldEntry.CommentCount, NewEntry.CommentCount, clsDataPointField.VideoDataFields.COMMENT_COUNT, NewEntry);
  472. _compare_stat(OldEntry.FavoritedCount, NewEntry.FavoritedCount, clsDataPointField.VideoDataFields.FAVORITED_COUNT, NewEntry);
  473. _compare_stat(OldEntry.Raters, NewEntry.Raters, clsDataPointField.VideoDataFields.RATERS, NewEntry);
  474. _compare_stat(OldEntry.ViewsCount, NewEntry.ViewsCount, clsDataPointField.VideoDataFields.VIEWS, NewEntry);
  475. }
  476. private void _compare_stat(double Old, double New, clsDataPointField.VideoDataFields Field, clsVideoEntry Entry)
  477. {
  478. if (Old == New)
  479. return;
  480. else
  481. {
  482. clsDataPoint new_datapoint = new clsDataPoint(Old, New, new clsDataPointField(Field), Entry.VideoID);
  483. if (_historical_data.ContainsKey(Entry.VideoID))
  484. _historical_data[Entry.VideoID].Add(new_datapoint);
  485. else
  486. {
  487. List<clsDataPoint> new_dp_list = new List<clsDataPoint>();
  488. new_dp_list.Add(new_datapoint);
  489. _historical_data.Add(Entry.VideoID, new_dp_list);
  490. }
  491. _entry_updated(new_datapoint, Entry);
  492. }
  493. }
  494. private clsVideoEntry _GetEntryByIdFromList(List<clsVideoEntry> Entries, string VideoID)
  495. {
  496. int index = Entries.FindIndex(delegate(clsVideoEntry e) { return e.VideoID.Equals(VideoID); });
  497. return (index == -1) ? null : Entries[index];
  498. }
  499. private void _update_videos()
  500. {
  501. foreach (clsVideoFeedReader r in _feed_readers)
  502. if (!r.IsBusy)
  503. r.GetVideosModifiedSince(r.Updated);
  504. }
  505. // public properties
  506. public bool Enabled { get; private set; }
  507. public List<clsVideoEntry> CurrentDataSet
  508. {
  509. get { return new List<clsVideoEntry>(_current_dataset); }
  510. }
  511. public List<clsVideoEntry> InitialDataSet
  512. {
  513. get { return _initial_dataset; }
  514. }
  515. public Dictionary<string, List<clsDataPoint>> HistoricalDataPoints
  516. {
  517. get { return _historical_data; }
  518. }
  519. public clsFileLogger FileLogger
  520. {
  521. get { return _file_logger; }
  522. set { _file_logger = value; }
  523. }
  524. public List<clsVideoEntry> GetVideosByUsername(string username)
  525. {
  526. List<clsVideoEntry> ret = new List<clsVideoEntry>();
  527. foreach (clsVideoEntry e in _current_dataset)
  528. if (e.Account.Username == username)
  529. ret.Add(e);
  530. return ret;
  531. }
  532. public List<clsVideoFeedReader> FeedReaders { get { return _feed_readers; } }
  533. }
  534. /* Type-ish class to hold youtube usernames, and passwords */
  535. public class clsCredentials
  536. {
  537. public clsCredentials(string Username, string Password) { this.Username = Username; this.Password = Password; }
  538. public clsCredentials() { }
  539. public string Username
  540. {
  541. get;
  542. set;
  543. }
  544. public string Password
  545. {
  546. get;
  547. set;
  548. }
  549. }
  550. /* Type-ish class to hold data points (old/new value + data field) */
  551. public class clsDataPoint
  552. {
  553. public clsDataPoint()
  554. {
  555. this.Old = this.New = 0;
  556. this.Field = new clsDataPointField();
  557. this.Time = DateTime.Now;
  558. }
  559. public clsDataPoint(double OldValue, double NewValue, clsDataPointField DataField, string VideoID)
  560. {
  561. this.Old = OldValue;
  562. this.New = NewValue;
  563. this.Field = DataField;
  564. this.Time = DateTime.Now;
  565. this.VideoID = VideoID;
  566. }
  567. public clsDataPoint(clsDataPoint dp)
  568. {
  569. this.Old = dp.Old;
  570. this.New = dp.New;
  571. this.Field = dp.Field;
  572. this.Time = dp.Time;
  573. this.VideoID = dp.VideoID;
  574. }
  575. public override string ToString()
  576. {
  577. string[] values = {"upd : d{" + Time.ToShortDateString() + "}", "t{" + Time.ToShortTimeString() + "}", "vId{" + VideoID, Field.Field.ToString() + "}", "old{" + Old.ToString() + "}", "new{" + New.ToString() + "}" };
  578. return string.Join(",", values);
  579. }
  580. public clsDataPointField Field { get; set; }
  581. public double Old { get; set; }
  582. public double New { get; set; }
  583. public double Delta { get { return New - Old; } }
  584. public DateTime Time { get; set; }
  585. public string VideoID { get; set; }
  586. }
  587. /* Type-ish class to hold data field values and convert to string equivalents */
  588. public class clsDataPointField
  589. {
  590. public enum VideoDataFields
  591. {
  592. UNKNOWN = -1,
  593. VIEWS,
  594. RATERS,
  595. AVERAGE_RATING,
  596. COMMENT_COUNT,
  597. FAVORITED_COUNT
  598. }
  599. public clsDataPointField() { this.Field = VideoDataFields.UNKNOWN; }
  600. public clsDataPointField(VideoDataFields f) { this.Field = f; }
  601. public VideoDataFields Field { get; set; }
  602. public override string ToString()
  603. {
  604. switch (this.Field)
  605. {
  606. case VideoDataFields.AVERAGE_RATING:
  607. return "Average Rating";
  608. case VideoDataFields.COMMENT_COUNT:
  609. return "Comment Count";
  610. case VideoDataFields.FAVORITED_COUNT:
  611. return "Favorited Count";
  612. case VideoDataFields.RATERS:
  613. return "Raters";
  614. case VideoDataFields.UNKNOWN:
  615. return "Unknown";
  616. case VideoDataFields.VIEWS:
  617. return "Views";
  618. default:
  619. return "Error";
  620. }
  621. }
  622. }
  623. /* Wrapper class to access the information needed from YouTubeEntry objects */
  624. public class clsVideoEntry
  625. {
  626. private YouTubeEntry _yt_entry = null;
  627. // constructor
  628. public clsVideoEntry(YouTubeEntry e)
  629. {
  630. _yt_entry = e;
  631. }
  632. public override string ToString()
  633. {
  634. string[] values = { "init : t{" + Time.ToShortDateString() + "}", "d{" + Time.ToShortTimeString() + "}","vIf{" + VideoID + "}","ti{" + Title + "}", "r#{" + Raters.ToString() +"}", "ar{" + AverageRating.ToString() + "}", "v#{" + ViewsCount.ToString() + "}", "c#{" + CommentCount.ToString() + "}", "f#{" + FavoritedCount.ToString() + "}" };
  635. return string.Join(",", values);
  636. }
  637. public override bool Equals(object obj)
  638. {
  639. clsVideoEntry e = (clsVideoEntry)obj;
  640. return this.VideoID.Equals(e.VideoID);
  641. }
  642. public override int GetHashCode()
  643. {
  644. return this.Account.Username.GetHashCode();
  645. }
  646. // public properties
  647. public bool IsNull
  648. {
  649. get { return (_yt_entry == null); }
  650. }
  651. public int Raters
  652. {
  653. get { try { return _yt_entry.Rating.NumRaters; } catch { return 0; } }
  654. }
  655. public double AverageRating
  656. {
  657. get { try { return _yt_entry.Rating.Average; } catch { return 0; } }
  658. }
  659. public int ViewsCount
  660. {
  661. get { try { return int.Parse(_yt_entry.Statistics.ViewCount); } catch { return 0; } }
  662. }
  663. public int CommentCount
  664. {
  665. get { try { return _yt_entry.Comments.FeedLink.CountHint; } catch { return 0; } }
  666. }
  667. public int FavoritedCount
  668. {
  669. get { try { return int.Parse(_yt_entry.Statistics.FavoriteCount); } catch { return 0; } }
  670. }
  671. public string VideoID
  672. {
  673. get { try { return _yt_entry.VideoId; } catch { return string.Empty; } }
  674. }
  675. public string Title
  676. {
  677. get { try { return _yt_entry.Title.Text; } catch { return string.Empty; } }
  678. }
  679. public YouTubeEntry YouTubeEntry
  680. {
  681. get { return _yt_entry; }
  682. }
  683. public DateTime Time { get; set; }
  684. public clsCredentials Account { get; set; }
  685. }
  686. /* Utility class for reading video feeds asynchronously for a single account */
  687. public class clsVideoFeedReader
  688. {
  689. private class QueryArgs
  690. {
  691. public YouTubeService Service { get; set; }
  692. public YouTubeQuery Query { get; set; }
  693. }
  694. private class QueryReturn
  695. {
  696. public YouTubeFeed Feed { get; set; }
  697. public Exception Exception { get; set; }
  698. public YouTubeQuery NextQuery { get; set; }
  699. }
  700. public enum enumFeedReaderState
  701. {
  702. ERROR = -1,
  703. IDLE,
  704. GETTING_FIRST_CHUNK,
  705. GOT_FIRST_CHUNK,
  706. GETTING_ANOTHER_CHUNK,
  707. GOT_ANOTHER_CHUUNK
  708. }
  709. private enumFeedReaderState _state = enumFeedReaderState.IDLE;
  710. private YouTubeService _yt_service = null;
  711. private string _username = string.Empty;
  712. private string _password = string.Empty;
  713. private string _dev_key = string.Empty;
  714. private string _app_name = string.Empty;
  715. private int _retrieved = 0;
  716. private int _max_retry_query = 3;
  717. private int _current_retry_query = 1;
  718. private System.ComponentModel.BackgroundWorker _query_thread = new System.ComponentModel.BackgroundWorker();
  719. private DateTime _last_updated = DateTime.MinValue;
  720. public clsVideoFeedReader(string DevelopersKey, string ApplicationName, string Username)
  721. {
  722. _dev_key = DevelopersKey;
  723. _app_name = ApplicationName;
  724. _username = Username;
  725. _yt_service = new YouTubeService(ApplicationName, string.Empty, DevelopersKey);
  726. _query_thread.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(_query_thread_RunWorkerCompleted);
  727. _query_thread.DoWork += new System.ComponentModel.DoWorkEventHandler(_query_thread_DoWork);
  728. }
  729. public clsVideoFeedReader(string DevelopersKey, string ApplicationName, string Username, string Password)
  730. : this(DevelopersKey, ApplicationName, Username)
  731. {
  732. if (_password != "-")
  733. {
  734. _password = Password;
  735. SetCredentials(Username, Password);
  736. }
  737. }
  738. // public methods
  739. public void SetCredentials(string Username, string Password)
  740. {
  741. _username = Username;
  742. _password = Password;
  743. _yt_service.setUserCredentials(Username, Password);
  744. }
  745. public void GetVideos()
  746. {
  747. YouTubeQuery query = new YouTubeQuery(YouTubeQuery.CreateUserUri(_username));
  748. query.NumberToRetrieve = 50;
  749. _retrieved = 0;
  750. _current_retry_query = 1;
  751. _status_changed(enumFeedReaderState.GETTING_FIRST_CHUNK);
  752. _last_updated = DateTime.Now;
  753. _do_query(query);
  754. }
  755. public void GetVideosModifiedSince(DateTime When)
  756. {
  757. YouTubeQuery query = new YouTubeQuery(YouTubeQuery.CreateUserUri(_username));
  758. query.NumberToRetrieve = 50;
  759. query.ModifiedSince = When;
  760. _retrieved = 0;
  761. _current_retry_query = 1;
  762. _status_changed(enumFeedReaderState.GETTING_FIRST_CHUNK);
  763. _last_updated = DateTime.Now;
  764. _do_query(query);
  765. }
  766. public void Dispose()
  767. {
  768. _query_thread.Dispose();
  769. OnEntryFetched = null;
  770. OnException = null;
  771. OnProgress = null;
  772. OnQueryRetry = null;
  773. OnStatusChange = null;
  774. }
  775. // private methods
  776. private void _do_query(YouTubeQuery q)
  777. {
  778. if (_query_thread.IsBusy)
  779. return;
  780. QueryArgs qa = new QueryArgs();
  781. qa.Query = q;
  782. qa.Service = _yt_service;
  783. _query_thread.RunWorkerAsync(qa);
  784. }
  785. private void _query_thread_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
  786. {
  787. QueryArgs qa = e.Argument as QueryArgs;
  788. QueryReturn qr = new QueryReturn();
  789. qr.Exception = null;
  790. qr.Feed = null;
  791. try
  792. {
  793. YouTubeFeed feed = qa.Service.Query(qa.Query);
  794. qr.Feed = feed;
  795. }
  796. catch (Exception exception)
  797. {
  798. qr.Exception = exception;
  799. qr.NextQuery = qa.Query;
  800. }
  801. e.Result = qr;
  802. }
  803. private void _query_thread_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
  804. {
  805. QueryReturn qr = e.Result as QueryReturn;
  806. YouTubeFeed feed = qr.Feed;
  807. if (feed != null)
  808. {
  809. _retrieved += feed.Entries.Count;
  810. foreach (YouTubeEntry entry in feed.Entries)
  811. _entry_fetched(entry);
  812. if (_state == enumFeedReaderState.GETTING_FIRST_CHUNK || _state == enumFeedReaderState.GETTING_ANOTHER_CHUNK)
  813. _status_changed(_state + 1);
  814. if (feed.NextChunk != null)
  815. {
  816. _progress(_retrieved, feed.TotalResults);
  817. _status_changed(enumFeedReaderState.GETTING_ANOTHER_CHUNK);
  818. _do_query(new YouTubeQuery(feed.NextChunk));
  819. }
  820. else
  821. {
  822. _progress(1, 1);
  823. _status_changed(enumFeedReaderState.IDLE);
  824. }
  825. }
  826. else
  827. {
  828. _exception(qr.Exception);
  829. if (_current_retry_query < _max_retry_query)
  830. {
  831. _current_retry_query++;
  832. if (qr.NextQuery != null)
  833. {
  834. _status_changed(_state);
  835. _do_query(new YouTubeQuery(qr.NextQuery.Uri.AbsoluteUri));
  836. }
  837. else
  838. {
  839. _progress(1, 1);
  840. _status_changed(enumFeedReaderState.ERROR);
  841. }
  842. }
  843. else
  844. {
  845. _progress(1, 1);
  846. _status_changed(enumFeedReaderState.ERROR);
  847. }
  848. }
  849. }
  850. // public events
  851. public delegate void StatusChangeHandler(object Sender, enumFeedReaderState NewState);
  852. public event StatusChangeHandler OnStatusChange;
  853. private void _status_changed(enumFeedReaderState state)
  854. {
  855. _state = state;
  856. if (OnStatusChange != null)
  857. OnStatusChange(this, state);
  858. }
  859. public delegate void EntryFetchedHandler(object Sender, clsVideoEntry Entry);
  860. public event EntryFetchedHandler OnEntryFetched;
  861. private void _entry_fetched(YouTubeEntry Entry)
  862. {
  863. if (OnEntryFetched != null)
  864. {
  865. clsVideoEntry nEntry = new clsVideoEntry(Entry);
  866. nEntry.Time = DateTime.Now;
  867. nEntry.Account = new clsCredentials(_username, _password);
  868. OnEntryFetched(this, nEntry);
  869. }
  870. }
  871. public delegate void ProgressHandler(object Sender, int Current, int Total);
  872. public event ProgressHandler OnProgress;
  873. private void _progress(int Current, int Total)
  874. {
  875. if (OnProgress != null)
  876. OnProgress(this, Current, Total);
  877. }
  878. public delegate void QueryRetryHandler(object Sender, Exception e);
  879. public event QueryRetryHandler OnQueryRetry;
  880. private void _query_retry(Exception e)
  881. {
  882. if (OnQueryRetry != null)
  883. OnQueryRetry(this, e);
  884. }
  885. public delegate void ExceptionHandler(object Sender, Exception e);
  886. public event ExceptionHandler OnException;
  887. private void _exception(Exception e)
  888. {
  889. if (OnException != null)
  890. OnException(this, e);
  891. }
  892. // public properties
  893. public string StateString
  894. {
  895. get
  896. {
  897. switch (_state)
  898. {
  899. case enumFeedReaderState.ERROR:
  900. return "Error";
  901. case enumFeedReaderState.GETTING_ANOTHER_CHUNK:
  902. return "Getting another chunk";
  903. case enumFeedReaderState.GETTING_FIRST_CHUNK:
  904. return "Getting first chunk";
  905. case enumFeedReaderState.GOT_ANOTHER_CHUUNK:
  906. return "Got another chunk";
  907. case enumFeedReaderState.GOT_FIRST_CHUNK:
  908. return "Got first chunk";
  909. case enumFeedReaderState.IDLE:
  910. return "Idle";
  911. default:
  912. return "Error";
  913. }
  914. }
  915. }
  916. public enumFeedReaderState State
  917. {
  918. get { return _state; }
  919. }
  920. public int MaxRetries
  921. {
  922. get { return _max_retry_query; }
  923. set { _max_retry_query = value; }
  924. }
  925. public bool IsBusy
  926. {
  927. get { return _query_thread.IsBusy; }
  928. }
  929. public DateTime Updated
  930. {
  931. get { return _last_updated; }
  932. }
  933. public string Username
  934. {
  935. get { return _username; }
  936. }
  937. }
  938. /* Math class for crunching all of the numbers in any given data set */
  939. public class clsStatMasher
  940. {
  941. private List<clsVideoEntry> _initial_dataset = null;
  942. private Dictionary<string, List<clsDataPoint>> _historical_data = null;
  943. public clsStatMasher() {}
  944. public clsStatMasher(List<clsVideoEntry> InitialDataSet, Dictionary<string, List<clsDataPoint>> HistoricalData)
  945. {
  946. _initial_dataset = InitialDataSet;
  947. _historical_data = HistoricalData;
  948. }
  949. // public methods
  950. public List<clsDataPoint> GetDataPointsByID(string VideoID)
  951. {
  952. if (_historical_data.ContainsKey(VideoID))
  953. return _historical_data[VideoID];
  954. else
  955. return null;
  956. }
  957. public clsVideoEntry GetInitialDataByID(string VideoID)
  958. {
  959. if (_initial_dataset == null)
  960. return null;
  961. int index = _initial_dataset.FindIndex(delegate(clsVideoEntry e) { return e.VideoID.Equals(VideoID); });
  962. if (index >= 0)
  963. return _initial_dataset[index];
  964. else
  965. return null;
  966. }
  967. public double AverageNewRatingByID(string VideoID)
  968. {
  969. clsVideoEntry InitialData = GetInitialDataByID(VideoID);
  970. List<clsDataPoint> HistoricalData = GetDataPointsByID(VideoID);
  971. if (InitialData == null || HistoricalData == null || HistoricalData.Count == 0)
  972. return 0;
  973. double[] A = { InitialData.AverageRating, -1 };
  974. double[] N = { InitialData.Raters, -1 };
  975. int counted = 0;
  976. for (int i = HistoricalData.Count - 1; i >= 0; i--)
  977. {
  978. if (A[1] != -1 && N[1] != -1)
  979. break;
  980. if (HistoricalData[i].Field.Field == clsDataPointField.VideoDataFields.RATERS)
  981. {
  982. if (N[1] == -1)
  983. N[1] = HistoricalData[i].New;
  984. counted += (int)HistoricalData[i].Delta;
  985. }
  986. if (HistoricalData[i].Field.Field == clsDataPointField.VideoDataFields.AVERAGE_RATING && A[1] == -1)
  987. A[1] = HistoricalData[i].New;
  988. }
  989. if (counted == 0)
  990. return 0;
  991. if (A[1] == -1)
  992. return 0;
  993. return _calc_average_new_rating(A[0], A[1], N[0], N[1]);
  994. }
  995. public KeyValuePair<int,double> AverageNewRatingByID(string VideoID, int numMostRecentVotes)
  996. {
  997. clsVideoEntry InitialData = GetInitialDataByID(VideoID);
  998. List<clsDataPoint> HistoricalData = GetDataPointsByID(VideoID);
  999. if (InitialData == null || HistoricalData == null || HistoricalData.Count == 0)
  1000. return new KeyValuePair<int,double>(0,0);
  1001. double[] A = { 0, -1 };
  1002. double[] N = { 0, -1 };
  1003. int counted = 0;
  1004. for (int i = HistoricalData.Count - 1; i >= 0; i--)
  1005. {
  1006. if ((counted >= numMostRecentVotes) && A[1] != -1 && N[1] != 1)
  1007. break;
  1008. if (HistoricalData[i].Field.Field == clsDataPointField.VideoDataFields.RATERS)
  1009. {
  1010. if( N[1] == -1)
  1011. N[1] = HistoricalData[i].New;
  1012. N[0] = HistoricalData[i].Old;
  1013. counted += (int)HistoricalData[i].Delta;
  1014. }
  1015. if (HistoricalData[i].Field.Field == clsDataPointField.VideoDataFields.AVERAGE_RATING)
  1016. {
  1017. if (A[1] == -1)
  1018. A[1] = HistoricalData[i].New;
  1019. A[0] = HistoricalData[i].Old;
  1020. }
  1021. }
  1022. if (counted == 0)
  1023. return new KeyValuePair<int, double>(0, 0);
  1024. if (A[1] == -1)
  1025. return new KeyValuePair<int, double>(0, 0);
  1026. return new KeyValuePair<int,double>(counted, _calc_average_new_rating(A[0], A[1], N[0], N[1]));
  1027. }
  1028. public void CleanUp()
  1029. {
  1030. _initial_dataset = null;
  1031. _historical_data = null;
  1032. }
  1033. public double GetMovedStatByField(string VideoID, clsDataPointField.VideoDataFields field)
  1034. {
  1035. clsVideoEntry InitialData = GetInitialDataByID(VideoID);
  1036. List<clsDataPoint> HistoricalData = GetDataPointsByID(VideoID);
  1037. if (InitialData == null || HistoricalData == null || HistoricalData.Count == 0)
  1038. return 0;
  1039. double ret = 0;
  1040. int num = 0;
  1041. foreach (clsDataPoint d in HistoricalData)
  1042. {
  1043. if (d.Field.Field == field)
  1044. {
  1045. if (d.Field.Field == clsDataPointField.VideoDataFields.AVERAGE_RATING)
  1046. num++;
  1047. else
  1048. ret += d.Delta;
  1049. }
  1050. }
  1051. if (field == clsDataPointField.VideoDataFields.AVERAGE_RATING)
  1052. return num;
  1053. else
  1054. return ret;
  1055. }
  1056. // private methods
  1057. private double _calc_average_new_rating(double old_average, double new_average, double old_raters, double new_raters)
  1058. {
  1059. System.Diagnostics.Debug.Print((((new_average * new_raters) - (old_average * old_raters)) / (new_raters - old_raters)).ToString());
  1060. return ((new_average * new_raters) - (old_average * old_raters)) / (new_raters - old_raters);
  1061. }
  1062. // public properties
  1063. public List<clsVideoEntry> InitialDataSet
  1064. {
  1065. get { return _initial_dataset; }
  1066. set { _initial_dataset = value; }
  1067. }
  1068. public Dictionary<string, List<clsDataPoint>> HistoricalDataPoints
  1069. {
  1070. get { return _historical_data; }
  1071. set { _historical_data = value; }
  1072. }
  1073. }
  1074. /* Utility class to check video settings */
  1075. public class clsSettingsManager
  1076. {
  1077. [DllImport("urlmon.dll")]
  1078. [PreserveSig]
  1079. [return: MarshalAs(UnmanagedType.Error)]
  1080. static extern int CoInternetSetFeatureEnabled(
  1081. int FeatureEntry,
  1082. [MarshalAs(UnmanagedType.U4)] int dwFlags,
  1083. bool fEnable);
  1084. private const int FEATURE_DISABLE_NAVIGATION_SOUNDS = 21;
  1085. private const int SET_FEATURE_ON_PROCESS = 0x00000002;
  1086. // private enums
  1087. public enum InternalState
  1088. {
  1089. unknown = -1,
  1090. idle,
  1091. login_step_1,
  1092. login_step_2,
  1093. login_step_3,
  1094. login_check,
  1095. get_settings,
  1096. save_settings
  1097. }
  1098. public enum FailureCodes
  1099. {
  1100. FAILURE_LOGGING_IN,
  1101. FAILURE_CHANGING_SETTINGS
  1102. }
  1103. private class Action
  1104. {
  1105. public string VideoID { get; set; }
  1106. public bool Enable { get; set; }
  1107. public Action(string ID, bool act)
  1108. {
  1109. this.VideoID = ID;
  1110. this.Enable = act;
  1111. }
  1112. }
  1113. // private member variable
  1114. private InternalState _state = InternalState.idle;
  1115. private HttpWebRequest _last_request = null;
  1116. private CookieContainer _cookie_jar = null;
  1117. private Cookie _login_info = null;
  1118. private int _request_timeout = 20 * 1000; // 20 seconds default
  1119. private System.ComponentModel.BackgroundWorker _response_fetcher = new System.ComponentModel.BackgroundWorker();
  1120. private System.ComponentModel.BackgroundWorker _post_response_fetcher = new System.ComponentModel.BackgroundWorker();
  1121. private bool _enable = false;
  1122. private string _video_id = string.Empty;
  1123. private System.Timers.Timer _activity_timer = new System.Timers.Timer();
  1124. private List<Action> _action_list = new List<Action>();
  1125. private Action _current_action;
  1126. private int _current_action_fail_count = 0;
  1127. public void Dispose()
  1128. {
  1129. _cookie_jar = null;
  1130. OnException = null;
  1131. OnFailure = null;
  1132. OnStatusChange = null;
  1133. OnSuccess = null;
  1134. _activity_timer.Enabled = false;
  1135. _response_fetcher.Dispose();
  1136. _post_response_fetcher.Dispose();
  1137. _activity_timer.Dispose();
  1138. _action_list.Clear();
  1139. }
  1140. // public events
  1141. public delegate void ExceptionEventHandler(object sender, Exception e);
  1142. public event ExceptionEventHandler OnException;
  1143. private void _raise_exception(Exception e)
  1144. {
  1145. if (OnException != null)
  1146. OnException(this, e);
  1147. }
  1148. public delegate void StatusEventHandler(object sender, InternalState s);
  1149. public event StatusEventHandler OnStatusChange;
  1150. private void _raise_status_change(InternalState s)
  1151. {
  1152. _state = s;
  1153. if (OnStatusChange != null)
  1154. OnStatusChange(this, s);
  1155. if (s == InternalState.idle)
  1156. if (_action_list.Count > 0)
  1157. _do_next_action();
  1158. else
  1159. _activity_timer.Enabled = true;
  1160. }
  1161. public delegate void FailureEventHandler(object sender, FailureCodes f);
  1162. public event FailureEventHandler OnFailure;
  1163. private void _raise_failure(FailureCodes f)
  1164. {
  1165. _current_action_fail_count++;
  1166. if (_current_action_fail_count <

Large files files are truncated, but you can click here to view the full file