{"id":16974637,"url":"https://github.com/alttiri/keep-lister","last_synced_at":"2026-03-02T04:31:28.690Z","repository":{"id":37437076,"uuid":"396644014","full_name":"AlttiRi/keep-lister","owner":"AlttiRi","description":"Offline file browser. Create a JSON snapshot with the meta information (name, size, time, …) of your files and watch it later with this explorer. The scanner to create the snapshots is included.","archived":false,"fork":false,"pushed_at":"2023-01-07T18:39:32.000Z","size":2078,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-14T20:41:59.786Z","etag":null,"topics":["archiving","datahoarder","explorer","files","harddrive","index","metainformation","scanner","search","site","snap2html","snap2json","snapshot"],"latest_commit_sha":null,"homepage":"https://alttiri.github.io/keep-lister","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AlttiRi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-08-16T06:01:43.000Z","updated_at":"2024-08-04T13:59:55.000Z","dependencies_parsed_at":"2023-02-07T21:01:50.486Z","dependency_job_id":null,"html_url":"https://github.com/AlttiRi/keep-lister","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/AlttiRi/keep-lister","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlttiRi%2Fkeep-lister","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlttiRi%2Fkeep-lister/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlttiRi%2Fkeep-lister/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlttiRi%2Fkeep-lister/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AlttiRi","download_url":"https://codeload.github.com/AlttiRi/keep-lister/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlttiRi%2Fkeep-lister/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29992302,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-02T01:47:34.672Z","status":"online","status_checked_at":"2026-03-02T02:00:07.342Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["archiving","datahoarder","explorer","files","harddrive","index","metainformation","scanner","search","site","snap2html","snap2json","snapshot"],"created_at":"2024-10-14T01:07:21.012Z","updated_at":"2026-03-02T04:31:28.669Z","avatar_url":"https://github.com/AlttiRi.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# KeepLister\n\nIt lists the files, that you keep on your storage drives, as a common file browser.\nIt's aimed to help digital hoarders finding their files.\n\nIn other words: it's a file manager snapshot explorer, or a file explorer for JSON snapshots with meta information of local files.\n\nThe scanner for creating the snapshots is included.\n\n---\n\nCurrently, it's implemented this way:\n- The website is used as the explorer. _(You can also run it locally.)_\n- JSON files (snapshots) are used as a database for the explorer.\n- The scanner, a JavaScript file, is used for creating snapshots. You need to run it with Node.js from a terminal.\n\nYeah, it would much more convenient if it was a standalone GUI application, but anyway it's usable.\n\n---\n\nSo, you can create a meta snapshot of your external/local hard drive's content and use this explorer to look at the \ncontent of the hard drive when it's detached. For the local hard drives it is pretty too, it can be an alternative for \nWindows Explorer's search thing.\n\nThere are two parts of the software:\n\n- The scanner. It creates a JSON snapshots with meta information _(`name`, `type`, `size`, `mtime`, `crtime`, `hardlink`/`symlink` info)_ of your local files. (See: [how to use ↓](#how-to-use)))\n- [The explorer](https://alttiri.github.io/keep-lister/) to open these JSON snapshots. \n\n---\n\n\n### Online demos\n\nJust look at the examples (click on the links to open the site with the demo scans):\n\n**[Win 10 scan (as Admin)](https://alttiri.github.io/keep-lister/?filepath=/json-flat-scans/windows-admin.json.gz)** | \n[Win 10 scan](https://alttiri.github.io/keep-lister/?filepath=/json-flat-scans/windows.json.gz)\n\n![Screenshot Win](https://user-images.githubusercontent.com/16310547/133657123-d1547a7b-6497-4da6-88ec-6e4928b2b044.png)\n\n\n**[Ubuntu scan (as Root)](https://alttiri.github.io/keep-lister/?filepath=/json-flat-scans/ubuntu-admin.json.gz)** | \n[Ubuntu scan](https://alttiri.github.io/keep-lister/?filepath=/json-flat-scans/ubuntu.json.gz)\n\n![Screenshot Ubuntu](https://user-images.githubusercontent.com/16310547/133657142-75f15c86-ce70-4ef6-a21b-cdc0310bbb7e.png)\n\n[Linux Source Code scan](https://alttiri.github.io/keep-lister/?filepath=/json-flat-scans/linux-master.json.gz)\n\n---\n\n**Mega.nz folders**\n\nAlso, it supports mega.nz folder links (like this: `https://mega.nz/folder/ABcDE123#XXX`).\n\nJust paste them in the search field.\n\nOnly for a browsing, but even this is useful since you can use the advanced search of this explorer (for example, to find the recently added files, find a file by its size).\n\n\n---\n\n## Search\n\nSome search examples:\n\n[.exe](https://alttiri.github.io/keep-lister/?filepath=/json-flat-scans/windows-admin.json.gz\u0026search=.exe)\n\n![Screenshot Search](https://user-images.githubusercontent.com/16310547/133657172-685801b2-5895-4876-8730-b11b8553f168.png)\n\nFor case-sensitive search use `//`, for example: [//.EXE](https://alttiri.github.io/keep-lister/?filepath=/json-flat-scans/windows-admin.json.gz\u0026search=//.EXE)\n\n### Search by type\n\nSearch by file type, for example: `/type:folder/query`\n(`folder`, `file`, `symlink` for Windows and `fifo`, `charDev`, `blockDev`, `socket` in additional for other platforms).\n\n[/type:folder/.exe](https://alttiri.github.io/keep-lister/?filepath=/json-flat-scans/windows-admin.json.gz\u0026search=/type:folder/.exe)\n\n![Screenshot Search Folder](https://user-images.githubusercontent.com/16310547/133657180-9fc03183-d50d-47ff-badc-252fcdfe6952.png)\n\n---\n\n### List everything\n\nTo list recursively all items of an opened folder use [`//`](https://alttiri.github.io/keep-lister/?filepath=/json-flat-scans/windows-admin.json.gz\u0026search=//) search.\n\n---\n\n### Size search\n\n#### Base\n- `/size:0`       — find 0 byte size entries; aka `/size/0` aka `/s:0` aka `/s/0`\n- `/s/120,900`    — find 120900 bytes size entries; aka `/s/120 900`\n- `/size:120+80`  — find from 120 to 200\n- `/size:150+-50` — find from 100 to 150\n- `/size:80-110`  — find from 80 to 110; aka `/size:110-80`\n- `/size:200~50`  — find from 150 to 250\n\n#### Byte substring\n- `/size:^2`      — find byte size starts with \"2\"\n- `/size:%2`      — find byte size includes    \"2\"\n- `/size:$0`      — find byte size ends with   \"0\"\n\n#### Range widening\n- `/size:120~`    — find from 120 -5% to 120 +5%\n- `/size:120~~`   — find from 120-10% to 120+10%\n- `/size:120~~~`  — find from 120-15% to 120+15%\n\n#### Prefixes (`k`, `m`, `g`, `t`)\n- `/sizek:5`      — find 5 KB ± 0.1 KB\n- `/sizek:50`     — find 50 KB  ± 1 KB\n- `/sizek:500`    — find 500 KB ± 1 KB\n- `/sizem:5`      — find 5 MB ± 0.1 MB\n- `/sizeg/50`     — find 50 GB ± 1 GB\n\n- `/size:5m`      — find 5 MB ± 0.1 MB\n\n#### Decimal\n- `/s/12.9`       — find 12 bytes size entries\n- `/sk/12.9`      — find 12.9 KB ± 1 KB\n\n#### Range narrowing\n- `/sizek:5!`     — find 5 KB + (0 - 0.01) KB\n- `/sizek:5!!`    — find 5 KB + (0 - 0.001) KB\n- `/s/5k!!`       — find 5 KB + (0 - 0.001) KB\n- `/sizem:50!`    — find 50 MB + (0 - 0.1) MB\n- `/sizem:50!!`   — find 50 MB + (0 - 0.01) MB\n\n---\n\n### URL Search\n\nYou can search by pasting URL in the search fiels, if the URL's pathname ends with an information included in file names you will find it, for example:\n\nSearch `https://i.imgur.com/x09ICAM.jpeg` will list:\n- [imgur] 2015.09.18—x09ICAM—3456.jpg\n\nSearch `https://pbs.twimg.com/media/FFyAEbTUYAEUm9p?format=jpg\u0026name=4096x4096` will list:\n- [twitter] SpaceX—2021.12.04—1467202583840243712—FFyAEbTUYAEUm9p.jpg\n\nSearch `https://twitter.com/SpaceX/status/1463536409667530755` will list:\n- [twitter] SpaceX—2021.11.24—1463536409667530755—FE-GSYWUcAgc9BN.jpg\n- [twitter] SpaceX—2021.11.24—1463536409667530755—FE-GWCKVgAorchy.jpg\n- [twitter] SpaceX—2021.11.24—1463536409667530755—FE-GXQYUYAcfvUl.jpg\n\n_BTW, look at [this thing](https://github.com/AlttiRi/twitter-click-and-save)._\n\nSearch `https://www.instagram.com/p/CWqleONFgK4/` will list:\n- [inst] spacex—2021.11.24—CWqleONFgK4—260727536_471249894638202_6939249621560480797_n.jpg\n- [inst] spacex—2021.11.24—CWqleONFgK4—259683601_272614248155822_44594239323667647_n.jpg\n\nSearch `https://www.youtube.com/watch?v=_qwLHlVjRyw` will list:\n- [yt] SpaceX—2020.12.23—_qwLHlVjRyw—Starship _ SN8 _ High-Altitude Flight Recap.description\n- [yt] SpaceX—2020.12.23—_qwLHlVjRyw—Starship _ SN8 _ High-Altitude Flight Recap.webm\n- [yt] SpaceX—2020.12.23—_qwLHlVjRyw—Starship _ SN8 _ High-Altitude Flight Recap.webp\n\nSearch `https://gfycat.com/incompletealarmedicelandichorse` will list:\n\n- [gfycat] blaze0044—2021.03.27—IncompleteAlarmedIcelandichorse—Space X rocket breakup.mp4\n- [gfycat] blaze0044—2021.03.27—IncompleteAlarmedIcelandichorse—Space X rocket breakup.webm\n\n\n_BTW, looks at [this thing](https://github.com/AlttiRi/gfycat-id-camel-caser#readme). \nIt will CamelCase `incompletealarmedicelandichorse` to `IncompleteAlarmedIcelandichorse`._\n\n---\n\n# Search in multiple scans\n\nIt supports multiple scans selecting. Just open two (or more scans) with the file input, or just drag'n'drop multiple scan files into the site.\n\nAdditionally, you can open multiple tabs and add `searchSync=true` URLSearchParam, for example:\n- [...?filepath=.../windows-scan.json.gz\u0026searchSync=true](https://alttiri.github.io/keep-lister/?filepath=/json-flat-scans/windows-admin.json.gz\u0026searchSync=true)\n- [...?searchSync=true\u0026filepath=.../ubuntu-scan.json.gz](https://alttiri.github.io/keep-lister/?searchSync=true\u0026filepath=/json-flat-scans/ubuntu-admin.json.gz)\n\nThe search in one tab will trigger the search in other tabs with `searchSync=true` URLSearchParam. \nAlso, each tab will display the search result count.\n\nJust use this link for your scans:\n- https://alttiri.github.io/keep-lister/?searchSync=true\n\n\n---\n# How to use\n\nIn short.\n- [Installed Node.js](https://nodejs.org/en/download/) is required,\n- Download the scanner file — [qq-keep-lister-scanner.mjs](https://github.com/AlttiRi/keep-lister/releases/download/0.3.1/qq-keep-lister-scanner.mjs),\n- Open a terminal (CMD.exe, for example) in a folder you want to scan, \n- Run the scanner (JS file) with Node.js — type in a terminal _(for Windows with CMD)_:\n```cmd\nnode %USERPROFILE%\\Downloads\\qq-keep-lister-scanner.mjs\n```\n- Don't forget to type enter.\n\n_If you use Linux your command looks like this:_\n```bash\n~/Downloads/qq-keep-lister-scanner.mjs\n```\n\nIn progress:\n\n![Screenshot](https://user-images.githubusercontent.com/16310547/161448861-520b65d2-e2e6-4af3-88cb-01ded97fcc45.png)\n\nThe result will be in your download folder.\n\n\n\n---\n\n_Note: replace `%USERPROFILE%\\Downloads\\qq-keep-lister-scanner.mjs` with the path to the scanner file if you have moved it from the download folder to another place._\n\n---\n\nTo explore the scan result use https://alttiri.github.io/keep-lister/ site.\n\nOr run it locally:\n- Download the source code [keep-lister-master.zip](https://github.com/AlttiRi/keep-lister/archive/refs/heads/master.zip),\n- Unpack it,\n- Open terminal in `keep-lister-master` folder,\n- Type `npm ci`,\n- Then `npm run build`,\n- Then `npm run serve`.\n\nThe site will be available on http://localhost:5000/. Use `Ctrl + C` in the console to stop the server. Next time you only need to type `npm run serve` to start the server.\n\n\n---\n\n# About\n\n\n### File size formatting\n\nIt uses Windows-like file size formatting (`1133158 bytes` → `1.08 MB`). \nIn most cases the result is equal to Windows Explorer result, but in very rare cases is not. See [the test file](https://github.com/AlttiRi/keep-lister/blob/master/tests/win-like-file-sizes.test.js).\n\n\n### JSON size\n\nJSON scans can have a noticeable size, so they are gzipped to reduce the size in 5-10 times. \nFor example, [Windows' disk C scan](https://alttiri.github.io/keep-lister/?filepath=/json-flat-scans/windows-admin.json.gz) (300k files, 90k folders) takes 5.8 MB gzipped (55 MB of raw JSON). \nThe explorer handles the scan in stream way, so it displays the result as soon as the first bytes of the scan are read.\n\n\n### JSON format\n\nJSON snapshot is a valid JSON file, but it is special formatted to simplify the stream parsing and opening with text editors. \n\nJust look at the example _(Note: some lines are trimmed to reduce size)_:\n```json\n[\n{\n \"path\": [\"C:\", \"Users\", \"User\", \"Downloads\"],\n \"separator\": \"\\\\\",\n \"scanDate\": \"2021.09.24 23:52:02Z\",\n \"platform\": \"win32\",\n \"files\": 37,\n \"folders\": 7,\n \"symlinks\": 0,\n \"fifos\": 0,\n \"charDevs\": 0,\n \"blockDevs\": 0,\n \"sockets\": 0,\n \"total\": 44,\n \"errors\": 0,\n \"mHardLinks\": 0,\n \"mHardLinksTotal\": 0,\n \"errorsMap\": {}\n},\n\n{\"type\":\"folder\",\"name\":\"keep-lister-master\",\"pid\":null,\"id\":0,\"mtime\":1632527508334,\"btime\":1632527508312},\n{\"type\":\"folder\",\"name\":\".github\",\"pid\":0,\"id\":1,\"mtime\":1632526451000,\"btime\":1632527508312},\n{\"type\":\"file\",\"name\":\".gitignore\",\"pid\":0,\"mtime\":1632526451000,\"btime\":1632527508314,\"size\":53},\n{\"type\":\"file\",\"name\":\"index.html\",\"pid\":0,\"mtime\":1632526451000,\"btime\":1632527508315,\"size\":843},\n{\"type\":\"file\",\"name\":\"package-lock.json\",\"pid\":0,\"mtime\":1632526451000,\"btime\":1632527508315,\"size\":70293},\n{\"type\":\"file\",\"name\":\"package.json\",\"pid\":0,\"mtime\":1632526451000,\"btime\":1632527508316,\"size\":874},\n{\"type\":\"file\",\"name\":\"README.md\",\"pid\":0,\"mtime\":1632526451000,\"btime\":1632527508314,\"size\":4437},\n{\"type\":\"folder\",\"name\":\"scanner\",\"pid\":0,\"id\":2,\"mtime\":1632526451000,\"btime\":1632527508316},\n{\"type\":\"folder\",\"name\":\"src\",\"pid\":0,\"id\":3,\"mtime\":1632526451000,\"btime\":1632527508319},\n{\"type\":\"folder\",\"name\":\"tests\",\"pid\":0,\"id\":4,\"mtime\":1632526451000,\"btime\":1632527508332},\n{\"type\":\"file\",\"name\":\"vite.config.js\",\"pid\":0,\"mtime\":1632526451000,\"btime\":1632527508334,\"size\":5379},\n{\"type\":\"folder\",\"name\":\"workflows\",\"pid\":1,\"id\":5,\"mtime\":1632526451000,\"btime\":1632527508313},\n{\"type\":\"file\",\"name\":\"blank.yml\",\"pid\":5,\"mtime\":1632526451000,\"btime\":1632527508313,\"size\":3260},\n{\"type\":\"file\",\"name\":\"flat-scan-object.js\",\"pid\":2,\"mtime\":1632526451000,\"btime\":1632527508316,\"size\":5087},\n{\"type\":\"file\",\"name\":\"meta.js\",\"pid\":2,\"mtime\":1632526451000,\"btime\":1632527508317,\"size\":4119},\n{\"type\":\"file\",\"name\":\"scanner.js\",\"pid\":2,\"mtime\":1632526451000,\"btime\":1632527508318,\"size\":8607},\n{\"type\":\"file\",\"name\":\"util-node.js\",\"pid\":2,\"mtime\":1632526451000,\"btime\":1632527508318,\"size\":7314},\n{\"type\":\"file\",\"name\":\"App.vue\",\"pid\":3,\"mtime\":1632526451000,\"btime\":1632527508319,\"size\":288},\n{\"type\":\"folder\",\"name\":\"components\",\"pid\":3,\"id\":6,\"mtime\":1632526451000,\"btime\":1632527508319},\n{\"type\":\"file\",\"name\":\"AddressBar.vue\",\"pid\":6,\"mtime\":1632526451000,\"btime\":1632527508320,\"size\":854}\n]\n```\n\n### Global variables\n\nThere are `folder` and `search` variables in the browser console to handle entries of \"opened folder\"/\"search result\" in program way.\n\nFor example:\n\n```js\n// List names of files in an opened folder as one string\nfolder.files.map(e =\u003e e.name).join(\"\\n\")\n```\n```js\n// Find the most long filenames\n// (`flat` (`folder.flat()`) recursively lists all files of the selected directory in an array)\nflat.reduce((acc, entry) =\u003e {\n    const name = entry.name;\n    const length = acc[0].length;\n    if (name.length \u003e length) {\n        return [name];\n    }\n    if (name.length === length) {\n        acc.push(name);\n        return acc;\n    }\n    return acc;\n}, [\"\"])\n```\n\nIf you have files with the special filenames (see [@AlttiRi/twitter-click-and-save#filename-format](https://github.com/AlttiRi/twitter-click-and-save#filename-format)) which include some additional information about the file it's not a problem, for example, to count downloaded posts (one post can have multiple files):\n\nFirst list all files of certain author, for example, with `[twitter] SpaceX` search and then:\n```js\n// Parse the post ID from the filenames, then count the number of unique IDs.\nnew Set(\n    folder\n        .flat()\n        .filter(entry =\u003e entry.type === \"file\")\n        .map(entry =\u003e entry.name)\n        .map(name =\u003e {\n            const result = name.match(/\\[twitter\\] (?\u003cauthor\u003e.+)—(?\u003cdate\u003e\\d{4}\\.\\d{2}\\.\\d{2})—(?\u003cpostId\u003e[^—]+)—(?\u003cfilename\u003e.+)/);\n            if (result) {\n                return result.groups;\n            }\n            return null;\n        })\n        .filter(result =\u003e result)\n        .map(result =\u003e result.postId)\n).size\n```\n\n## Static site\n\n[This site](https://alttiri.github.io/keep-lister/) is static, it has no backend to handle JSON snapshots, it handles them locally on your machine after your browser have downloaded the site's files (HTML, JS, CSS) hosted by GitHub Pages.\n\nSo, you do not send your personal data (JSON snapshots) anywhere. Also, the site has no analytics, it contains only code is necessary for the work.\n\nBut it's just a text. If you don't trust it (it's OK) to be sure that your data in safety (without verifying of the source code, or inspecting the site with DevTools) you can just open the site in an incognito window, disable the Internet access, work with the site, then close the incognito window and only after that enable the Internet access.\n\n_(Note: `pako_inflate` library is lazy loaded, so you can open one of the demo snapshots first to load it, or just unpack your JSON snapshot from `.gz` archive — in this case `pako_inflate` is no needed.)_\n\n\n## Similar software\n- [Snap2HTML](https://github.com/rlv-dan/Snap2HTML)*\n- Everything (with unchecked `Automatically remove offline volumes` option)\n- FilelistCreator\n- [Directory Snapshot](https://github.com/Anmol-Singh-Jaggi/Directory-Snapshot)\n \n\\*Technically I also can generate a standalone HTML file as output, but do you need it?\n\n\n---\n\n## Postscript\n\nThere are a lot of things that to do to improve it. But the core functional is ready.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falttiri%2Fkeep-lister","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falttiri%2Fkeep-lister","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falttiri%2Fkeep-lister/lists"}