66
77#include " winrt\Windows.Web.Http.Headers.h"
88#include < ranges>
9- #include < winrt\Windows.Web.Http.h>
109
1110#include " CoreFfi.h"
1211#include " CoreProxy.h"
@@ -41,10 +40,25 @@ namespace winrt::YtFlowApp::implementation
4140 auto conn = FfiDbInstance.Connect ();
4241 auto const proxyGroups = conn.GetProxyGroups ();
4342 std::vector<YtFlowApp::ProxyGroupModel> proxyGroupModels;
43+ std::vector<std::pair<ProxyGroupModel *, FfiProxyGroupSubscription>> subscriptionInfoToAttach;
4444 proxyGroupModels.reserve (proxyGroups.size ());
45+ subscriptionInfoToAttach.reserve (proxyGroups.size ());
4546 std::transform (proxyGroups.begin (), proxyGroups.end (), std::back_inserter (proxyGroupModels),
46- [](auto const &group) { return make<ProxyGroupModel>(group); });
47+ [&conn, &subscriptionInfoToAttach](auto const &group) {
48+ auto ret = make<ProxyGroupModel>(group);
49+ if (group.type == " subscription" )
50+ {
51+ subscriptionInfoToAttach.emplace_back (
52+ std::make_pair (get_self<ProxyGroupModel>(ret),
53+ conn.GetProxySubscriptionByProxyGroup (group.id )));
54+ }
55+ return ret;
56+ });
4757 co_await resume_foreground (Dispatcher ());
58+ for (auto &&[model, subscriptionInfo] : subscriptionInfoToAttach)
59+ {
60+ model->AttachSubscriptionInfo (subscriptionInfo);
61+ }
4862 m_model->ProxyGroups (single_threaded_observable_vector (std::move (proxyGroupModels)));
4963 }
5064 catch (...)
@@ -72,6 +86,27 @@ namespace winrt::YtFlowApp::implementation
7286 }
7387 }
7488
89+ void LibraryPage::Page_Loaded (IInspectable const &, RoutedEventArgs const &)
90+ {
91+ ProxySubscriptionUpdatesRunning$.get_observable ()
92+ .scan (std::make_pair (0 , 0 ),
93+ [](auto prev, int curr) { return std::make_pair (prev.second , prev.second + curr); })
94+ .filter ([](auto change) { return change.first == 0 || change.second == 0 ; })
95+ .subscribe ([weak{get_weak ()}](auto change) {
96+ if (auto const self = weak.get ())
97+ {
98+ if (change.first == 0 )
99+ {
100+ self->SyncSubscriptionButtonRunStoryboard ().Begin ();
101+ }
102+ else
103+ {
104+ self->SyncSubscriptionButtonRunStoryboard ().Stop ();
105+ }
106+ }
107+ });
108+ }
109+
75110 fire_and_forget LibraryPage::ProxyGroupItemDelete_Click (IInspectable const &sender, RoutedEventArgs const &e)
76111 {
77112 try
@@ -178,66 +213,84 @@ namespace winrt::YtFlowApp::implementation
178213 }
179214 }
180215
216+ Windows::Web::Http::HttpClient LibraryPage::GetHttpClientForSubscription ()
217+ {
218+ static Windows::Web::Http::HttpClient client{nullptr };
219+ if (client == nullptr )
220+ {
221+ client = Windows::Web::Http::HttpClient ();
222+ client.DefaultRequestHeaders ().UserAgent ().Clear ();
223+ client.DefaultRequestHeaders ().UserAgent ().ParseAdd (L" YtFlowApp/0.0 SubscriptionUpdater/0.0" );
224+ }
225+ return client;
226+ }
227+ IAsyncAction LibraryPage::DownloadSubscriptionProxies (Windows::Web::Http::HttpClient client, Uri uri,
228+ char const *format,
229+ std::shared_ptr<SubscriptionDownloadDecodeResult> result)
230+ {
231+ auto const res = (co_await client.GetAsync (uri)).EnsureSuccessStatusCode ();
232+ auto const userinfoHeader = res.Headers ().TryLookup (L" subscription-userinfo" );
233+ DecodedSubscriptionUserInfo userinfo{};
234+ if (userinfoHeader.has_value ())
235+ {
236+ userinfo = DecodeSubscriptionUserInfoFromResponseHeaderValue (to_string (*userinfoHeader));
237+ }
238+
239+ auto const resStr = to_string (co_await res.Content ().ReadAsStringAsync ());
240+ auto proxies = DecodeSubscriptionProxies (resStr, format);
241+ if (!proxies.has_value () || format == nullptr )
242+ {
243+ throw hresult_invalid_argument (L" The subscription data contains no valid proxy." );
244+ }
245+ *result = SubscriptionDownloadDecodeResult{.proxies = std::move (proxies).value (),
246+ .format = format,
247+ .userinfo = std::move (userinfo),
248+ .expiresAt = nullptr };
249+ if (result->userinfo .expires_at .has_value ())
250+ {
251+ result->expiresAt = userinfo.expires_at ->c_str ();
252+ }
253+ }
181254 fire_and_forget LibraryPage::CreateSubscriptionButton_Click (IInspectable const &, RoutedEventArgs const &)
182255 {
183256 try
184257 {
185258 auto const lifetime = get_strong ();
186259 ProxyGroupAddSubscriptionError ().Text (L" " );
187260 ProxyGroupAddSubscriptionError ().Visibility (Visibility::Collapsed);
261+ auto const client = GetHttpClientForSubscription ();
188262 while (co_await ProxyGroupAddSubscriptionDialog ().ShowAsync () == ContentDialogResult::Primary)
189263 {
190264 auto const url = ProxyGroupAddSubscriptionUrlText ().Text ();
191265 std::optional<hstring> errMsg = std::nullopt ;
266+ lifetime->ProxySubscriptionUpdatesRunning $.get_subscriber ().on_next (1 );
192267
193268 try
194269 {
195270 Uri const uri{url};
196- // TODO:
197-
198- static Windows::Web::Http::HttpClient client{};
199- client.DefaultRequestHeaders ().UserAgent ().Clear ();
200- client.DefaultRequestHeaders ().UserAgent ().ParseAdd (L" YtFlowApp/0.0 SubscriptionUpdater/0.0" );
201- auto const res = (co_await client.GetAsync (uri)).EnsureSuccessStatusCode ();
202- auto const userinfoHeader = res.Headers ().TryLookup (L" subscription-userinfo" );
203- DecodedSubscriptionUserInfo userinfo{};
204- if (userinfoHeader.has_value ())
205- {
206- userinfo = DecodeSubscriptionUserInfoFromResponseHeaderValue (to_string (*userinfoHeader));
207- }
208- // TODO: total, not remaining
209-
210- auto const resStr = to_string (co_await res.Content ().ReadAsStringAsync ());
211- char const *format{nullptr };
212- auto const proxies = DecodeSubscriptionProxies (resStr, format);
213- if (!proxies.has_value () || format == nullptr )
214- {
215- throw hresult_invalid_argument (L" The subscription data contains no valid proxy." );
216- }
271+ auto const res = std::make_shared<SubscriptionDownloadDecodeResult>();
272+ co_await DownloadSubscriptionProxies (client, uri, nullptr , res);
217273
218274 co_await resume_background ();
219275 auto conn = FfiDbInstance.Connect ();
220- auto const newGroupId = conn.CreateProxySubscriptionGroup (to_string (uri.Domain ()).c_str (), format,
221- to_string (url).c_str ());
222- conn.BatchUpdateProxyInGroup (newGroupId, proxies->data (), proxies->size ());
223- char const *expiresAt{nullptr };
224- if (userinfo.expires_at .has_value ())
225- {
226- expiresAt = userinfo.expires_at ->c_str ();
227- }
228- conn.UpdateProxySubscriptionRetrievedByProxyGroup (newGroupId, userinfo.upload_bytes_used ,
229- userinfo.download_bytes_used ,
230- userinfo.bytes_total , expiresAt);
231- auto const newGroupModel = make<ProxyGroupModel>(conn.GetProxyGroupById (newGroupId),
232- conn.GetProxySubscriptionByProxyGroup (newGroupId));
276+ auto const newGroupId = conn.CreateProxySubscriptionGroup (to_string (uri.Domain ()).c_str (),
277+ res->format , to_string (url).c_str ());
278+ conn.BatchUpdateProxyInGroup (newGroupId, res->proxies .data (), res->proxies .size ());
279+ conn.UpdateProxySubscriptionRetrievedByProxyGroup (newGroupId, res->userinfo .upload_bytes_used ,
280+ res->userinfo .download_bytes_used ,
281+ res->userinfo .bytes_total , res->expiresAt );
282+ auto const newGroupModel = make<ProxyGroupModel>(conn.GetProxyGroupById (newGroupId));
233283 co_await resume_foreground (lifetime->Dispatcher ());
284+ get_self<ProxyGroupModel>(newGroupModel)
285+ ->AttachSubscriptionInfo (conn.GetProxySubscriptionByProxyGroup (newGroupId));
234286 m_model->ProxyGroups ().Append (newGroupModel);
235287 }
236288 catch (hresult_error const &hr)
237289 {
238290 errMsg = {hr.message ()};
239291 }
240292 co_await resume_foreground (lifetime->Dispatcher ());
293+ lifetime->ProxySubscriptionUpdatesRunning $.get_subscriber ().on_next (-1 );
241294 if (errMsg.has_value ())
242295 {
243296 ProxyGroupAddSubscriptionError ().Text (*errMsg);
@@ -255,6 +308,81 @@ namespace winrt::YtFlowApp::implementation
255308 }
256309 }
257310
311+ void LibraryPage::SyncSubscriptionButton_Click (IInspectable const &, RoutedEventArgs const &)
312+ {
313+ UpdateSubscription (std::nullopt );
314+ }
315+
316+ fire_and_forget LibraryPage::UpdateSubscription (std::optional<uint32_t > id)
317+ {
318+ auto const lifetime = get_strong ();
319+ auto proxyGroupIt =
320+ std::ranges::views::transform (lifetime->m_model ->ProxyGroups (),
321+ [](auto const &model) { return model.as <ProxyGroupModel>(); }) |
322+ std::ranges::views::filter ([&id](auto const &model) {
323+ return !model->IsManualGroup () && !model->IsUpdating && (!id.has_value () || model->Id () == *id);
324+ });
325+ std::vector const proxyGroups (proxyGroupIt.begin (), proxyGroupIt.end ());
326+ try
327+ {
328+ for (auto &&model : proxyGroups)
329+ {
330+ model->IsUpdating = true ;
331+ }
332+ lifetime->ProxySubscriptionUpdatesRunning $.get_subscriber ().on_next (1 );
333+ std::vector<std::pair<com_ptr<ProxyGroupModel>, FfiProxyGroupSubscription>> subscriptionInfoToAttach;
334+ subscriptionInfoToAttach.reserve (proxyGroups.size ());
335+ auto const client = GetHttpClientForSubscription ();
336+
337+ co_await resume_background ();
338+
339+ hstring errors;
340+ auto res = std::make_shared<SubscriptionDownloadDecodeResult>();
341+ auto conn = FfiDbInstance.Connect ();
342+ for (auto &&model : proxyGroups)
343+ {
344+ try
345+ {
346+ auto const groupId = model->Id ();
347+ auto const subscription = conn.GetProxySubscriptionByProxyGroup (groupId);
348+ co_await DownloadSubscriptionProxies (client, Uri{to_hstring (subscription.url )},
349+ ConvertSubscriptionFormatToStatic (subscription.format .data ()),
350+ res);
351+ conn.BatchUpdateProxyInGroup (groupId, res->proxies .data (), res->proxies .size ());
352+ conn.UpdateProxySubscriptionRetrievedByProxyGroup (groupId, res->userinfo .upload_bytes_used ,
353+ res->userinfo .download_bytes_used ,
354+ res->userinfo .bytes_total , res->expiresAt );
355+ subscriptionInfoToAttach.emplace_back (
356+ std::make_pair (model, conn.GetProxySubscriptionByProxyGroup (groupId)));
357+ }
358+ catch (hresult_error const &hr)
359+ {
360+ errors = errors + hstring{L" \r\n " } + model->Name () + L" : " + hr.message ();
361+ }
362+ }
363+
364+ co_await resume_foreground (lifetime->Dispatcher ());
365+ for (auto &&[model, subscriptionInfo] : subscriptionInfoToAttach)
366+ {
367+ model->AttachSubscriptionInfo (subscriptionInfo);
368+ }
369+ if (!errors.empty ())
370+ {
371+ NotifyUser (errors, L" Update errors" );
372+ }
373+ }
374+ catch (...)
375+ {
376+ NotifyException (L" Updating Subscription" );
377+ }
378+ co_await resume_foreground (lifetime->Dispatcher ());
379+ for (auto &&model : proxyGroups)
380+ {
381+ model->IsUpdating = false ;
382+ }
383+ lifetime->ProxySubscriptionUpdatesRunning $.get_subscriber ().on_next (-1 );
384+ }
385+
258386 void LibraryPage::ProxyGroupItem_Click (IInspectable const &sender, RoutedEventArgs const &)
259387 {
260388 auto const source = sender.as <FrameworkElement>();
@@ -505,5 +633,4 @@ namespace winrt::YtFlowApp::implementation
505633 ProxyGroupProxyExportText ().Text (std::move (text));
506634 auto const _ = ProxyGroupProxyExportDialog ().ShowAsync ();
507635 }
508-
509636}
0 commit comments