Merge branch 'GameLogSQLite'

This commit is contained in:
Natsumi
2021-09-26 22:32:34 +13:00
12 changed files with 2594 additions and 1752 deletions
+29 -13
View File
@@ -229,20 +229,15 @@ namespace VRCX
return CpuMonitor.Instance.CpuUsage; return CpuMonitor.Instance.CpuUsage;
} }
public void CacheImage(string Base64File) public void DesktopNotification(string BoldText, string Text, string Image)
{
String Icon = Path.Combine(Program.AppDataDirectory, "cache\\toast");
File.WriteAllBytes(Icon, Convert.FromBase64String(Base64File));
}
public void DesktopNotification(string BoldText, string Text, bool Image)
{ {
XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastImageAndText02); XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastImageAndText02);
XmlNodeList stringElements = toastXml.GetElementsByTagName("text"); XmlNodeList stringElements = toastXml.GetElementsByTagName("text");
String imagePath = Path.Combine(Program.BaseDirectory, "VRCX.ico"); String imagePath = Path.Combine(Program.BaseDirectory, "VRCX.ico");
if (Image) if (!String.IsNullOrEmpty(Image))
{ {
imagePath = Path.Combine(Program.AppDataDirectory, "cache\\toast"); imagePath = Path.Combine(Program.AppDataDirectory, "cache\\toast");
File.WriteAllBytes(imagePath, Convert.FromBase64String(Image));
} }
stringElements[0].AppendChild(toastXml.CreateTextNode(BoldText)); stringElements[0].AppendChild(toastXml.CreateTextNode(BoldText));
stringElements[1].AppendChild(toastXml.CreateTextNode(Text)); stringElements[1].AppendChild(toastXml.CreateTextNode(Text));
@@ -268,14 +263,20 @@ namespace VRCX
public string sourceApp { get; set; } public string sourceApp { get; set; }
} }
public void XSNotification(string Title, string Content, int Timeout, bool Image) public void XSNotification(string Title, string Content, int Timeout, string Image)
{ {
bool UseBase64Icon = true; bool UseBase64Icon;
String Icon = "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAHaGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIxLTA0LTA4VDE0OjU3OjAxKzEyOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMS0wNC0wOFQxNjozMzoxMCsxMjowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMS0wNC0wOFQxNjozMzoxMCsxMjowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2YTY5MmQzYi03ZTJkLTNiNGUtYTMzZC1hN2MwOTNlOGU0OTkiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo1NTE2MWIyMi1hYzgxLTY3NDYtODAyYi1kODIzYWFmN2RjYjciIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3ZjJjNTA2ZS02YTVhLWRhNGEtOTg5Mi02NDZiMzQ0MGQxZTgiPiA8cGhvdG9zaG9wOkRvY3VtZW50QW5jZXN0b3JzPiA8cmRmOkJhZz4gPHJkZjpsaT5hZG9iZTpkb2NpZDpwaG90b3Nob3A6NmJmOGE5MTgtY2QzZS03OTRjLTk3NzktMzM0YjYwZWJiNTYyPC9yZGY6bGk+IDwvcmRmOkJhZz4gPC9waG90b3Nob3A6RG9jdW1lbnRBbmNlc3RvcnM+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6N2YyYzUwNmUtNmE1YS1kYTRhLTk4OTItNjQ2YjM0NDBkMWU4IiBzdEV2dDp3aGVuPSIyMDIxLTA0LTA4VDE0OjU3OjAxKzEyOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmJhM2ZjODI3LTM0ZjQtYjU0OC05ZGFiLTZhMTZlZmQzZjAxMSIgc3RFdnQ6d2hlbj0iMjAyMS0wNC0wOFQxNTowMTozMSsxMjowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo2YTY5MmQzYi03ZTJkLTNiNGUtYTMzZC1hN2MwOTNlOGU0OTkiIHN0RXZ0OndoZW49IjIwMjEtMDQtMDhUMTY6MzM6MTArMTI6MDAiIHN0RXZ0OnNv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"; string Icon;
if (Image) if (String.IsNullOrEmpty(Image))
{
UseBase64Icon = true;
Icon = "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAHaGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIxLTA0LTA4VDE0OjU3OjAxKzEyOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMS0wNC0wOFQxNjozMzoxMCsxMjowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMS0wNC0wOFQxNjozMzoxMCsxMjowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2YTY5MmQzYi03ZTJkLTNiNGUtYTMzZC1hN2MwOTNlOGU0OTkiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo1NTE2MWIyMi1hYzgxLTY3NDYtODAyYi1kODIzYWFmN2RjYjciIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3ZjJjNTA2ZS02YTVhLWRhNGEtOTg5Mi02NDZiMzQ0MGQxZTgiPiA8cGhvdG9zaG9wOkRvY3VtZW50QW5jZXN0b3JzPiA8cmRmOkJhZz4gPHJkZjpsaT5hZG9iZTpkb2NpZDpwaG90b3Nob3A6NmJmOGE5MTgtY2QzZS03OTRjLTk3NzktMzM0YjYwZWJiNTYyPC9yZGY6bGk+IDwvcmRmOkJhZz4gPC9waG90b3Nob3A6RG9jdW1lbnRBbmNlc3RvcnM+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6N2YyYzUwNmUtNmE1YS1kYTRhLTk4OTItNjQ2YjM0NDBkMWU4IiBzdEV2dDp3aGVuPSIyMDIxLTA0LTA4VDE0OjU3OjAxKzEyOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmJhM2ZjODI3LTM0ZjQtYjU0OC05ZGFiLTZhMTZlZmQzZjAxMSIgc3RFdnQ6d2hlbj0iMjAyMS0wNC0wOFQxNTowMTozMSsxMjowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo2YTY5MmQzYi03ZTJkLTNiNGUtYTMzZC1hN2MwOTNlOGU0OTkiIHN0RXZ0OndoZW49IjIwMjEtMDQtMDhUMTY6MzM6MTArMTI6MDAiIHN0RXZ0OnNv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";
}
else
{ {
UseBase64Icon = false; UseBase64Icon = false;
Icon = Path.Combine(Program.AppDataDirectory, "cache\\toast"); Icon = Path.Combine(Program.AppDataDirectory, "cache\\toast");
File.WriteAllBytes(Icon, Convert.FromBase64String(Image));
} }
IPAddress broadcastIP = IPAddress.Parse("127.0.0.1"); IPAddress broadcastIP = IPAddress.Parse("127.0.0.1");
@@ -314,13 +315,28 @@ namespace VRCX
System.Environment.Exit(0); System.Environment.Exit(0);
} }
public bool checkForUpdateZip() public bool CheckForUpdateZip()
{ {
if (File.Exists(Path.Combine(Program.AppDataDirectory, "update.exe"))) if (File.Exists(Path.Combine(Program.AppDataDirectory, "update.exe")))
return true; return true;
return false; return false;
} }
public void ExecuteAppFunction(string function, string json)
{
MainForm.Instance.Browser.ExecuteScriptAsync($"$app.{function}", json);
}
public void ExecuteVrFeedFunction(string function, string json)
{
VRCXVR._browser1.ExecuteScriptAsync($"$app.{function}", json);
}
public void ExecuteVrOverlayFunction(string function, string json)
{
VRCXVR._browser2.ExecuteScriptAsync($"$app.{function}", json);
}
public void SetStartup(bool enabled) public void SetStartup(bool enabled)
{ {
try try
+28 -5
View File
@@ -1,4 +1,4 @@
// Copyright(c) 2019 pypy. All rights reserved. // Copyright(c) 2019 pypy. All rights reserved.
// //
// This work is licensed under the terms of the MIT license. // This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>. // For a copy, see <https://opensource.org/licenses/MIT>.
@@ -17,6 +17,7 @@ namespace VRCX
private DiscordRpcClient m_Client; private DiscordRpcClient m_Client;
private Timer m_Timer; private Timer m_Timer;
private bool m_Active; private bool m_Active;
public static string DiscordAppId;
static Discord() static Discord()
{ {
@@ -78,7 +79,7 @@ namespace VRCX
{ {
if (m_Client == null) if (m_Client == null)
{ {
m_Client = new DiscordRpcClient("525953831020920832"); m_Client = new DiscordRpcClient(DiscordAppId);
if (m_Client.Initialize() == false) if (m_Client.Initialize() == false)
{ {
m_Client.Dispose(); m_Client.Dispose();
@@ -126,7 +127,7 @@ namespace VRCX
} }
} }
public void SetAssets(string largeKey, string largeText, string smallKey, string smallText) public void SetAssets(string largeKey, string largeText, string smallKey, string smallText, string partyId, int partySize, int partyMax, string buttonText, string buttonUrl, string appId)
{ {
m_Lock.EnterWriteLock(); m_Lock.EnterWriteLock();
try try
@@ -139,13 +140,35 @@ namespace VRCX
else else
{ {
if (m_Presence.Assets == null) if (m_Presence.Assets == null)
{
m_Presence.Assets = new Assets(); m_Presence.Assets = new Assets();
} if (m_Presence.Party == null)
m_Presence.Party = new Party();
m_Presence.Assets.LargeImageKey = largeKey; m_Presence.Assets.LargeImageKey = largeKey;
m_Presence.Assets.LargeImageText = largeText; m_Presence.Assets.LargeImageText = largeText;
m_Presence.Assets.SmallImageKey = smallKey; m_Presence.Assets.SmallImageKey = smallKey;
m_Presence.Assets.SmallImageText = smallText; m_Presence.Assets.SmallImageText = smallText;
m_Presence.Party.ID = partyId;
m_Presence.Party.Size = partySize;
m_Presence.Party.Max = partyMax;
Button[] Buttons = { };
if (!string.IsNullOrEmpty(buttonUrl))
{
Buttons = new Button[]
{
new Button() { Label = buttonText, Url = buttonUrl }
};
}
m_Presence.Buttons = Buttons;
if (DiscordAppId != appId)
{
DiscordAppId = appId;
if (m_Client != null)
{
m_Client.Dispose();
m_Client = null;
}
Update();
}
} }
} }
finally finally
+92 -21
View File
@@ -3,6 +3,7 @@
// This work is licensed under the terms of the MIT license. // This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>. // For a copy, see <https://opensource.org/licenses/MIT>.
using CefSharp;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
@@ -30,6 +31,8 @@ namespace VRCX
private readonly List<string[]> m_LogList; private readonly List<string[]> m_LogList;
private Thread m_Thread; private Thread m_Thread;
private bool m_ResetLog; private bool m_ResetLog;
private bool m_FirstRun = true;
private static DateTime tillDate = DateTime.Now;
// NOTE // NOTE
// FileSystemWatcher() is unreliable // FileSystemWatcher() is unreliable
@@ -65,6 +68,17 @@ namespace VRCX
thread.Join(); thread.Join();
} }
public void Reset()
{
m_ResetLog = true;
m_Thread?.Interrupt();
}
public void SetDateTill(string date)
{
tillDate = DateTime.Parse(date);
}
private void ThreadLoop() private void ThreadLoop()
{ {
while (m_Thread != null) while (m_Thread != null)
@@ -86,6 +100,7 @@ namespace VRCX
{ {
if (m_ResetLog == true) if (m_ResetLog == true)
{ {
m_FirstRun = true;
m_ResetLog = false; m_ResetLog = false;
m_LogContextMap.Clear(); m_LogContextMap.Clear();
m_LogListLock.EnterWriteLock(); m_LogListLock.EnterWriteLock();
@@ -109,26 +124,17 @@ namespace VRCX
// sort by creation time // sort by creation time
Array.Sort(fileInfos, (a, b) => a.CreationTimeUtc.CompareTo(b.CreationTimeUtc)); Array.Sort(fileInfos, (a, b) => a.CreationTimeUtc.CompareTo(b.CreationTimeUtc));
var utcNow = DateTime.UtcNow;
var minLimitDateTime = utcNow.AddDays(-7d);
var minRefreshDateTime = utcNow.AddMinutes(-3d);
foreach (var fileInfo in fileInfos) foreach (var fileInfo in fileInfos)
{ {
var lastWriteTimeUtc = fileInfo.LastWriteTimeUtc; fileInfo.Refresh();
if (fileInfo.Exists == false)
if (lastWriteTimeUtc < minLimitDateTime)
{ {
continue; continue;
} }
if (lastWriteTimeUtc >= minRefreshDateTime) if (DateTime.Compare(fileInfo.LastWriteTime, tillDate) < 0)
{ {
fileInfo.Refresh(); continue;
if (fileInfo.Exists == false)
{
continue;
}
} }
if (m_LogContextMap.TryGetValue(fileInfo.Name, out LogContext logContext) == true) if (m_LogContextMap.TryGetValue(fileInfo.Name, out LogContext logContext) == true)
@@ -155,6 +161,8 @@ namespace VRCX
{ {
m_LogContextMap.Remove(name); m_LogContextMap.Remove(name);
} }
m_FirstRun = false;
} }
private void ParseLog(FileInfo fileInfo, LogContext logContext) private void ParseLog(FileInfo fileInfo, LogContext logContext)
@@ -184,17 +192,33 @@ namespace VRCX
continue; continue;
} }
if (DateTime.TryParseExact(
line.Substring(0, 19),
"yyyy.MM.dd HH:mm:ss",
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeLocal,
out DateTime lineDate
))
{
if (DateTime.Compare(lineDate, tillDate) <= 0)
{
continue;
}
}
var offset = 34; var offset = 34;
if (line[offset] == '[') if (line[offset] == '[')
{ {
if (ParseLogOnPlayerJoinedOrLeft(fileInfo, logContext, line, offset) == true || if (ParseLogOnPlayerJoinedOrLeft(fileInfo, logContext, line, offset) == true ||
ParseLogLocation(fileInfo, logContext, line, offset) == true || ParseLogLocation(fileInfo, logContext, line, offset) == true ||
ParseLogLocationDestination(fileInfo, logContext, line, offset) == true ||
ParseLogPortalSpawn(fileInfo, logContext, line, offset) == true || ParseLogPortalSpawn(fileInfo, logContext, line, offset) == true ||
ParseLogNotification(fileInfo, logContext, line, offset) == true || ParseLogNotification(fileInfo, logContext, line, offset) == true ||
ParseLogJoinBlocked(fileInfo, logContext, line, offset) == true || ParseLogJoinBlocked(fileInfo, logContext, line, offset) == true ||
ParseLogAvatarPedestalChange(fileInfo, logContext, line, offset) == true || ParseLogAvatarPedestalChange(fileInfo, logContext, line, offset) == true ||
ParseLogVideoError(fileInfo, logContext, line, offset) == true || ParseLogVideoError(fileInfo, logContext, line, offset) == true ||
ParseLogVideoPlay(fileInfo, logContext, line, offset) == true) ParseLogVideoPlay(fileInfo, logContext, line, offset) == true ||
ParseLogWorldVRCX(fileInfo, logContext, line, offset) == true)
{ {
continue; continue;
} }
@@ -221,6 +245,12 @@ namespace VRCX
m_LogListLock.EnterWriteLock(); m_LogListLock.EnterWriteLock();
try try
{ {
if (!m_FirstRun)
{
var logLine = System.Text.Json.JsonSerializer.Serialize(item);
if (MainForm.Instance != null && MainForm.Instance.Browser != null)
MainForm.Instance.Browser.ExecuteScriptAsync("$app.addGameLogEvent", logLine);
}
m_LogList.Add(item); m_LogList.Add(item);
} }
finally finally
@@ -244,7 +274,7 @@ namespace VRCX
dt = DateTime.UtcNow; dt = DateTime.UtcNow;
} }
return $"{dt:yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'}"; return $"{dt:yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'}";
} }
private bool ParseLogLocation(FileInfo fileInfo, LogContext logContext, string line, int offset) private bool ParseLogLocation(FileInfo fileInfo, LogContext logContext, string line, int offset)
@@ -284,6 +314,29 @@ namespace VRCX
return false; return false;
} }
private bool ParseLogLocationDestination(FileInfo fileInfo, LogContext logContext, string line, int offset)
{
// 2021.09.02 00:02:12 Log - [Behaviour] Destination set: wrld_4432ea9b-729c-46e3-8eaf-846aa0a37fdd:15609~private(usr_032383a7-748c-4fb2-94e4-bcb928e5de6b)~nonce(72CC87D420C1D49AEFFBEE8824C84B2DF0E38678E840661E)
// 2021.09.02 00:49:15 Log - [Behaviour] Destination fetching: wrld_4432ea9b-729c-46e3-8eaf-846aa0a37fdd
if (string.Compare(line, offset, "[Behaviour] Destination fetching: ", 0, 34, StringComparison.Ordinal) == 0)
{
var location = line.Substring(offset + 34);
AppendLog(new[]
{
fileInfo.Name,
ConvertLogTimeToISO8601(line),
"location-destination",
location
});
return true;
}
return false;
}
private bool ParseLogOnPlayerJoinedOrLeft(FileInfo fileInfo, LogContext logContext, string line, int offset) private bool ParseLogOnPlayerJoinedOrLeft(FileInfo fileInfo, LogContext logContext, string line, int offset)
{ {
// 2020.10.31 23:36:58 Log - [NetworkManager] OnPlayerJoined pypy // 2020.10.31 23:36:58 Log - [NetworkManager] OnPlayerJoined pypy
@@ -515,6 +568,28 @@ namespace VRCX
return true; return true;
} }
private bool ParseLogWorldVRCX(FileInfo fileInfo, LogContext logContext, string line, int offset)
{
// [VRCX] VideoPlay(PyPyDance) "https://jd.pypy.moe/api/v1/videos/-Q3pdlsQxOk.mp4",0.5338666,260.6938,"1339 : Le Freak (Random)"
if (string.Compare(line, offset, "[VRCX] ", 0, 7, StringComparison.Ordinal) == 0)
{
var data = line.Substring(offset + 7);
AppendLog(new[]
{
fileInfo.Name,
ConvertLogTimeToISO8601(line),
"vrcx",
data
});
return true;
}
return false;
}
private bool ParseLogSDK2VideoPlay(FileInfo fileInfo, LogContext logContext, string line, int offset) private bool ParseLogSDK2VideoPlay(FileInfo fileInfo, LogContext logContext, string line, int offset)
{ {
// 2021.04.23 13:12:25 Log - User Natsumi-sama added URL https://www.youtube.com/watch?v=dQw4w9WgXcQ // 2021.04.23 13:12:25 Log - User Natsumi-sama added URL https://www.youtube.com/watch?v=dQw4w9WgXcQ
@@ -579,14 +654,10 @@ namespace VRCX
return true; return true;
} }
public void Reset()
{
m_ResetLog = true;
m_Thread?.Interrupt();
}
public string[][] Get() public string[][] Get()
{ {
Update();
if (m_ResetLog == false && if (m_ResetLog == false &&
m_LogList.Count > 0) m_LogList.Count > 0)
{ {
+2 -2
View File
@@ -34,8 +34,8 @@ namespace VRCX
private Device _device; private Device _device;
private Texture2D _texture1; private Texture2D _texture1;
private Texture2D _texture2; private Texture2D _texture2;
private OffScreenBrowser _browser1; public static OffScreenBrowser _browser1;
private OffScreenBrowser _browser2; public static OffScreenBrowser _browser2;
private bool _active; private bool _active;
static VRCXVR() static VRCXVR()
+4
View File
@@ -147,6 +147,10 @@ namespace VRCX
{ {
request.UserAgent = value; request.UserAgent = value;
} }
else if (string.Compare(key, "Referer", StringComparison.OrdinalIgnoreCase) == 0)
{
request.Referer = value;
}
else else
{ {
request.Headers.Add(key, value); request.Headers.Add(key, value);
+1667 -768
View File
File diff suppressed because it is too large Load Diff
+123 -56
View File
@@ -173,6 +173,7 @@ html
span(v-else-if="scope.row.status === 'busy'") Do Not Disturb span(v-else-if="scope.row.status === 'busy'") Do Not Disturb
span(v-else) Offline span(v-else) Offline
i.x-user-status(:class="statusClass(scope.row.status)") i.x-user-status(:class="statusClass(scope.row.status)")
span
span(v-text="scope.row.statusDescription") span(v-text="scope.row.statusDescription")
template(v-else-if="scope.row.type === 'Avatar'") template(v-else-if="scope.row.type === 'Avatar'")
avatar-info(:imageurl="scope.row.currentAvatarImageUrl" :userid="scope.row.userId" :hintownerid="scope.row.ownerId" :hintavatarname="scope.row.avatarName") avatar-info(:imageurl="scope.row.currentAvatarImageUrl" :userid="scope.row.userId" :hintownerid="scope.row.ownerId" :hintavatarname="scope.row.avatarName")
@@ -183,10 +184,10 @@ html
template(#tool) template(#tool)
div(style="margin:0 0 10px;display:flex;align-items:center") div(style="margin:0 0 10px;display:flex;align-items:center")
el-select(v-model="gameLogTable.filters[0].value" @change="saveTableFilters" multiple clearable collapse-tags style="flex:1" placeholder="Filter") el-select(v-model="gameLogTable.filters[0].value" @change="saveTableFilters" multiple clearable collapse-tags style="flex:1" placeholder="Filter")
el-option(v-once v-for="type in ['Location', 'OnPlayerJoined', 'OnPlayerLeft', 'PortalSpawn', 'Event', 'VideoPlay']" :key="type" :label="type" :value="type") el-option(v-once v-for="type in ['Location', 'OnPlayerJoined', 'OnPlayerLeft', 'PortalSpawn', 'AvatarChange', 'Event', 'VideoPlay']" :key="type" :label="type" :value="type")
el-input(v-model="gameLogTable.filters[1].value" placeholder="Search" style="flex:none;width:150px;margin:0 10px") el-input(v-model="gameLogTable.filters[1].value" placeholder="Search" style="flex:none;width:150px;margin:0 10px")
el-tooltip(placement="bottom" content="Reset game log" :disabled="hideTooltips") el-tooltip(placement="bottom" content="Reload game log" :disabled="hideTooltips")
el-button(type="default" @click="resetGameLog()" icon="el-icon-refresh" circle style="flex:none") el-button(type="default" @click="refreshEntireGameLog" icon="el-icon-refresh" circle style="flex:none")
el-table-column(label="Date" prop="created_at" sortable="custom" width="90") el-table-column(label="Date" prop="created_at" sortable="custom" width="90")
template(v-once #default="scope") template(v-once #default="scope")
el-tooltip(placement="right") el-tooltip(placement="right")
@@ -194,17 +195,28 @@ html
span {{ scope.row.created_at | formatDate('YYYY-MM-DD HH24:MI:SS') }} span {{ scope.row.created_at | formatDate('YYYY-MM-DD HH24:MI:SS') }}
span {{ scope.row.created_at | formatDate('MM-DD HH24:MI') }} span {{ scope.row.created_at | formatDate('MM-DD HH24:MI') }}
el-table-column(label="Type" prop="type" width="120") el-table-column(label="Type" prop="type" width="120")
template(v-once #default="scope")
span.x-link(v-if="scope.row.location && scope.row.type !== 'Location'" v-text="scope.row.type" @click="showWorldDialog(scope.row.location)")
span(v-else v-text="scope.row.type")
el-table-column(label="User" prop="displayName" width="160")
template(v-once #default="scope")
span.x-link(v-text="scope.row.displayName" @click="lookupUser(scope.row)")
el-table-column(label="Detail" prop="data") el-table-column(label="Detail" prop="data")
template(v-once #default="scope") template(v-once #default="scope")
location(v-if="scope.row.type === 'Location'" :location="scope.row.data[0]" :hint="scope.row.data[1]") location(v-if="scope.row.type === 'Location'" :location="scope.row.location" :hint="scope.row.worldName")
location(v-else-if="scope.row.type === 'PortalSpawn'" :location="scope.row.instanceId" :hint="scope.row.worldName")
template(v-else-if="scope.row.type === 'AvatarChange'")
span.x-link(@click="showUserDialog(scope.row.authorId)" v-text="scope.row.name")
template(v-if="scope.row.description && scope.row.name !== scope.row.description")
| - {{ scope.row.description }}
template(v-else-if="scope.row.type === 'Event'") template(v-else-if="scope.row.type === 'Event'")
span(v-text="scope.row.data") span(v-text="scope.row.data")
template(v-else-if="scope.row.type === 'VideoPlay'") template(v-else-if="scope.row.type === 'VideoPlay'")
span.x-link(v-text="scope.row.data" @click="openExternalLink(scope.row.data)") span(v-if="scope.row.videoId") {{ scope.row.videoId }}:
template(v-if="scope.row.displayName") span.x-link(v-if="scope.row.videoName" @click="openExternalLink(scope.row.videoUrl)" v-text="scope.row.videoName")
span.x-link(@click="lookupUser(scope.row.displayName)") ({{ scope.row.displayName }}) span.x-link(v-else @click="openExternalLink(scope.row.videoUrl)" v-text="scope.row.videoUrl")
template(v-else-if="scope.row.type === 'Notification'") template(v-else-if="scope.row.type === 'Notification' || scope.row.type === 'OnPlayerJoined' || scope.row.type === 'OnPlayerLeft'")
span.x-link(v-else v-text="scope.row.data" @click="lookupUser(scope.row.data)") span.x-link(v-else v-text="scope.row.data")
//- search //- search
.x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'search'") .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'search'")
@@ -458,15 +470,26 @@ html
span(v-else-if='scope.row.details && scope.row.details.inviteMessage' v-text="scope.row.details.inviteMessage") span(v-else-if='scope.row.details && scope.row.details.inviteMessage' v-text="scope.row.details.inviteMessage")
span(v-else-if='scope.row.details && scope.row.details.requestMessage' v-text="scope.row.details.requestMessage") span(v-else-if='scope.row.details && scope.row.details.requestMessage' v-text="scope.row.details.requestMessage")
span(v-else-if='scope.row.details && scope.row.details.responseMessage' v-text="scope.row.details.responseMessage") span(v-else-if='scope.row.details && scope.row.details.responseMessage' v-text="scope.row.details.responseMessage")
el-table-column(label="Action" width="80" align="right") el-table-column(label="Action" width="100" align="right")
template(v-once #default="scope") template(v-once #default="scope")
template(v-if="scope.row.senderUserId !== API.currentUser.id") template(v-if="scope.row.senderUserId !== API.currentUser.id && !scope.row.$isExpired")
el-button(v-if="scope.row.type === 'friendRequest'" type="text" icon="el-icon-check" size="mini" @click="acceptNotification(scope.row)") template(v-if="scope.row.type === 'friendRequest'")
el-button(v-else-if="scope.row.type === 'invite'" type="text" icon="el-icon-chat-line-square" size="mini" @click="showSendInviteResponseDialog(scope.row)") el-tooltip(placement="top" content="Accept" :disabled="hideTooltips")
el-button(type="text" icon="el-icon-check" size="mini" @click="acceptNotification(scope.row)")
template(v-else-if="scope.row.type === 'invite'")
el-tooltip(placement="top" content="Decline with message" :disabled="hideTooltips")
el-button(type="text" icon="el-icon-chat-line-square" size="mini" @click="showSendInviteResponseDialog(scope.row)")
template(v-else-if="scope.row.type === 'requestInvite'") template(v-else-if="scope.row.type === 'requestInvite'")
el-button(v-if="lastLocation.location && isGameRunning && !checkCanInvite(lastLocation.location)" type="text" icon="el-icon-check" size="mini" @click="acceptRequestInvite(scope.row)") template(v-if="lastLocation.location && isGameRunning && !checkCanInvite(lastLocation.location)")
el-button(type="text" icon="el-icon-chat-line-square" size="mini" style="margin-left:5px" @click="showSendInviteRequestResponseDialog(scope.row)") el-tooltip(placement="top" content="Invite" :disabled="hideTooltips")
el-button(type="text" icon="el-icon-close" size="mini" style="margin-left:5px" @click="hideNotification(scope.row)") el-button(type="text" icon="el-icon-check" size="mini" @click="acceptRequestInvite(scope.row)")
el-tooltip(placement="top" content="Decline with message" :disabled="hideTooltips")
el-button(type="text" icon="el-icon-chat-line-square" size="mini" style="margin-left:5px" @click="showSendInviteRequestResponseDialog(scope.row)")
template(v-if="scope.row.type !== 'requestInviteResponse' && scope.row.type !== 'inviteResponse' && scope.row.type !== 'message'")
el-tooltip(placement="top" content="Decline" :disabled="hideTooltips")
el-button(type="text" icon="el-icon-close" size="mini" style="margin-left:5px" @click="hideNotification(scope.row)")
el-tooltip(placement="top" content="Delete log" :disabled="hideTooltips")
el-button(type="text" icon="el-icon-delete" size="mini" style="margin-left:5px" @click="deleteNotificationLog(scope.row)")
//- profile //- profile
.x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'profile'") .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'profile'")
@@ -696,10 +719,10 @@ html
el-radio-button(label="dark") Dark el-radio-button(label="dark") Dark
div.options-container-item div.options-container-item
span.name VRCPlus Profile Icons span.name VRCPlus Profile Icons
el-switch(v-model="displayVRCPlusIconsAsAvatar") el-switch(v-model="displayVRCPlusIconsAsAvatar" @change="saveOpenVROption")
div.options-container-item div.options-container-item
span.name Disable Tooltips span.name Disable Tooltips
el-switch(v-model="hideTooltips") el-switch(v-model="hideTooltips" @change="saveOpenVROption")
div.options-container div.options-container
span.header Side Panel span.header Side Panel
br br
@@ -766,10 +789,16 @@ html
span * Only works when VRChat is running. span * Only works when VRChat is running.
div.options-container-item div.options-container-item
span.name Enable span.name Enable
el-switch(v-model="discordActive") el-switch(v-model="discordActive" @change="saveDiscordOption")
div.options-container-item div.options-container-item
span.name Instance details span.name Instance type/player count
el-switch(v-model="discordInstance" :disabled="!discordActive") el-switch(v-model="discordInstance" @change="saveDiscordOption" :disabled="!discordActive")
div.options-container-item
span.name Join button (public only)
el-switch(v-model="discordJoinButton" @change="saveDiscordOption" :disabled="!discordActive")
div.options-container-item
span.name Hide world details in private
el-switch(v-model="discordHideInvite" @change="saveDiscordOption" :disabled="!discordActive")
div.options-container div.options-container
span.header SteamVR Overlay span.header SteamVR Overlay
div.options-container-item div.options-container-item
@@ -782,61 +811,64 @@ html
br br
div.options-container-item div.options-container-item
span.name Enable span.name Enable
el-switch(v-model="openVR") el-switch(v-model="openVR" @change="saveOpenVROption")
div.options-container-item div.options-container-item
span.name Force Run (Opens SteamVR) span.name Force Run (Opens SteamVR)
el-switch(v-model="openVRAlways" :disabled="!openVR") el-switch(v-model="openVRAlways" @change="saveOpenVROption" :disabled="!openVR")
div.options-container-item div.options-container-item
span.name Hide Private Worlds span.name Hide Private Worlds
el-switch(v-model="hidePrivateFromFeed") el-switch(v-model="hidePrivateFromFeed" @change="saveOpenVROption")
br br
span.sub-header Wrist Feed span.sub-header Wrist Feed
div.options-container-item div.options-container-item
span.name Wrist Feed Overlay span.name Wrist Feed Overlay
el-switch(v-model="overlayWrist" :disabled="!openVR") el-switch(v-model="overlayWrist" @change="saveOpenVROption" :disabled="!openVR")
div.options-container-item div.options-container-item
span.name(style="min-width:137px") Overlay Button span.name(style="min-width:137px") Overlay Button
el-switch(v-model="overlaybutton" inactive-text="Grip" active-text="Menu" :disabled="!openVR || !overlayWrist") el-switch(v-model="overlaybutton" @change="saveOpenVROption" inactive-text="Grip" active-text="Menu" :disabled="!openVR || !overlayWrist")
div.options-container-item div.options-container-item
span.name Background Color span.name Background Color
el-switch(v-model="vrBackgroundEnabled" :disabled="!openVR || !overlayWrist") el-switch(v-model="vrBackgroundEnabled" @change="saveOpenVROption" :disabled="!openVR || !overlayWrist")
div.options-container-item div.options-container-item
span.name Minimal Feed Icons span.name Minimal Feed Icons
el-switch(v-model="minimalFeed" :disabled="!openVR || !overlayWrist") el-switch(v-model="minimalFeed" @change="saveOpenVROption" :disabled="!openVR || !overlayWrist")
div.options-container-item div.options-container-item
span.name Hide VR Devices span.name Hide VR Devices
el-switch(v-model="hideDevicesFromFeed" :disabled="!openVR || !overlayWrist") el-switch(v-model="hideDevicesFromFeed" @change="saveOpenVROption" :disabled="!openVR || !overlayWrist")
div.options-container-item div.options-container-item
el-button(size="small" icon="el-icon-notebook-2" @click="showWristFeedFiltersDialog()" :disabled="!openVR || !overlayWrist") Wrist Feed Filters el-button(size="small" icon="el-icon-notebook-2" @click="showWristFeedFiltersDialog" :disabled="!openVR || !overlayWrist") Wrist Feed Filters
br br
span.sub-header Notifications span.sub-header Notifications
div.options-container-item div.options-container-item
span.name Overlay Notifications span.name Overlay Notifications
el-switch(v-model="overlayNotifications" :disabled="!openVR") el-switch(v-model="overlayNotifications" @change="saveOpenVROption" :disabled="!openVR")
div.options-container-item div.options-container-item
el-button(size="small" icon="el-icon-rank" @click="showNotificationPositionDialog()" :disabled="!overlayNotifications || !openVR") Notification Position el-button(size="small" icon="el-icon-rank" @click="showNotificationPositionDialog" :disabled="!overlayNotifications || !openVR") Notification Position
div.options-container-item div.options-container-item
span.name XSOverlay Notifications span.name XSOverlay Notifications
el-switch(v-model="xsNotifications") el-switch(v-model="xsNotifications" @change="saveOpenVROption")
div.options-container-item div.options-container-item
el-button(size="small" icon="el-icon-time" @click="promptNotificationTimeout()" :disabled="(!overlayNotifications || !openVR) && !xsNotifications") Notification Timeout span.name User images (slower)
el-switch(v-model="imageNotifications" @change="saveOpenVROption")
div.options-container-item
el-button(size="small" icon="el-icon-time" @click="promptNotificationTimeout" :disabled="(!overlayNotifications || !openVR) && !xsNotifications") Notification Timeout
div.options-container-item div.options-container-item
span.name Desktop Notifications, When to display: span.name Desktop Notifications, When to display:
br br
el-radio-group(v-model="desktopToast" size="mini") el-radio-group(v-model="desktopToast" @change="saveOpenVROption" size="mini")
el-radio-button(label="Never") el-radio-button(label="Never")
el-radio-button(label="Inside VR") el-radio-button(label="Inside VR")
el-radio-button(label="Game Closed") el-radio-button(label="Game Closed")
el-radio-button(label="Game Running") el-radio-button(label="Game Running")
el-radio-button(label="Always") el-radio-button(label="Always")
div.options-container-item div.options-container-item
el-button(size="small" icon="el-icon-chat-square" @click="showNotyFeedFiltersDialog()") Notification Filters el-button(size="small" icon="el-icon-chat-square" @click="showNotyFeedFiltersDialog") Notification Filters
br br
span.sub-header Text-To-Speech Options span.sub-header Text-To-Speech Options
div.options-container-item div.options-container-item
span.name Notification TTS, When to play: span.name Notification TTS, When to play:
br br
el-radio-group(v-model="notificationTTS" size="mini") el-radio-group(v-model="notificationTTS" @change="saveNotificationTTS" size="mini")
el-radio-button(label="Never") el-radio-button(label="Never")
el-radio-button(label="Inside VR") el-radio-button(label="Inside VR")
el-radio-button(label="Game Closed") el-radio-button(label="Game Closed")
@@ -857,31 +889,30 @@ html
div.options-container-item div.options-container-item
span.name Download on invite: span.name Download on invite:
br br
el-radio-group(v-model="worldAutoCacheInvite" size="mini") el-radio-group(v-model="worldAutoCacheInvite" @change="saveOpenVROption" size="mini")
el-radio-button(label="Never") el-radio-button(label="Never")
el-radio-button(label="Game Closed") el-radio-button(label="Game Closed")
el-radio-button(label="Game Running") el-radio-button(label="Game Running")
el-radio-button(label="Always") el-radio-button(label="Always")
div.options-container-item div.options-container-item
el-switch(v-model="worldAutoCacheInviteFilter" inactive-text="VIP" active-text="Everyone" :disabled="worldAutoCacheInvite == 'Never'") el-switch(v-model="worldAutoCacheInviteFilter" @change="saveOpenVROption" inactive-text="VIP" active-text="Everyone" :disabled="worldAutoCacheInvite === 'Never'")
div.options-container-item div.options-container-item
span.name Download on GPS: span.name Download on GPS:
br br
el-radio-group(v-model="worldAutoCacheGPS" size="mini") el-radio-group(v-model="worldAutoCacheGPS" @change="saveOpenVROption" size="mini")
el-radio-button(label="Never") el-radio-button(label="Never")
el-radio-button(label="Game Closed") el-radio-button(label="Game Closed")
el-radio-button(label="Game Running") el-radio-button(label="Game Running")
el-radio-button(label="Always") el-radio-button(label="Always")
div.options-container-item div.options-container-item
el-switch(v-model="worldAutoCacheGPSFilter" inactive-text="VIP" active-text="Everyone" :disabled="worldAutoCacheGPS == 'Never'") el-switch(v-model="worldAutoCacheGPSFilter" @change="saveOpenVROption" inactive-text="VIP" active-text="Everyone" :disabled="worldAutoCacheGPS === 'Never'")
div.options-container-item div.options-container-item
el-button-group el-button(size="small" icon="el-icon-download" @click="showDownloadDialog") Download History
el-button(size="small" icon="el-icon-download" @click="showDownloadDialog()") Download History
br br
span.sub-header Automatically Manage Cache When Closing VRChat span.sub-header Automatically Manage Cache When Closing VRChat
div.options-container-item div.options-container-item
span.name(style="min-width:300px") Auto delete old versions from cache span.name(style="min-width:300px") Auto delete old versions from cache
el-switch(v-model="autoSweepVRChatCache") el-switch(v-model="autoSweepVRChatCache" @change="saveOpenVROption")
div.options-container div.options-container
span.header Application span.header Application
div.options-container-item div.options-container-item
@@ -900,6 +931,13 @@ html
el-button-group el-button-group
el-button(size="small" icon="el-icon-s-operation" @click="showLaunchOptions()") Launch Options el-button(size="small" icon="el-icon-s-operation" @click="showLaunchOptions()") Launch Options
el-button(size="small" icon="el-icon-s-operation" @click="showVRChatConfig()") VRChat config.json el-button(size="small" icon="el-icon-s-operation" @click="showVRChatConfig()") VRChat config.json
div.options-container
span.header YouTube API
div.options-container-item
span.name Enabled
el-switch(v-model="youTubeApi" @change="changeYouTubeApi")
div.options-container-item
el-button(size="small" icon="el-icon-caret-right" @click="showYouTubeApiDialog") YouTube API Key
div.options-container(style="margin-top:45px;border-top:1px solid #eee;padding-top:30px") div.options-container(style="margin-top:45px;border-top:1px solid #eee;padding-top:30px")
span.header Legal Notice span.header Legal Notice
div.options-container-item div.options-container-item
@@ -1053,6 +1091,7 @@ html
el-button(:type="(userDialog.incomingRequest || userDialog.outgoingRequest) ? 'success' : (userDialog.isBlock || userDialog.isMute || userDialog.isHideAvatar) ? 'danger' : 'default'" icon="el-icon-more" circle style="margin-left:5px") el-button(:type="(userDialog.incomingRequest || userDialog.outgoingRequest) ? 'success' : (userDialog.isBlock || userDialog.isMute || userDialog.isHideAvatar) ? 'danger' : 'default'" icon="el-icon-more" circle style="margin-left:5px")
el-dropdown-menu(#default="dropdown") el-dropdown-menu(#default="dropdown")
el-dropdown-item(icon="el-icon-refresh" command="Refresh") Refresh el-dropdown-item(icon="el-icon-refresh" command="Refresh") Refresh
el-dropdown-item(icon="el-icon-s-order" command="Copy User") Copy User URL
template(v-if="userDialog.ref.id === API.currentUser.id") template(v-if="userDialog.ref.id === API.currentUser.id")
el-dropdown-item(icon="el-icon-picture-outline" command="Manage Gallery" divided) Manage Gallery/Icons el-dropdown-item(icon="el-icon-picture-outline" command="Manage Gallery" divided) Manage Gallery/Icons
el-dropdown-item(icon="el-icon-s-custom" command="Show Avatar Author") Show Avatar Author el-dropdown-item(icon="el-icon-s-custom" command="Show Avatar Author") Show Avatar Author
@@ -1075,7 +1114,7 @@ html
el-dropdown-item(v-else icon="el-icon-plus" command="Send Friend Request") Send Friend Request el-dropdown-item(v-else icon="el-icon-plus" command="Send Friend Request") Send Friend Request
el-dropdown-item(icon="el-icon-s-custom" command="Show Avatar Author" divided) Show Avatar Author el-dropdown-item(icon="el-icon-s-custom" command="Show Avatar Author" divided) Show Avatar Author
el-dropdown-item(icon="el-icon-s-custom" command="Show Fallback Avatar Details") Show Fallback Avatar Details el-dropdown-item(icon="el-icon-s-custom" command="Show Fallback Avatar Details") Show Fallback Avatar Details
el-dropdown-item(v-if="userDialog.currentAvatarImageUrl !== 'https://assets.vrchat.com/system/defaultAvatar.png'" icon="el-icon-picture-outline" command="Previous Images") Show Avatar Previous Images el-dropdown-item(v-if="userDialog.ref.currentAvatarImageUrl !== 'https://assets.vrchat.com/system/defaultAvatar.png'" icon="el-icon-picture-outline" command="Previous Images") Show Avatar Previous Images
el-dropdown-item(v-if="userDialog.isBlock" icon="el-icon-circle-check" command="Unblock" divided style="color:#F56C6C") Unblock el-dropdown-item(v-if="userDialog.isBlock" icon="el-icon-circle-check" command="Unblock" divided style="color:#F56C6C") Unblock
el-dropdown-item(v-else icon="el-icon-circle-close" command="Block" divided :disabled="userDialog.ref.$isModerator") Block el-dropdown-item(v-else icon="el-icon-circle-close" command="Block" divided :disabled="userDialog.ref.$isModerator") Block
el-dropdown-item(v-if="userDialog.isMute" icon="el-icon-microphone" command="Unmute" style="color:#F56C6C") Unmute el-dropdown-item(v-if="userDialog.isMute" icon="el-icon-microphone" command="Unmute" style="color:#F56C6C") Unmute
@@ -1665,6 +1704,16 @@ html
el-button(size="small" @click="VRChatConfigDialog.visible = false") Cancel el-button(size="small" @click="VRChatConfigDialog.visible = false") Cancel
el-button(type="primary" size="small" :disabled="VRChatConfigDialog.loading" @click="saveVRChatConfigFile") Save el-button(type="primary" size="small" :disabled="VRChatConfigDialog.loading" @click="saveVRChatConfigFile") Save
//- dialog: YouTube Api Dialog
el-dialog.x-dialog(ref="youTubeApiDialog" :visible.sync="youTubeApiDialog.visible" title="YouTube API" width="400px")
div(style='font-size:12px;')
| Enter your YouTube API Key (optional) #[br]
el-input(type="textarea" v-model="youTubeApiKey" placeholder="YouTube API Key" maxlength="39" show-word-limit style="dispaly:block;margin-top:10px")
template(#footer)
div(style="display:flex")
el-button(size="small" @click="openExternalLink('https://rapidapi.com/blog/how-to-get-youtube-api-key/')") Guide
el-button(type="primary" size="small" @click="testYouTubeApiKey" style="margin-left:auto") Save
//- dialog: Cache Download //- dialog: Cache Download
el-dialog.x-dialog(ref="downloadDialog" :visible.sync="downloadDialog.visible" title="Download History" width="770px") el-dialog.x-dialog(ref="downloadDialog" :visible.sync="downloadDialog.visible" title="Download History" width="770px")
div(v-if="downloadInProgress && downloadCurrent.ref") div(v-if="downloadInProgress && downloadCurrent.ref")
@@ -1769,7 +1818,7 @@ html
el-button(type="primary" size="small" style="margin-left:auto" @click="notificationPositionDialog.visible = false") OK el-button(type="primary" size="small" style="margin-left:auto" @click="notificationPositionDialog.visible = false") OK
//- dialog: Noty feed filters //- dialog: Noty feed filters
el-dialog.x-dialog(ref="notyFeedFiltersDialog" :visible.sync="notyFeedFiltersDialog.visible" title="Notification Filters" width="480px") el-dialog.x-dialog(ref="notyFeedFiltersDialog" :visible.sync="notyFeedFiltersDialog.visible" title="Notification Filters" width="500px")
.toggle-list .toggle-list
.toggle-item .toggle-item
span.toggle-name OnPlayerJoining span.toggle-name OnPlayerJoining
@@ -1868,21 +1917,30 @@ html
el-radio-button(label="Friends") el-radio-button(label="Friends")
.toggle-item .toggle-item
span.toggle-name Portal Spawn span.toggle-name Portal Spawn
el-tooltip(placement="top" style="margin-left:5px" content="Requires '--enable-sdk-log-levels' steam launch option")
i.el-icon-warning
el-radio-group(v-model="sharedFeedFilters.noty.PortalSpawn" size="mini") el-radio-group(v-model="sharedFeedFilters.noty.PortalSpawn" size="mini")
el-radio-button(label="Off") el-radio-button(label="Off")
el-radio-button(label="VIP") el-radio-button(label="VIP")
el-radio-button(label="Friends") el-radio-button(label="Friends")
el-radio-button(label="Everyone") el-radio-button(label="Everyone")
.toggle-item //- .toggle-item
span.toggle-name Events //- span.toggle-name Avatar Change
el-radio-group(v-model="sharedFeedFilters.noty.Event" size="mini") //- el-radio-group(v-model="sharedFeedFilters.noty.AvatarChange" size="mini")
el-radio-button(label="Off") //- el-radio-button(label="Off")
el-radio-button(label="On") //- el-radio-button(label="VIP")
//- el-radio-button(label="Friends")
//- el-radio-button(label="Everyone")
.toggle-item .toggle-item
span.toggle-name Video Play span.toggle-name Video Play
el-radio-group(v-model="sharedFeedFilters.noty.VideoPlay" size="mini") el-radio-group(v-model="sharedFeedFilters.noty.VideoPlay" size="mini")
el-radio-button(label="Off") el-radio-button(label="Off")
el-radio-button(label="On") el-radio-button(label="On")
.toggle-item
span.toggle-name Events
el-radio-group(v-model="sharedFeedFilters.noty.Event" size="mini")
el-radio-button(label="Off")
el-radio-button(label="On")
.toggle-item .toggle-item
span.toggle-name Blocked Player Joins span.toggle-name Blocked Player Joins
el-radio-group(v-model="sharedFeedFilters.noty.BlockedOnPlayerJoined" size="mini") el-radio-group(v-model="sharedFeedFilters.noty.BlockedOnPlayerJoined" size="mini")
@@ -1916,7 +1974,7 @@ html
el-button(type="primary" size="small" style="margin-left:10px" @click="saveSharedFeedFilters") Save el-button(type="primary" size="small" style="margin-left:10px" @click="saveSharedFeedFilters") Save
//- dialog: wrist feed filters //- dialog: wrist feed filters
el-dialog.x-dialog(ref="wristFeedFiltersDialog" :visible.sync="wristFeedFiltersDialog.visible" title="Wrist Feed Filters" width="480px") el-dialog.x-dialog(ref="wristFeedFiltersDialog" :visible.sync="wristFeedFiltersDialog.visible" title="Wrist Feed Filters" width="500px")
.toggle-list .toggle-list
.toggle-item .toggle-item
span.toggle-name Self Location span.toggle-name Self Location
@@ -2020,19 +2078,28 @@ html
el-radio-button(label="Friends") el-radio-button(label="Friends")
.toggle-item .toggle-item
span.toggle-name Portal Spawn span.toggle-name Portal Spawn
el-tooltip(placement="top" style="margin-left:5px" content="Requires '--enable-sdk-log-levels' steam launch option")
i.el-icon-warning
el-radio-group(v-model="sharedFeedFilters.wrist.PortalSpawn" size="mini") el-radio-group(v-model="sharedFeedFilters.wrist.PortalSpawn" size="mini")
el-radio-button(label="Off") el-radio-button(label="Off")
el-radio-button(label="VIP") el-radio-button(label="VIP")
el-radio-button(label="Friends") el-radio-button(label="Friends")
el-radio-button(label="Everyone") el-radio-button(label="Everyone")
//- .toggle-item
//- span.toggle-name Avatar Change
//- el-radio-group(v-model="sharedFeedFilters.wrist.AvatarChange" size="mini")
//- el-radio-button(label="Off")
//- el-radio-button(label="VIP")
//- el-radio-button(label="Friends")
//- el-radio-button(label="Everyone")
.toggle-item .toggle-item
span.toggle-name Events span.toggle-name Video Play
el-radio-group(v-model="sharedFeedFilters.wrist.Event" size="mini") el-radio-group(v-model="sharedFeedFilters.wrist.VideoPlay" size="mini")
el-radio-button(label="Off") el-radio-button(label="Off")
el-radio-button(label="On") el-radio-button(label="On")
.toggle-item .toggle-item
span.toggle-name Video Play span.toggle-name Events
el-radio-group(v-model="sharedFeedFilters.noty.VideoPlay" size="mini") el-radio-group(v-model="sharedFeedFilters.wrist.Event" size="mini")
el-radio-button(label="Off") el-radio-button(label="Off")
el-radio-button(label="On") el-radio-button(label="On")
.toggle-item .toggle-item
+275 -1
View File
@@ -1,7 +1,7 @@
import sqliteService from '../service/sqlite.js'; import sqliteService from '../service/sqlite.js';
class Database { class Database {
async init(userId) { async initUserTables(userId) {
Database.userId = userId.replaceAll('-', '').replaceAll('_', ''); Database.userId = userId.replaceAll('-', '').replaceAll('_', '');
await sqliteService.executeNonQuery( await sqliteService.executeNonQuery(
`CREATE TABLE IF NOT EXISTS ${Database.userId}_feed_gps (id INTEGER PRIMARY KEY, created_at TEXT, user_id TEXT, display_name TEXT, location TEXT, world_name TEXT, previous_location TEXT, time INTEGER)` `CREATE TABLE IF NOT EXISTS ${Database.userId}_feed_gps (id INTEGER PRIMARY KEY, created_at TEXT, user_id TEXT, display_name TEXT, location TEXT, world_name TEXT, previous_location TEXT, time INTEGER)`
@@ -21,11 +21,32 @@ class Database {
await sqliteService.executeNonQuery( await sqliteService.executeNonQuery(
`CREATE TABLE IF NOT EXISTS ${Database.userId}_friend_log_history (id INTEGER PRIMARY KEY, created_at TEXT, type TEXT, user_id TEXT, display_name TEXT, previous_display_name TEXT, trust_level TEXT, previous_trust_level TEXT)` `CREATE TABLE IF NOT EXISTS ${Database.userId}_friend_log_history (id INTEGER PRIMARY KEY, created_at TEXT, type TEXT, user_id TEXT, display_name TEXT, previous_display_name TEXT, trust_level TEXT, previous_trust_level TEXT)`
); );
await sqliteService.executeNonQuery(
`CREATE TABLE IF NOT EXISTS ${Database.userId}_notifications (id TEXT PRIMARY KEY, created_at TEXT, type TEXT, sender_user_id TEXT, sender_username TEXT, receiver_user_id TEXT, message TEXT, world_id TEXT, world_name TEXT, image_url TEXT, invite_message TEXT, request_message TEXT, response_message TEXT, expired INTEGER)`
);
await sqliteService.executeNonQuery( await sqliteService.executeNonQuery(
`CREATE TABLE IF NOT EXISTS memos (user_id TEXT PRIMARY KEY, edited_at TEXT, memo TEXT)` `CREATE TABLE IF NOT EXISTS memos (user_id TEXT PRIMARY KEY, edited_at TEXT, memo TEXT)`
); );
} }
async initTables() {
await sqliteService.executeNonQuery(
`CREATE TABLE IF NOT EXISTS gamelog_location (id INTEGER PRIMARY KEY, created_at TEXT, location TEXT, world_id TEXT, world_name TEXT, time INTEGER, UNIQUE(created_at, location))`
);
await sqliteService.executeNonQuery(
`CREATE TABLE IF NOT EXISTS gamelog_join_leave (id INTEGER PRIMARY KEY, created_at TEXT, type TEXT, display_name TEXT, location TEXT, user_id TEXT, time INTEGER, UNIQUE(created_at, type, display_name))`
);
await sqliteService.executeNonQuery(
`CREATE TABLE IF NOT EXISTS gamelog_portal_spawn (id INTEGER PRIMARY KEY, created_at TEXT, display_name TEXT, location TEXT, user_id TEXT, instance_id TEXT, world_name TEXT, UNIQUE(created_at, display_name))`
);
await sqliteService.executeNonQuery(
`CREATE TABLE IF NOT EXISTS gamelog_video_play (id INTEGER PRIMARY KEY, created_at TEXT, video_url TEXT, video_name TEXT, video_id TEXT, location TEXT, display_name TEXT, user_id TEXT, UNIQUE(created_at, video_url))`
);
await sqliteService.executeNonQuery(
`CREATE TABLE IF NOT EXISTS gamelog_event (id INTEGER PRIMARY KEY, created_at TEXT, data TEXT, UNIQUE(created_at, data))`
);
}
async getFeedDatabase() { async getFeedDatabase() {
var feedDatabase = []; var feedDatabase = [];
var date = new Date(); var date = new Date();
@@ -349,6 +370,259 @@ class Database {
} }
); );
} }
async getGamelogDatabase() {
var gamelogDatabase = [];
var date = new Date();
date.setDate(date.getDate() - 7); // 7 day limit
var dateOffset = date.toJSON();
await sqliteService.execute((dbRow) => {
var row = {
rowId: dbRow[0],
created_at: dbRow[1],
type: 'Location',
location: dbRow[2],
worldId: dbRow[3],
worldName: dbRow[4],
time: dbRow[5]
};
gamelogDatabase.unshift(row);
}, `SELECT * FROM gamelog_location WHERE created_at >= date('${dateOffset}')`);
await sqliteService.execute((dbRow) => {
var row = {
rowId: dbRow[0],
created_at: dbRow[1],
type: dbRow[2],
displayName: dbRow[3],
location: dbRow[4],
userId: dbRow[5],
time: dbRow[6]
};
gamelogDatabase.unshift(row);
}, `SELECT * FROM gamelog_join_leave WHERE created_at >= date('${dateOffset}')`);
await sqliteService.execute((dbRow) => {
var row = {
rowId: dbRow[0],
created_at: dbRow[1],
type: 'PortalSpawn',
displayName: dbRow[2],
location: dbRow[3],
userId: dbRow[4],
instanceId: dbRow[5],
worldName: dbRow[6]
};
gamelogDatabase.unshift(row);
}, `SELECT * FROM gamelog_portal_spawn WHERE created_at >= date('${dateOffset}')`);
await sqliteService.execute((dbRow) => {
var row = {
rowId: dbRow[0],
created_at: dbRow[1],
type: 'VideoPlay',
videoUrl: dbRow[2],
videoName: dbRow[3],
videoId: dbRow[4],
location: dbRow[5],
displayName: dbRow[6],
userId: dbRow[7]
};
gamelogDatabase.unshift(row);
}, `SELECT * FROM gamelog_video_play WHERE created_at >= date('${dateOffset}')`);
await sqliteService.execute((dbRow) => {
var row = {
rowId: dbRow[0],
created_at: dbRow[1],
type: 'Event',
data: dbRow[2]
};
gamelogDatabase.unshift(row);
}, `SELECT * FROM gamelog_event WHERE created_at >= date('${dateOffset}')`);
var compareByCreatedAt = function (a, b) {
var A = a.created_at;
var B = b.created_at;
if (A < B) {
return -1;
}
if (A > B) {
return 1;
}
return 0;
};
gamelogDatabase.sort(compareByCreatedAt);
return gamelogDatabase;
}
addGamelogLocationToDatabase(entry) {
sqliteService.executeNonQuery(
`INSERT OR IGNORE INTO gamelog_location (created_at, location, world_id, world_name, time) VALUES (@created_at, @location, @world_id, @world_name, @time)`,
{
'@created_at': entry.created_at,
'@location': entry.location,
'@world_id': entry.worldId,
'@world_name': entry.worldName,
'@time': entry.time
}
);
}
updateGamelogLocationTimeToDatabase(entry) {
sqliteService.executeNonQuery(
`UPDATE gamelog_location SET time = @time WHERE created_at = @created_at`,
{
'@created_at': entry.created_at,
'@time': entry.time
}
);
}
addGamelogJoinLeaveToDatabase(entry) {
sqliteService.executeNonQuery(
`INSERT OR IGNORE INTO gamelog_join_leave (created_at, type, display_name, location, user_id, time) VALUES (@created_at, @type, @display_name, @location, @user_id, @time)`,
{
'@created_at': entry.created_at,
'@type': entry.type,
'@display_name': entry.displayName,
'@location': entry.location,
'@user_id': entry.userId,
'@time': entry.time
}
);
}
addGamelogPortalSpawnToDatabase(entry) {
sqliteService.executeNonQuery(
`INSERT OR IGNORE INTO gamelog_portal_spawn (created_at, display_name, location, user_id, instance_id, world_name) VALUES (@created_at, @display_name, @location, @user_id, @instance_id, @world_name)`,
{
'@created_at': entry.created_at,
'@display_name': entry.displayName,
'@location': entry.location,
'@user_id': entry.userId,
'@instance_id': entry.instanceId,
'@world_name': entry.worldName
}
);
}
addGamelogVideoPlayToDatabase(entry) {
sqliteService.executeNonQuery(
`INSERT OR IGNORE INTO gamelog_video_play (created_at, video_url, video_name, video_id, location, display_name, user_id) VALUES (@created_at, @video_url, @video_name, @video_id, @location, @display_name, @user_id)`,
{
'@created_at': entry.created_at,
'@video_url': entry.videoUrl,
'@video_name': entry.videoName,
'@video_id': entry.videoId,
'@location': entry.location,
'@display_name': entry.displayName,
'@user_id': entry.userId
}
);
}
addGamelogEventToDatabase(entry) {
sqliteService.executeNonQuery(
`INSERT OR IGNORE INTO gamelog_event (created_at, data) VALUES (@created_at, @data)`,
{
'@created_at': entry.created_at,
'@data': entry.data
}
);
}
async getNotifications() {
var notifications = [];
await sqliteService.execute((dbRow) => {
var row = {
id: dbRow[0],
created_at: dbRow[1],
type: dbRow[2],
senderUserId: dbRow[3],
senderUsername: dbRow[4],
receiverUserId: dbRow[5],
message: dbRow[6],
details: {
worldId: dbRow[7],
worldName: dbRow[8],
imageUrl: dbRow[9],
inviteMessage: dbRow[10],
requestMessage: dbRow[11],
responseMessage: dbRow[12]
}
};
row.$isExpired = false;
if (dbRow[13] === 1) {
row.$isExpired = true;
}
notifications.unshift(row);
}, `SELECT * FROM ${Database.userId}_notifications LIMIT 5000`);
return notifications;
}
addNotificationToDatabase(row) {
var entry = {
id: '',
created_at: '',
type: '',
senderUserId: '',
senderUsername: '',
receiverUserId: '',
message: '',
...row,
details: {
worldId: '',
worldName: '',
imageUrl: '',
inviteMessage: '',
requestMessage: '',
responseMessage: '',
...row.details
}
};
var expired = 0;
if (row.$isExpired) {
expired = 1;
}
sqliteService.executeNonQuery(
`INSERT OR IGNORE INTO ${Database.userId}_notifications (id, created_at, type, sender_user_id, sender_username, receiver_user_id, message, world_id, world_name, image_url, invite_message, request_message, response_message, expired) VALUES (@id, @created_at, @type, @sender_user_id, @sender_username, @receiver_user_id, @message, @world_id, @world_name, @image_url, @invite_message, @request_message, @response_message, @expired)`,
{
'@id': entry.id,
'@created_at': entry.created_at,
'@type': entry.type,
'@sender_user_id': entry.senderUserId,
'@sender_username': entry.senderUsername,
'@receiver_user_id': entry.receiverUserId,
'@message': entry.message,
'@world_id': entry.details.worldId,
'@world_name': entry.details.worldName,
'@image_url': entry.details.imageUrl,
'@invite_message': entry.details.inviteMessage,
'@request_message': entry.details.requestMessage,
'@response_message': entry.details.responseMessage,
'@expired': expired
}
);
}
deleteNotification(rowId) {
sqliteService.executeNonQuery(
`DELETE FROM ${Database.userId}_notifications WHERE id = @row_id`,
{
'@row_id': rowId
}
);
}
updateNotificationExpired(entry) {
var expired = 0;
if (entry.$isExpired) {
expired = 1;
}
sqliteService.executeNonQuery(
`UPDATE ${Database.userId}_notifications SET expired = @expired WHERE id = @id`,
{
'@id': entry.id,
'@expired': expired
}
);
}
} }
var self = new Database(); var self = new Database();
+62 -73
View File
@@ -1,93 +1,82 @@
// requires binding of LogWatcher // requires binding of LogWatcher
// <string, object>
var contextMap = new Map();
function parseRawGameLog(dt, type, args) {
var gameLog = {
dt,
type
};
switch (type) {
case 'location':
gameLog.location = args[0];
gameLog.worldName = args[1];
break;
case 'player-joined':
gameLog.userDisplayName = args[0];
gameLog.userType = args[1];
break;
case 'player-left':
gameLog.userDisplayName = args[0];
break;
case 'notification':
gameLog.json = args[0];
break;
case 'portal-spawn':
gameLog.userDisplayName = args[0];
break;
case 'event':
gameLog.event = args[0];
break;
case 'video-play':
gameLog.videoURL = args[0];
gameLog.displayName = args[1];
break;
default:
break;
}
return gameLog;
}
class GameLogService { class GameLogService {
async poll() { parseRawGameLog(dt, type, args) {
var rawGameLogs = await LogWatcher.Get(); var gameLog = {
var gameLogs = []; dt,
var now = Date.now(); type
};
for (var [fileName, dt, type, ...args] of rawGameLogs) { switch (type) {
var context = contextMap.get(fileName); case 'location':
if (typeof context === 'undefined') { gameLog.location = args[0];
context = { gameLog.worldName = args[1];
updatedAt: null, break;
// location case 'location-destination':
location: null gameLog.location = args[0];
}; break;
contextMap.set(fileName, context);
}
var gameLog = parseRawGameLog(dt, type, args); case 'player-joined':
gameLog.userDisplayName = args[0];
gameLog.userType = args[1];
break;
switch (gameLog.type) { case 'player-left':
case 'location': gameLog.userDisplayName = args[0];
context.location = gameLog.location; break;
break;
default: case 'notification':
break; gameLog.json = args[0];
} break;
context.updatedAt = now; case 'portal-spawn':
gameLog.userDisplayName = args[0];
break;
gameLogs.push(gameLog); case 'event':
gameLog.event = args[0];
break;
case 'video-play':
gameLog.videoUrl = args[0];
gameLog.displayName = args[1];
break;
case 'vrcx':
gameLog.data = args[0];
break;
default:
break;
} }
return gameLog;
}
async getAll() {
var gameLogs = [];
var done = false;
while (!done) {
var rawGameLogs = await LogWatcher.Get();
// eslint-disable-next-line no-unused-vars
for (var [fileName, dt, type, ...args] of rawGameLogs) {
var gameLog = this.parseRawGameLog(dt, type, args);
gameLogs.push(gameLog);
}
if (rawGameLogs.length === 0) {
done = true;
}
}
return gameLogs; return gameLogs;
} }
async setDateTill(dateTill) {
await LogWatcher.SetDateTill(dateTill);
}
async reset() { async reset() {
await LogWatcher.Reset(); await LogWatcher.Reset();
contextMap.clear();
} }
} }
+221 -789
View File
File diff suppressed because it is too large Load Diff
+70 -18
View File
@@ -36,17 +36,23 @@ html
.detail .detail
span.extra span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }} span.time {{ feed.created_at | formatDate('HH:MI') }}
| #[span.name(v-text="feed.displayName")] #[i.x-user-status(:class="statusClass(feed.status)")] {{feed.statusDescription}} | #[span.name(v-text="feed.displayName")]
template(v-if="feed.statusDescription === feed.previousStatusDescription")
i.x-user-status(:class="statusClass(feed.previousStatus)")
i.el-icon-right
i.x-user-status(:class="statusClass(feed.status)")
template(v-else)
| #[i.x-user-status(:class="statusClass(feed.status)")] {{feed.statusDescription}}
div(v-else-if="feed.type === 'OnPlayerJoined'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }") div(v-else-if="feed.type === 'OnPlayerJoined'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail .detail
span.extra span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }} span.time {{ feed.created_at | formatDate('HH:MI') }}
| ▶️ #[span.name(v-text="feed.data")] | ▶️ #[span.name(v-text="feed.displayName")]
div(v-else-if="feed.type === 'OnPlayerLeft'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }") div(v-else-if="feed.type === 'OnPlayerLeft'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail .detail
span.extra span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }} span.time {{ feed.created_at | formatDate('HH:MI') }}
| ◀️ #[span.name(v-text="feed.data")] | ◀️ #[span.name(v-text="feed.displayName")]
div(v-else-if="feed.type === 'OnPlayerJoining'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }") div(v-else-if="feed.type === 'OnPlayerJoining'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail .detail
span.extra span.extra
@@ -57,7 +63,16 @@ html
.detail .detail
span.extra span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }} span.time {{ feed.created_at | formatDate('HH:MI') }}
location(:location="feed.data[0]" :hint="feed.data[1]") location(:location="feed.location" :hint="feed.worldName")
div(v-else-if="feed.type === 'VideoPlay'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail
span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }}
| 🎵 #[span.name(v-text="feed.displayName")]
template(v-if="feed.videoName")
| #[span(v-text="feed.videoName")]
template(v-else)
| #[span(v-text="feed.videoUrl")]
div(v-else-if="feed.type === 'invite'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }") div(v-else-if="feed.type === 'invite'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail .detail
span.extra span.extra
@@ -107,7 +122,16 @@ html
.detail .detail
span.extra span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }} span.time {{ feed.created_at | formatDate('HH:MI') }}
| ✨ #[span.name(v-text="feed.data")] | ✨ #[span.name(v-text="feed.displayName")]
template(v-if="feed.worldName")
| #[location(:location="feed.instanceId" :hint="feed.worldName")]
div(v-else-if="feed.type === 'AvatarChange'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail
span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }}
| 🧍 #[span.name(v-text="feed.displayName")] {{ feed.name }}
template(v-if="feed.description && feed.description !== feed.name")
| - {{ feed.description }}
div(v-else-if="feed.type === 'Event'" class="x-friend-item") div(v-else-if="feed.type === 'Event'" class="x-friend-item")
.detail .detail
span.extra span.extra
@@ -159,17 +183,23 @@ html
.detail .detail
span.extra span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }} span.time {{ feed.created_at | formatDate('HH:MI') }}
| #[span.name(v-text="feed.displayName")] is #[i.x-user-status(:class="statusClass(feed.status)")] {{feed.statusDescription}} | #[span.name(v-text="feed.displayName")]
template(v-if="feed.statusDescription === feed.previousStatusDescription")
i.x-user-status(:class="statusClass(feed.previousStatus)")
i.el-icon-right
i.x-user-status(:class="statusClass(feed.status)")
template(v-else)
| #[i.x-user-status(:class="statusClass(feed.status)")] {{feed.statusDescription}}
div(v-else-if="feed.type === 'OnPlayerJoined'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }") div(v-else-if="feed.type === 'OnPlayerJoined'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail .detail
span.extra span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }} span.time {{ feed.created_at | formatDate('HH:MI') }}
| #[span.name(v-text="feed.data")] has joined | #[span.name(v-text="feed.displayName")] has joined
div(v-else-if="feed.type === 'OnPlayerLeft'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }") div(v-else-if="feed.type === 'OnPlayerLeft'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail .detail
span.extra span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }} span.time {{ feed.created_at | formatDate('HH:MI') }}
| #[span.name(v-text="feed.data")] has left | #[span.name(v-text="feed.displayName")] has left
div(v-else-if="feed.type === 'OnPlayerJoining'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }") div(v-else-if="feed.type === 'OnPlayerJoining'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail .detail
span.extra span.extra
@@ -179,7 +209,16 @@ html
.detail .detail
span.extra span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }} span.time {{ feed.created_at | formatDate('HH:MI') }}
location(:location="feed.data[0]" :hint="feed.data[1]") location(:location="feed.location" :hint="feed.worldName")
div(v-else-if="feed.type === 'VideoPlay'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail
span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }}
| #[span.name(v-text="feed.displayName")] changed video to
template(v-if="feed.videoName")
| #[span(v-text="feed.videoName")]
template(v-else)
| #[span(v-text="feed.videoUrl")]
div(v-else-if="feed.type === 'invite'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }") div(v-else-if="feed.type === 'invite'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail .detail
span.extra span.extra
@@ -229,7 +268,16 @@ html
.detail .detail
span.extra span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }} span.time {{ feed.created_at | formatDate('HH:MI') }}
| #[span.name(v-text="feed.data")] has spawned a portal | #[span.name(v-text="feed.displayName")] has spawned a portal
template(v-if="feed.worldName")
| to #[location(:location="feed.instanceId" :hint="feed.worldName")]
div(v-else-if="feed.type === 'AvatarChange'" class="x-friend-item" :class="{ friend: feed.isFriend, favorite: feed.isFavorite }")
.detail
span.extra
span.time {{ feed.created_at | formatDate('HH:MI') }}
| #[span.name(v-text="feed.displayName")] changed into avatar {{ feed.name }}
template(v-if="feed.description && feed.description !== feed.name")
| - {{ feed.description }}
div(v-else-if="feed.type === 'Event'" class="x-friend-item") div(v-else-if="feed.type === 'Event'" class="x-friend-item")
.detail .detail
span.extra span.extra
@@ -301,21 +349,25 @@ html
br br
span {{ device[2] }}% span {{ device[2] }}%
.x-containerbottom .x-containerbottom
template(v-if="nowPlaying.playing")
span(style="float:right;padding-left:10px") {{ nowPlaying.remainingText }}
marquee-text {{ nowPlaying.name }}
div.np-progress-bar(:style="{ width: nowPlaying.percentage + '%' }")
template(v-if="config && config.minimalFeed") template(v-if="config && config.minimalFeed")
template(v-if="downloadProgress === 100") template(v-if="config.downloadProgress === 100")
span(style="display:inline-block;margin-right:5px") #[i.el-icon-loading] span(style="display:inline-block;margin-right:5px") #[i.el-icon-loading]
template(v-else-if="downloadProgress > 0") template(v-else-if="config.downloadProgress > 0")
span(style="display:inline-block;margin-right:5px") {{ downloadProgress }}% span(style="display:inline-block;margin-right:5px") {{ config.downloadProgress }}%
template(v-if="lastLocation.date != 0") template(v-if="lastLocation.date !== 0")
span(style="float:right") {{ lastLocationTimer }} span(style="float:right") {{ lastLocationTimer }}
span(style="display:inline-block") {{ lastLocation.playerList.length }} span(style="display:inline-block") {{ lastLocation.playerList.length }}
span(style="display:inline-block;font-weight:bold") {{ lastLocation.friendList.length !== 0 ? ` (${lastLocation.friendList.length})` : ''}} span(style="display:inline-block;font-weight:bold") {{ lastLocation.friendList.length !== 0 ? ` (${lastLocation.friendList.length})` : ''}}
template(v-else) template(v-else)
template(v-if="downloadProgress === 100") template(v-if="config.downloadProgress === 100")
span(style="display:inline-block;margin-right:5px") Downloading: #[i.el-icon-loading] span(style="display:inline-block;margin-right:5px") Downloading: #[i.el-icon-loading]
template(v-else-if="downloadProgress > 0") template(v-else-if="config.downloadProgress > 0")
span(style="display:inline-block;margin-right:5px") Downloading: {{ downloadProgress }}% span(style="display:inline-block;margin-right:5px") Downloading: {{ config.downloadProgress }}%
template(v-if="lastLocation.date != 0") template(v-if="lastLocation.date !== 0")
span(style="float:right") Timer: {{ lastLocationTimer }} span(style="float:right") Timer: {{ lastLocationTimer }}
span(style="display:inline-block") Players: {{ lastLocation.playerList.length }} span(style="display:inline-block") Players: {{ lastLocation.playerList.length }}
span(style="display:inline-block;font-weight:bold") {{ lastLocation.friendList.length !== 0 ? ` (${lastLocation.friendList.length})` : ''}} span(style="display:inline-block;font-weight:bold") {{ lastLocation.friendList.length !== 0 ? ` (${lastLocation.friendList.length})` : ''}}
+21 -6
View File
@@ -22,9 +22,6 @@
.noty_body { .noty_body {
display: block; display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
.noty_layout { .noty_layout {
@@ -34,6 +31,7 @@
.noty_theme__relax.noty_bar, .noty_theme__relax.noty_bar,
.noty_theme__sunset.noty_bar { .noty_theme__sunset.noty_bar {
height: 42px;
position: relative; position: relative;
margin: 4px 0; margin: 4px 0;
overflow: hidden; overflow: hidden;
@@ -42,9 +40,7 @@
.noty_theme__relax.noty_bar .noty_body, .noty_theme__relax.noty_bar .noty_body,
.noty_theme__sunset.noty_bar .noty_body { .noty_theme__sunset.noty_bar .noty_body {
padding: 5px 10px 10px;
font-size: 15px; font-size: 15px;
text-align: center;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1); text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);
} }
@@ -143,6 +139,19 @@
opacity: 0.6; opacity: 0.6;
} }
.noty-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 8px 8px 0 11px;
}
.noty-img {
height: 42px;
float: left;
border-radius: 4px;
}
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 8px; width: 8px;
height: 8px; height: 8px;
@@ -202,16 +211,22 @@ button {
} }
.x-containerbottom span { .x-containerbottom span {
padding: 0px;
display: block; display: block;
overflow: hidden; overflow: hidden;
} }
.np-progress-bar {
width: 0%;
height: 2px;
background-color: white;
}
.x-friend-item { .x-friend-item {
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
align-items: center; align-items: center;
font-size: 18px; font-size: 18px;
height: 27.1px;
} }
.x-friend-item .time { .x-friend-item .time {