// ==UserScript==
// @name Romance.io integration with Goodreads
// @namespace http://tampermonkey.net/
// @version 1.4
// @description Get steam rating from Romance.io for Goodreads books using JSON API
// @author swesiast
// @match *://*.goodreads.com/book/show/*
// @grant GM_xmlhttpRequest
// @connect romance.io
// ==/UserScript==
(function() {
'use strict';
// Function to extract book title and author from Goodreads
function getBookInfo() {
let title, author;
const titleSelectors = [
'h1.Text__title1'
];
const authorSelectors = [
'span.ContributorLink__name'
];
for (const selector of titleSelectors) {
const element = document.querySelector(selector);
if (element) {
title = element.textContent.trim();
break;
}
}
for (const selector of authorSelectors) {
const element = document.querySelector(selector);
if (element) {
author = element.textContent.trim();
break;
}
}
return { title, author };
}
function displaySteamRating(steamLevel) {
const existingDisplay = document.getElementById('romance-io-steam-rating');
if (existingDisplay) {
existingDisplay.remove();
}
const display = document.createElement('div');
display.id = 'romance-io-steam-rating';
display.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: #ff6b6b;
color: white;
padding: 15px;
border-radius: 10px;
z-index: 10000;
font-family: Arial, sans-serif;
font-size: 14px;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
max-width: 300px;
border: 2px solid #ff5252;
`;
display.innerHTML = `
🔥 Rating
${steamLevel}
From Romance.io
`;
document.body.appendChild(display);
display.addEventListener('click', function() {
display.remove();
});
}
// Function to search Romance.io using JSON API
function searchRomanceIO(title, author) {
const cleanTitle = encodeURIComponent(title.trim());
const jsonUrl = `https://www.romance.io/json/search_books?search=${cleanTitle}`;
console.log('🔍 Searching Romance.io via JSON:', jsonUrl);
// Add loading indicator
const loadingDiv = document.createElement('div');
loadingDiv.id = 'romance-io-loading';
loadingDiv.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: #4ecdc4;
color: white;
padding: 15px;
border-radius: 10px;
z-index: 10000;
font-family: Arial, sans-serif;
font-size: 14px;
`;
loadingDiv.innerHTML = '🔍 Searching Romance.io...';
document.body.appendChild(loadingDiv);
GM_xmlhttpRequest({
method: 'GET',
url: jsonUrl,
onload: function(response) {
console.log('✅ JSON API request completed');
const loading = document.getElementById('romance-io-loading');
if (loading) loading.remove();
if (response.status === 200) {
try {
const data = JSON.parse(response.responseText);
console.log(`📚 Found ${data.books ? data.books.length : 0} books`);
if (data.books && data.books.length > 0) {
// Find the book that matches both title and author
const matchedBook = findMatchingBook(data.books, title, author);
if (matchedBook) {
console.log('✅ Found matching book:', matchedBook.title);
fetchBookSteamRating(matchedBook.url);
} else {
console.log('❌ No exact match found, using first result');
fetchBookSteamRating(data.books[0].url);
}
} else {
displaySteamRating('No books found');
}
} catch (e) {
console.error('❌ JSON parse error:', e);
displaySteamRating('JSON Parse Error');
}
} else {
console.error('❌ HTTP Error:', response.status);
displaySteamRating('API Error: ' + response.status);
}
},
onerror: function(error) {
console.error('❌ GM_xmlhttpRequest error:', error);
const loading = document.getElementById('romance-io-loading');
if (loading) loading.remove();
displaySteamRating('Network Error');
}
});
}
// Function to find the best matching book by author
function findMatchingBook(books, goodreadsTitle, goodreadsAuthor) {
// Clean the author name for comparison
const cleanGoodreadsAuthor = goodreadsAuthor.toLowerCase().trim();
console.log(`🔍 Looking for author: "${cleanGoodreadsAuthor}"`);
// First, try exact author match
for (const book of books) {
if (book.authors && book.authors.length > 0) {
const romanceAuthor = book.authors[0].name.toLowerCase().trim();
console.log(` Comparing with: "${romanceAuthor}"`);
if (romanceAuthor === cleanGoodreadsAuthor) {
console.log('✅ Exact author match found');
return book;
}
}
}
// Fallback: partial author match
for (const book of books) {
if (book.authors && book.authors.length > 0) {
const romanceAuthor = book.authors[0].name.toLowerCase().trim();
if (romanceAuthor.includes(cleanGoodreadsAuthor) || cleanGoodreadsAuthor.includes(romanceAuthor)) {
console.log('✅ Partial author match found');
return book;
}
}
}
// Last resort: check if author name contains any part
const authorParts = cleanGoodreadsAuthor.split(' ');
for (const book of books) {
if (book.authors && book.authors.length > 0) {
const romanceAuthor = book.authors[0].name.toLowerCase().trim();
for (const part of authorParts) {
if (part.length > 2 && romanceAuthor.includes(part)) {
console.log('✅ Author name part match found');
return book;
}
}
}
}
return null;
}
// Function to fetch steam rating from individual book page
function fetchBookSteamRating(bookPath) {
const bookUrl = `https://www.romance.io${bookPath}`;
console.log('📖 Fetching book page:', bookUrl);
const loadingDiv = document.createElement('div');
loadingDiv.id = 'romance-io-loading';
loadingDiv.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: #4ecdc4;
color: white;
padding: 15px;
border-radius: 10px;
z-index: 10000;
font-family: Arial, sans-serif;
font-size: 14px;
`;
loadingDiv.innerHTML = '📖 Loading book details...';
document.body.appendChild(loadingDiv);
GM_xmlhttpRequest({
method: 'GET',
url: bookUrl,
onload: function(response) {
console.log('✅ Book page request completed');
const loading = document.getElementById('romance-io-loading');
if (loading) loading.remove();
if (response.status === 200) {
const parser = new DOMParser();
const doc = parser.parseFromString(response.responseText, 'text/html');
const steamRating = extractSteamRating(doc);
if (steamRating) {
displaySteamRating(steamRating);
} else {
displaySteamRating('Steam rating not found');
console.log('Could not find steam rating on book page');
}
} else {
displaySteamRating('Book page error: ' + response.status);
}
},
onerror: function(error) {
console.error('❌ Book page request error:', error);
const loading = document.getElementById('romance-io-loading');
if (loading) loading.remove();
displaySteamRating('Book page load error');
}
});
}
// Function to extract steam rating from book page
function extractSteamRating(doc) {
console.log('🔍 Extracting steam rating from book page...');
const steamSelectors = [
'[class*="steam-rating"]'
];
for (const selector of steamSelectors) {
try {
const elements = doc.querySelectorAll(selector);
console.log('steam elements,' , elements);
for (const element of elements) {
const text = element.textContent.trim();
if (text.includes('steam')) {
console.log('🔥 Found steam element:', text);
return text;
}
}
} catch (e) {
// Skip invalid selectors
}
}
return null;
}
// Main function
function main() {
const bookInfo = getBookInfo();
if (bookInfo.title && bookInfo.author) {
console.log('📖 Goodreads book:', bookInfo.title, 'by', bookInfo.author);
setTimeout(() => {
searchRomanceIO(bookInfo.title, bookInfo.author);
}, 1000);
} else {
console.log('Could not extract book information from Goodreads');
}
}
// Run when page loads
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', main);
} else {
main();
}
})();