// ==UserScript== // @name TMDB 한국 지원 강화 // @namespace http://tampermonkey.net/ // @version 1.0 // @description TMDB 영화/TV 시리즈 페이지에 한국어, 영어, 원어 제목 추가, 개별 클립보드 복사 기능 및 한국 시청등급 표시 // @match https://www.themoviedb.org/* // @grant GM_xmlhttpRequest // @grant GM_addStyle // @author DongHaerang // @downloadURL none // ==/UserScript== // 주의사항: 아래 YOUR_API_KEY 부분을 실제 TMDB API 키로 교체하는 것을 잊지 마세요. const apiKey = "YOUR_API_KEY"; (function() { 'use strict'; GM_addStyle(` .additional-titles { font-size: 1.1em; line-height: 1.4; } .additional-title { cursor: pointer; transition: color 0.3s; } .additional-title:hover { color: blue !important; } `); const copyToClipboard = text => { navigator.clipboard.writeText(text).then(() => { showTemporaryMessage(`${text} 클립보드에 복사됨`); }); }; const showTemporaryMessage = message => { const messageElement = document.createElement('div'); Object.assign(messageElement.style, { position: 'fixed', top: '10px', left: '50%', transform: 'translateX(-50%)', backgroundColor: 'rgba(0, 0, 0, 0.7)', color: 'white', padding: '10px', borderRadius: '5px', zIndex: '9999' }); messageElement.textContent = message; document.body.appendChild(messageElement); setTimeout(() => document.body.removeChild(messageElement), 1000); }; const getIdAndType = () => { const [, type, id] = window.location.pathname.split('/'); return { id, type }; }; const fetchData = async () => { const { id, type } = getIdAndType(); const appendToResponse = type === 'movie' ? 'release_dates' : 'content_ratings'; const urls = [ `https://api.themoviedb.org/3/${type}/${id}?api_key=${apiKey}&language=ko-KR&append_to_response=${appendToResponse}`, `https://api.themoviedb.org/3/${type}/${id}?api_key=${apiKey}&language=en-US` ]; try { const [dataKo, dataEn] = await Promise.all(urls.map(url => new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: url, onload: response => response.status === 200 ? resolve(JSON.parse(response.responseText)) : reject(`API 요청 실패: ${response.status}`), onerror: reject }); }) )); const koTitle = dataKo.title || dataKo.name || '한국어 제목 미기재'; const enTitle = dataEn.title || dataEn.name || '영어 제목 미기재'; const originalTitle = dataKo.original_title || dataKo.original_name || '원어 제목 미기재'; const koreanRating = getKoreanCertification(dataKo, type); displayTitles(koTitle, enTitle, originalTitle); displayKoreanRating(koreanRating); } catch (error) { console.error('TMDB API 요청 오류:', error); } }; const displayTitles = (koTitle, enTitle, originalTitle) => { const titleElement = document.querySelector('.title h2'); if (!titleElement) return; const titleContainer = document.createElement('div'); titleContainer.className = 'additional-titles'; titleContainer.style.margin = '10px 0'; titleContainer.innerHTML = ` 한제: ${koTitle} / 영제: ${enTitle} / 원제: ${originalTitle} `; titleElement.parentNode.insertBefore(titleContainer, titleElement); ['ko-title', 'en-title', 'original-title'].forEach(className => { titleContainer.querySelector(`.${className}`).addEventListener('click', function() { copyToClipboard(this.textContent); }); }); }; const getKoreanCertification = (data, type) => { const ratings = type === 'movie' ? data.release_dates?.results : data.content_ratings?.results; const koreanRating = ratings?.find(r => r.iso_3166_1 === 'KR')?.release_dates?.[0]?.certification || ratings?.find(r => r.iso_3166_1 === 'KR')?.rating; return koreanRating || '등급미정'; }; const displayKoreanRating = rating => { const factsElement = document.querySelector('.facts'); if (!factsElement) return; let koreanRatingElement = document.getElementById('korean-rating'); if (!koreanRatingElement) { koreanRatingElement = Object.assign(document.createElement('span'), { id: 'korean-rating', style: 'font-size: 1em; margin-right: 10px; font-weight: bold;' }); factsElement.insertBefore(koreanRatingElement, factsElement.firstChild); } koreanRatingElement.textContent = rating; }; const init = () => fetchData(); window.addEventListener('load', init); new MutationObserver(() => { const url = location.href; if (url !== lastUrl) { lastUrl = url; init(); } }).observe(document, {subtree: true, childList: true}); let lastUrl = location.href; })();