debug(sync): endpoint diag multi-endpoints (Data API + InnerTube) + bouton UI

This commit is contained in:
2026-09-27 08:26:10 -04:00
parent 720367fe13
commit fdb5a48023
5 changed files with 95 additions and 1 deletions
+23 -1
View File
@@ -108,7 +108,7 @@ import {
fetchGoogleLiked, fetchTwitchProfile, fetchTwitchFollows, hasGoogleWriteScope,
fetchGoogleWatchLater, pushGoogleWatchLater, fetchInnerTubeHistory,
fetchInnerTubeWatchLaterViaLib, pushInnerTubeWatchLater, fetchGoogleWatchLaterId,
fetchAccountChannels,
fetchAccountChannels, diagnoseInnertube,
} from './oauth.mjs';
import { getSearchMode, getYtDlpBin, hasCookiesFile, metricsSnapshot } from './providers/youtube-common.mjs';
import { ytScrapeCacheStats } from './providers/youtube.mjs';
@@ -585,6 +585,28 @@ r.get('/oauth/google/yt-channels', authMiddlewareCookieAware, oauthLimiter, asyn
}
});
// Diagnostic : état de chaque endpoint InnerTube authentifié + Data API.
r.get('/oauth/google/diag', authMiddlewareCookieAware, oauthLimiter, async (req, res) => {
try {
const conn = await freshOAuthToken(req.user.id, 'google');
const dataApi = {};
try {
await fetchGoogleWatchLaterId(conn.accessToken);
dataApi.watchLaterId = true;
} catch (e) {
dataApi.watchLaterId = false;
dataApi.watchLaterError = String(e?.message || '').slice(0, 80);
}
const innertube = await diagnoseInnertube(conn);
try {
console.log(`[oauth] diag user=${req.user?.id} ${JSON.stringify({ dataApi, innertube }).slice(0, 800)}`);
} catch {}
return res.json({ dataApi, innertube });
} catch (error) {
return res.status(error?.status || 502).json({ error: error?.message || 'diag_failed' });
}
});
r.put('/oauth/google/yt-channel', authMiddlewareCookieAware, oauthLimiter, (req, res) => {
try {
const channelId = String(req.body?.channelId || '').trim().slice(0, 64) || null;
+44
View File
@@ -717,6 +717,50 @@ function textOf(t) {
return String(t?.simpleText || '');
}
/**
* Diagnostic InnerTube authentifié : teste plusieurs endpoints et rapporte
* pour chacun ok/count ou le motif YouTube. But : isoler un rejet global
* du compte d'un problème spécifique à l'historique.
*/
export async function diagnoseInnertube(conn) {
const out = {};
let innertube = null;
try {
innertube = await getAuthedInnertube(conn);
out.session = { ok: true };
} catch (e) {
out.session = { ok: false, reason: String(e?.message || e).slice(0, 200) };
return out;
}
const probe = async (name, fn) => {
try {
const r = await fn();
out[name] = { ok: true, ...r };
} catch (e) {
out[name] = { ok: false, reason: libErrorDetail(e) };
}
};
await probe('subscriptionsFeed', async () => {
const feed = await innertube.getSubscriptionsFeed();
return { count: Array.isArray(feed?.contents) ? feed.contents.length : 0 };
});
await probe('library', async () => {
await innertube.getLibrary();
return {};
});
await probe('accountMenu', async () => {
const data = await innertube.session.actions.execute('/account/account_menu', {});
const blob = JSON.stringify(data?.data || data || '').slice(0, 200);
return { sample: blob.slice(0, 120) };
});
await probe('history', async () => {
const feed = await innertube.getHistory();
const n = Array.isArray(feed?.sections) ? feed.sections.reduce((a, s) => a + (s?.contents?.length || 0), 0) : 0;
return { count: n };
});
return out;
}
/**
* Chaînes YouTube du compte (sélecteur façon SmartTube) via account_menu.
* La chaîne par défaut peut être une coquille vide (relatedPlaylists sans