Статья на хабре - https://habr.com/ru/articles/1089900/
Программа переносит ваши оценки фильмов с Кинопоиска на IMDB. Работает в два простых шага:
- Парсинг — собирает ваши оценки с Кинопоиска и сохраняет их в файл.
- Проставление — берёт собранные оценки и ставит их на IMDB.
Эта часть предназначена для обычных пользователей — тех, кто просто хочет перенести свои оценки. Здесь нет технических деталей, только то, что нужно знать для работы.
Программа переносит оценки фильмов с Кинопоиска на IMDB. Она делает это сама: вам нужно лишь один раз войти в свои аккаунты и нажать пару кнопок.
-
Перейдите на страницу последнего релиза и скачайте установщик
.msi: 👉 Скачать последнюю версиюНа странице релиза найдите asset вида
KP IMDB Importer-<версия>.msi(например,KP IMDB Importer-1.0.2.msi) и скачайте его. -
Запустите скачанный файл двойным щелчком мыши.
-
Подтвердите установку, если Windows попросит права администратора (нажмите «Да»).
-
Дождитесь окончания установки.
- Windows (7/10/11 и новее).
- Установленный Google Chrome — программа управляет браузером для входа на Кинопоиск и IMDB.
- Интернет-соединение.
После установки в меню «Пуск» (и на рабочем столе) появится ярлык «KP IMDB Importer». Нажмите на него — откроется окно программы (версия отображается внизу слева).
Внутри программы следуйте шагам на экране:
- Выберите «Парсинг» — программа откроет Кинопоиск. Войдите в свой аккаунт Кинопоиска и нажмите Start Parsing. Оценки будут собраны и сохранены.
- Выберите «Проставление» — программа откроет IMDB. Войдите в свой аккаунт IMDB и нажмите Start Proset. Оценки будут проставлены.
Во время работы можно ставить процесс на паузу (Pause), продолжать (Continue) или полностью остановить (Stop).
Все файлы программы находятся в папке:
C:\Users\<ваше имя>\KP-IMDB-Importer
Здесь хранятся собранные оценки (kp-ratings-{ID}-{метод}.csv, например kp-ratings-123-api.csv) и служебные файлы (логи, профиль браузера).
⚠️ Не открывайте и не редактируйте файл дампа (kp-ratings-*.csv) во время работы программы. Программа постоянно записывает в него данные, и ручное изменение может привести к потере оценок.⚠️ Не запускайте «Парсинг» и «Проставление» одновременно. Программа сама запретит второй запуск, пока первый не закончится — дождитесь завершения.⚠️ Если файл дампа открыт в Excel, программа встанет на паузу — закройте файл и нажмите Continue.
Ниже — техническая информация для тех, кто будет запускать или дорабатывать проект.
- Java 21, Spring Boot 3.3.2, Thymeleaf
- Selenium (управление Chrome), jsoup
- JavaFX WebView (десктоп-оболочка), jpackage (сборка установщика)
- Сборка: Maven (wrapper
mvnw)
| Класс | Назначение |
|---|---|
ru.importer.notes.NotesApplication |
Разработка: запуск Spring Boot, UI открывается в браузере по адресу http://localhost:8085/notes. |
ru.importer.notes.desktop.DesktopLauncher |
Десктоп-версия: JavaFX WebView-окно, рендерит тот же UI без адресной строки. Можно запускать из IDE. |
ru.importer.notes.desktop.AppLauncher |
Главный класс для упакованного билда (.exe/.msi). Обходит ограничение JavaFX при запуске с classpath. |
Точка входа — NotesApplication.main (UI: http://localhost:8085/notes). Для отладки десктоп-окна запустите DesktopLauncher.main — JavaFX-окно само поднимет встроенный сервер на порту 8085.
В pom.xml по умолчанию skipTests=true, поэтому без аргументов тесты пропускаются. Запускайте явно:
# Unit-тесты
mvnw clean test -DskipTests=false
# Включая интеграционные тесты (реальные сайты КП/IMDB, отмечены @Tag("integration"))
mvnw test -DskipTests=false -Dgroups=integrationСейчас в проекте 273 unit-теста; интеграционные тесты в этот счёт не входят.
Десктоп-версия — это JavaFX WebView-оболочка, которая рендерит тот же Thymeleaf-UI в настоящем окне. Сборка в установщик выполняется через jpackage (входит в JDK 21).
- JDK 21 (включает
jpackageиjlink). - JavaFX 21.0.2 (win-модули в локальном Maven-кэше — уже объявлены в
pom.xml). - WiX Toolset 3.x — нужен только для
--type msi(одноразовая установка, скачать с https://wixtoolset.org/docs/v3/). Без WiX можно собрать только промежуточныйapp-image,.msiне создастся.
# 1. Fat-JAR (для разработки)
mvnw clean package -DskipTests=false
# 2. app-image (промежуточная папка target/dist/KP IMDB Importer/)
mvnw package -Pdesktop -DskipTests=false
# 3. .msi (нужен WiX) — результат: target/dist/KP IMDB Importer-<версия>.msi
mvnw package -Pdesktop-msi -DskipTests=falseЛибо напрямую скриптом (из корня проекта):
powershell -ExecutionPolicy Bypass -File build-msi.ps1 -MsiОба desktop-профиля (
desktop,desktop-msi) делегируют фактическую сборку вbuild-msi.ps1: тонкий jar +app/*, jlink runtime-image с JavaFX, jpackage. Скрипт вызывается с-SkipBuild, т.к. Maven уже собрал thin jar к фазеpackage.
Workflow .github/workflows/build-msi.yml на каждый push в main (и по ручному запуску workflow_dispatch):
- Ставит JDK 21 (Temurin) через
actions/setup-java(с кешем Maven-репозитория). - Запускает юнит-тесты (
mvnw clean test -DskipTests=false). - Ставит WiX Toolset 3.x (
choco install wixtoolset) — jpackage находит его автоматически вC:\Program Files (x86)\WiX Toolset v3.14\bin. - Собирает
.msiчерезbuild-msi.ps1 -Msi(JavaFX win-jar подтягиваются в локальный m2-кеш самой сборкой). - Загружает
.msiкак build-artifact (сохраняется всегда). - Публикует GitHub Release (вариант B) — только если версия в
pom.xmlизменилась относительно последнего опубликованного Release. Если версия не менялась — Release не трогается.
Версия для релиза берётся из pom.xml (<version>), тег релиза = версия, asset = KP IMDB Importer-<версия>.msi.
После установки .msi в меню «Пуск» и на рабочем столе появляется ярлык «KP IMDB Importer». По клику открывается десктоп-окно, которое само запускает встроенный сервер и Chrome для Selenium. Пользователю не нужна установленная Java — jpackage включает вырезанный JRE 21.
Иконка приложения подключена:
packaging/app.ico(валидный многомерный.ico, перегенерируется скриптомpackaging/create-icon.ps1, если исходный файл — PNG).build-msi.ps1автоматически передаёт--icon packaging/app.icoв jpackage.
В установленной версии (десктоп-окно / .msi) программа ставится в Program Files, куда писать нельзя. Поэтому все рабочие файлы по умолчанию хранятся в рабочей папке приложения:
%USERPROFILE%\KP-IMDB-Importer (например C:\Users\<user>\KP-IMDB-Importer)
├── kp-ratings-{ID}-{метод}.csv # дамп оценок КП (результат парсинга)
└── chrome-profile\ # профиль Chrome для Selenium (сессии КП/IMDB)
Пользователь по-прежнему может указать собственную директорию на формах — рабочая папка подставляется только как значение по умолчанию.
Перенос оценок выполняется двумя независимыми этапами:
- «Парсинг» — получение оценок пользователя с Кинопоиска и сохранение их в дамп
kp-ratings-{ID}-{метод}.csv. - «Проставление» — чтение дампа и проставление оценок на IMDB.
Этапы запускаются отдельно друг от друга и могут выполняться в разное время: сначала парсинг формирует дамп, затем проставление работает уже только с ним. Одновременный запуск парсинга и проставления запрещён — приложение отклоняет второй запуск, пока первый не завершён.
Оценки пользователя получаются одним из трёх способов (выбор на странице приложения):
| Способ | Ключ | Что делает | Что нужно |
|---|---|---|---|
| API (token) | api |
Быстрый парсинг через kinopoiskapiunofficial.tech без браузера. API отдаёт все оценки пользователя | ID пользователя КП, API-токен |
| Браузер | selenium |
Полный парсинг всех оценок в открытом Chrome (залогиненная сессия), догружает оригинальные названия | ID пользователя КП, вход в КП в Chrome |
| Полный (браузер + API) | full |
Selenium собирает полный список оценок (title/оценка/год/kpId), затем API-обогащение каждого фильма — оригинальное название, год (истина из API), imdbId. Повторный запуск дописывает дельту: новые фильмы и «дефицитные» (нет imdb_id / оригинального названия / невалидный год); удалённые с КП оценки остаются в дампе | ID пользователя КП, API-токен, вход в КП в Chrome |
Название с «приклеенным» годом зачищается («Мортал Комбат 22025» → «Мортал Комбат 2» + 2025); год проверяется на диапазон 1890–2050: при API — дозапросом данных фильма (ретраи при 429/5xx, троттлинг 600 мс), при Selenium — открытием страницы фильма.
При способе full парсинг выполняется в две фазы: (1) Selenium собирает полный список оценок со страниц выгрузки КП (по 20 фильмов, title/оценка/год/kpId); (2) для каждого фильма вызывается GET /v2.2/films/{id} — возвращает оригинальное название, год (источник истины) и imdbId. При исчерпании дневной квоты API (402 после трёх проверок) этап корректно завершается с сообщением «квота токена на сегодня исчерпана, дамп сохранён, продолжите завтра»; следующий запуск продолжит обогащение с места остановки. Коды 429 и 5xx — ретраи; 401/403 — невалидный токен, парсинг прерывается.
Результат парсинга сохраняется в kp-ratings-{ID}-{метод}.csv (например, kp-ratings-123-api.csv: ID профиля КП + способ парсинга; повторный парсинг тем же способом перезаписывает файл, кроме full — тот дописывает дельту, не удаляя существующие строки). Файл перезаписывается после каждых 5 обработанных фильмов, чтобы статусы появлялись даже при остановке приложения.
- Источник данных — самый свежий по времени изменения файл
kp-ratings-*.csvиз директории логов (путь указывается на форме); если файлов нового формата нет — старыйkp-ratings.csv. Статусы сохраняются в тот же файл. - Дубли внутри дампа по
kp_idубираются; одинаковый валидныйimdb_idу разных названий сбрасывается у обоих (легитимная пара «фильм + сериал» с одинаковым названием не трогается). - Фильм с
imdb_idоткрывается напрямую и проверяется (JSON-LD: тип Movie/TVSeries, название, год ±1); чужой тип (подкаст-эпизод, игра) — id сбрасывается, строка идёт в «не найден» без ставки. - Поиск по названию и году: при нескольких точных совпадениях названия выбор различается по году (ровно один матч с годом ±1 от года фильма), иначе берётся первый по релевантности; выбранная страница проходит полную верификацию (тип, название, год ±1). Успешная ставка при таком неоднозначном выборе помечается статусом проставлено с оговоркой; не прошла верификацию — «не найден».
- Результаты: та же оценка — пропущено (уже стоит оценка); другая — руками (оценки отличаются); нет названий или рейтинг 0 — неполные данные (финальный статус); иначе оценка проставляется (успешно) и проверяется повторным чтением.
- При повторном прогоне «не найден» ретраится как «ошибка» (imdb_id сбрасывается); остальные завершённые статусы пропускаются.
- Между фильмами пауза 1–2 секунды (имитация пользователя).
Открыть http://localhost:8085/notes/ — страница-«выбор» с двумя карточками: «Парсинг» и «Проставление».
- На странице выбора нажать «Парсинг» → открывается выбор способа (три карточки: API / Браузер / Полный) с пояснением. Выбрать и нажать «Next».
- Форма показывает только нужные поля:
- Браузер — ID пользователя КП и директорию логов;
- API — дополнительно API-токен;
- Полный — ID пользователя КП, директорию логов и API-токен.
- Если дамп
kp-ratings-{ID}-{метод}.csvв указанной директории уже существует — появится предупреждение о перезаписи (подтвердить или вернуться). - Нажать «Next» — откроется Chrome. На странице показывается число оценок на КП (для Браузера и Полного — реальное число, для API — сколько вернул API).
- Вручную войти в аккаунт Кинопоиска в открывшемся окне (новая вкладка).
- Нажать Start Parsing — парсинг пойдёт в фоне, страница с прогрессом обновится автоматически.
- После завершения — страница со статистикой и таблицей по каждому фильму (
/parsing-result).
- На странице выбора нажать «Проставление» → форма, где указывается путь к директории с дампом оценок.
- Нажать «Next» — откроется Chrome на imdb.com.
- Вручную войти в аккаунт IMDB в открывшемся окне (новая вкладка).
- Нажать Start Proset — проставление пойдёт в фоне, страница с прогрессом обновится автоматически.
- После завершения — страница со статистикой и таблицей по каждому фильму (
/result).
- Доступны кнопки Pause (пауза после текущего фильма), Continue (продолжить), Stop (полная остановка после текущего фильма; дамп сохраняется).
- Если файл дампа открыт в Excel, этап встаёт на паузу с подсказкой закрыть файл и нажать Continue.
- Прогресс и лог видны в реальном времени (SSE) на раздельных страницах прогресса парсинга и проставления.
Важно: не трогайте файл дампа во время процесса — файл перезаписывается по ходу работы, и ручное редактирование может привести к потере данных или конфликту записи.
| Файл | Описание |
|---|---|
kp-ratings-{ID}-{метод}.csv |
Дамп оценок КП со статусами (имя = ID профиля КП + способ парсинга; примеры: kp-ratings-123-api.csv, kp-ratings-123-full.csv). Способ full дописывает дельту при повторном запуске, не перезаписывая существующие строки. Если файлов нового формата нет, этап «Оценка» читает старый kp-ratings.csv. UTF-8 с BOM, разделитель ;, заголовок title;original_title;english_title;year;rating;kp_id;imdb_id;status;error. Запись атомарная (сначала .tmp, затем rename) — при сбое файл не усекается. |
Подробный лог хода процесса пишется в консоль приложения (SLF4J).
| Статус | Описание |
|---|---|
| успешно | Оценка проставлена на IMDB |
| проставлено с оговоркой | Оценка проставлена при неоднозначной выдаче поиска (несколько точных совпадений названия) — завершённый статус |
| не найден | Фильм не найден на IMDB |
| пропущено (уже стоит оценка) | На IMDB уже стоит такая же оценка |
| руками (оценки отличаются) | На IMDB стоит другая оценка |
| неполные данные | В строке нет названий или рейтинг 0 — считается обработанной |
| ошибка | Произошла ошибка при обработке |
Статусы записываются в дамп по ходу проставления. Завершённые (все, кроме «не найден» и «ошибка») при повторном запуске пропускаются.
notes/
├── src/main/java/ru/importer/notes/
│ ├── movie/ # Processor (оркестратор этапов), ParsingStage (КП → дамп),
│ │ # ProsettingStage (дамп → IMDB), KpDumpWriter (запись дампа),
│ │ # ProcessCoordinator (запрет параллельных этапов), ImportProgress (SSE)
│ ├── imdb/ # ImdbNotesExporter (остов этапа), ImdbSearchAndSelect (поиск/выбор),
│ │ # ImdbPageVerifier (верификация JSON-LD), ImdbRatingClicker (оценка),
│ │ # auth/AuthManager (единственный WebDriver)
│ ├── kp/ # KpNotesImporter (остов парсинга КП), KpRatingsPageParser,
│ │ # GluedYearTitleCleaner (склеенный год), KpYearValidator (1890–2050),
│ │ # Selenium/Api/CsvKpRatingsProvider (способы получения оценок)
│ ├── web/MainController.java # веб-контроллер (маршруты этапов, страница ошибки)
│ ├── desktop/ # DesktopLauncher (JavaFX WebView-окно), AppLauncher (.exe/.msi)
│ ├── util/ # WorkingDir (рабочая папка), AppVersionReader (версия в окне),
│ │ # ChromeProcessKiller (авто-закрытие своего Chrome), ErrorFormatter
│ ├── log/LogFileService.java # атомарная запись/чтение дампа (kp-ratings-{ID}-{метод}.csv / kp-ratings.csv)
│ └── dto/ # модели данных (MovieData: статусы MovieStatus)
├── src/main/resources/templates/ # home, main, method-form, confirm-overwrite, login-kp, login-imdb,
│ # importing-parsing, importing-proset, parsing-success, proset-form,
│ # success, error
├── packaging/ # иконка app.ico, скрипт create-icon.ps1
├── build-msi.ps1 # сборка десктоп-установщика через jpackage
└── pom.xml
| URL | Метод | Назначение |
|---|---|---|
/ |
GET | Страница-«выбор» этапа (Парсинг / Проставление) |
/main |
GET | Выбор способа парсинга |
/method |
POST | Форма данных для выбранного способа парсинга |
/submit |
POST | Подготовка парсинга (с проверкой перезаписи дампа) |
/submit-confirmed |
POST | Подтверждение перезаписи дампа → открытие браузера КП |
/start-parsing |
POST | Запуск этапа «Парсинг» |
/parsing-result |
GET | Результат этапа «Парсинг» |
/proset |
GET | Форма проставления (путь к дампу) |
/proset-submit |
POST | Подготовка проставления (проверка дампа, открытие IMDB) |
/start-proset |
POST | Запуск этапа «Проставление» |
/result |
GET | Результат этапа «Проставление» |
/progress, /log |
GET | SSE: прогресс и лог текущего этапа |
/stop, /pause, /resume |
POST | Управление текущим этапом |
/change-log-dir |
POST | Смена директории дампа во время паузы «нужен новый путь» |
/error |
GET | Страница ошибки (полный стек-трейс в сворачиваемом блоке) |