77# Can be removed in a future version of python (3.8+)
88from __future__ import annotations
99
10- from datetime import datetime , timedelta
1110import json
1211import os
1312import pathlib
4039)
4140from .models .channel import Channel
4241from .network import (
43- _getAddonStoreURL ,
4442 _getCurrentApiVersionForURL ,
43+ _getAddonStoreURL ,
44+ _getCacheHashURL ,
4545 _LATEST_API_VER ,
4646)
4747
@@ -68,7 +68,6 @@ def initialize():
6868class _DataManager :
6969 _cacheLatestFilename : str = "_cachedLatestAddons.json"
7070 _cacheCompatibleFilename : str = "_cachedCompatibleAddons.json"
71- _cachePeriod = timedelta (hours = 6 )
7271 _downloadsPendingInstall : Set [Tuple ["AddonListItemVM" , os .PathLike ]] = set ()
7372
7473 def __init__ (self ):
@@ -107,27 +106,43 @@ def _getLatestAddonsDataForVersion(self, apiVersion: str) -> Optional[bytes]:
107106 return None
108107 return response .content
109108
110- def _cacheCompatibleAddons (self , addonData : str , fetchTime : datetime ):
109+ def _getCacheHash (self ) -> Optional [str ]:
110+ url = _getCacheHashURL ()
111+ try :
112+ response = requests .get (url )
113+ except requests .exceptions .RequestException as e :
114+ log .debugWarning (f"Unable to get cache hash: { e } " )
115+ return None
116+ if response .status_code != requests .codes .OK :
117+ log .error (
118+ f"Unable to get data from API ({ url } ),"
119+ f" response ({ response .status_code } ): { response .content } "
120+ )
121+ return None
122+ cacheHash = response .json ()
123+ return cacheHash
124+
125+ def _cacheCompatibleAddons (self , addonData : str , cacheHash : str ):
111126 if not NVDAState .shouldWriteToDisk ():
112127 return
113- if not addonData :
128+ if not addonData or not cacheHash :
114129 return
115130 cacheData = {
116- "cacheDate " : fetchTime . isoformat () ,
131+ "cacheHash " : cacheHash ,
117132 "data" : addonData ,
118133 "cachedLanguage" : self ._lang ,
119134 "nvdaAPIVersion" : addonAPIVersion .CURRENT ,
120135 }
121136 with open (self ._cacheCompatibleFile , 'w' ) as cacheFile :
122137 json .dump (cacheData , cacheFile , ensure_ascii = False )
123138
124- def _cacheLatestAddons (self , addonData : str , fetchTime : datetime ):
139+ def _cacheLatestAddons (self , addonData : str , cacheHash : str ):
125140 if not NVDAState .shouldWriteToDisk ():
126141 return
127- if not addonData :
142+ if not addonData or not cacheHash :
128143 return
129144 cacheData = {
130- "cacheDate " : fetchTime . isoformat () ,
145+ "cacheHash " : cacheHash ,
131146 "data" : addonData ,
132147 "cachedLanguage" : self ._lang ,
133148 "nvdaAPIVersion" : _LATEST_API_VER ,
@@ -139,15 +154,24 @@ def _getCachedAddonData(self, cacheFilePath: str) -> Optional[CachedAddonsModel]
139154 if not os .path .exists (cacheFilePath ):
140155 return None
141156 with open (cacheFilePath , 'r' ) as cacheFile :
142- cacheData = json .load (cacheFile )
143- if not cacheData :
157+ try :
158+ cacheData = json .load (cacheFile )
159+ except Exception :
160+ log .exception (f"Invalid add-on store cache" )
161+ return None
162+ try :
163+ data = cacheData ["data" ]
164+ cacheHash = cacheData ["cacheHash" ]
165+ cachedLanguage = cacheData ["cachedLanguage" ]
166+ nvdaAPIVersion = cacheData ["nvdaAPIVersion" ]
167+ except KeyError :
168+ log .exception (f"Invalid add-on store cache:\n { cacheData } " )
144169 return None
145- fetchTime = datetime .fromisoformat (cacheData ["cacheDate" ])
146170 return CachedAddonsModel (
147- cachedAddonData = _createStoreCollectionFromJson (cacheData [ " data" ] ),
148- cachedAt = fetchTime ,
149- cachedLanguage = cacheData [ " cachedLanguage" ] ,
150- nvdaAPIVersion = tuple (cacheData [ " nvdaAPIVersion" ] ), # loads as list
171+ cachedAddonData = _createStoreCollectionFromJson (data ),
172+ cacheHash = cacheHash ,
173+ cachedLanguage = cachedLanguage ,
174+ nvdaAPIVersion = tuple (nvdaAPIVersion ), # loads as list,
151175 )
152176
153177 # Translators: A title of the dialog shown when fetching add-on data from the store fails
@@ -157,24 +181,25 @@ def getLatestCompatibleAddons(
157181 self ,
158182 onDisplayableError : Optional [DisplayableError .OnDisplayableErrorT ] = None ,
159183 ) -> "AddonGUICollectionT" :
184+ cacheHash = self ._getCacheHash ()
160185 shouldRefreshData = (
161186 not self ._compatibleAddonCache
162187 or self ._compatibleAddonCache .nvdaAPIVersion != addonAPIVersion .CURRENT
163- or _DataManager ._cachePeriod < (datetime .now () - self ._compatibleAddonCache .cachedAt )
188+ or cacheHash is None
189+ or self ._compatibleAddonCache .cacheHash != cacheHash
164190 or self ._compatibleAddonCache .cachedLanguage != self ._lang
165191 )
166192 if shouldRefreshData :
167- fetchTime = datetime .now ()
168193 apiData = self ._getLatestAddonsDataForVersion (_getCurrentApiVersionForURL ())
169194 if apiData :
170195 decodedApiData = apiData .decode ()
171196 self ._cacheCompatibleAddons (
172197 addonData = decodedApiData ,
173- fetchTime = fetchTime ,
198+ cacheHash = cacheHash ,
174199 )
175200 self ._compatibleAddonCache = CachedAddonsModel (
176201 cachedAddonData = _createStoreCollectionFromJson (decodedApiData ),
177- cachedAt = fetchTime ,
202+ cacheHash = cacheHash ,
178203 cachedLanguage = self ._lang ,
179204 nvdaAPIVersion = addonAPIVersion .CURRENT ,
180205 )
@@ -195,23 +220,24 @@ def getLatestAddons(
195220 self ,
196221 onDisplayableError : Optional [DisplayableError .OnDisplayableErrorT ] = None ,
197222 ) -> "AddonGUICollectionT" :
223+ cacheHash = self ._getCacheHash ()
198224 shouldRefreshData = (
199225 not self ._latestAddonCache
200- or _DataManager ._cachePeriod < (datetime .now () - self ._latestAddonCache .cachedAt )
226+ or cacheHash is None
227+ or self ._latestAddonCache .cacheHash != cacheHash
201228 or self ._latestAddonCache .cachedLanguage != self ._lang
202229 )
203230 if shouldRefreshData :
204- fetchTime = datetime .now ()
205231 apiData = self ._getLatestAddonsDataForVersion (_LATEST_API_VER )
206232 if apiData :
207233 decodedApiData = apiData .decode ()
208234 self ._cacheLatestAddons (
209235 addonData = decodedApiData ,
210- fetchTime = fetchTime ,
236+ cacheHash = cacheHash ,
211237 )
212238 self ._latestAddonCache = CachedAddonsModel (
213239 cachedAddonData = _createStoreCollectionFromJson (decodedApiData ),
214- cachedAt = fetchTime ,
240+ cacheHash = cacheHash ,
215241 cachedLanguage = self ._lang ,
216242 nvdaAPIVersion = _LATEST_API_VER ,
217243 )
0 commit comments