diff --git a/packages/tools/generate-images.json b/packages/tools/generate-images.json new file mode 100644 index 000000000..dda5d4aa6 --- /dev/null +++ b/packages/tools/generate-images.json @@ -0,0 +1,51 @@ +{ + "done": { + "1_80abab89a0e638b5a3f12f96ab5c8792_packages/app-mobile/ios/Joplin/Images.xcassets/AppIcon.appiconset/ios_marketing1024x1024.png_1024_1024__": true, + "1_80abab89a0e638b5a3f12f96ab5c8792_packages/app-mobile/ios/Joplin/Images.xcassets/AppIcon.appiconset/ipad_app76x76.png_76_76__": true, + "1_80abab89a0e638b5a3f12f96ab5c8792_packages/app-mobile/ios/Joplin/Images.xcassets/AppIcon.appiconset/ipad_app76x76@2x.png_152_152__": true, + "1_80abab89a0e638b5a3f12f96ab5c8792_packages/app-mobile/ios/Joplin/Images.xcassets/AppIcon.appiconset/ipad_notification20x20.png_20_20__": true, + "1_80abab89a0e638b5a3f12f96ab5c8792_packages/app-mobile/ios/Joplin/Images.xcassets/AppIcon.appiconset/ipad_notification20x20@2x.png_40_40__": true, + "1_80abab89a0e638b5a3f12f96ab5c8792_packages/app-mobile/ios/Joplin/Images.xcassets/AppIcon.appiconset/ipad_pro_app83.5x83.5@2x.png_167_167__": true, + "1_80abab89a0e638b5a3f12f96ab5c8792_packages/app-mobile/ios/Joplin/Images.xcassets/AppIcon.appiconset/ipad_settings29x29.png_29_29__": true, + "1_80abab89a0e638b5a3f12f96ab5c8792_packages/app-mobile/ios/Joplin/Images.xcassets/AppIcon.appiconset/ipad_settings29x29@2x.png_58_58__": true, + "1_80abab89a0e638b5a3f12f96ab5c8792_packages/app-mobile/ios/Joplin/Images.xcassets/AppIcon.appiconset/ipad_spotlight40x40.png_40_40__": true, + "1_80abab89a0e638b5a3f12f96ab5c8792_packages/app-mobile/ios/Joplin/Images.xcassets/AppIcon.appiconset/ipad_spotlight40x40@2x.png_80_80__": true, + "1_80abab89a0e638b5a3f12f96ab5c8792_packages/app-mobile/ios/Joplin/Images.xcassets/AppIcon.appiconset/iphone_app60x60@2x.png_120_120__": true, + "1_80abab89a0e638b5a3f12f96ab5c8792_packages/app-mobile/ios/Joplin/Images.xcassets/AppIcon.appiconset/iphone_app60x60@3x.png_180_180__": true, + "1_80abab89a0e638b5a3f12f96ab5c8792_packages/app-mobile/ios/Joplin/Images.xcassets/AppIcon.appiconset/iphone_notification20x20@2x.png_40_40__": true, + "1_80abab89a0e638b5a3f12f96ab5c8792_packages/app-mobile/ios/Joplin/Images.xcassets/AppIcon.appiconset/iphone_notification20x20@3x.png_60_60__": true, + "1_80abab89a0e638b5a3f12f96ab5c8792_packages/app-mobile/ios/Joplin/Images.xcassets/AppIcon.appiconset/iphone_settings29x29@2x.png_58_58__": true, + "1_80abab89a0e638b5a3f12f96ab5c8792_packages/app-mobile/ios/Joplin/Images.xcassets/AppIcon.appiconset/iphone_settings29x29@3x.png_87_87__": true, + "1_80abab89a0e638b5a3f12f96ab5c8792_packages/app-mobile/ios/Joplin/Images.xcassets/AppIcon.appiconset/iphone_spotlight40x40@2x.png_80_80__": true, + "1_80abab89a0e638b5a3f12f96ab5c8792_packages/app-mobile/ios/Joplin/Images.xcassets/AppIcon.appiconset/iphone_spotlight40x40@3x.png_120_120__": true, + "2_980ab814dfce30eb15adf0a90d85bb1a_Assets/macOs.iconset/icon_16x16.png_16_16__": true, + "3_a7fed49fa153dc9f4e58f038011bce4d_Assets/macOs.iconset/icon_16x16@2x.png_32_32__": true, + "3_a7fed49fa153dc9f4e58f038011bce4d_Assets/macOs.iconset/icon_32x32.png_32_32__": true, + "3_a7fed49fa153dc9f4e58f038011bce4d_Assets/macOs.iconset/icon_32x32@2x.png_64_64__": true, + "7_a088bffc3266cee9cf14dcb575533df2_Assets/macOs.iconset/icon_128x128.png_128_128__": true, + "7_a088bffc3266cee9cf14dcb575533df2_Assets/macOs.iconset/icon_128x128@2x.png_256_256__": true, + "7_a088bffc3266cee9cf14dcb575533df2_Assets/macOs.iconset/icon_256x256.png_256_256__": true, + "7_a088bffc3266cee9cf14dcb575533df2_Assets/macOs.iconset/icon_256x256@2x.png_512_512__": true, + "7_a088bffc3266cee9cf14dcb575533df2_Assets/macOs.iconset/icon_512x512.png_512_512__": true, + "7_a088bffc3266cee9cf14dcb575533df2_Assets/macOs.iconset/icon_512x512@2x.png_1024_1024__": true, + "5_ec41184ad08ccd9471bfe57eef12a0e7_packages/tools/PortableAppsLauncher/App/AppInfo/appicon.ico____": true, + "2_980ab814dfce30eb15adf0a90d85bb1a_packages/tools/PortableAppsLauncher/App/AppInfo/appicon_16.png____": true, + "3_a7fed49fa153dc9f4e58f038011bce4d_packages/tools/PortableAppsLauncher/App/AppInfo/appicon_32.png_32_32__": true, + "4_1f3d9b9736fe8787c84c8e28275c6721_packages/tools/PortableAppsLauncher/App/AppInfo/appicon_75.png_75_75__": true, + "4_1f3d9b9736fe8787c84c8e28275c6721_packages/tools/PortableAppsLauncher/App/AppInfo/appicon_128.png_128_128__": true, + "4_1f3d9b9736fe8787c84c8e28275c6721_packages/tools/PortableAppsLauncher/App/AppInfo/Launcher/splash.jpg_144_144__": true, + "6_8042e442dfb3e976238ce3896383cb69_packages/app-desktop/build-win/icons/Square150x150Logo.png_150_150_99_75": true, + "6_8042e442dfb3e976238ce3896383cb69_packages/app-desktop/build-win/icons/SmallTile.png_70_70_46_46": true, + "8_645581f9f6de750b50fec3dbf031cee3_Assets/WebsiteAssets/images/home-top-img-4x.webp_4820_2938__": true, + "8_645581f9f6de750b50fec3dbf031cee3_Assets/WebsiteAssets/images/home-top-img-2x.png_2388_1456__": true, + "8_645581f9f6de750b50fec3dbf031cee3_Assets/WebsiteAssets/images/home-top-img-2x.webp_2388_1456__": true, + "8_645581f9f6de750b50fec3dbf031cee3_Assets/WebsiteAssets/images/home-top-img.png_1205_734__": true, + "8_645581f9f6de750b50fec3dbf031cee3_Assets/WebsiteAssets/images/home-top-img.webp_1205_734__": true, + "9_2f78dde9d15370ae6adc7d1a0879ed4b_Assets/WebsiteAssets/images/home-top-img-cn-4x.webp_4820_2938__": true, + "9_2f78dde9d15370ae6adc7d1a0879ed4b_Assets/WebsiteAssets/images/home-top-img-cn-2x.png_2388_1456__": true, + "9_2f78dde9d15370ae6adc7d1a0879ed4b_Assets/WebsiteAssets/images/home-top-img-cn-2x.webp_2388_1456__": true, + "9_2f78dde9d15370ae6adc7d1a0879ed4b_Assets/WebsiteAssets/images/home-top-img-cn.png_1205_734__": true, + "9_2f78dde9d15370ae6adc7d1a0879ed4b_Assets/WebsiteAssets/images/home-top-img-cn.webp_1205_734__": true, + "icns_to_icon_set_0b9e402f9c5127284d98d78c7b604123_f69848e65692758af4ac5a05007d5142_e439c7e591993f33597ab18b1dd8f6a8_277cfac6c9cdea99f30717a0e6dadfe2_f69848e65692758af4ac5a05007d5142_ae8c5643f968c45dabe2d663f88d387f_277cfac6c9cdea99f30717a0e6dadfe2_886337d6356057de4c915734b85a87fd_ae8c5643f968c45dabe2d663f88d387f_e8639f50b47004f35aa92edff2142741": true + } +} \ No newline at end of file diff --git a/packages/tools/generate-images.ts b/packages/tools/generate-images.ts index 9a2856a30..8a35bbbf0 100644 --- a/packages/tools/generate-images.ts +++ b/packages/tools/generate-images.ts @@ -1,7 +1,8 @@ -import * as fs from 'fs-extra'; +import { pathExists, readFile, writeFile, copyFile, readdir } from 'fs-extra'; import { dirname } from 'path'; import { execCommand } from './tool-utils'; import { fileExtension } from '@joplin/lib/path-utils'; +const md5File = require('md5-file'); const sharp = require('sharp'); interface Source { @@ -18,6 +19,10 @@ interface Operation { iconHeight?: number; } +interface Results { + done: Record; +} + const sources: Source[] = [ { id: 1, @@ -373,9 +378,43 @@ const operations: Operation[] = [ }, ]; +const md5Dir = async (dirPath: string): Promise => { + const files = await readdir(dirPath); + files.sort(); + const output: string[] = []; + for (const file of files) { + output.push(await md5File(`${dirPath}/${file}`)); + } + return output.join('_'); +}; + +const readResults = async (filePath: string): Promise => { + if (!(await pathExists(filePath))) return { done: {} }; + const content = await readFile(filePath, 'utf8'); + return JSON.parse(content) as Results; +}; + +const saveResults = async (filePath: string, results: Results) => { + await writeFile(filePath, JSON.stringify(results, null, '\t'), 'utf8'); +}; + +const makeOperationKey = async (source: Source, sourcePath: string, operation: Operation): Promise => { + const output: any[] = []; + output.push(source.id); + output.push(await md5File(sourcePath)); + output.push(operation.dest); + output.push(operation.width); + output.push(operation.height); + output.push(operation.iconWidth); + output.push(operation.iconHeight); + return output.join('_'); +}; + async function main() { const rootDir = dirname(dirname(__dirname)); const sourceImageDir = `${rootDir}/Assets/ImageSources`; + const resultFilePath = `${__dirname}/generate-images.json`; + const results: Results = await readResults(resultFilePath); for (const operation of operations) { const source = sourceById(operation.source); @@ -386,6 +425,13 @@ async function main() { const sourceExt = fileExtension(sourcePath).toLowerCase(); const destExt = fileExtension(destPath).toLowerCase(); + const operationKey = await makeOperationKey(source, sourcePath, operation); + if (results.done[operationKey]) { + console.info(`Skipping: ${operation.dest} (Already done)`); + } else { + console.info(`Processing: ${operation.dest}`); + } + if ((operation.width && operation.height) || (sourceExt !== destExt)) { let s = sharp(sourcePath); @@ -422,18 +468,30 @@ async function main() { s = s.toFile(destPath); } else { - await fs.copyFile(sourcePath, destPath); + await copyFile(sourcePath, destPath); } + + results.done[operationKey] = true; } if (process && process.platform === 'darwin') { const icnsDest = `${rootDir}/Assets/macOs.icns`; const icnsSource = `${rootDir}/Assets/macOs.iconset`; - console.info(`iconutil -c icns -o "${icnsDest}" "${icnsSource}"`); - await execCommand(`iconutil -c icns -o "${icnsDest}" "${icnsSource}"`); + const operationKey = ['icns_to_icon_set', await md5Dir(icnsSource)].join('_'); + if (!results.done[operationKey]) { + console.info(`Processing: ${icnsDest}`); + console.info(`iconutil -c icns -o "${icnsDest}" "${icnsSource}"`); + await execCommand(`iconutil -c icns -o "${icnsDest}" "${icnsSource}"`); + results.done[operationKey] = true; + } else { + console.info(`Skipping: ${icnsDest} (Already done)`); + } } else { console.info('If the macOS icon has been updated, this script should be run on macOS too'); } + + console.info(`Saving results to ${resultFilePath}`); + await saveResults(resultFilePath, results); } main().catch((error) => {