diff --git a/README.txt b/README.txt index fb1aa34..4b8a3d3 100644 --- a/README.txt +++ b/README.txt @@ -1,7 +1,7 @@ =============================================================================== 【タイトル】 comeconv 【ファイル】 comeconv.exe -【作成月日】 2023/04/17 +【作成月日】 2023/04/30 【著 作 者】 nnn-revo2012 【開発環境】 Microsoft Windows 10 Microsoft Visual Studio Community 2019 @@ -18,7 +18,7 @@ ・GUI(Windows Forms)使用 ・ニコ生新配信録画ツール(仮などでダウンロードした動画やコメントを古いさきゅばす(1.67.7.11)や古いニコ動のツールで 結合・再生するための各種変換をおこないます -・TwitchDownloaderおよびchat-downloaderでダウンロードしたTwitchの・YouTubeのコメントをニコ動形式および +・TwitchDownloader、Chat Downloader、yt-dlpでダウンロードしたTwitchの・YouTubeのコメントをニコ動形式および  さきゅばす結合用に変換します ※さきゅばすはこのツールを使わないで直接ニコ生の動画とコメントを結合できるようになりました。 @@ -122,14 +122,18 @@ https://blog.nicovideo.jp/niconews/127212.html Simple Text形式(UTCおよびRelative)、JSON形式 JSON形式の代替文字列は削除されます。Text形式の代替絵文字はそのまま表示されます JSON形式の場合コメント数が多いとメモリーを500~1GBくらい消費します -2.chat-downloader +2.Chat Downloader *.json/*.jsonl形式 Twitch 代替絵文字は削除されます YouTube 絵文字は絵文字として表示されます。代替絵文字は削除されます +3.yt-dlp + youtubeのみ対応 -※chat-downloaderでYouTubeのスーパーチャットを動画開始00:00:00から取得する +※Chat DownloaderでYouTubeのスーパーチャットを動画開始00:00:00から取得する (開始時間を指定しないと開始時間以前のチャットもすべて取得されます) chat_downloader --message_groups "messages superchat" -s 00:00:00 -o chatlog.json [URL] +※yt-dlpでのコメント取得(--write-subs以外のオプションは各自適宜に追加してください) +yt-dlp --write-subs [youtubeのURL] ■動作環境 .Net Framework 4.8が必要です。Windows 10では標準でインストールされているので新たにインストールする必要はありません。 @@ -147,9 +151,11 @@ https://github.com/Saccubus/Saccubus1.x/releases - NCV (コメントファイルのみ) ・TwitchDownloader https://github.com/lay295/TwitchDownloader/releases -・chat-downloader +・Chat Downloader https://github.com/xenova/chat-downloader パソコンにPythonをインストールした後、pipというコマンドでインストール +・yt-dlp +https://github.com/yt-dlp/yt-dlp/releases ■免責事項 本ソフトウェアを利用して発生した如何なる損害について著作者は一切の責任を負いません。 @@ -243,4 +249,8 @@ GNU General Public License v3.0 - Chat Downloader 0.2.4のTwitchのjson/jsonlファイルを変換中エラーになるのを修正 .NET Framework 4.8 にアップデート Json.NET 13.0.3 にアップデート +2023/04/30 Version 0.0.1.22 + Twitch変換機能追加 + - yt-dlp(Youtube)の --write-subs に対応 + Json.NET 13.0.3 のDLLが更新されていなかったので追加 diff --git a/comeconv/ConvTwitch.cs b/comeconv/ConvTwitch.cs index 13dc4c6..ddd5650 100644 --- a/comeconv/ConvTwitch.cs +++ b/comeconv/ConvTwitch.cs @@ -65,18 +65,33 @@ public bool TwitchConvert(string sfile, string dfile) switch (filetype) { - case 0: + case 0: //Chat Downloader + _form.AddLog("Chat Downloader(jsonl)", 1); result = TwitchConvertChatDownloader(sfile, dfile); break; - case 1: + case 1: //Chat Downloader + _form.AddLog("Chat Downloader(json)", 1); result = TwitchConvertChatDownloader(sfile, dfile); break; - case 5: + case 5: //TwitchDownloader (json) + _form.AddLog("TwitchDownloader(json)", 1); result = TwitchConvertTwitchDownloaderJson(sfile, dfile); break; - case 10: + case 6: //TwitchDownloader (text) + _form.AddLog("TwitchDownloader(text)", 1); result = TwitchConvertTwitchDownloaderText(sfile, dfile); break; + case 10: //yt-dlp (Youtube) + _form.AddLog("yt-dlp(Youtube)", 1); + result = TwitchConvertYtDlpYoutube(sfile, dfile); + break; + case 11: //yt-dlp (Youtube) + _form.AddLog("yt-dlp(Twitch)", 1); + //result = TwitchConvertYtDlpTwitch(sfile, dfile); + break; + default: + _form.AddLog("ファイルの型式が違います", 2); + break; } } catch (Exception Ex) @@ -283,6 +298,141 @@ public bool TwitchConvertChatDownloader(string sfile, string dfile) } return true; } + private static JToken FindJTokenByName(JToken jtoken, string name) + { + if (jtoken is JObject) + { + foreach (KeyValuePair kvp in (JObject)jtoken) + { + if (kvp.Key == name) + { + return kvp.Value; + } + else + { + JToken retVal = FindJTokenByName(kvp.Value, name); + if (retVal != null) + { + return retVal; + } + } + } + } + else if (jtoken is JArray) + { + foreach (JToken jtokenInArray in (JArray)jtoken) + { + JToken retVal = FindJTokenByName(jtokenInArray, name); + if (retVal != null) + { + return retVal; + } + } + } + else + { + return null; + } + return null; + } + + public bool TwitchConvertYtDlpYoutube(string sfile, string dfile) + { + var enc = new System.Text.UTF8Encoding(false); + + try + { + using (var sr = new StreamReader(sfile, enc)) + using (var sw = new StreamWriter(dfile, true, enc)) + { + StringBuilder sb = new StringBuilder(); + string line; + int count = 0; + ConvComment.BeginXmlDoc(sw); + while (!sr.EndOfStream) // ファイルが最後になるまで順に読み込み + { + line = sr.ReadLine(); + if (Utils.RgxYTJson.IsMatch(line.TrimStart())) + { + //チャットの処理 + { + var data = new Dictionary(); + var jo = JObject.Parse(line); + JToken jt_lenderer = null; + jt_lenderer = FindJTokenByName((JToken)jo, "liveChatTextMessageRenderer"); + if (jt_lenderer == null) + jt_lenderer = FindJTokenByName((JToken)jo, "liveChatPaidMessageRenderer"); + if (jt_lenderer == null) + continue; + data.Add("threadid", jt_lenderer["id"].ToString()); + data.Add("vpos", ((long)((double)jo["replayChatItemAction"]["videoOffsetTimeMsec"]/10D)).ToString()); + var utime = jt_lenderer["timestampUsec"].ToString(); + if (utime.Length > 6) + { + //01 234567 + data.Add("date", utime.Substring(0, utime.Length - 6)); + data.Add("date_usec", utime.Substring(utime.Length - 6)); + } + data.Add("user_id", jt_lenderer["authorName"]["simpleText"].ToString()); + data.Add("name", jt_lenderer["authorName"]["simpleText"].ToString()); + + string message = ""; + JToken jt_runs = FindJTokenByName(jt_lenderer, "runs"); + if (jt_runs != null) + { + foreach (var emt in jt_runs) + { + if (emt["text"] != null) + message += emt["text"].ToString(); + else if (emt["emoji"] != null) + if (emt["emoji"]["isCustomEmoji"] == null || + emt["emoji"]["isCustomEmoji"].ToString() == "false") + message += emt["emoji"]["emojiId"].ToString(); + else + if (emt["emoji"]["shortcuts"] != null) + message += emt["emoji"]["shortcuts"].FirstOrDefault().ToString(); + } + } + + if (FindJTokenByName(jt_lenderer, "purchaseAmountText") != null) + { + if (_props.IsTwiGift) + { + message = jt_lenderer["purchaseAmountText"]["simpleText"] + " " + message; + data.Add("mail", "shita"); + } + else + { + message = ""; + } + } + if (message.Length <= 0) + continue; + data.Add("content", message); + + if (data.Count() > 0) + { + count++; + var ttt = ConvChatData(data, _props).TrimEnd(); + if (!string.IsNullOrEmpty(ttt)) + sw.WriteLine(ttt); + //else + //_form.AddLog("deleted:" + line, 9); + } + } + } + } + _form.AddLog("コメント数: " + count, 1); + ConvComment.EndXmlDoc(sw); + } + } + catch (Exception Ex) + { + DebugWrite.Writeln(nameof(TwitchConvertChatDownloader), Ex); + return false; + } + return true; + } private static Regex _RegUtc = new Regex("^\\[(.+ UTC)\\] (.+)\\: (.*)", RegexOptions.Compiled); private static Regex _RegRelative = new Regex("^\\[(\\d+\\:\\d+\\:\\d+)\\] (.+)\\: (.*)", RegexOptions.Compiled); diff --git a/comeconv/Form1.Designer.cs b/comeconv/Form1.Designer.cs index f1ba873..3c51234 100644 --- a/comeconv/Form1.Designer.cs +++ b/comeconv/Form1.Designer.cs @@ -616,10 +616,10 @@ private void InitializeComponent() this.label19.AutoSize = true; this.label19.Location = new System.Drawing.Point(91, 45); this.label19.Name = "label19"; - this.label19.Size = new System.Drawing.Size(325, 48); + this.label19.Size = new System.Drawing.Size(331, 48); this.label19.TabIndex = 6; - this.label19.Text = "TwitchDownloader (txt/json)\r\nchat-downloader (json/jsonl)\r\n\r\nTwitchDownloader t" + - "xt形式のコメントは代替絵文字が表示されます"; + this.label19.Text = "TwitchDownloader (text/json)\r\nChat Downloader (json/jsonl)\r\nyt-dlp (Youtube)\r\nT" + + "witchDownloader text形式のコメントは代替絵文字が表示されます"; // // label18 // diff --git a/comeconv/Prop/Version.cs b/comeconv/Prop/Version.cs index 444a33d..b9d45fd 100644 --- a/comeconv/Prop/Version.cs +++ b/comeconv/Prop/Version.cs @@ -4,8 +4,8 @@ namespace comeconv.Prop { public class Ver { - public static readonly string Version = "0.0.1.21"; - public static readonly string VerDate = "2023/04/17"; + public static readonly string Version = "0.0.1.22"; + public static readonly string VerDate = "2023/04/30"; public static string GetFullVersion() { diff --git a/comeconv/Util/Utils.cs b/comeconv/Util/Utils.cs index b1413c1..7865628 100644 --- a/comeconv/Util/Utils.cs +++ b/comeconv/Util/Utils.cs @@ -121,10 +121,17 @@ public static int IsFileType(string filename) return result; } + public static readonly Regex RgxYTJson = new Regex("^\\{(\'|\")(clickTrackingParams|replayChatItemAction).*(\'|\")\\: ", RegexOptions.Compiled | RegexOptions.Singleline); public static readonly Regex RgxCDJsonl = new Regex("^\\{(\'|\").*(\'|\")\\: ", RegexOptions.Compiled | RegexOptions.Singleline); private static readonly Regex RgxCDJson = new Regex("^\\[\n +\\{\n", RegexOptions.Compiled | RegexOptions.Singleline); private static char[] _read_buf = new char[256]; //Twitchのコメントファイルの種類を返す + // 0 Chat Downloader (*.jsonl) + // 1 Chat Downloader (*.json) + // 5 TwitchDownloader (json) + // 6 TwitchDownloader (text) + // 10 yt-dlp (Youtube) + // 11 yt-dlp (Twitch) public static int IsTwitchFileType(string filename) { var enc = new System.Text.UTF8Encoding(false); @@ -136,14 +143,18 @@ public static int IsTwitchFileType(string filename) if (len > 0) { var str = new string(_read_buf); - if (RgxCDJsonl.IsMatch(str)) - result = 0; + if (RgxYTJson.IsMatch(str)) + result = 10; //yt-dlp (Youtube) + else if (RgxCDJsonl.IsMatch(str)) + result = 0; //Chat Downloader else if (RgxCDJson.IsMatch(str)) - result = 1; - else if (str.StartsWith("{\"streamer\":{\"name\":")|| str.StartsWith("{\"FileInfo\":{\"Version\":")) - result = 5; + result = 1; //Chat Downloader + else if (str.StartsWith("{\"streamer\":{\"name\":") || str.StartsWith("{\"FileInfo\":{\"Version\":")) + result = 5; //Twitch + else if (str.StartsWith("{\"comments\":[")) + result = 11; //yt-dlp (Twitch) else - result = 10; + result = 6; } }