Надоело вручную отмечать каждую серию на двух сайтах сразу? Этот скрипт сделает всё за вас.
Как только вы меняете количество серий, ставите оценку или меняете статус аниме на Шикимори, данные мгновенно улетают в ваш профиль на MyAnimeList. Больше никаких забытых тайтлов и расхождений в списках — всё работает в фоновом режиме, аккуратно и быстро.
SHIKI ↔ MAL SYNC
Автоматическая синхронизация списков в реальном времени
Скрипт для тех, кто ведет списки на обоих ресурсах. Он сам переносит серии, оценки и статусы с Шикимори на MyAnimeList.
ЧТО УМЕЕТ СКРИПТ:
🚀 Мгновенный перенос — изменили серию или оценку на Шики? Данные тут же улетели на MAL.
🧠 Умный поиск — сам находит аниме по названию и количеству эпизодов.
🛠 Ручной контроль — если автоматика ошиблась, можно в один клик привязать ID вручную.
🌐 Всеядность — полная поддержка всех доменов: .one, .me и нового .io.
🎨 Нативный дизайн — аккуратная панель, которая не мешает просмотру.
🔄 КАК ОБНОВИТЬСЯ НА НОВУЮ ВЕРСИЮ (v2.9.9 / 2.8.7):
Если у вас уже стоит старая версия скрипта, просто откройте редактор Tampermonkey, полностью удалите старый код и вставьте новый из спойлеров ниже.
Внимание: Не забудьте заново вставить ваш Client ID в строку
Если у вас уже стоит старая версия скрипта, просто откройте редактор Tampermonkey, полностью удалите старый код и вставьте новый из спойлеров ниже.
Внимание: Не забудьте заново вставить ваш Client ID в строку
const CLIENT_ID = '...';, иначе синхронизация не запустится.ИНСТРУКЦИЯ ПО УСТАНОВКЕ:
Шаг 1. Регистрация вашего приложения на MyAnimeList
Чтобы MAL разрешил скрипту обновлять ваш список, нужно получить личный ключ доступа (Client ID).
Перейдите в раздел API на MyAnimeList: myanimelist.net/apiconfig (нужно быть залогиненным).
Нажмите кнопку "Create ID".
Заполните форму следующими данными:
App Name: ShikiSync (или любое другое название).
App Type: Web.
App URL: shikimori.one (или любой из рабочих доменов).
Redirect URL: https://myanimelist.net/ — Внимание: это самый важный пункт. Нужно вписать именно этот адрес, иначе авторизация выдаст ошибку 401.
Description: Просто напишите "Syncing anime progress".
Поставьте галочку о согласии с правилами и нажмите Submit.
Теперь в списке приложений вы увидите свой Client ID (длинная строка из букв и цифр). Скопируйте её.
Шаг 2. Установка расширения и создание скрипта
Установите расширение Tampermonkey из магазина вашего браузера.
Нажмите на иконку расширения и выберите «Создать новый скрипт».
В открывшемся редакторе полностью удалите стандартный текст.
Скопируйте и вставьте код выбранной вами версии (Pepe или Classic) из блоков ниже.
Шаг 3. Активация ключа и сохранение
В самом начале кода найдите строку:
const CLIENT_ID = 'ВАШ_ID';Вставьте ваш скопированный ID вместо слов ВАШ_ID, оставив кавычки.
Нажмите в меню File -> Save (Ctrl + S).
Шаг 4. Первый запуск и авторизация
Откройте любую страницу аниме на Шикимори.
В нижнем правом углу появится розовая панель. Нажмите на кнопку «ОБНОВИТЬ ДОСТУП».
В открывшемся окне MAL нажмите синюю кнопку Allow. Окно закроется само.
Кнопка на панели сменится на «СИНХРОНИЗИРОВАТЬ». Готово!
ВЫБОР ВЕРСИИ:
Проблема с работоспособностью у пользователей
Привет всем! Столкнулся с проблемой: мой скрипт для синхронизации Shikimori и MyAnimeList отлично работает у меня, но, судя по отзывам, отказывается заводиться у других пользователей.
В чем может быть причина?
Я подозреваю, что дело либо в обработке авторизации, либо в логике получения ID тайтлов. Скрипт довольно комплексный: он умеет отслеживать изменение серий, оценок и даже удаление тайтлов из списка, но где-то закралась ошибка, мешающая стабильной работе "из коробки".
Прикладываю исходную, можно сказать старую версию кода v2.9.8.
Важно: Для безопасности я вырезал свой Client ID. Чтобы проверить скрипт, вам нужно вставить свой ID в переменную
CLIENT_ID в самом начале кода.Буду очень благодарен опытным разработчикам за советы по оптимизации и баг-фиксы!
Исходный код скрипта (v2.9.8)
// ==UserScript==
// @name Shiki ↔ MAL Sync (v2.9.8 - MAIN Pepe Look-Around)
// @namespace shiki-mal-sync
// @version 2.9.8
// @description Полная база 2.8.6. Розовый Пепе снова вертит головой.
// @author Gemini
// @match *://shiki.one/*
// @match *://shikimori.one/*
// @match *://shikimori.me/*
// @match *://shikimori.io/*
// @match *://myanimelist.net/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_xmlhttpRequest
// @connect myanimelist.net
// ==/UserScript==
(function() {
'use strict';
const CLIENT_ID = 'СЮДА ВПИСЫВАТЬ СВОЙ ID';
// Векторный Пепе с анимацией поворота
const PEPE_ICON = `
<svg width="22" height="18" viewBox="0 0 24 20" fill="none" xmlns="http://www.w3.org/2000/svg" style="animation: pepe-look 6s infinite ease-in-out; transform-origin: center;">
<path d="M6 8C6 5.79086 7.79086 4 10 4C12.2091 4 14 5.79086 14 8M14 8C14 10.2091 12.2091 12 10 12C7.79086 12 6 10.2091 6 8ZM14 8C14 5.79086 15.7909 4 18 4C20.2091 4 22 5.79086 22 8C22 10.2091 20.2091 12 18 12C16.8954 12 15.8954 11.5523 15.1716 10.8284" stroke="#ff9a9e" stroke-width="1.2" stroke-linecap="round"/>
<path d="M4 17C4 15 7 14 12 14C17 14 20 15 20 17" stroke="#ff9a9e" stroke-width="1.2" stroke-linecap="round"/>
<circle cx="10" cy="8" r="0.8" fill="#ff9a9e"/>
<circle cx="18" cy="8" r="0.8" fill="#ff9a9e"/>
</svg>`;
function isAnimePage() { return window.location.pathname.startsWith('/animes/'); }
if (window.location.hostname.includes('shiki')) {
GM_setValue('last_shiki_domain', window.location.origin);
}
if (window.location.hostname === 'myanimelist.net') {
if (window.location.hash.includes('access_token')) {
const params = new URLSearchParams(window.location.hash.replace('#', '?'));
const token = params.get('access_token');
if (token) {
GM_setValue('mal_token', token);
document.body.innerHTML = `<div style="background:#121212; color:#ff9a9e; height:100vh; display:flex; align-items:center; justify-content:center; font-family:sans-serif; flex-direction:column; text-align:center;"><h1>ГОТОВО!</h1><p>Токен обновлен.</p></div>`;
setTimeout(() => window.close(), 1500);
}
return;
}
if (window.location.pathname.startsWith('/anime/')) {
const animeTitle = document.querySelector('h1.title-name')?.innerText || "";
const savedDomain = GM_getValue('last_shiki_domain', 'https://shikimori.one');
if (animeTitle) {
const backBtn = document.createElement('a');
backBtn.href = `${savedDomain}/animes?search=${encodeURIComponent(animeTitle)}`;
backBtn.innerText = "← SHIKIMORI";
backBtn.style = `position: fixed; bottom: 20px; right: 20px; z-index: 9999; background: rgba(15, 15, 15, 0.85); backdrop-filter: blur(10px); border: 1px solid rgba(255, 154, 158, 0.5); border-radius: 12px; padding: 10px 18px; color: #ff9a9e; font-family: 'Consolas', monospace; font-size: 11px; font-weight: bold; text-decoration: none; box-shadow: 0 4px 20px rgba(0,0,0,0.6); transition: 0.3s;`;
document.body.appendChild(backBtn);
}
}
return;
}
const SHIKI_TO_MAL = { 'planned': 'plan_to_watch', 'watching': 'watching', 'rewatching': 'watching', 'completed': 'completed', 'on_hold': 'on_hold', 'dropped': 'dropped' };
function getAuthToken() { return GM_getValue('mal_token'); }
function startAuth() { window.open(`https://myanimelist.net/v1/oauth2/authorize?response_type=token&client_id=${CLIENT_ID}&code_challenge=static_challenge_string`, 'MAL Auth', 'width=500,height=600'); }
function getVal(selector) { const el = document.querySelector(selector); return el ? (parseInt(el.innerText.trim()) || 0) : 0; }
function getToday() { return new Date().toISOString().split('T')[0]; }
function getCleanName() {
const h1 = document.querySelector('h1');
if (!h1) return null;
let text = h1.innerText;
if (text.includes('/')) text = text.split('/').pop().trim();
return text.replace(/[½¼¾]/g, '').trim();
}
function logToBtn(msg, type = 'info') {
const btn = document.getElementById('btn_manual');
const dot = document.getElementById('mal_status_dot');
if (!btn || !dot) return;
btn.innerText = `> ${msg.toUpperCase()}`;
if (type === 'auth') {
dot.style.background = '#f44336'; btn.style.background = '#f44336';
btn.innerText = "ОБНОВИТЬ ДОСТУП"; btn.onclick = () => startAuth();
} else if (type === 'process') {
dot.style.background = '#ff9a9e'; dot.style.animation = 'mal-pulse 0.8s infinite';
} else if (type === 'success') {
dot.style.background = '#ff9a9e'; dot.style.animation = 'none';
btn.innerText = `>> ${msg.toUpperCase()}`; setTimeout(resetBtn, 3000);
}
}
function resetBtn() {
const btn = document.getElementById('btn_manual');
if (!btn) return;
btn.style.background = 'linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%)';
btn.innerText = "СИНХРОНИЗИРОВАТЬ"; btn.onclick = () => sync();
const dot = document.getElementById('mal_status_dot');
if (dot) { dot.style.background = '#ff9a9e'; dot.style.animation = 'none'; }
}
function drawUI() {
if (!isAnimePage()) { const ex = document.getElementById('mal_sync_panel'); if (ex) ex.remove(); return; }
if (document.getElementById('mal_sync_panel')) return;
const panel = document.createElement('div');
panel.id = 'mal_sync_panel';
panel.style = `position: fixed; bottom: 20px; right: 20px; z-index: 999999; background: rgba(15, 15, 15, 0.7); backdrop-filter: blur(20px); border: 1px solid rgba(255, 154, 158, 0.2); padding: 14px; border-radius: 18px; font-family: 'Consolas', monospace; width: 210px; box-shadow: 0 8px 32px rgba(0,0,0,0.4);`;
panel.innerHTML = `
<style>
@keyframes mal-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
@keyframes pepe-look { 0%, 45%, 55%, 100% { transform: scaleX(1); } 50% { transform: scaleX(-1); } }
</style>
<div style="display: flex; align-items: center; justify-content: center; margin-bottom: 10px; gap: 12px;">
<div style="color: #ff9a9e; font-size: 11px; font-weight: 900; text-shadow: 0 0 5px rgba(255,154,158,0.5);">M</div>
<div id="mal_status_dot" style="width: 6px; height: 6px; background: #ff9a9e; border-radius: 50%; box-shadow: 0 0 8px #ff9a9e;"></div>
<div style="display: flex; align-items: center; justify-content: center;">${PEPE_ICON}</div>
</div>
<button id="btn_manual" style="width: 100%; border: none; padding: 11px 5px; border-radius: 10px; background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%); color: #5d2a2c; font-size: 10px; font-weight: 800; cursor: pointer; text-transform: uppercase;">ЗАГРУЗКА...</button>
<div id="mal_info" style="font-size: 9px; color: #ff9a9e; text-align: center; margin-top: 10px; display: none; opacity: 0.9; font-weight: bold;"></div>
<div style="display: flex; gap: 5px; margin-top: 10px;">
<a id="mal_link" target="_blank" style="display: none; flex: 1; text-decoration: none; text-align: center; padding: 8px 0; border-radius: 8px; background: rgba(255,154,158,0.05); color: #ff9a9e; font-size: 10px; font-weight: bold; border: 1px solid rgba(255,154,158,0.2);">VIEW ON MAL ↗</a>
<button id="mal_set_id" style="background: rgba(255,154,158,0.1); border: 1px solid rgba(255,154,158,0.2); border-radius: 8px; color: #ff9a9e; font-size: 8px; cursor: pointer; padding: 0 8px;">ID</button>
</div>
`;
document.body.appendChild(panel);
document.getElementById('btn_manual').onclick = () => sync();
document.getElementById('mal_set_id').onclick = () => {
const curId = GM_getValue('mal_id_' + window.location.pathname, '');
const nid = prompt('Вставь ID тайтла с MyAnimeList:', curId);
if (nid !== null) { GM_setValue('mal_id_' + window.location.pathname, nid.trim()); location.reload(); }
};
fetchCurrentMALStatus();
}
async function getBestId(animeName, shikiTotal) {
const manualId = GM_getValue('mal_id_' + window.location.pathname);
if (manualId) return manualId;
return new Promise((resolve) => {
GM_xmlhttpRequest({
method: 'GET',
url: `https://api.myanimelist.net/v2/anime?q=${encodeURIComponent(animeName)}&limit=10&fields=my_list_status,num_episodes`,
headers: { 'Authorization': `Bearer ${getAuthToken()}` },
onload: (res) => {
if (res.status !== 200) return resolve(null);
const search = JSON.parse(res.responseText);
if (!search.data || search.data.length === 0) return resolve(null);
const lowShiki = animeName.toLowerCase();
const words = lowShiki.split(' ').filter(w => w.length > 2);
let valid = search.data.filter(item => {
const lowMal = item.node.title.toLowerCase();
return words.slice(0, 2).every(word => lowMal.includes(word));
});
if (valid.length === 0) valid = search.data;
let epMatch = valid.filter(item => item.node.num_episodes === shikiTotal);
if (epMatch.length === 1) return resolve(epMatch[0].node.id);
if (epMatch.length > 1) {
const markers = ['double', 'ω', '2nd', 'season 2', '2'];
const isS2 = markers.some(m => lowShiki.includes(m));
let best = epMatch.find(item => markers.some(m => item.node.title.toLowerCase().includes(m)));
return resolve(isS2 && best ? best.node.id : epMatch.sort((a,b)=>a.node.title.length - b.node.title.length)[0].node.id);
}
resolve(valid[0].node.id);
}
});
});
}
async function fetchCurrentMALStatus() {
const token = getAuthToken();
const animeName = getCleanName();
if (!token || !animeName) return;
const id = await getBestId(animeName, getVal('.total-episodes'));
if (!id) return;
GM_xmlhttpRequest({
method: 'GET',
url: `https://api.myanimelist.net/v2/anime/${id}?fields=my_list_status`,
headers: { 'Authorization': `Bearer ${token}` },
onload: (res) => {
if (res.status === 401) { logToBtn("AUTH", "auth"); return; }
const data = JSON.parse(res.responseText);
if (document.getElementById('mal_link')) { document.getElementById('mal_link').href = `https://myanimelist.net/anime/${id}`; document.getElementById('mal_link').style.display = 'block'; }
const malData = data.my_list_status;
if (malData && document.getElementById('mal_info')) {
document.getElementById('mal_info').style.display = 'block';
document.getElementById('mal_info').innerText = `MAL: ${malData.status.toUpperCase()} [${malData.num_episodes_watched} EP]`;
logToBtn(malData.score > 0 ? `${malData.num_episodes_watched}EP / ${malData.score}⭐` : "В СПИСКЕ", "success");
} else { resetBtn(); }
}
});
}
async function sync(action = 'update', targetStatus = null) {
const token = getAuthToken();
const animeName = getCleanName();
if (!token || !animeName) return;
logToBtn(action === 'delete' ? "УДАЛЕНИЕ..." : "SYNCING...", "process");
const id = await getBestId(animeName, getVal('.total-episodes'));
if (!id) return;
if (action === 'delete') {
GM_xmlhttpRequest({
method: 'DELETE',
url: `https://api.myanimelist.net/v2/anime/${id}/my_list_status`,
headers: { 'Authorization': `Bearer ${token}` },
onload: () => { logToBtn("УДАЛЕНО", "success"); document.getElementById('mal_info').style.display = 'none'; }
});
} else {
const score = getVal('.score-value');
const episodes = getVal('.current-episodes');
const total = getVal('.total-episodes');
let status = targetStatus || SHIKI_TO_MAL[document.querySelector('input[name="user_rate[status]"]')?.value] || 'watching';
if (total > 0 && episodes === total && status !== 'plan_to_watch') status = 'completed';
let data = `status=${status}&num_watched_episodes=${episodes}&score=${score}`;
if (status === 'watching') data += `&start_date=${getToday()}`;
if (status === 'completed') data += `&finish_date=${getToday()}`;
GM_xmlhttpRequest({
method: 'PUT',
url: `https://api.myanimelist.net/v2/anime/${id}/my_list_status`,
headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/x-www-form-urlencoded' },
data: data,
onload: () => fetchCurrentMALStatus()
});
}
}
document.addEventListener('mousedown', (e) => {
const option = e.target.closest('.option');
if (option) {
const status = option.getAttribute('data-status');
if (option.innerText.includes('Удалить')) setTimeout(() => sync('delete'), 500);
else if (SHIKI_TO_MAL[status]) setTimeout(() => sync('update', SHIKI_TO_MAL[status]), 1200);
return;
}
if (e.target.closest('.increment')) setTimeout(() => sync('update'), 2500);
if (e.target.closest('.stars-container')) setTimeout(() => sync('update'), 1500);
});
setInterval(drawUI, 2000);
setTimeout(drawUI, 1000);
})();Открыть код скрипта (Clean Edition)
// ==UserScript==
// @name Shiki ↔ MAL Sync (v2.9.8 - Clean Edition)
// @namespace shiki-mal-sync
// @version 2.9.8
// @description Старая добрая чистая версия без визуальных эффектов.
// @author Gemini
// @match *://shiki.one/*
// @match *://shikimori.one/*
// @match *://shikimori.me/*
// @match *://shikimori.io/*
// @match *://myanimelist.net/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_xmlhttpRequest
// @connect myanimelist.net
// ==/UserScript==
(function() {
'use strict';
// ВСТАВЬТЕ ВАШ CLIENT ID НИЖЕ
const CLIENT_ID = 'СЮДА ВПИСЫВАТЬ СВОЙ ID';
function isAnimePage() { return window.location.pathname.startsWith('/animes/'); }
if (window.location.hostname.includes('shiki')) {
GM_setValue('last_shiki_domain', window.location.origin);
}
if (window.location.hostname === 'myanimelist.net') {
if (window.location.hash.includes('access_token')) {
const params = new URLSearchParams(window.location.hash.replace('#', '?'));
const token = params.get('access_token');
if (token) {
GM_setValue('mal_token', token);
document.body.innerHTML = `<div style="background:#fff; color:#000; height:100vh; display:flex; align-items:center; justify-content:center; font-family:sans-serif;"><h1>Token Updated!</h1></div>`;
setTimeout(() => window.close(), 1500);
}
return;
}
return;
}
const SHIKI_TO_MAL = { 'planned': 'plan_to_watch', 'watching': 'watching', 'rewatching': 'watching', 'completed': 'completed', 'on_hold': 'on_hold', 'dropped': 'dropped' };
function getAuthToken() { return GM_getValue('mal_token'); }
function startAuth() { window.open(`https://myanimelist.net/v1/oauth2/authorize?response_type=token&client_id=${CLIENT_ID}&code_challenge=static_challenge_string`, 'MAL Auth', 'width=500,height=600'); }
function getVal(selector) { const el = document.querySelector(selector); return el ? (parseInt(el.innerText.trim()) || 0) : 0; }
function getToday() { return new Date().toISOString().split('T')[0]; }
function getCleanName() {
const h1 = document.querySelector('h1');
if (!h1) return null;
let text = h1.innerText;
if (text.includes('/')) text = text.split('/').pop().trim();
return text.replace(/[½¼¾]/g, '').trim();
}
function drawUI() {
if (!isAnimePage()) return;
if (document.getElementById('mal_sync_panel')) return;
const panel = document.createElement('div');
panel.id = 'mal_sync_panel';
panel.style = `position: fixed; bottom: 10px; right: 10px; z-index: 9999; background: #212121; color: #fff; padding: 10px; border-radius: 5px; font-family: Arial, sans-serif; font-size: 12px; border: 1px solid #444;`;
panel.innerHTML = `
<div style="margin-bottom: 5px; font-weight: bold;">MAL Sync</div>
<button id="btn_manual" style="display: block; width: 100%; margin-bottom: 5px; cursor: pointer;">Sync Now</button>
<button id="btn_auth" style="display: block; width: 100%; cursor: pointer; font-size: 10px;">Update Token</button>
`;
document.body.appendChild(panel);
document.getElementById('btn_manual').onclick = () => sync();
document.getElementById('btn_auth').onclick = () => startAuth();
}
async function getBestId(animeName, shikiTotal) {
return new Promise((resolve) => {
GM_xmlhttpRequest({
method: 'GET',
url: `https://api.myanimelist.net/v2/anime?q=${encodeURIComponent(animeName)}&limit=5&fields=num_episodes`,
headers: { 'Authorization': `Bearer ${getAuthToken()}` },
onload: (res) => {
if (res.status !== 200) return resolve(null);
const search = JSON.parse(res.responseText);
if (!search.data || search.data.length === 0) return resolve(null);
resolve(search.data[0].node.id);
}
});
});
}
async function sync(action = 'update', targetStatus = null) {
const token = getAuthToken();
const animeName = getCleanName();
if (!token || !animeName) return;
const id = await getBestId(animeName, getVal('.total-episodes'));
if (!id) return;
if (action === 'delete') {
GM_xmlhttpRequest({
method: 'DELETE',
url: `https://api.myanimelist.net/v2/anime/${id}/my_list_status`,
headers: { 'Authorization': `Bearer ${token}` }
});
} else {
const score = getVal('.score-value');
const episodes = getVal('.current-episodes');
const total = getVal('.total-episodes');
let status = targetStatus || SHIKI_TO_MAL[document.querySelector('input[name="user_rate[status]"]')?.value] || 'watching';
if (total > 0 && episodes === total) status = 'completed';
let data = `status=${status}&num_watched_episodes=${episodes}&score=${score}`;
GM_xmlhttpRequest({
method: 'PUT',
url: `https://api.myanimelist.net/v2/anime/${id}/my_list_status`,
headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/x-www-form-urlencoded' },
data: data
});
}
}
document.addEventListener('mousedown', (e) => {
const option = e.target.closest('.option');
if (option) {
const status = option.getAttribute('data-status');
if (option.innerText.includes('Удалить')) setTimeout(() => sync('delete'), 500);
else if (SHIKI_TO_MAL[status]) setTimeout(() => sync('update', SHIKI_TO_MAL[status]), 1000);
}
});
setInterval(drawUI, 3000);
})();❓Решение возможных проблем
Нажимаю «ОБНОВИТЬ ДОСТУП», но вылетает ошибка 401 или 400
Причина: Ошибка в заполнении Redirect URL на сайте MyAnimeList.
Решение: В настройках API на MAL нажмите Edit и проверьте, что в поле Redirect URL написано именно https://myanimelist.net/. Если там указан Шикимори — работать не будет.
Панель скрипта вообще не появилась на странице аниме
Причина: Tampermonkey не запустил скрипт.
Решение: Убедитесь, что вы нажали File -> Save после вставки кода. Обновите страницу Шикимори (F5). Проверьте, что в скрипте правильно указан ваш домен (особенно если вы на .io).
Скрипт нашел «не то» аниме на MAL
Причина: Названия на Шики и MAL иногда сильно различаются.
Решение: На панели скрипта есть кнопка [ID]. Зайдите на MAL, найдите нужное аниме, скопируйте цифры из ссылки (например, в myanimelist.net/anime/5114/... это 5114). Нажмите кнопку [ID] на панели Шики и вставьте эти цифры. Скрипт запомнит привязку навсегда.
Кнопка стала красной и написано «AUTH»
Причина: У токена MAL истек срок жизни (обычно раз в месяц).
Решение: Просто нажмите на кнопку и еще раз нажмите Allow в открывшемся окне.
Прилагаю скрины, чтобы было понятно, как это выглядит в браузере:
Так выглядит панель на Шикимори.
На странице MAL будет появляться кнопка возврата на Шики.(Защита: если скрипт не найдет прямую ссылку на тайтл, он откроет страницу поиска с нужным названием).

@FudoBenjiro@Graf_NEET, Спасибо за замечание, подпарвил)@Leo1558@FudoBenjiro@FudoBenjiro@Vert1go54321,Привет!
На MyAnimeList интерфейс создания ID (API Client) иногда меняется.
Главное — заполнить основные поля:
App Name: любое (например, ShikiSync).
App Type: выбирай Web.
Redirect URI: обязательно вставь myanimelist.net/ (это важно для работы скрипта).
Если форма всё равно выглядит странно, попробуй просто заполнить обязательные поля, отмеченные звёздочкой. После сохранения ты получишь Client ID — его и нужно вставить в код скрипта, так же скрипт был обновлён и поддерживает новый домен .io
И извини за долгий ответ(работа сожрала)
@Kirsky,На данный момент скрипт переносит только основной прогресс: статус (смотрю/просмотрено и т.д.), количество серий и оценку.
Заметки (личное текстовое описание) пока не синхронизируются, так как API MAL требует для этого отдельных прав доступа.
Если функция будет очень востребована, попробую добавить её в будущих обновлениях!
@KyarovБуду рад помощи!
Так же если у кого-то остался вариант скрипта со дня публикации, потерял старый исходник(на момент написания этого комментария 22 дня назад) буду рад его получить в свои руки для сравнения возможных ошибок.
@Blue cat, каких именно? А то я чё т не в курсе. Заранее спасибо.@_Just_Monika_95_@Leo1558, Сенен и Седзе аи попали под цензуру, а имеено превращение страницы в 404.@Меня взломали!@_Just_Monika_95_, А ты лолек любишь или я забыл?@_Just_Monika_95_@FudoBenjiro, Нужен перенос заметок 🙏