// ==UserScript==
// @name Google News Display
// @description Display news on Google's homepage
// @version 1.2
// @match https://www.google.com/
// @match https://www.google.com/webhp*
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @author Gen1x/G1nX
// @namespace https://greasyfork.org/users/916589
// ==/UserScript==
(function () {
const userLanguage = (navigator.language || navigator.userLanguage).substr(0, 2).toLowerCase();
const translations = {
en: {
loading: 'Loading news...',
error: 'Error fetching news.',
headline: 'News in',
originalPlaces: "It's recommended you check these\nin their original places.",
},
es: {
loading: 'Cargando noticias...',
error: 'Error al obtener noticias.',
headline: 'Noticias en',
originalPlaces: "Se recomienda que verifiques estas\nnoticias en sus lugares originales.",
},
fr: {
loading: 'Chargement des actualités...',
error: 'Erreur lors de la récupération des actualités.',
headline: 'Actualités en',
originalPlaces: "Il est recommandé de vérifier ces actualités\nà leurs emplacements d'origine.",
},
de: {
loading: 'Nachrichten werden geladen...',
error: 'Fehler beim Abrufen der Nachrichten.',
headline: 'Nachrichten in',
originalPlaces: "Es wird empfohlen, diese Nachrichten\nan ihren Originalstellen zu überprüfen.",
},
zh: {
loading: '新闻加载中...',
error: '获取新闻时出错。',
headline: '新闻在',
originalPlaces: "建议您在原始位置检查这些新闻。",
},
ru: {
loading: 'Загрузка новостей...',
error: 'Ошибка при получении новостей.',
headline: 'Новости на',
originalPlaces: "Рекомендуется проверить эти новости\nв их оригинальных местах.",
},
ja: {
loading: 'ニュースを読み込んでいます...',
error: 'ニュースの取得中にエラーが発生しました。',
headline: 'のニュース',
originalPlaces: "これらは元の場所で確認することをお勧めします。",
}
};
// Function to get the translation based on the user's language
function getTranslation(key, language) {
return translations[language] && translations[language][key]
? translations[language][key]
: translations['en'][key];
}
// Fetch user's country code using ipapi.co
async function getUserCountryCode() {
try {
const response = await fetch('https://ipapi.co/json');
if (response.ok) {
const data = await response.json();
return data.country_code.toLowerCase();
} else {
console.error('Error fetching user country code:', response.statusText);
// Return a default country code in case of an error
return 'us';
}
} catch (error) {
console.error('Error fetching user country code:', error);
// Return a default country code in case of an error
return 'us';
}
}
// Get the user's country code
getUserCountryCode().then((userCountryCode) => {
// Fetch news data based on the user's country code
const apiUrl = `https://personal-toolkit.genarunchisacoa.repl.co/news/alt?country=${userCountryCode}`;
// Display loading text
displayLoadingText();
GM_xmlhttpRequest({
method: 'GET',
url: apiUrl,
headers: {
'User-Agent': 'Mozilla/5.0 (compatible; Greasemonkey; UserScript)',
Accept: 'application/json',
},
onload: function (response) {
if (response.status === 200) {
const newsData = JSON.parse(response.responseText);
// Display news once loaded
displayNews(newsData, userCountryCode);
} else {
console.error('Error fetching news:', response.statusText);
// Display error message
displayErrorText('Error fetching news.');
}
},
onerror: function (error) {
console.error('Error fetching news:', error);
// Display error message
displayErrorText('Error fetching news.');
},
});
});
function displayLoadingText() {
const newsContainer = document.createElement('div');
newsContainer.classList.add('news-container');
const loadingText = document.createElement('div');
loadingText.textContent = getTranslation('loading', userLanguage);
loadingText.classList.add('loading-text');
newsContainer.appendChild(loadingText);
// Find the specified div on the Google homepage
const targetDiv = document.querySelector('.FPdoLc.lJ9FBc');
if (targetDiv) {
// Insert the news container at the end
targetDiv.appendChild(newsContainer);
} else {
console.error('Target div not found.');
}
}
// Function to display error text within the news container
function displayErrorText(errorMessage) {
const newsContainer = document.querySelector('.news-container');
if (newsContainer) {
const errorText = document.createElement('div');
errorText.textContent = errorMessage || getTranslation('error', userLanguage);
errorText.classList.add('error-text');
newsContainer.innerHTML = ''; // Clear loading text
newsContainer.appendChild(errorText);
} else {
console.error('News container not found.');
}
}
// Function to get the country name from the country code using Navigator API
function getCountryNameFromCode(countryCode) {
try {
const countryName = new Intl.DisplayNames([userLanguage], { type: 'region' }).of(countryCode.toUpperCase());
return countryName;
} catch (error) {
console.error('Error getting country name:', error);
// Return the country code if an error occurs
return countryCode;
}
}
// Function to display news titles with links in grid items and insert them at the end of the specified div
function displayNews(newsData, userCountryCode) {
const newsContainer = document.querySelector('.news-container');
if (newsContainer) {
// Clear loading text
newsContainer.innerHTML = '';
// Add heading for the news based on the user's country
const userCountryHeading = document.createElement('h3');
userCountryHeading.textContent = `${getTranslation("headline", userLanguage)} ${getCountryNameFromCode(userCountryCode)}\n`;
userCountryHeading.style.color = 'white';
userCountryHeading.classList.add('news-header');
newsContainer.appendChild(userCountryHeading);
// Add the text recommending checking news in their original places
const originalPlacesText = document.createElement('div');
originalPlacesText.textContent = getTranslation('originalPlaces', userLanguage);
originalPlacesText.classList.add('original-places-text');
newsContainer.appendChild(originalPlacesText);
// Iterate through each news item and create a grid item for the title with a link
newsData.results.forEach((item) => {
const newsItem = document.createElement('a');
newsItem.classList.add('grid-item');
newsItem.href = item.link;
newsItem.target = '_blank'; // Open link in a new tab
// Check if the video URL is not null
if (item.video_url) {
const newsVideo = document.createElement('video');
newsVideo.src = item.video_url;
newsVideo.autoplay = true;
newsVideo.loop = true;
newsVideo.muted = true;
newsVideo.classList.add('headline-video');
newsItem.appendChild(newsVideo);
} else if (item.image_url) { // If video_url is null, check for image_url
const newsImage = document.createElement('img');
newsImage.src = item.image_url;
newsImage.alt = item.title;
newsImage.classList.add('headline-image');
newsItem.appendChild(newsImage);
}
const newsTitle = document.createElement('div');
newsTitle.textContent = item.title;
newsTitle.classList.add('title-text');
const newsDescription = document.createElement('div');
newsDescription.classList.add('description-text');
newsDescription.style.display = 'none'; // Hide description by default
// Extract the first paragraph of the description and cut off at 200 characters
const firstParagraph = item.content.split('\n\n')[0].substring(0, 200);
// Add three dots if the description was truncated
newsDescription.textContent =
firstParagraph.length < item.content.length ? `${firstParagraph}...` : firstParagraph;
// You might want to add additional styling or processing for each news title
newsItem.appendChild(newsTitle);
newsItem.appendChild(newsDescription);
// Add event listener for hover
newsItem.addEventListener('mouseenter', () => {
// Display description on hover
newsTitle.style.display = 'none';
newsDescription.style.display = 'block';
});
newsItem.addEventListener('mouseleave', () => {
// Hide description on leave
newsTitle.style.display = 'block';
newsDescription.style.display = 'none';
});
newsContainer.appendChild(newsItem);
});
} else {
console.error('News container not found.');
}
}
// Add styles for grid items, news container, loading text, and error text
GM_addStyle(`
.news-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 20px;
max-height: 240%;
overflow-y: auto;
margin: 20px auto; /* Update this line to center-align the container */
text-align: center;
border: 1px solid #ddd;
padding: 20px auto;
background-color: #2d2c38;
border-radius: 10px;
}
.grid-item {
border: 1px solid #ddd;
padding: 10px;
text-align: center;
display: flex;
flex-direction: column; /* Display items in a column */
align-items: center; /* Center the text vertically */
justify-content: center; /* Center the text horizontally */
text-decoration: none; /* Remove default link underline */
color: #5489de; /* Set link color */
background-color: #2d2c38;
border-radius: 5px; /* Adjust the value to change the level of rounding */
margin: 10px; /* Add margin to the grid items */
}
.grid-item:hover {
background-color: #494857; /* Add a subtle background color on hover */
transition: background-color 0.3s ease; /* Smooth transition for background color */
}
.loading-text, .error-text {
font-size: 18px;
font-weight: bold;
color: white;
}
.error-text {
color: red;
}
.original-places-text {
font-size: 14px;
color: #ddd;
margin-top: 10px;
}
/* Add rounded corners to the scrollbar */
.news-container::-webkit-scrollbar {
width: 12px;
padding: 10px;
}
.news-container::-webkit-scrollbar-thumb {
background-color: #6b6a7d; /* Color of the scrollbar thumb */
border-radius: 6px; /* Rounded corners for the thumb */
}
.news-container::-webkit-scrollbar-track {
background-color: #2d2c38; /* Color of the scrollbar track */
border-radius: 8px; /* Rounded corners for the track */
}
.news-header {
text-align: center;
display: flex;
align-items: center; /* Center the text vertically */
justify-content: center; /* Center the text horizontally */
}
.headline-image, .headline-video {
max-width: 100%;
max-height: 100%;
border-radius: 5px;
margin-bottom: 10px; /* Adjust margin as needed */
}
`);
})();