44 normalizePath ,
55 base64ToArrayBuffer ,
66 EventRef ,
7+ arrayBufferToBase64 ,
78} from "obsidian" ;
89import GithubClient , {
910 GetTreeResponseItem ,
@@ -20,6 +21,7 @@ import { GitHubSyncSettings } from "./settings/settings";
2021import Logger from "./logger" ;
2122import { decodeBase64String } from "./utils" ;
2223import GitHubSyncPlugin from "./main" ;
24+ import { fileTypeFromBuffer } from "file-type" ;
2325
2426interface SyncAction {
2527 type : "upload" | "download" | "delete_local" | "delete_remote" ;
@@ -666,8 +668,8 @@ export default class SyncManager {
666668 * @returns String containing the file SHA1
667669 */
668670 async calculateSHA ( filePath : string ) : Promise < string > {
669- const content = await this . vault . adapter . read ( filePath ) ;
670- const contentBytes = new TextEncoder ( ) . encode ( content ) ;
671+ const contentBuffer = await this . vault . adapter . readBinary ( filePath ) ;
672+ const contentBytes = new Uint8Array ( contentBuffer ) ;
671673 const header = new TextEncoder ( ) . encode ( `blob ${ contentBytes . length } \0` ) ;
672674 const store = new Uint8Array ( [ ...header , ...contentBytes ] ) ;
673675 return await crypto . subtle . digest ( "SHA-1" , store ) . then ( ( hash ) =>
@@ -713,11 +715,40 @@ export default class SyncManager {
713715 // We don't save the metadata file after setting the SHAs cause we do that when
714716 // the sync is fully commited at the end.
715717 // TODO: Understand whether it's a problem we don't revert the SHA setting in case of sync failure
718+ //
719+ // In here we also upload blob is file is a binary. We do it here because when uploading a blob we
720+ // also get back its SHA, so we can set it together with other files.
721+ // We also do that right before creating the new tree because we need the SHAs of those blob to
722+ // correctly create it.
716723 await Promise . all (
717724 Object . keys ( treeFiles )
718725 . filter ( ( filePath : string ) => treeFiles [ filePath ] . content )
719726 . map ( async ( filePath : string ) => {
720- const newSha = await this . calculateSHA ( filePath ) ;
727+ const buffer = await this . vault . adapter . readBinary ( filePath ) ;
728+ const fileType = await fileTypeFromBuffer ( buffer ) ;
729+ let newSha = "" ;
730+ if (
731+ // We can't determine the file type
732+ fileType === undefined ||
733+ // This is not a text file
734+ ! fileType . mime . startsWith ( "text/" ) ||
735+ // Neither a json file
736+ fileType . mime !== "application/json"
737+ ) {
738+ // We treat this file as a binary file. We can't upload these setting the content
739+ // of a tree item, we first need to create a Git blob by uploading the file, then
740+ // we must update the tree item to point the SHA to the blob we just created.
741+ const hash = arrayBufferToBase64 ( buffer ) ;
742+ const { sha } = await this . client . createBlob ( hash ) ;
743+ treeFiles [ filePath ] . sha = sha ;
744+ // Can't have both sha and content set, so we delete it
745+ delete treeFiles [ filePath ] . content ;
746+ newSha = sha ;
747+ } else {
748+ // File is text, we can upload the content directly
749+ // so we just calculate the new SHA to keep track of it
750+ newSha = await this . calculateSHA ( filePath ) ;
751+ }
721752 this . metadataStore . data . files [ filePath ] . sha = newSha ;
722753 } ) ,
723754 ) ;
0 commit comments