TL;DR Автору понадобилось экспортировать состояние домашнего экрана Microsoft Launcher. Для этого пришлось написать два скрипта и столкнуться с различными проблемами. Кому нужна сразу инструкция, тык сюда.
Дисклеймеры
Общий дисклеймер • О личности автора • Отказ от ответственности • Об использовании ChatGPT
-
Для сведущих в JS и в операциях с JSON: никаких срывов прокровов тут не будет, это простейшие манипуляции с объектами JavaScript посредством NodeJS.
Аннотация
Рассмотрен вопрос конвертации JSON-экспорта из Microsoft Launcher в текстовое и машиночитаемое представление. Представлено два скрипта: первый для очистки и подготовки бекапа лаунчера, второй для конвертации данных в нужный формат. В статье обсуждаются проблемы, связанные с различными приколами файла экспорта Microsoft Launcher и его структурами данных (например, это base64-данные, которые попусту занимали много места). Представлена инструкция для полного цикла очистки, подготовки и конвертации данных из Microsoft Launcher. У автора это был самый первый опыт вайб-кодинга.
Введение
Когда вы полностью зависимы от одного лаунчера от одного производителя, и меняете его на другой, где установлено иное приложение домашнего экрана, то возникает большая трата времени по настройке нового лаунчера — все эти перетыки иконок, папок, виджетов и так далее. Хотелось бы иметь возможность экспортировать состояние домашнего экрана хотя бы чисто для себя — в какой, например, папке у меня какие приложения, какие виджеты и так далее. Было бы ещё круто иметь машиночитаемое представление, чтобы можно было в будущем, если появится такая возможность, сконвертировать эти данные в новый формат, который будет поддерживать новый лаунчер (вдруг получится?). В общем, я решил написать скрипт, который бы конвертировал JSON-экспорт из Microsoft Launcher в текстовое и машиночитаемое представление. Но оказалось, что это не так просто, как я думал.
Мой случай
У меня установлено около 550 приложений, они распределены по 5 страницам домашнего экрана. Есть папки, виджеты и так далее.
Есть первый экран — “глобальный”, он содержит до 20 папок, где внутри по 60 приложений. В “глобальном” экране я кладу ярлыки только тех приложений, которые будут актуальны для любой юрисдикции, где бы я ни находился — например, браузер, почта, мессенджеры, карты, тулзы/утилиты и т. д.
Дальше идут “региональные” экраны, где я размещаю приложения, которые могут быть специфичны для определённой страны. Те самые нацмессенджеры, местные сервисы и так далее. В каждом “региональном” экране либо тупо список приложений, либо папки с приложениями по удобству.
В конце идут фирменные лаунчеровские виджеты, типа времени проведения за экраном, статистики запусков, статистика прилётов, расчёт траектории попадания, кнопка запуска COVID-19, “Райан Гослинг каждый день”, цитаты Стетхема, новые расистские панчи против белых и всё в таком духе.
Самое ценное здесь — ярлыки и папки приложений, так как организовывался этот экран несколько (четыре) лет подряд, и терять наработки ой как не хочется. Казалось бы, в чём проблема? На старом устройстве ты делаешь бекап, на новом — импортируешь. Но что, если мне придётся сменить лаунчер? Часто они не поддерживают импорт/экспорт из сторонних программ домашних экранов, а помнишь, в какой из 60 папок какие из 550 приложений находятся — я как-то не очень в этом. Даже если мне придётся раскладывать всё это вручную, я хотел бы иметь файл-помощник, который содержал бы список всех приложений, их расположение на домашнем экране, какие папки и виджеты есть, и так далее. И желательно, чтобы этот файл был в удобном для чтения формате как человеком, так и для машины.
Я хотел бы иметь возможность видеть всю эту схему текстом. Я нашёл, что Microsoft Launcher позволяет экспортировать данные в формате JSON, но этот файл был огромным — около 17 МБ. Я решил написать скрипт на JS, который бы конвертировал этот JSON в более удобный формат. Но когда я начал разбирать структуру данных, я столкнулся с проблемой — многие (бесполезные) данные были закодированы в base64 и занимали много места. Решено: сначала делаем скрипт для очистки и подготовки бекапа лаунчера, а уже потом — скрипт для конвертации данных в нужный формат.
Будем, кстати, вайб-кодить (это новое слово от слонов и слоних Кремниевой долины, я уверен, будет на ходу через полгода). Как раз GPT-4 вышла, и вроде бы она научилась что-то внятное генерировать. Погнали!
1. Делаем бекап, разбираем его
Что там сложного-то? Записывайте!
-
Открываете настройки лаунчера;
-
Заходите в настройки;
-
Выбираете “Резервное копирование и восстановление”;
-
Выбираете “Создание резервной копии Microsoft Launcher”;
-
Куда-то там копируете, например, в облако OneDrive.
Получается такой файл, что-то типа Arrow36backup17899665511220.zip. Распаковываем. Внутри — единственный файл launcher_backup_main.bak. Меняем расширение на .json и открываем его в любом текстовом редакторе.
Предупреждаю, это от 3 до 24 МБ данных, которые не каждый редактор может переварить!
Как выглядит JSON-файл
Видим, что это JSON-структура, которая содержит много данных о нашем домашнем экране, папках, виджетах и так далее. Но там же есть много бесполезных данных, которые закодированы в base64 и занимают много места. Для нашего текстового представления нам эти данные не нужны, поэтому мы будем их удалять.
Пример данных, закодированных в base64
Поэтому я написал скрипт на JS, который бы проходился по всему JSON-дереву, находил все строки, которые выглядят как base64, и удалял их.
2. Разработка скрипта очистки: что пошло не так и как это решалось
Начнём со списка проблем:
Проблема №1: base64 — не просто строки
В бекапе Microsoft Launcher медиа представлены не в одном формате, это могут быть:
-
обычный base64;
-
base64url (
-и_вместо+и/); -
data URL (
data:image/png;base64,...); -
строки с “реальными” переносами, не экранированными;
-
строки с переводами каретки
\n,\r,\tпрямо внутри текста.
Последний случай особенно неприятный: JSON валиден, но данные внутри строки — «разорванные». Простое регулярное выражение перестаёт работать.
Решил это дело нормализацией строки, сносил whitespace’ы и разворачивал (десериализовывал) escape-последовательности (иногда двойные).
Проблема №2: как понять, что это именно изображение
Расписал техзхадание, цели, ценности, контекст в OpenAI ChatGPT. Конечно, сказывается лимит в 20 000 знаков, после которого ихнее окошко ввода начинает безб-жно виснуть, поэтому копируем это всё из VS Code.
Спросил у нейросетки, как отличить изображения от любой другой длинной строки, ну и попросил накидать алгоритм. С техзаданием оно поплыло мощно, выдало несуразицу про жестокость контента (видимо, впихивание большого контекста для модели — это издевательство над её электронными мозгами).
Получилось примерно так по детекту экранированных медиа:
-
декодировать base64 в буфер;
-
проверить сигнатуры файлов:
-
PNG (
89 50 4E 47); -
JPEG (
FF D8); -
GIF (
47 49 46); -
WEBP (
RIFF...WEBP); -
TIFF, BMP, HEIF/AVIF;
-
-
плюс попытка распознать SVG через текст.
Остальные блобы оно не трогает (их и не будет, по идее).
Проблема №3: строки, которые на самом деле JSON
Пример:
{ "someField": "{\"nested\": {\"image\": \"base64...\"}}"}
Тут нейросеть сдалась. Детект JSON Statham’а писал сам, нейронка чисто причесала его:
-
эвристика: строка начинается с
{или[и заканчивается соответственно; -
попытка
JSON.parse; -
дальше рекурсивно:
-
либо сериализовать в строку (сохраняя оригинальную схему),
-
либо “развернуть” в объект (
--unwrap-strings).
-
Это сильно усложнило алгоритм, но без этого половина мусора оставалась внутри.
Проблема №4: контроль агрессивности очистки
Посмотрел — а там иконки, на самом деле, и не такие уж и большие, можно их оставить.
Поэтому добавлены параметры, цитирую:
-
--min-bytes— минимальный размер blob, который считаем «мусором»; -
--keys— фильтр по именам полей (image,icon,avatarи т. д.); -
--remove-array-element— удалять элемент массива полностью, а не заменять наnull.
Это делает скрипт настраиваемым под разные форматы данных.
Иконки остаются нетронутыми:
3. Разработка скрипта конвертации
Хорошо, мы почистили это месиво от лишнего мусора. Теперь задача — вытащить из него ровно то, ради чего всё это затевалось: структуру экранов, папок и приложений.
Что вообще есть в бекапе
Если открыть очищенный JSON и немного в него потыкать, быстро становится понятно, что нас интересуют два массива:
-
KeyForAllDesktopFolders— список папок; -
KeyForAllDesktopShortcuts— список ярлыков.
Причём связь между ними плоская: ярлыки лежат отдельно и просто ссылаются на контейнер (то есть папку) через поле container.
Соответственно, схема такая:
-
есть папка с
id; -
есть куча ярлыков, у которых
container === id папки.
ОK, основной каркас есть, пошли дальше собирать инфу о бекапе Microsoft Launcher.
Проблема: где тут вообще приложение
Самый неприятный момент — это извлечение package name приложения.
Он лежит не в отдельном поле, а внутри строки intent. Причём формат может отличаться. Встречаются варианты:
#Intent;...;component=com.example.app/.MainActivity;end
или
Intent { ... cmp=com.example.app/.MainActivity }
или вообще что-то третье.
То есть это не JSON, не структура, а просто строка, которую нужно парсить регулярками. ОК, парсим… ты давай, генерируй-генерируй, не захлебнись…
Вот метод
function parsePackageFromIntent(intent) { if (!intent || typeof intent !== 'string') return null; // Pattern 1: URI-style "#Intent;...;component=com.pkg/.Activity;end" let m = intent.match(/(?:component=)([^\/;\s]+)\//); if (m && m[1]) return m[1]; // Pattern 2: "Intent { ... cmp=com.pkg/.Activity }" m = intent.match(/(?:\bcmp=)([^\/\s}]+)\//); if (m && m[1]) return m[1]; // Pattern 3: "cmp=com.pkg/.Something" (fallback parsing) m = intent.match(/\bcmp=([^\s;}]+)/); if (m && m[1]) return m[1].split('/')[0]; // Pattern 4: "package=com.pkg" m = intent.match(/\bpackage=([^\s;]+)/); if (m && m[1]) return m[1]; return null;}
В итоге пришлось сделать несколько паттернов:
-
component=com.pkg/... -
cmp=com.pkg/... -
fallback на
package=com.pkg
Если ни один не сработал, то считаем, что пакет неизвестен, выкидываем фуфло.
Как выглядит JSON-вывод
Логика в итоге довольно простая:
-
берём список папок;
-
индексируем ярлыки по
container; -
для каждой папки:
-
находим все ярлыки с нужным
container; -
вытаскиваем из них
packageиtitle; -
собираем в итоговую структуру.
-
Получается примерно такая модель:
{ "folderId": 123, "folderTitle": "Мессенджеры", "screenId": 0, "items": [ { "package": "org.telegram.messenger", "title": "Telegram" } ]}
Ничего лишнего. Остальные поля всё равно от лаунчера к лаунчеру разня́тся…
Теперь человекочитаемый формат
Поэтому второй выход — обычный текст. Тут магии нет, тупо маппинг:
• Folder "Мессенджеры" [id=123, screen=0] - 5 item(s) - Telegram <org.telegram.messenger> - WhatsApp <com.whatsapp>
Краткая инструкция для использования:
-
Очищаем бекап от мусора:
Bash:
wget -qO- https://gist.githubusercontent.com/Kenya-West/60f6297e16b8991204f2ee8073f298fa/raw/bc2458a11d42fbe92bd83de76b2aaef701306d8b/script.js | node - ./launcher_backup_main.json ./cleaned.json --unwrap-strings
PowerShell:
irm https://gist.githubusercontent.com/Kenya-West/60f6297e16b8991204f2ee8073f298fa/raw/bc2458a11d42fbe92bd83de76b2aaef701306d8b/script.js | node - .\launcher_backup_main.json .\cleaned.json --unwrap-strings
-
Конвертируем в нужный формат:
Bash:
wget -qO- https://gist.githubusercontent.com/Kenya-West/29049f39aeaf9d3b6730fc3ad529047c/raw/6c0cb3bfce733d603128975caeabe474d7859925/script.js | node - ./cleaned.json --json-out ./list.json --text-out ./list.txt
PowerShell:
irm https://gist.githubusercontent.com/Kenya-West/29049f39aeaf9d3b6730fc3ad529047c/raw/6c0cb3bfce733d603128975caeabe474d7859925/script.js | node - .\cleaned.json --json-out .\list.json --text-out .\list.txt
Ну или всё сразу:
Bash:
wget -qO- https://gist.githubusercontent.com/Kenya-West/60f6297e16b8991204f2ee8073f298fa/raw/bc2458a11d42fbe92bd83de76b2aaef701306d8b/script.js | node - ./launcher_backup_main.json ./cleaned.json --unwrap-stringswget -qO- https://gist.githubusercontent.com/Kenya-West/29049f39aeaf9d3b6730fc3ad529047c/raw/6c0cb3bfce733d603128975caeabe474d7859925/script.js | node - ./cleaned.json --json-out ./list.json --text-out ./list.txt
PowerShell:
irm https://gist.githubusercontent.com/Kenya-West/60f6297e16b8991204f2ee8073f298fa/raw/bc2458a11d42fbe92bd83de76b2aaef701306d8b/script.js | node - .\launcher_backup_main.json .\cleaned.json --unwrap-stringsirm https://gist.githubusercontent.com/Kenya-West/29049f39aeaf9d3b6730fc3ad529047c/raw/6c0cb3bfce733d603128975caeabe474d7859925/script.js | node - .\cleaned.json --json-out .\list.json --text-out .\list.txt
Получается всё довольно красиво:
Вывод
Фуф, навайбкодил. Подписка, купленная на честные $20, вполне себя оправдала. Но ушло на это 5 диалогов, так как нейросеть где-то на 10 сообщении уже начинает плыть. Системный промпт особо не помогал, ну и зачем он нужен, когда он распространяется на все диалоги, а мне нужно только на конкретно эти пять? Придумали бы общий контекст для диалогов, тег какой-нибудь общий или папку… Ушло на всё про всё пять дней, так как дебаг сгенерированного кода — дело небыстрое.
Два скрипта сделаны не просто так, а потому что первый из них — JSON Media Cleaner — это универсальный инструмент, который может пригодиться не только для Microsoft Launcher, но и для других случаев, когда нужно очистить JSON от мусора. Он может быть полезен для любых бекапов, которые содержат закодированные данные.
Ну, а второй пусть кушает очищенный от первого скрипта JSON и выдаёт уже конкретно структуру домашнего экрана. Он уже специфичный для Microsoft Launcher.
В результате получаем:
-
компактный JSON;
-
удобный текстовый файл.
А дальше уже можно делать что угодно:
-
переносить вручную;
-
писать конвертер под другой лаунчер;
-
или просто хранить это как документацию своего сетапа.
Таким образом, мы (“кто мы-то <…>, я один здесь на**й!” © Зелёный слоник) решили задачу получения текстового и машиночитаемого представления из Microsoft Launcher, несмотря на все приколы с его бекапами. Теперь у нас есть инструмент, который может помочь нам сохранить и перенести нашу домашнюю экранную конфигурацию в будущем.
ссылка на оригинал статьи https://habr.com/ru/articles/1024832/