11using System . Diagnostics ;
2+ using System . IO ;
3+ using System . Reflection ;
24using System . Windows ;
35using DiscordRPC ;
46using DiscordRPC . Logging ;
@@ -13,14 +15,15 @@ public class YmmRpcPlugin : IPlugin, IDisposable
1315{
1416 public string Name => "YMM-RPC" ;
1517
16- private static readonly string ClientId = IsLiteEdition ( ) ? "1455192227734098075" : "1353376132732420136" ;
17- private const string Version = "0.3.0" ;
18+ private const string ClientIdLite = "1455192227734098075" ;
19+ private const string ClientIdNormal = "1353376132732420136" ;
20+ private const string Version = "0.3.1" ;
1821 private const int UpdateIntervalMs = 15000 ;
1922
2023 private static DiscordRpcClient ? _client ;
2124 private static Timer ? _updateTimer ;
2225 private static DateTime _startTime ;
23- private static volatile bool _updateRequested ;
26+ private static bool ? _isLiteEdition ;
2427 private bool _disposed ;
2528
2629 public YmmRpcPlugin ( )
@@ -34,7 +37,9 @@ private static void InitializeClient()
3437 {
3538 if ( _client is { IsDisposed : false } ) return ;
3639
37- _client = new DiscordRpcClient ( ClientId )
40+ var clientId = GetIsLiteEdition ( ) ? ClientIdLite : ClientIdNormal ;
41+
42+ _client = new DiscordRpcClient ( clientId )
3843 {
3944 Logger = new ConsoleLogger { Level = LogLevel . Warning }
4045 } ;
@@ -43,21 +48,34 @@ private static void InitializeClient()
4348 _client . OnError += ( _ , e ) => Console . WriteLine ( $ "[YMM-RPC] Error: { e . Message } ") ;
4449
4550 _client . Initialize ( ) ;
46- UpdatePresence ( ) ;
4751 }
4852
4953 private static void StartUpdateTimer ( )
5054 {
5155 _updateTimer ? . Dispose ( ) ;
5256 _updateTimer = new Timer ( _ =>
5357 {
54- if ( ! _updateRequested ) return ;
55- _updateRequested = false ;
56- UpdatePresence ( ) ;
58+ SafeUpdatePresence ( ) ;
5759 } , null , UpdateIntervalMs , UpdateIntervalMs ) ;
5860 }
5961
60- public static void RequestUpdate ( ) => _updateRequested = true ;
62+ private static void SafeUpdatePresence ( )
63+ {
64+ try
65+ {
66+ var dispatcher = Application . Current ? . Dispatcher ;
67+ if ( dispatcher == null )
68+ {
69+ UpdatePresence ( ) ;
70+ return ;
71+ }
72+ dispatcher . Invoke ( UpdatePresence ) ;
73+ }
74+ catch ( Exception ex )
75+ {
76+ Console . WriteLine ( $ "[YMM-RPC] Update error: { ex . Message } ") ;
77+ }
78+ }
6179
6280 private static void UpdatePresence ( )
6381 {
@@ -71,19 +89,77 @@ private static void UpdatePresence()
7189 return ;
7290 }
7391
74- var presence = settings . CustomRpcEnabled
75- ? BuildCustomPresence ( settings )
92+ var presence = settings . CustomRpcEnabled
93+ ? BuildCustomPresence ( settings )
7694 : BuildDefaultPresence ( ) ;
7795
7896 _client . SetPresence ( presence ) ;
7997 }
8098
99+ private static string ? GetCurrentProjectName ( )
100+ {
101+ try
102+ {
103+ var mainWindow = Application . Current ? . Windows
104+ . OfType < Window > ( )
105+ . FirstOrDefault ( w =>
106+ string . Equals (
107+ w . GetType ( ) . FullName ,
108+ "YukkuriMovieMaker.Views.MainView" ,
109+ StringComparison . Ordinal ) ) ;
110+
111+ if ( mainWindow ? . DataContext == null ) return null ;
112+
113+ var vmType = mainWindow . DataContext . GetType ( ) ;
114+ var filePathProp = vmType . GetProperty ( "ProjectFilePath" , BindingFlags . Public | BindingFlags . Instance ) ;
115+
116+ if ( filePathProp == null ) return null ;
117+
118+ var filePathValue = filePathProp . GetValue ( mainWindow . DataContext ) ;
119+ var filePath = ExtractValue < string > ( filePathValue ) ;
120+
121+ return string . IsNullOrEmpty ( filePath ) ? null : Path . GetFileNameWithoutExtension ( filePath ) ;
122+ }
123+ catch
124+ {
125+ return null ;
126+ }
127+ }
128+
129+ private static T ? ExtractValue < T > ( object ? obj )
130+ {
131+ if ( obj == null ) return default ;
132+ if ( obj is T directValue ) return directValue ;
133+
134+ var valueProperty = obj . GetType ( ) . GetProperty ( "Value" , BindingFlags . Public | BindingFlags . Instance ) ;
135+ if ( valueProperty == null ) return default ;
136+
137+ var innerValue = valueProperty . GetValue ( obj ) ;
138+ if ( innerValue is T typedValue ) return typedValue ;
139+ return innerValue != null ? ExtractValue < T > ( innerValue ) : default ;
140+ }
141+
81142 private static RichPresence BuildDefaultPresence ( )
82143 {
144+ var settings = YmmRpcSettings . Default ;
145+ var isLite = GetIsLiteEdition ( ) ;
146+
147+ string details ;
148+ if ( settings . IsShowProject )
149+ {
150+ var projectName = GetCurrentProjectName ( ) ;
151+ var displayName = projectName ?? "無題" ;
152+ details = $ "{ displayName } .ymmp を編集中...";
153+ }
154+ else
155+ {
156+ details = "動画を編集中..." ;
157+ }
158+
83159 return new RichPresence
84160 {
85- Details = "動画を編集中..." ,
86- State = IsLiteEdition ( ) ? "Working on YMM4 Lite" : "Working on YMM4" ,
161+ Details = details ,
162+ State = isLite ? "Working on YMM4 Lite" : "Working on YMM4" ,
87163 Assets = new Assets
88164 {
89165 LargeImageKey = "icon" ,
@@ -95,31 +171,39 @@ private static RichPresence BuildDefaultPresence()
95171
96172 private static RichPresence BuildCustomPresence ( YmmRpcSettings settings )
97173 {
174+ var projectName = GetCurrentProjectName ( ) ;
175+ var displayName = projectName != null ? $ "{ projectName } .ymmp" : "無題.ymmp" ;
176+
177+ var details = ReplacePlaceholders ( settings . CustomRpcDetails , displayName ) ;
178+ var state = ReplacePlaceholders ( settings . CustomRpcState , displayName ) ;
179+ var largeImageText = ReplacePlaceholders ( settings . CustomRpcLargeImageText , displayName ) ;
180+ var smallImageText = ReplacePlaceholders ( settings . CustomRpcSmallImageText , displayName ) ;
181+
98182 var presence = new RichPresence
99183 {
100- Details = NullIfEmpty ( settings . CustomRpcDetails ) ,
101- State = NullIfEmpty ( settings . CustomRpcState ) ,
184+ Details = NullIfEmpty ( details ) ,
185+ State = NullIfEmpty ( state ) ,
102186 Assets = new Assets
103187 {
104188 LargeImageKey = string . IsNullOrEmpty ( settings . CustomRpcLargeImageKey ) ? "icon" : settings . CustomRpcLargeImageKey ,
105- LargeImageText = NullIfEmpty ( settings . CustomRpcLargeImageText ) ,
189+ LargeImageText = NullIfEmpty ( largeImageText ) ,
106190 SmallImageKey = NullIfEmpty ( settings . CustomRpcSmallImageKey ) ,
107- SmallImageText = NullIfEmpty ( settings . CustomRpcSmallImageText )
191+ SmallImageText = NullIfEmpty ( smallImageText )
108192 } ,
109193 Timestamps = new Timestamps { Start = _startTime }
110194 } ;
111195
112196 if ( ! settings . CustomRpcEnableButtons ) return presence ;
113-
197+
114198 var buttons = new List < Button > ( 2 ) ;
115-
116- if ( ! string . IsNullOrEmpty ( settings . CustomRpcButton1Label ) &&
199+
200+ if ( ! string . IsNullOrEmpty ( settings . CustomRpcButton1Label ) &&
117201 ! string . IsNullOrEmpty ( settings . CustomRpcButton1Url ) )
118202 {
119203 buttons . Add ( new Button { Label = settings . CustomRpcButton1Label , Url = settings . CustomRpcButton1Url } ) ;
120204 }
121-
122- if ( ! string . IsNullOrEmpty ( settings . CustomRpcButton2Label ) &&
205+
206+ if ( ! string . IsNullOrEmpty ( settings . CustomRpcButton2Label ) &&
123207 ! string . IsNullOrEmpty ( settings . CustomRpcButton2Url ) )
124208 {
125209 buttons . Add ( new Button { Label = settings . CustomRpcButton2Label , Url = settings . CustomRpcButton2Url } ) ;
@@ -131,8 +215,42 @@ private static RichPresence BuildCustomPresence(YmmRpcSettings settings)
131215 return presence ;
132216 }
133217
218+ private static string ? ReplacePlaceholders ( string ? input , string displayName )
219+ {
220+ return string . IsNullOrEmpty ( input ) ? input : input . Replace ( "{project}" , displayName ) ;
221+ }
222+
134223 private static string ? NullIfEmpty ( string ? s ) => string . IsNullOrEmpty ( s ) ? null : s ;
135224
225+ private static bool GetIsLiteEdition ( )
226+ {
227+ if ( _isLiteEdition . HasValue ) return _isLiteEdition . Value ;
228+
229+ var exeName = Process . GetCurrentProcess ( ) . MainModule ? . FileName ?? "" ;
230+ if ( exeName . Contains ( "Lite" , StringComparison . OrdinalIgnoreCase ) )
231+ {
232+ _isLiteEdition = true ;
233+ return true ;
234+ }
235+
236+ try
237+ {
238+ var result = Application . Current ? . Dispatcher ? . Invoke ( ( ) =>
239+ {
240+ var title = Application . Current ? . MainWindow ? . Title ?? "" ;
241+ return title . Contains ( "Lite" , StringComparison . OrdinalIgnoreCase ) ;
242+ } ) ?? false ;
243+
244+ _isLiteEdition = result ;
245+ return result ;
246+ }
247+ catch
248+ {
249+ _isLiteEdition = false ;
250+ return false ;
251+ }
252+ }
253+
136254 public void Dispose ( )
137255 {
138256 if ( _disposed ) return ;
@@ -150,14 +268,4 @@ public void Dispose()
150268
151269 GC . SuppressFinalize ( this ) ;
152270 }
153-
154- private static bool IsLiteEdition ( )
155- {
156- var exeName = Process . GetCurrentProcess ( ) . MainModule ? . FileName ?? "" ;
157- if ( exeName . Contains ( "Lite" , StringComparison . OrdinalIgnoreCase ) )
158- return true ;
159-
160- var title = Application . Current ? . MainWindow ? . Title ?? "" ;
161- return title . Contains ( "Lite" , StringComparison . OrdinalIgnoreCase ) ;
162- }
163271}
0 commit comments