import { decodeBase64Image , Extension , MediaLibrary , Notifications , parseWinPath , Settings , Sorting } from "." ; import { Dashboard } from "../commands/Dashboard" ; import { Folders } from "../commands/Folders" ; import { ExtensionState , HOME_PAGE_NAVIGATION_ID , SETTINGS_CONTENT_STATIC_FOLDER } from "../constants" ; import { SortingOption } from "../dashboardWebView/models" ; import { MediaInfo , MediaPaths , SortOrder , SortType } from "../models" ; import { basename , extname , join , parse , dirname } from "path" ; import { existsSync , readdirSync , statSync , unlinkSync , writeFileSync } from "fs" ; import { commands , Uri , workspace , window , Position } from "vscode" ; import imageSize from "image-size" ; import { EditorHelper } from "@estruyf/vscode" ; import { ExplorerView } from "../explorerView/ExplorerView" ; export class MediaHelpers { private static media : MediaInfo [ ] = [ ] ; param page param requestedFolder param sort returns public static async getMedia ( page = 0 , requestedFolder = '' , sort = null ) { const wsFolder = Folders . getWorkspaceFolder ( ) ; const staticFolder = Settings . get < string > ( SETTINGS_CONTENT_STATIC_FOLDER ) ; const contentFolders = Folders . get ( ) ; const viewData = Dashboard . viewData ; let selectedFolder = requestedFolder ; const ext = Extension . getInstance ( ) ; const crntSort = sort === null ? await ext . getState < SortingOption | undefined > ( ExtensionState . Dashboard . Media . Sorting , "workspace" ) : sort ; if ( ! selectedFolder ) { const stateValue = await ext . getState < string | undefined > ( ExtensionState . SelectedFolder , "workspace" ) ; if ( stateValue !== HOME_PAGE_NAVIGATION_ID ) { if ( viewData ?. data ?. filePath && ( viewData ?. data ?. filePath . endsWith ( 'index.md' ) || viewData ?. data ?. filePath . endsWith ( 'index.mdx' ) ) ) { const folderPath = parse ( viewData . data . filePath ) . dir ; selectedFolder = folderPath ; } else if ( stateValue && existsSync ( stateValue ) ) { selectedFolder = stateValue ; } } } if ( selectedFolder === HOME_PAGE_NAVIGATION_ID ) { selectedFolder = '' ; } let relSelectedFolderPath = selectedFolder ; const parsedPath = parseWinPath ( wsFolder ?. fsPath || "" ) ; if ( selectedFolder && selectedFolder . startsWith ( parsedPath ) ) { relSelectedFolderPath = selectedFolder . replace ( parsedPath , '' ) ; } if ( relSelectedFolderPath . startsWith ( '/' ) ) { relSelectedFolderPath = relSelectedFolderPath . substring ( 1 ) ; } let allMedia = [ ] ; if ( relSelectedFolderPath ) { const files = await workspace . findFiles ( join ( relSelectedFolderPath , '/*' ) ) ; const media = await MediaHelpers . updateMediaData ( MediaHelpers . filterMedia ( files ) ) ; allMedia = [ ... media ] ; } else { if ( staticFolder ) { const folderSearch = join ( staticFolder || "" , '/*' ) ; const files = await workspace . findFiles ( folderSearch ) ; const media = await MediaHelpers . updateMediaData ( MediaHelpers . filterMedia ( files ) ) ; allMedia = [ ... media ] ; } if ( contentFolders && wsFolder ) { for ( let i = 0 ; i < contentFolders . length ; i ++ ) { const contentFolder = contentFolders [ i ] ; const relFolderPath = contentFolder . path . substring ( wsFolder . fsPath . length + 1 ) ; const folderSearch = relSelectedFolderPath ? join ( relSelectedFolderPath , '/*' ) : join ( relFolderPath , '/*' ) ; const files = await workspace . findFiles ( folderSearch ) ; const media = await MediaHelpers . updateMediaData ( MediaHelpers . filterMedia ( files ) ) ; allMedia = [ ... allMedia , ... media ] ; } } } if ( crntSort ?. type === SortType . string ) { allMedia = allMedia . sort ( Sorting . alphabetically ( "fsPath" ) ) ; } else if ( crntSort ?. type === SortType . date ) { allMedia = allMedia . sort ( Sorting . dateWithFallback ( "mtime" , "fsPath" ) ) ; } else { allMedia = allMedia . sort ( Sorting . alphabetically ( "fsPath" ) ) ; } if ( crntSort ?. order === SortOrder . desc ) { allMedia = allMedia . reverse ( ) ; } MediaHelpers . media = Object . assign ( [ ] , allMedia ) ; let files = MediaHelpers . media ; const total = files . length ; files = files . map ( ( file ) => { try { const metadata = MediaLibrary . getInstance ( ) . get ( file . fsPath ) ; return { ... file , dimensions : imageSize ( file . fsPath ) , ... metadata } ; } catch ( e ) { return { ... file } ; } } ) ; files = files . filter ( f => f . mtime !== undefined ) ; let allContentFolders = [ ] ; let allFolders = [ ] ; if ( selectedFolder ) { if ( existsSync ( selectedFolder ) ) { allFolders = readdirSync ( selectedFolder , { withFileTypes : true } ) . filter ( dir => dir . isDirectory ( ) ) . map ( dir => parseWinPath ( join ( selectedFolder , dir . name ) ) ) ; } } else { for ( const contentFolder of contentFolders ) { const contentPath = contentFolder . path ; if ( contentPath && existsSync ( contentPath ) ) { const subFolders = readdirSync ( contentPath , { withFileTypes : true } ) . filter ( dir => dir . isDirectory ( ) ) . map ( dir => parseWinPath ( join ( contentPath , dir . name ) ) ) ; allContentFolders = [ ... allContentFolders , ... subFolders ] ; } } const staticPath = join ( parseWinPath ( wsFolder ?. fsPath || "" ) , staticFolder || "" ) ; if ( staticPath && existsSync ( staticPath ) ) { allFolders = readdirSync ( staticPath , { withFileTypes : true } ) . filter ( dir => dir . isDirectory ( ) ) . map ( dir => parseWinPath ( join ( staticPath , dir . name ) ) ) ; } } await Extension . getInstance ( ) . setState ( ExtensionState . SelectedFolder , requestedFolder === HOME_PAGE_NAVIGATION_ID ? HOME_PAGE_NAVIGATION_ID : selectedFolder , "workspace" ) ; let sortedFolders = [ ... allContentFolders , ... allFolders ] ; sortedFolders = sortedFolders . sort ( ( a , b ) => { if ( a . toLowerCase ( ) < b . toLowerCase ( ) ) { return - 1 ; } if ( a . toLowerCase ( ) > b . toLowerCase ( ) ) { return 1 ; } return 0 ; } ) ; if ( crntSort ?. order === SortOrder . desc ) { sortedFolders = sortedFolders . reverse ( ) ; } return { media : files , total : total , folders : sortedFolders , selectedFolder } as MediaPaths ; } /**
     * Reset media array
     */ public static resetMedia ( ) { MediaHelpers . media = [ ] ; } param fileData public static async saveFile ( { fileName , contents , folder } ) { if ( fileName && contents ) { const wsFolder = Folders . getWorkspaceFolder ( ) ; const staticFolder = Settings . get < string > ( SETTINGS_CONTENT_STATIC_FOLDER ) ; const wsPath = wsFolder ? wsFolder . fsPath : "" ; let absFolderPath = join ( wsPath , staticFolder || "" ) ; if ( folder ) { absFolderPath = folder ; } if ( ! existsSync ( absFolderPath ) ) { absFolderPath = join ( wsPath , folder || "" ) ; } if ( ! existsSync ( absFolderPath ) ) { Notifications . error ( `We couldn't find your selected folder.` ) ; return ; } const staticPath = join ( absFolderPath , fileName ) ; const imgData = decodeBase64Image ( contents ) ; if ( imgData ) { writeFileSync ( staticPath , imgData . data ) ; Notifications . info ( `File ${ fileName } uploaded to: ${ folder }` ) ; return true ; } else { Notifications . error ( `Something went wrong uploading ${ fileName }` ) ; throw new Error ( `Something went wrong uploading ${ fileName }` ) ; } } return false ; } param data returns public static async deleteFile ( { file , page , folder } ) { if ( ! file ) { return ; } try { unlinkSync ( file ) ; MediaHelpers . media = [ ] ; return true ; } catch ( err ) { Notifications . error ( `Something went wrong deleting ${ basename ( file ) }` ) ; throw new Error ( `Something went wrong deleting ${ basename ( file ) }` ) ; } } param data public static async insertMediaToMarkdown ( data ) { if ( data ?. file && data ?. image ) { if ( ! data ?. position ) { await commands . executeCommand ( `workbench.view.extension.frontmatter-explorer` ) ; } await EditorHelper . showFile ( data . file ) ; Dashboard . resetViewData ( ) ; const extensionUri = Extension . getInstance ( ) . extensionPath ; const panel = ExplorerView . getInstance ( extensionUri ) ; if ( data ?. position ) { const wsFolder = Folders . getWorkspaceFolder ( ) ; const editor = window . activeTextEditor ; const line = data . position . line ; const character = data . position . character ; if ( line ) { let imgPath = data . image ; const filePath = data . file ; const absImgPath = join ( parseWinPath ( wsFolder ?. fsPath || "" ) , imgPath ) ; const imgDir = dirname ( absImgPath ) ; const fileDir = dirname ( filePath ) ; if ( imgDir === fileDir ) { imgPath = join ( '/' , basename ( imgPath ) ) ; if ( data . snippet ) { data . snippet = data . snippet . replace ( data . image , imgPath ) ; } } const selection = editor ?. selection ; await editor ?. edit ( builder => { const snippet = data . snippet || `![${ data . alt || data . caption || "" }](${ imgPath })` ; if ( selection !== undefined ) { builder . replace ( selection , snippet ) ; } else { builder . insert ( new Position ( line , character ) , snippet ) ; } } ) ; } panel . getMediaSelection ( ) ; } else { panel . getMediaSelection ( ) ; panel . updateMetadata ( { field : data . fieldName , value : data . image } ) ; } } } param data public static updateMetadata ( data ) { const { file , filename , page , folder , ... metadata } = data ; const mediaLib = MediaLibrary . getInstance ( ) ; mediaLib . set ( file , metadata ) ; mediaLib . updateFilename ( file , filename ) ; } /**
     * Filter the media files
     */ private static filterMedia ( files ) { return files . filter ( file => { const ext = extname ( file . fsPath ) ; return [ '.jpg' , '.jpeg' , '.png' , '.gif' , '.svg' ] . includes ( ext . toLowerCase ( ) ) ; } ) . map ( ( file ) => ( { filename : basename ( file . fsPath ) , fsPath : file . fsPath , vsPath : Dashboard . getWebview ( ) ?. asWebviewUri ( file ) . toString ( ) , stats : undefined } as MediaInfo ) ) ; } param files private static async updateMediaData ( files ) { files = files . map ( ( m ) => { const stats = statSync ( m . fsPath ) ; return Object . assign ( { } , m , stats ) ; } ) ; return Object . assign ( [ ] , files ) ; } }