From 2dcc2f425ec45346b48678c85949b6c622c2ed18 Mon Sep 17 00:00:00 2001 From: lenafx Date: Sun, 7 Dec 2025 19:43:40 +0100 Subject: [PATCH] added tracking --- src/scripts/anime/animes.js | 14 ++++++--- src/scripts/anime/player.js | 48 +++++++++++++++++++++++++++++ src/scripts/books/book.js | 18 +++++++++++ src/scripts/books/books.js | 14 ++++++--- src/scripts/books/reader.js | 60 +++++++++++++++++++++++++++++++++++++ 5 files changed, 146 insertions(+), 8 deletions(-) diff --git a/src/scripts/anime/animes.js b/src/scripts/anime/animes.js index 5eaf2d4..822a697 100644 --- a/src/scripts/anime/animes.js +++ b/src/scripts/anime/animes.js @@ -113,10 +113,16 @@ function renderContinueWatching(id, list) { const el = document.createElement('div'); el.className = 'card'; - el.onclick = () => window.location.href = - item.source === 'anilist' - ? `/anime/${item.entry_id}` - : `/anime/${item.source}/${item.entry_id}`; + el.onclick = () => { + const ep = item.progress || 1; + + if (item.source === 'anilist') { + window.location.href = `http://localhost:54322/watch/${item.entry_id}/${ep}`; + } else { + window.location.href = `http://localhost:54322/watch/${item.entry_id}/${ep}?${item.source}`; + } + }; + const progressText = item.total_episodes ? `${item.progress || 0}/${item.total_episodes}` diff --git a/src/scripts/anime/player.js b/src/scripts/anime/player.js index 0d0d0c2..72072b7 100644 --- a/src/scripts/anime/player.js +++ b/src/scripts/anime/player.js @@ -350,6 +350,20 @@ function playVideo(url, subtitles = []) { settings: ['captions', 'quality', 'speed'] }); + let alreadyTriggered = false; + + video.addEventListener('timeupdate', () => { + if (!video.duration) return; + + const percent = (video.currentTime / video.duration) * 100; + + if (percent >= 80 && !alreadyTriggered) { + alreadyTriggered = true; + sendProgress(); + } + }); + + video.play().catch(() => console.log("Autoplay blocked")); } @@ -378,6 +392,40 @@ if (currentEpisode <= 1) { document.getElementById('prev-btn').disabled = true; } + +async function sendProgress() { + const token = localStorage.getItem('token'); + if (!token) return; + + const source = extName + ? extName + : "anilist"; + + const body = { + entry_id: animeId, + source: source, + entry_type: "ANIME", + status: 'CURRENT', + progress: source === 'anilist' + ? Math.floor(currentEpisode) + : currentEpisode + }; + + try { + await fetch('/api/list/entry', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + }, + body: JSON.stringify(body) + }); + } catch (err) { + console.error('Error updating progress:', err); + } +} + + loadMetadata(); loadExtensions(); diff --git a/src/scripts/books/book.js b/src/scripts/books/book.js index c77806e..fe27285 100644 --- a/src/scripts/books/book.js +++ b/src/scripts/books/book.js @@ -39,6 +39,23 @@ function getSimpleAuthHeaders() { }; } +function applyChapterFromUrlFilter() { + const params = new URLSearchParams(window.location.search); + const chapterParam = params.get('chapter'); + + if (!chapterParam) return; + + const chapterNumber = parseFloat(chapterParam); + if (isNaN(chapterNumber)) return; + + filteredChapters = allChapters.filter( + ch => parseFloat(ch.number) === chapterNumber + ); + + currentPage = 1; +} + + function getBookEntryType(bookData) { if (!bookData) return 'MANGA'; @@ -449,6 +466,7 @@ async function loadChapters(idForFetch) { allChapters = data.chapters || []; filteredChapters = [...allChapters]; + applyChapterFromUrlFilter(); const totalEl = document.getElementById('total-chapters'); diff --git a/src/scripts/books/books.js b/src/scripts/books/books.js index 14fab28..e8278ba 100644 --- a/src/scripts/books/books.js +++ b/src/scripts/books/books.js @@ -241,10 +241,16 @@ function renderContinueReading(id, list) { const el = document.createElement('div'); el.className = 'card'; - el.onclick = () => window.location.href = - item.source === 'anilist' - ? `/book/${item.entry_id}` - : `/book/${item.source}/${item.entry_id}`; + el.onclick = () => { + const ch = item.progress || 1; + + if (item.source === 'anilist') { + window.location.href = `http://localhost:54322/book/${item.entry_id}?chapter=${ch}`; + } else { + window.location.href = `http://localhost:54322/read/${item.source}/${ch}/${item.entry_id}?source=${item.source}`; + } + }; + const progressText = item.total_episodes ? `${item.progress || 0}/${item.total_episodes}` diff --git a/src/scripts/books/reader.js b/src/scripts/books/reader.js index 2c344e0..ab11699 100644 --- a/src/scripts/books/reader.js +++ b/src/scripts/books/reader.js @@ -143,6 +143,8 @@ async function loadChapter() { document.title = `Chapter ${chapter}`; } + setupProgressTracking(data, source); + const res2 = await fetch(`/api/book/${bookId}?source=${source}`); const data2 = await res2.json(); @@ -619,6 +621,64 @@ window.addEventListener('resize', () => { }, 250); }); +let progressSaved = false; + +function setupProgressTracking(data, source) { + progressSaved = false; + + async function sendProgress(chapterNumber) { + const token = localStorage.getItem('token'); + if (!token) return; + + const body = { + entry_id: bookId, + source: source, + entry_type: data.type === 'manga' ? 'MANGA' : 'NOVEL', + status: 'CURRENT', + progress: source === 'anilist' + ? Math.floor(chapterNumber) // ✅ AniList solo enteros + : chapterNumber // ✅ Local acepta decimales + }; + + try { + await fetch('/api/list/entry', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + }, + body: JSON.stringify(body) + }); + } catch (err) { + console.error('Error updating progress:', err); + } + } + + + function checkProgress() { + const scrollTop = window.scrollY; + const scrollHeight = document.documentElement.scrollHeight - window.innerHeight; + const percent = scrollHeight > 0 ? scrollTop / scrollHeight : 0; + + if (percent >= 0.8 && !progressSaved) { + progressSaved = true; + + const chapterNumber = (typeof data.number !== 'undefined' && data.number !== null) + ? data.number + : Number(chapter); + + sendProgress(chapterNumber); + + window.removeEventListener('scroll', checkProgress); + } + } + + // remove previous listener just in case + window.removeEventListener('scroll', checkProgress); + window.addEventListener('scroll', checkProgress); +} + + if (!bookId || !chapter || !provider) { reader.innerHTML = `