Получение текстового и машиночитаемого представления из Microsoft Launcher

от автора

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
Пример данных, закодированных в 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’а писал сам, нейронка чисто причесала его:

  1. эвристика: строка начинается с { или [ и заканчивается соответственно;

  2. попытка JSON.parse;

  3. дальше рекурсивно:

    • либо сериализовать в строку (сохраняя оригинальную схему),

    • либо “развернуть” в объект (--unwrap-strings).

Это сильно усложнило алгоритм, но без этого половина мусора оставалась внутри.

Проблема №4: контроль агрессивности очистки

Посмотрел — а там иконки, на самом деле, и не такие уж и большие, можно их оставить.

Поэтому добавлены параметры, цитирую:

  • --min-bytes — минимальный размер blob, который считаем «мусором»;

  • --keys — фильтр по именам полей (image, icon, avatar и т. д.);

  • --remove-array-element — удалять элемент массива полностью, а не заменять на null.

Это делает скрипт настраиваемым под разные форматы данных.

Иконки остаются нетронутыми:
base64

base64

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;}

В итоге пришлось сделать несколько паттернов:

  1. component=com.pkg/...

  2. cmp=com.pkg/...

  3. fallback на package=com.pkg

Если ни один не сработал, то считаем, что пакет неизвестен, выкидываем фуфло.

Как выглядит JSON-вывод

Логика в итоге довольно простая:

  1. берём список папок;

  2. индексируем ярлыки по container;

  3. для каждой папки:

    • находим все ярлыки с нужным 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>

Краткая инструкция для использования:

  1. Очищаем бекап от мусора:

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
  1. Конвертируем в нужный формат:

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
Получается всё довольно красиво:
JSON

JSON
TXT

TXT

Вывод

Фуф, навайбкодил. Подписка, купленная на честные $20, вполне себя оправдала. Но ушло на это 5 диалогов, так как нейросеть где-то на 10 сообщении уже начинает плыть. Системный промпт особо не помогал, ну и зачем он нужен, когда он распространяется на все диалоги, а мне нужно только на конкретно эти пять? Придумали бы общий контекст для диалогов, тег какой-нибудь общий или папку… Ушло на всё про всё пять дней, так как дебаг сгенерированного кода — дело небыстрое.

Два скрипта сделаны не просто так, а потому что первый из них — JSON Media Cleaner — это универсальный инструмент, который может пригодиться не только для Microsoft Launcher, но и для других случаев, когда нужно очистить JSON от мусора. Он может быть полезен для любых бекапов, которые содержат закодированные данные.

Ну, а второй пусть кушает очищенный от первого скрипта JSON и выдаёт уже конкретно структуру домашнего экрана. Он уже специфичный для Microsoft Launcher.

В результате получаем:

  • компактный JSON;

  • удобный текстовый файл.

А дальше уже можно делать что угодно:

  • переносить вручную;

  • писать конвертер под другой лаунчер;

  • или просто хранить это как документацию своего сетапа.

Таким образом, мы (“кто мы-то <…>, я один здесь на**й!” © Зелёный слоник) решили задачу получения текстового и машиночитаемого представления из Microsoft Launcher, несмотря на все приколы с его бекапами. Теперь у нас есть инструмент, который может помочь нам сохранить и перенести нашу домашнюю экранную конфигурацию в будущем.

ссылка на оригинал статьи https://habr.com/ru/articles/1024832/