@@ -15,7 +15,7 @@ import {
1515import { randomBytes } from "crypto"
1616import { release } from "os"
1717import { EventEmitter } from "events"
18- import { mkdir , outputFile , readFile , rename , unlink } from "fs-extra"
18+ import { mkdir , outputFile , readFile , rename , unlink , copyFile , pathExists } from "fs-extra"
1919import { OutgoingHttpHeaders } from "http"
2020import { load } from "js-yaml"
2121import { Lazy } from "lazy-val"
@@ -31,7 +31,7 @@ import { Provider, ProviderPlatform } from "./providers/Provider"
3131import type { TypedEmitter } from "tiny-typed-emitter"
3232import Session = Electron . Session
3333import type { AuthInfo } from "electron"
34- import { gunzipSync } from "zlib"
34+ import { gunzipSync , gzipSync } from "zlib"
3535import { blockmapFiles } from "./util"
3636import { DifferentialDownloaderOptions } from "./differentialDownloader/DifferentialDownloader"
3737import { GenericDifferentialDownloader } from "./differentialDownloader/GenericDifferentialDownloader"
@@ -117,6 +117,18 @@ export abstract class AppUpdater extends (EventEmitter as new () => TypedEmitter
117117 */
118118 forceDevUpdateConfig = false
119119
120+ /**
121+ * The base URL of the old block map file.
122+ *
123+ * When null, the updater will use the base URL of the update file to download the update.
124+ * When set, the updater will use this string as the base URL of the old block map file.
125+ * Some servers like github cannot download the old block map file from latest release,
126+ * so you need to compute the old block map file base URL manually.
127+ *
128+ * @default null
129+ */
130+ public previousBlockmapBaseUrlOverride : string | null = null
131+
120132 /**
121133 * The current application version.
122134 */
@@ -736,6 +748,10 @@ export abstract class AppUpdater extends (EventEmitter as new () => TypedEmitter
736748 ...updateInfo ,
737749 downloadedFile : updateFile ,
738750 } )
751+ const currentBlockMapFile = path . join ( cacheDir , "current.blockmap" )
752+ if ( await pathExists ( currentBlockMapFile ) ) {
753+ await copyFile ( currentBlockMapFile , path . join ( downloadedUpdateHelper . cacheDir , "current.blockmap" ) )
754+ }
739755 return packageFile == null ? [ updateFile ] : [ updateFile , packageFile ]
740756 }
741757
@@ -790,7 +806,7 @@ export abstract class AppUpdater extends (EventEmitter as new () => TypedEmitter
790806 if ( this . _testOnlyOptions != null && ! this . _testOnlyOptions . isUseDifferentialDownload ) {
791807 return true
792808 }
793- const blockmapFileUrls = blockmapFiles ( fileInfo . url , this . app . version , downloadUpdateOptions . updateInfoAndProvider . info . version )
809+ const blockmapFileUrls = blockmapFiles ( fileInfo . url , this . app . version , downloadUpdateOptions . updateInfoAndProvider . info . version , this . previousBlockmapBaseUrlOverride )
794810 this . _logger . info ( `Download block maps (old: "${ blockmapFileUrls [ 0 ] } ", new: ${ blockmapFileUrls [ 1 ] } )` )
795811
796812 const downloadBlockMap = async ( url : URL ) : Promise < BlockMap > => {
@@ -824,8 +840,33 @@ export abstract class AppUpdater extends (EventEmitter as new () => TypedEmitter
824840 downloadOptions . onProgress = it => this . emit ( DOWNLOAD_PROGRESS , it )
825841 }
826842
827- const blockMapDataList = await Promise . all ( blockmapFileUrls . map ( u => downloadBlockMap ( u ) ) )
828- await new GenericDifferentialDownloader ( fileInfo . info , this . httpExecutor , downloadOptions ) . download ( blockMapDataList [ 0 ] , blockMapDataList [ 1 ] )
843+ const saveBlockMapToCacheDir = async ( blockMapData : BlockMap , cacheDir : string ) => {
844+ const blockMapFile = path . join ( cacheDir , "current.blockmap" )
845+ await outputFile ( blockMapFile , gzipSync ( JSON . stringify ( blockMapData ) ) )
846+ }
847+
848+ const getBlockMapFromCacheDir = async ( cacheDir : string ) => {
849+ const blockMapFile = path . join ( cacheDir , "current.blockmap" )
850+ try {
851+ if ( await pathExists ( blockMapFile ) ) {
852+ return JSON . parse ( gunzipSync ( await readFile ( blockMapFile ) ) . toString ( ) )
853+ }
854+ } catch ( e : any ) {
855+ this . _logger . warn ( `Cannot parse blockmap "${ blockMapFile } ", error: ${ e } ` )
856+ }
857+ return null
858+ }
859+
860+ const newBlockMapData = await downloadBlockMap ( blockmapFileUrls [ 1 ] )
861+ await saveBlockMapToCacheDir ( newBlockMapData , this . downloadedUpdateHelper ! . cacheDirForPendingUpdate )
862+
863+ // get old blockmap from cache dir first, if not found, download it
864+ let oldBlockMapData = await getBlockMapFromCacheDir ( this . downloadedUpdateHelper ! . cacheDir )
865+ if ( oldBlockMapData == null ) {
866+ oldBlockMapData = await downloadBlockMap ( blockmapFileUrls [ 0 ] )
867+ }
868+
869+ await new GenericDifferentialDownloader ( fileInfo . info , this . httpExecutor , downloadOptions ) . download ( oldBlockMapData , newBlockMapData )
829870 return false
830871 } catch ( e : any ) {
831872 this . _logger . error ( `Cannot download differentially, fallback to full download: ${ e . stack || e } ` )
0 commit comments