VkArchiveViewer
⤓Download latest version
README.md
VK Archive Reader
🌐 Open the app in your browser → vkviewer.normno.ru
No installation required—the web version works right in your browser, and your archive is processed locally on your device and isn’t sent anywhere. Want a native app? Check out the “Download and Install” section.
A convenient reader for your VKontakte archive. When you download your data from VK, it comes as a collection of “raw” web pages that are inconvenient to browse and in which you can’t search properly. This app transforms them into a familiar messaging app: a list of chats, the ability to read conversations, search across all messages, and a gallery of all photos.
🔒 Everything runs only on your device. The app doesn’t send anything anywhere or post anything online. Your conversations stay with you.
📸 What it looks like
| Home screen | Search |
|---|---|
![]() |
![]() |
| Gallery | Grouped by People |
![]() |
![]() |
Screenshots will appear here after you upload files to
docs/screenshots/(see instructions).
⬇️ Download and install
Ready-to-use installers are available on the Releases page (the latest version is at the top).
| System | File | Size | How to install |
|---|---|---|---|
| 🪟 Windows | VkArchiveReader-*.msi |
~120 MB | Run the file and follow the installation wizard. If Windows displays "SmartScreen," click "More info" → "Run anyway." |
| 🍎 macOS | VkArchiveReader-*.dmg |
~128 MB | Open the image and drag the app to “Applications.” When you launch it for the first time, right-click the icon → “Open” (app not signed by Apple). |
| 🤖 Android | VkArchiveReader-android.apk |
~14 MB | Copy the file to your phone and open it. Allow “installation from unknown sources” if your phone prompts you. |
The facial recognition model (~40 MB) is not included in the installer—it is downloaded only once when you run the facial search for the first time. This keeps the installer compact.
System warnings about an “unknown developer” are normal for free apps without a paid digital signature. The installers are built automatically from the source code in this repository (see the section for developers below).
🗂 How to get your VKontakte archive
- Open the VK guide: How to get an archive of your data?
- Request the download and wait for an email or notification that the archive is ready (this may take several hours or days).
- Download the archive. It will be
.zipa file or a folder containing a fileindex.htmlinside and a directorymessages/. - Open the app and drag the folder or
.zipinto the window—or tap the “Select Archive” button.
Done—the app will read the conversations and display them in a user-friendly format.
🎬 Don’t have your own archive? Try the demo
Don’t want to wait for the archive to load? Just tap “Don’t have your own archive? Open demo data” on the home screen . The app will open a built-in demo archive with several fictional chats, photos, and people, so you can try out all the features: reading, searching, the gallery, and grouping by people.
The demo data is fictional; photos are loaded from public dummy services, so you’ll need an internet connection for the demo.
✨ What the app can do
- 💬 A list of chats with previews of the latest message and counters for messages and media.
- 🔀 Sort chats by date, number of messages, number of media files, or name.
- 🔎 Search by messages—across all conversations or within a single chat, with filters by person/group and by author.
- 🧵 Read conversations in convenient “bubbles” with emojis and content that loads as you scroll.
- 🖼 Gallery of all photos—across all chats or in a specific one, with options to set the number of columns, full-screen view, and a filter by format (JPG, PNG, GIF, WebP, MP4 …).
- ⬇️ Download media—either as a single file or in bulk: from one or multiple chats, with the option to filter by year. On a computer/Android—to a folder (which you can select in settings); in a browser—as a single ZIP archive.
- 🎧 You can listen to voice messages right in the app (Android and web).
- 🧑🤝🧑 Grouping by people (on a computer)—the app automatically detects faces in photos and groups them by person, entirely locally.
- 💾 Image cache for fast reloading, with customizable size settings.
❓ Frequently Asked Questions
Some photos won’t open—why? Photos in the archive are stored not as files, but as links to VK servers with a limited lifespan. For older archives, some links are no longer accessible—this is a limitation of the data itself, not the app.
Does the app send my chat history to the internet? No. The archive is parsed and grouped by people on your device. Only requests to download the images themselves via their links are sent over the network (just like in a browser).
What’s the best device to use? Any device will work for reading and searching. For grouping by person, you’ll need a computer (Windows/macOS/Linux).
🛠 Documentation for developers and LLM
Below is the technical section: architecture, stack, build, archive format, and how the demo data and face recognition work.
Stack and Platforms
| Layer | Technology |
|---|---|
| Language | Kotlin Multiplatform |
| UI | Compose Multiplatform (Material 3) |
| Image Loading | Coil 3 on top of Ktor 3 (CIO/OkHttp/JS/Darwin per platform) |
| DI | Koin 4 |
| Face storage (Desktop) | SQLite (sqlite-jdbc) |
| Face ML (Desktop) | OpenCV via Bytedeco (YuNet + SFace) |
| Pagination | Custom incremental page-by-page loader (50 messages at a time) |
| Platform | Status |
|---|---|
| Desktop (macOS/Windows/Linux, JVM) | ✅ Folder/ZIP selection + drag-and-drop; face recognition |
| Android | ✅ Open .zip via system file selection |
| Web (JS / Wasm) | ✅ Select extracted folder (webkitdirectory); ZIP and other formats not yet supported |
| iOS | ⚙️ UI is under development; file selection is not yet implemented |
Pagination was written manually because AndroidX/Cash App Paging 3 do not support wasmJS, and Web is the target platform. The custom loader
ConversationViewModelworks on all targets.
Architecture
shared/commonMain/.../ru/normno/vkarchivereader
├── core/ Cp1251 (декодер/энкодер windows-1251), Html (сущности/теги)
├── domain/model/ ChatSummary, Message, Attachment, MediaItem, ChatSortOrder
├── data/
│ ├── parser/ VkArchiveParser — чистый парсинг HTML архива
│ ├── source/ ArchiveSource (expect-доступ к файлам), ArchivePicker (expect),
│ │ DemoArchiveSource (встроенные демо-данные, common)
│ └── repository/ ArchiveRepository — сканирование архива, поиск, чтение страниц
├── face/ Детекция/эмбеддинги/кластеризация лиц (expect/actual)
├── di/ AppModule (Koin)
└── presentation/ welcome / archive / conversation / media / face / components
Data Flow
- User selects a folder/zip file → platform-specific
ArchiveSource(or built-inDemoArchiveSource). - Two-phase upload for responsiveness with large archives:
ArchiveRepository.open()— light pass (≈2 reads per chat): a list of chats with the exact number of messages and previews almost instantly.ArchiveRepository.indexMedia()— Background processing across all pages, filling media counters and the gallery, cooperatively yielding (yield).
- Chat messages are loaded page by page as you scroll.
- Search (
search) re-reads the necessary pages and filters them.
The archive format itself is documented in VK_ARCHIVE_STRUCTURE.md.
⚠️ Media in the archive consists of signed VK links with a limited lifespan; for older archives, some content is no longer available (a placeholder is displayed).
How the demo data is structured
DemoArchiveSource (in data/source, commonMain) — this is ArchiveSource,
which generates HTML in exactly the same format as an actual VK upload and
encodes it in Windows-1251 via Cp1251.encode. Thanks to this, the demo passes
through the same VkArchiveParser/ArchiveRepositoryas the real archive—without
a single special case in the rest of the code. The button on WelcomeScreen simply outputs
ArchivePickOutcome.Success(DemoArchiveSource()).
- Cyrillic characters are encoded in CP1251; emojis are written as numeric HTML entities
(
😀), since CP1251 doesn't support them—the parser converts them back. - Photos link to public placeholders (
randomuser.me— stable portraits for grouping by faces,picsum.photos— landscapes), so the demo requires an internet connection. - The “Memes and Cats” chat intentionally contains 56 posts (> 50) to demonstrate pagination; the “Friends” conversation features multiple authors (for filtering by author and people).
- Covered by the test
DemoArchiveTest(parsing, pagination, authors, media, search).
Grouping by people (Desktop)
Fully local pipeline (nothing leaves the device), launched from the “All Media” screen using the “People” button:
- Detection: YuNet (OpenCV Zoo, ~340 KB ONNX).
- Embeddings: SFace (128-dimensional vector; “one person” if cosine proximity > 0.363).
- Clustering: greedy online clustering (
OnlineFaceClusterer) — streaming, without prior knowledge of the number of people. - Runtime: OpenCV via Bytedeco. Only native libraries for the current desktop—
javacpp.platformingradle.properties(by defaultmacosx-arm64); for other operating systems, override (windows-x86_64,linux-x86_64,macosx-x86_64). Version pinned to4.9.0-1.5.10(in4.10.0nativevideoioon macOS arm64, it references a non-existentlibOrbbecSDK.1.9.dyliband dropsFaceDetectorYN). Models are downloaded once every~/.vkarchivereader/modelsfrom the LFS endpointmedia.githubusercontent.com. - Storage: local SQLite (
~/.vkarchivereader/faces.db); the photos themselves are not saved—only the link, group, and source chat.
Recognition is not yet available on Android/iOS/Web; expect/actual
(createFaceEngine) it can be added later.
Integration test
FaceDetectIntegrationTestdownloads real models and detects a face in a real portrait. It makes network requests, so by default it is skipped—to run:./gradlew :shared:jvmTest -DrunFaceIntegration=true.
Building from source
# Desktop (JVM)
./gradlew :desktopApp:run
# Android (debug APK)
./gradlew :androidApp:assembleDebug
# Web
./gradlew :webApp:wasmJsBrowserDevelopmentRun # Wasm
./gradlew :webApp:jsBrowserDevelopmentRun # JS
# iOS — открыть ./iosApp в Xcode
# Тесты
./gradlew :shared:jvmTest
Building installers
Installers are built jpackage using the Compose Desktop plugin, so each
format is built only on its own OS (it’s not possible to build .msi on macOS).
# macOS → .dmg (на macOS)
./gradlew :desktopApp:packageDmg -Pjavacpp.platform=macosx-arm64
# Windows → .msi (на Windows)
./gradlew :desktopApp:packageMsi -Pjavacpp.platform=windows-x86_64
# Linux → .deb (на Linux)
./gradlew :desktopApp:packageDeb -Pjavacpp.platform=linux-x86_64
# Android → .apk
./gradlew :androidApp:assembleDebug
-Pjavacpp.platformspecifies which platform’s native OpenCV/OpenBLAS libraries to include in the build. Without it, binaries for all operating systems would be included (~380 MB of extra weight; the DMG would balloon to ~460 MB). With the flag, only the required platform is included—the installer size is reduced by about 3.5 times. When building locally for your own host, you can omit the flag: the platform will be determined automatically.
Final conclusion:
desktopApp/build/compose/binaries/main/{dmg,msi,deb}/androidApp/build/outputs/apk/debug/androidApp-debug.apk
CI
.github/workflows/release.yml
’s workflow builds all three installers on the corresponding runners (Windows/macOS/Ubuntu).
It is triggered manually (Actions → Run workflow) or via a version tag, and upon pushing
the tag, it publishes the artifacts to a GitHub Release:
git tag v1.0.0 && git push origin v1.0.0
License
MIT.
Releases
Download · 3
Download · 3
Open issues
No open issues 🎉



