| 1 |
using System; |
| 2 |
using System.Collections.Generic; |
| 3 |
using System.Linq; |
| 4 |
using System.Text; |
| 5 |
using System.Text.RegularExpressions; |
| 6 |
using System.Net; |
| 7 |
using System.Concurrency; |
| 8 |
using System.Web; |
| 9 |
|
| 10 |
namespace Aqua877.WinApp.IronLivetube |
| 11 |
{ |
| 12 |
public class LivetubeMirrors |
| 13 |
{ |
| 14 |
public string Host; |
| 15 |
public bool IsLivetubesServer; |
| 16 |
public int CurrentUsedBandWidth; |
| 17 |
public int CanUseBandWidth; |
| 18 |
} |
| 19 |
|
| 20 |
public class LivetubeInformation |
| 21 |
{ |
| 22 |
public string LiveTitle { get; set; } |
| 23 |
public string LiveAuthor { get; set; } |
| 24 |
public int Listeners { get; set; } |
| 25 |
public int Visitors { get; set; } |
| 26 |
public string[] Tags { get; set; } |
| 27 |
public double CatchCommentsPerHour { get; set; } |
| 28 |
public bool IsLoading = false; |
| 29 |
|
| 30 |
private IDisposable Timer; |
| 31 |
private Uri LiveUrl; |
| 32 |
|
| 33 |
public event Action OnLiveEnded; |
| 34 |
public event Action OnInformationRefreshed; |
| 35 |
|
| 36 |
public void LoadAsync(Uri url) |
| 37 |
{ |
| 38 |
this.LiveUrl = url; |
| 39 |
this.Timer = Observable.Timer(TimeSpan.FromSeconds(0), TimeSpan.FromMinutes(1)) |
| 40 |
.ObserveOnDispatcher() |
| 41 |
.Subscribe(_ => this.Elapsed()); |
| 42 |
} |
| 43 |
|
| 44 |
public void StopLoad() |
| 45 |
{ |
| 46 |
this.IsLoading = false; |
| 47 |
this.Timer.Dispose(); |
| 48 |
} |
| 49 |
|
| 50 |
private void Elapsed() |
| 51 |
{ |
| 52 |
var liveReader = new WebClient() { Encoding = Encoding.UTF8 }; |
| 53 |
liveReader.DownloadStringCompleted += this.GetLiveDataCompleted; |
| 54 |
this.IsLoading = true; |
| 55 |
liveReader.DownloadStringAsync(this.LiveUrl, liveReader); |
| 56 |
} |
| 57 |
|
| 58 |
private void GetLiveDataCompleted(object sender, DownloadStringCompletedEventArgs e) |
| 59 |
{ |
| 60 |
if (e.Error != null) |
| 61 |
{ |
| 62 |
(e.UserState as WebClient).Dispose(); |
| 63 |
return; |
| 64 |
} |
| 65 |
|
| 66 |
if (e.Result.Contains("フラッシュ動画ファイル(FLV9)")) |
| 67 |
{ |
| 68 |
this.NotifyLiveEnded(); |
| 69 |
return; |
| 70 |
} |
| 71 |
|
| 72 |
//配信タイトルと配信者名取得 |
| 73 |
Regex.Match(HttpUtility.HtmlDecode(e.Result), "<title>(.*?)</title>").Groups[1].Value |
| 74 |
.Let(m => |
| 75 |
{ |
| 76 |
this.LiveAuthor = m.Split(':')[2].Trim(); |
| 77 |
this.LiveTitle = m.Split(':')[1].Trim(); |
| 78 |
return m; |
| 79 |
}); |
| 80 |
|
| 81 |
//タグ取得 |
| 82 |
this.Tags = Regex.Matches(HttpUtility.UrlDecode(e.Result), @"<a href=""/tag\.(.*?)"">") |
| 83 |
.Cast<Match>() |
| 84 |
.Select(m => |
| 85 |
m.Let(xs => xs.Groups[1].Value) |
| 86 |
) |
| 87 |
.ToArray(); |
| 88 |
|
| 89 |
//リスナー数取得 |
| 90 |
this.Listeners = int.Parse(Regex.Match(e.Result, "今見ている人の数 : <span id=\"viewing\">(.*?)</span><br />").Groups[1].Value); |
| 91 |
|
| 92 |
//見た人の数取得 |
| 93 |
this.Visitors = int.Parse(Regex.Match(e.Result, "見た人の数 : <span id=\"view\">(.*?)</span><br />").Groups[1].Value); |
| 94 |
|
| 95 |
//1時間あたりのコメントの個数(流速)を計算 |
| 96 |
double comments = int.Parse(Regex.Match(e.Result, "コメント : <span id=\"comments_num\">(.*?)</span><br />").Groups[1].Value); |
| 97 |
var start = DateTime.ParseExact( |
| 98 |
Regex.Match(e.Result, "<span style=\"display: block; text-align: right; font-size: smaller;\">リリース: (.*?)</span>").Groups[1].Value, "M/d yyyy, H:mm", null |
| 99 |
); |
| 100 |
this.CatchCommentsPerHour = comments / (DateTime.Now - start).TotalHours; |
| 101 |
|
| 102 |
//通知 |
| 103 |
this.NotifyInformationRefreshed(); |
| 104 |
|
| 105 |
(e.UserState as WebClient).Dispose(); |
| 106 |
this.IsLoading = false; |
| 107 |
} |
| 108 |
|
| 109 |
|
| 110 |
protected virtual void NotifyLiveEnded() |
| 111 |
{ |
| 112 |
if (this.OnLiveEnded != null) |
| 113 |
{ |
| 114 |
this.OnLiveEnded(); |
| 115 |
} |
| 116 |
} |
| 117 |
|
| 118 |
protected virtual void NotifyInformationRefreshed() |
| 119 |
{ |
| 120 |
if (this.OnInformationRefreshed != null) |
| 121 |
{ |
| 122 |
this.OnInformationRefreshed(); |
| 123 |
} |
| 124 |
} |
| 125 |
} |
| 126 |
} |