Merge pull request #1 from yusufipk/claude/youtube-outlier-badge-bug-d58fe1

Score cards and the watch panel again after the player endpoint closed
This commit is contained in:
2026-08-22 15:27:03 +03:00
committed by GitHub
3 changed files with 59 additions and 13 deletions
+27 -2
View File
@@ -91,11 +91,26 @@
/* --- Kanal kimliği --------------------------------------------------- */
/* Kanal sayfasındaki kartlarda kanal bağlantısı hiç bulunmuyor: zaten o
* kanaldasın, YouTube adı kartta tekrar etmiyor. O kartların kanalı sayfanın
* kendi adresidir, video başına ayrı bir istek atmaya gerek yok. */
function pageChannel() {
var path = location.pathname;
var m = /^\/channel\/(UC[\w-]{22})(?:\/|$)/.exec(path);
if (m) return { channelId: m[1] };
m = /^\/(@[\w.\-]+)(?:\/|$)/.exec(path);
if (m) return { handle: m[1] };
/* Eski /c/ ve /user/ adresleri: çözüm için tam adres gerekiyor. */
m = /^\/((?:c|user)\/[^/]+)(?:\/|$)/.exec(path);
if (m) return { handle: location.origin + "/" + m[1] };
return null;
}
/* Handle'dan kanal kimliğine çözüm kalıcıdır (değişmez), o yüzden TTL'siz
* saklanır. İzleme sayfasının yan listesinde kartta kanal bağlantısı hiç
* olmayabiliyor; orada son çare videonun kendi ucundan sorulur. */
function channelIdOf(card, videoId) {
var found = handleOf(card);
var found = handleOf(card) || pageChannel();
if (found && found.channelId) return Promise.resolve(found.channelId);
if (found && found.handle) {
return OBStore.readChannelId(found.handle).then(function (cached) {
@@ -166,6 +181,16 @@
return m ? m[1] : null;
}
/* Videonun süresi InnerTube'un next yanıtında yok, sayfanın oynatıcısından
* okunuyor. Okunamazsa normal video sayılır: /watch adresinde açılan bir
* Short nadir, yanlış havuzda puanlamaktansa gecikmeli doğruyu beklemek
* anlamsız olurdu. */
function watchIsShort() {
if (location.pathname.indexOf("/shorts/") === 0) return true;
var v = document.querySelector("#movie_player video");
return !!(v && isFinite(v.duration) && v.duration > 0 && v.duration <= 60);
}
function updatePanel() {
if (!settings || !settings.enabled || !settings.showPanel) { OBPanel.remove(); return; }
var videoId = currentWatchId();
@@ -179,7 +204,7 @@
details = d;
if (!d.channelId) throw new Error("kanal bulunamadı");
OBStore.writeChannelId("v:" + videoId, d.channelId);
var isShort = d.lengthSeconds != null && d.lengthSeconds <= 60;
var isShort = watchIsShort();
return OBScore.baseline(d.channelId, isShort).then(function (blob) {
return { blob: blob, isShort: isShort };
});
+22 -8
View File
@@ -297,16 +297,30 @@ var OBTube = (function () {
}
/* Videonun kanal kimliği ve tam izlenme sayısı. Kartta kanal bağlantısı
* bulunamadığında son çare olarak kullanılır: video başına bir istek. */
* bulunamadığında son çare olarak kullanılır: video başına bir istek.
*
* Buna `player` ucu daha uygun görünür ve bir zamanlar öyleydi; oturum
* bilgisi göndermediğimiz için artık videoların büyük çoğunluğunda
* LOGIN_REQUIRED ("Sign in to confirm you're not a bot") dönüyor ve
* videoDetails hiç gelmiyor. `next` aynı iki alanı oturumsuz da veriyor.
* Yanıt büyük olduğu için bu uç bilerek son çare, ilk seçenek değil. */
function videoDetails(videoId) {
return post("player", { videoId: videoId }).then(function (data) {
var d = data.videoDetails || {};
return post("next", { videoId: videoId }).then(function (data) {
var owner = P.findFirst(data, "videoOwnerRenderer") || {};
var endpoint = P.findFirst(owner, "browseEndpoint") || {};
var channelId = endpoint.browseId || "";
var counter = P.findFirst(data, "videoViewCountRenderer") || {};
/* viewCount tam sayıyı verir ("63,087 views"); yoksa kısaltılmışa düş.
* Süren canlı yayında aynı alan "12,345 watching now" der; bu izlenme
* değil anlık izleyicidir, sayı sanılırsa skor uydurma çıkar. */
var countText = P.textOf(counter.viewCount) || P.textOf(counter.shortViewCount);
var views = /watching/i.test(countText) ? null : P.parseCountEn(countText);
var primary = P.findFirst(data, "videoPrimaryInfoRenderer") || {};
return {
channelId: d.channelId || null,
views: d.viewCount ? parseInt(d.viewCount, 10) : null,
title: d.title || "",
lengthSeconds: d.lengthSeconds ? parseInt(d.lengthSeconds, 10) : null,
isLive: !!d.isLiveContent
channelId: /^UC[\w-]{22}$/.test(channelId) ? channelId : null,
views: views,
title: P.textOf(primary.title),
publishedText: P.textOf(primary.dateText)
};
});
}
+10 -3
View File
@@ -98,6 +98,7 @@ eq(tiny.score, null, "3'ten az örnek: skor yok");
console.log("\n--- gerçek InnerTube çağrısı ---");
const CHANNEL = process.argv[2] || "UCXuqSBlHAE6Xw-yeJA0Tunw"; /* Linus Tech Tips */
let probe = null; /* videoDetails'ı sınamak için kanalın güncel bir videosu */
OBTube.channelTab(CHANNEL, "videos", 30).then((items) => {
console.log("videos sekmesi: " + items.length + " video");
const withViews = items.filter((v) => v.views);
@@ -108,6 +109,7 @@ OBTube.channelTab(CHANNEL, "videos", 30).then((items) => {
console.log(" " + v.videoId + " | " + OBParse.humanCount(v.views) + " | " +
(v.ageDays == null ? "?" : v.ageDays.toFixed(1) + " gün") + " | " + v.title.slice(0, 50));
}
probe = withViews.length ? withViews[0] : null;
const views = withViews.map((v) => v.views);
console.log("medyan: " + OBParse.humanCount(OBParse.median(views)));
if (withViews.length < 10) { fails++; console.log("FAIL izlenmesi çözülen video sayısı çok düşük"); }
@@ -118,10 +120,15 @@ OBTube.channelTab(CHANNEL, "videos", 30).then((items) => {
return OBTube.resolveChannel("@LinusTechTips");
}).then((id) => {
eq(id, CHANNEL, "handle -> kanal kimliği");
return OBTube.videoDetails("dQw4w9WgXcQ");
/* Bilerek kanalın güncel bir videosu: sabit bir klasik ("dQw4w9WgXcQ")
* YouTube tarafında ayrıcalıklı davranıp uç bozulduğunda bile yanıt
* verebiliyor ve testi yanlış yere yeşil gösteriyordu. */
if (!probe) { fails++; console.log("FAIL sınanacak video bulunamadı"); return {}; }
return OBTube.videoDetails(probe.videoId);
}).then((d) => {
console.log("player ucu: kanal " + d.channelId + ", izlenme " + OBParse.humanCount(d.views));
if (!d.channelId) { fails++; console.log("FAIL player ucu kanal kimliği vermedi"); }
console.log("video ucu: kanal " + d.channelId + ", izlenme " + OBParse.humanCount(d.views));
eq(d.channelId, CHANNEL, "video ucu kanal kimliği");
if (!d.views) { fails++; console.log("FAIL video ucu izlenme vermedi"); }
console.log(fails ? "\n" + fails + " test başarısız" : "\nhepsi geçti");
process.exit(fails ? 1 : 0);
}).catch((e) => {