// ==UserScript== // @name TMDB 한국 지원 강화 // @namespace http://tampermonkey.net/ // @version 1.9.7 // @description TMDB 영화/TV 시리즈 페이지에 한국어, 영어, 원어 제목 추가, 개별 클립보드 복사 기능, 한국 시청등급 및 제작국 표시 // @match https://www.themoviedb.org/* // @grant GM_xmlhttpRequest // @grant GM_addStyle // @author DongHaerang // @downloadURL https://update.greasyfork.icu/scripts/519746/TMDB%20%ED%95%9C%EA%B5%AD%20%EC%A7%80%EC%9B%90%20%EA%B0%95%ED%99%94.user.js // @updateURL https://update.greasyfork.icu/scripts/519746/TMDB%20%ED%95%9C%EA%B5%AD%20%EC%A7%80%EC%9B%90%20%EA%B0%95%ED%99%94.meta.js // ==/UserScript== // 주의사항: 아래 YOUR_API_KEY 부분을 실제 TMDB API 키로 교체하는 것을 잊지 마세요. const apiKey = "YOUR_API_KEY"; (function() { 'use strict'; GM_addStyle(` #tmdb-info-table { background-color: rgba(0, 0, 0, 0.3) !important; } .clickable-text[data-copyable="true"], #external-links a[data-copyable="true"] { color: yellow !important; content: "□"; margin-left: 0px; display: inline-block; } .clickable-text, #external-links a { cursor: pointer; text-decoration: none; transition: color 0.3s; font-size: 12pt !important; color: lightskyblue !important; } // #external-links a[target="_blank"]::after { // content: "↗"; // margin-left: 0px; // display: inline-block; // } // #external-links a::after, #korean-rating::after { // content: "↙"; // margin-left: 0px; // display: inline-block; // } .clickable-text:hover, #external-links a:hover, #korean-rating:hover { color: blue !important; } #external-links a { margin-right: 0px; } .additional-titles { line-height: 1.4; margin-bottom: 10px; font-size: 12pt !important; } #additional-info { margin-top: 10px; clear: both; display: flex; align-items: center; width: 100%; font-size: 12pt !important; } #production-countries { margin-right: 20px; font-size: 12pt !important; } #external-links { font-size: 12pt !important; } .right-aligned-links { float: right; display: inline-block; } `); const copyToClipboard = (text, clickedText) => { // MOVIE, TV, +TMDB, +영, +원, 한제, 영제, 원제 클릭시에만 특수문자 변환 const specialClickTargets = ['MOVIE', 'TV', '+TMDB', '+영', '+원', '한제', '영제', '원제', 'TTT']; text = text.replace(/:/g, ';').replace(/\?/g, '?').replace(/\//g, '/'); 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 translateCountry = (countryCode) => { const countryMap = { 'US': '미국', 'GB': '영국', 'KR': '한국', 'JP': '일본', 'CN': '중국', 'FR': '프랑스', 'DE': '독일', 'IT': '이탈리아', 'ES': '스페인', 'CA': '캐나다', 'AU': '호주', 'RU': '러시아', 'IN': '인도', 'BR': '브라질', 'MX': '멕시코', 'NL': '네덜란드', 'BE': '벨기에', 'SE': '스웨덴', 'DK': '덴마크', 'NO': '노르웨이', 'FI': '핀란드', 'PL': '폴란드', 'TR': '터키', 'TH': '태국', 'VN': '베트남', 'ID': '인도네시아', 'MY': '말레이시아', 'SG': '싱가포르', 'PH': '필리핀', 'TW': '대만', 'HK': '홍콩', 'NZ': '뉴질랜드', 'CO': '콜롬비아', 'AR': '아르헨티나', 'ZA': '남아프리카', // 국가 전체 이름도 매핑 'United States of America': '미국', 'United Kingdom': '영국', 'South Korea': '한국', 'Japan': '일본', 'China': '중국', 'France': '프랑스', 'Germany': '독일', 'Italy': '이탈리아', 'Spain': '스페인', 'Canada': '캐나다', 'Australia': '호주', 'Russia': '러시아', 'India': '인도', 'Brazil': '브라질', 'Mexico': '멕시코', 'Netherlands': '네덜란드', 'Belgium': '벨기에', 'Sweden': '스웨덴', 'Denmark': '덴마크', 'Norway': '노르웨이', 'Finland': '핀란드', 'Poland': '폴란드', 'Turkey': '터키', 'Thailand': '태국', 'Vietnam': '베트남', 'Indonesia': '인도네시아', 'Malaysia': '말레이시아', 'Singapore': '싱가포르', 'Philippines': '필리핀', 'Taiwan': '대만', 'Hong Kong': '홍콩', 'New Zealand': '뉴질랜드', 'Colombia': '콜롬비아', 'Argentina': '아르헨티나', 'South Africa': '남아프리카' }; return countryMap[countryCode] || '미기재'; }; const getIdAndType = () => { const [, type, id] = window.location.pathname.split('/'); return { id: id?.split('-')[0], type }; }; const goToMainPage = () => { const currentUrl = window.location.href; let mainUrl; const tvMatch = currentUrl.match(/\/tv\/(\d+)/); const movieMatch = currentUrl.match(/\/movie\/(\d+)/); if (tvMatch) { mainUrl = `https://www.themoviedb.org/tv/${tvMatch[1]}`; } else if (movieMatch) { mainUrl = `https://www.themoviedb.org/movie/${movieMatch[1]}`; } if (mainUrl) { window.location.href = mainUrl; } }; const changeLanguage = (lang) => { const currentUrl = new URL(window.location.href); currentUrl.searchParams.set('language', lang); window.location.href = currentUrl.toString(); }; const languageMap = { 'ko': '한국어', 'en': '영어', 'ja': '일본어', 'zh': '중국어', 'es': '스페인어', 'fr': '프랑스어', 'de': '독일어', 'it': '이탈리아어', 'pt': '포르투갈어', 'ru': '러시아어', 'hi': '힌디어', 'ar': '아랍어', 'th': '태국어', 'vi': '베트남어', 'tr': '터키어', 'nl': '네덜란드어', 'pl': '폴란드어', 'sv': '웨덴어', 'da': '덴마크어', 'fi': '핀란드어', 'no': '노르웨이어', 'cs': '체코어', 'el': '그리스어', 'he': '히브리어', 'id': '인도네시아어', 'ms': '말레이어', 'ro': '루마니아어', 'hu': '헝가리어', 'bn': '벵골어', 'uk': '우크라이나어', 'fa': '페르시아어' }; const getLanguagePrefix = async (id, type) => { try { const response = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: `https://api.themoviedb.org/3/${type}/${id}?api_key=${apiKey}`, onload: response => response.status === 200 ? resolve(JSON.parse(response.responseText)) : reject(`API 요청 실패: ${response.status}`), onerror: reject }); }); const originalLanguage = response.original_language; // languageMap에서 해당 언어의 전체 이름을 가져온 후, 첫 글자만 반환 return (languageMap[originalLanguage] || '기타언어').charAt(0); } catch (error) { console.error('언어 정보 가져오기 실패:', error); return '기'; } }; const displayTitles = async (koTitle, enTitle, originalTitle, type, id, koreanRating, originCountry, productionCountry, year, imdbId, wikidataId, tvdbId) => { const titleElement = document.querySelector('.title h2') || document.querySelector('.header .title h2'); if (!titleElement) return; // TMDB API로 대체 제목 가져오기 let koreanAltTitlesText = ''; let englishAltTitlesText = ''; let japaneseAltTitlesText = ''; try { const alternativeTitles = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: `https://api.themoviedb.org/3/${type}/${id}/alternative_titles?api_key=${apiKey}`, onload: response => response.status === 200 ? resolve(JSON.parse(response.responseText)) : reject(`API 요청 실패: ${response.status}`), onerror: reject }); }); // 한국 대체 제목 찾기 const koreanAltTitles = type === 'movie' ? alternativeTitles.titles?.filter(title => title.iso_3166_1 === 'KR') || [] : alternativeTitles.results?.filter(title => title.iso_3166_1 === 'KR') || []; koreanAltTitlesText = koreanAltTitles.length > 0 ? ` ${koreanAltTitles.map(t => `[${t.title}]`).join(', ')}` : ''; // 영어 대체 제목 찾기 const englishAltTitles = type === 'movie' ? alternativeTitles.titles?.filter(title => title.iso_3166_1 === 'US' || title.iso_3166_1 === 'GB') || [] : alternativeTitles.results?.filter(title => title.iso_3166_1 === 'US' || title.iso_3166_1 === 'GB') || []; englishAltTitlesText = englishAltTitles.length > 0 ? ` ${englishAltTitles.map(t => `[${t.title}]`).join(', ')}` : ''; // 일본 대체 제목 찾기 const japaneseAltTitles = type === 'movie' ? alternativeTitles.titles?.filter(title => title.iso_3166_1 === 'JP') || [] : alternativeTitles.results?.filter(title => title.iso_3166_1 === 'JP') || []; japaneseAltTitlesText = japaneseAltTitles.length > 0 ? ` ${japaneseAltTitles.map(t => `[${t.title}]`).join(', ')}` : ''; } catch (error) { console.error('대체 제목 가져오기 실패:', error); } // 변수 정의 const KoTitle = koTitle; const EnTitle = enTitle; const OriginalTitle = originalTitle; const ChangedKoTitle = koTitle.replace(/:/g, ';').replace(/\?/g, '?'); const ChangedEnTitle = enTitle.replace(/:/g, ';').replace(/\?/g, '?'); const ChangedOriginalTitle = originalTitle.replace(/:/g, ';').replace(/\?/g, '?'); const titleContainer = document.createElement('div'); titleContainer.className = 'additional-titles'; const titleColor = window.getComputedStyle(titleElement).color; const typeText = type === 'tv' ? 'TV' : 'MOVIE'; const processedEnTitle = enTitle.replace(/ /g, '+').replace(/[^\w\s+-]/g, ''); const osoSearchUrl = `https://www.opensubtitles.org/ko/search2/moviename-${encodeURIComponent(processedEnTitle)}+${year}/sublanguageid-kor`; titleContainer.innerHTML = `
`; // 이벤트 리스너 설정 const elements = { '메인': () => goToMainPage(), [id]: () => copyToClipboard(id, id), [typeText]: async () => { const languagePrefix = await getLanguagePrefix(id, type); const copyText = type === 'movie' ? `${ChangedKoTitle} (${year}) {tmdb-${id}} [${languagePrefix}A $${translateCountry(originCountry)} =${koreanRating}]` : `${ChangedKoTitle} (${year}) [${languagePrefix}A $${translateCountry(originCountry)} =${koreanRating}]`; copyToClipboard(copyText, typeText); }, '+TMDB': () => { copyToClipboard(`${ChangedKoTitle} (${year}) {tmdb-${id}} [$${translateCountry(originCountry)} =${koreanRating}]`, '+TMDB'); }, '+영': () => { copyToClipboard(`${ChangedKoTitle} (${year}) [${ChangedEnTitle}] {tmdb-${id}} [$${translateCountry(originCountry)} =${koreanRating}]`, '+영'); }, '+원': () => { copyToClipboard(`${ChangedKoTitle} (${year}) [${ChangedEnTitle}] [${ChangedOriginalTitle}] {tmdb-${id}} [$${translateCountry(originCountry)} =${koreanRating}]`, '+원'); }, '한제': () => copyToClipboard(`${KoTitle} (${year})`, '한제'), [KoTitle]: () => copyToClipboard(KoTitle, KoTitle), '영제': () => copyToClipboard(`[${EnTitle}]`, '영제'), [EnTitle]: () => copyToClipboard(EnTitle, EnTitle), '원제': () => copyToClipboard(`[${EnTitle}] [${OriginalTitle}]`, '원제'), [OriginalTitle]: () => copyToClipboard(OriginalTitle, OriginalTitle), '한제 대체': async () => { try { const { id, type } = getIdAndType(); if (!id || !type) throw new Error('ID 또는 타입이 없습니다.'); const alternativeTitles = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: `https://api.themoviedb.org/3/${type}/${id}/alternative_titles?api_key=${apiKey}`, onload: response => response.status === 200 ? resolve(JSON.parse(response.responseText)) : reject(`API 요청 실패: ${response.status}`), onerror: reject }); }); const koreanTitles = type === 'movie' ? alternativeTitles.titles?.filter(t => t.iso_3166_1 === 'KR').map(t => t.title) || [] : alternativeTitles.results?.filter(t => t.iso_3166_1 === 'KR').map(t => t.title) || []; const formattedTitles = koreanTitles.map(title => `[${title}]`).join(' '); copyToClipboard(formattedTitles, 'TTT'); } catch (error) { console.error('TTT 복사 실패:', error); } }, '영제 대체': async () => { try { const { id, type } = getIdAndType(); if (!id || !type) throw new Error('ID 또는 타입이 없습니다.'); const alternativeTitles = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: `https://api.themoviedb.org/3/${type}/${id}/alternative_titles?api_key=${apiKey}`, onload: response => response.status === 200 ? resolve(JSON.parse(response.responseText)) : reject(`API 요청 실패: ${response.status}`), onerror: reject }); }); const englishTitles = type === 'movie' ? alternativeTitles.titles?.filter(t => t.iso_3166_1 === 'US' || t.iso_3166_1 === 'GB').map(t => t.title) || [] : alternativeTitles.results?.filter(t => t.iso_3166_1 === 'US' || t.iso_3166_1 === 'GB').map(t => t.title) || []; const formattedTitles = englishTitles.map(title => `[${title}]`).join(' '); copyToClipboard(formattedTitles, 'TTT'); } catch (error) { console.error('TTT 복사 실패:', error); } }, '일제 대체': async () => { try { const { id, type } = getIdAndType(); if (!id || !type) throw new Error('ID 또는 타입이 없습니다.'); const alternativeTitles = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: `https://api.themoviedb.org/3/${type}/${id}/alternative_titles?api_key=${apiKey}`, onload: response => response.status === 200 ? resolve(JSON.parse(response.responseText)) : reject(`API 요청 실패: ${response.status}`), onerror: reject }); }); const japaneseTitles = type === 'movie' ? alternativeTitles.titles?.filter(t => t.iso_3166_1 === 'JP').map(t => t.title) || [] : alternativeTitles.results?.filter(t => t.iso_3166_1 === 'JP').map(t => t.title) || []; const formattedTitles = japaneseTitles.map(title => `[${title}]`).join(' '); copyToClipboard(formattedTitles, 'TTT'); } catch (error) { console.error('TTT 복사 실패:', error); } }, }; titleContainer.querySelectorAll('.clickable-text').forEach(element => { const text = element.textContent; if (elements[text]) { element.addEventListener('click', elements[text]); } else if (text.match(/^\(\d{4}\)$/)) { element.addEventListener('click', () => { const title = element.previousElementSibling.textContent; copyToClipboard(`${title} ${text.slice(1, -1)}`, text); }); } else if (text === 'TTT') { element.addEventListener('click', elements['TTT']); } else { // Alternative Titles에 대한 클릭 이벤트 처리 element.addEventListener('click', () => { copyToClipboard(`${text}`, text); }); element.addEventListener('dblclick', () => { const cleanText = text.replace(/^\[|\]$/g, ''); copyToClipboard(cleanText, text); }); } }); // 에피소드 목록 이벤트 리스너 추가 titleContainer.querySelectorAll('.episode-list').forEach(element => { element.addEventListener('click', async () => { try { const pathParts = window.location.pathname.split('/'); const tvId = pathParts[2]; const seasonNumber = pathParts[4]; // URL에서 언어 코드 추출 (예: /tv/1234-some-title/season/1?language=en-US) const urlParams = new URLSearchParams(window.location.search); let languageCode = urlParams.get('language'); // URL에서 language 파라미터 추출 if (languageCode) { languageCode = languageCode.split('-')[0]; // ko-KR -> ko } else { languageCode = 'ko'; // 기본값: 한국어 } if (!tvId || !seasonNumber) { throw new Error('TV ID 또는 시즌 번호를 찾을 수 없습니다.'); } // API 요청에 대한 응답 검증 함수 const validateResponse = (response, type) => { if (response.status !== 200) { throw new Error(`${type} API 요청 실패: ${response.status}`); } const data = JSON.parse(response.responseText); if (!data) { throw new Error(`${type} 데이터가 비어있습니다.`); } return data; }; const [tvInfo, seasonInfo] = await Promise.all([ new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", // language 파라미터를 API 요청에 추가 url: `https://api.themoviedb.org/3/tv/${tvId}?api_key=${apiKey}&language=${languageCode}`, onload: response => { try { resolve(validateResponse(response, 'TV 정보')); } catch (error) { reject(error); } }, onerror: error => reject(new Error(`TV 정보 요청 실패: ${error}`)) }); }), new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", // language 파라미터를 API 요청에 추가 url: `https://api.themoviedb.org/3/tv/${tvId}/season/${seasonNumber}?api_key=${apiKey}&language=${languageCode}`, onload: response => { try { resolve(validateResponse(response, '시즌 정보')); } catch (error) { reject(error); } }, onerror: error => reject(new Error(`시즌 정보 요청 실패: ${error}`)) }); }) ]); if (!tvInfo.first_air_date) { throw new Error('첫 방영일 정보가 없습니다.'); } const firstAirYear = new Date(tvInfo.first_air_date).getFullYear(); if (!seasonInfo.episodes || !Array.isArray(seasonInfo.episodes) || seasonInfo.episodes.length === 0) { throw new Error('에피소드 정보가 없습니다.'); } const episodeList = seasonInfo.episodes.map(ep => { if (!ep || typeof ep.episode_number === 'undefined') { console.warn('잘못된 에피소드 데이터:', ep); return null; } const formattedDate = ep.air_date || 'YYYY-MM-DD'; // 에피소드 제목 처리: 해당 언어에 제목이 없으면 'n화' 표시 const episodeTitle = ep.name || `${ep.episode_number}화`; return `${tvInfo.name} (${firstAirYear}) s${seasonNumber.padStart(2, '0')}e${ep.episode_number.toString().padStart(2, '0')}_${formattedDate} ${episodeTitle}`; }) .filter(Boolean) // null 값 제거 .join('\n'); if (!episodeList) { throw new Error('에피소드 목록을 생성할 수 없습니다.'); } copyToClipboard(episodeList, '에피소드 목록'); showTemporaryMessage('에피소드 목록이 클립보드에 복사되었습니다.'); } catch (error) { console.error('에피소드 목록 생성 실패:', error); showTemporaryMessage(`에피소드 목록 생성 실패: ${error.message}`); } }); }); titleElement.parentNode.insertBefore(titleContainer, titleElement); // 시즌 목록 표시 로직 if (type === 'tv') { try { GM_xmlhttpRequest({ method: "GET", url: `https://api.themoviedb.org/3/tv/${id}?api_key=${apiKey}`, onload: function(response) { const tvData = JSON.parse(response.responseText); const seasonList = document.getElementById('season-list'); const mainTvId = getMainTvId(); // 메인 TV ID 추출 if (seasonList && tvData.seasons) { let seasonLinks = `🌄 / `; tvData.seasons.forEach(season => { const seasonNum = season.season_number.toString().padStart(2, '0'); seasonLinks += `s${seasonNum} 🌄 / `; }); seasonList.innerHTML = seasonLinks.slice(0, -3); } }, onerror: function(error) { console.error('시즌 정보 가져오기 실패:', error); } }); } catch (error) { console.error('시즌 목록 표시 오류:', error); } } }; 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 getOriginCountry = (data) => { return data.origin_country?.[0] || data.production_countries?.[0]?.iso_3166_1 || null; }; const getProductionCountry = (data) => { const originCountry = data.origin_country?.[0] || null; const productionCountries = data.production_countries || []; // 원작국과 동일한 제작국이 있는지 확인 for (const country of productionCountries) { if (country.iso_3166_1 === originCountry) { return country.iso_3166_1; } } // 없으면 첫 번째 제작국 반환 return productionCountries[0]?.iso_3166_1 || null; }; const displayKoreanRating = rating => { if (!rating) return; const factsElement = document.querySelector('.facts'); if (!factsElement) return; let koreanRatingElement = document.getElementById('korean-rating'); if (!koreanRatingElement) { const currentPath = window.location.pathname; const type = currentPath.includes('/movie/') ? 'movie' : 'tv'; const editUrl = currentPath + (type === 'movie' ? '/edit?active_nav_item=release_information' : '/edit?active_nav_item=content_ratings'); koreanRatingElement = Object.assign(document.createElement('a'), { id: 'korean-rating', className: rating === '' ? 'unrated' : 'rated', href: editUrl, // 일반적인 href 링크 사용 style: ` font-size: 1em; margin-right: 10px; font-weight: bold; text-decoration: none; color: inherit; cursor: pointer; `, textContent: rating }); factsElement.insertBefore(koreanRatingElement, factsElement.firstChild); } koreanRatingElement.textContent = rating; }; const displayAdditionalInfo = (originCountry, productionCountry, koTitle, enTitle, imdbId, wikidataId, tvdbId, year) => { const factsElement = document.querySelector('.facts'); let additionalInfoContainer = document.getElementById('additional-info'); if (!additionalInfoContainer) { additionalInfoContainer = document.createElement('div'); additionalInfoContainer.id = "additional-info"; factsElement.parentNode.insertBefore(additionalInfoContainer, factsElement.nextSibling); const originCountryText = translateCountry(originCountry); const productionCountryText = translateCountry(productionCountry); let searchLinks = ''; if (imdbId) { const imdbIdNum = imdbId.replace('tt', ''); searchLinks = ` ▶ OSo OSc SUBDL 씨네(한) 씨네(영) 씨드(한) 씨드(영) `; } else { searchLinks = ` ▶ OSo OSc SUBDL 씨네(한) 씨네(영) 씨드(한) 씨드(영) `; } additionalInfoContainer.innerHTML = ` `; const titleColor = window.getComputedStyle(document.querySelector('.title h2')).color; additionalInfoContainer.querySelectorAll('#external-links a').forEach(link => { link.style.color = titleColor; link.style.marginRight = "0px"; }); } }; const getMainTvId = () => { const pathParts = window.location.pathname.split('/'); return pathParts[2]; // TV ID 추출 }; const fetchData = async () => { const { id, type } = getIdAndType(); if (!id || !type) return; try { const koResponse = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: `https://api.themoviedb.org/3/${type}/${id}?api_key=${apiKey}&language=ko-KR&append_to_response=external_ids,release_dates,content_ratings`, onload: response => response.status === 200 ? resolve(JSON.parse(response.responseText)) : reject(`API 요청 실패: ${response.status}`), onerror: reject }); }); const enResponse = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: `https://api.themoviedb.org/3/${type}/${id}?api_key=${apiKey}&language=en-US`, onload: response => response.status === 200 ? resolve(JSON.parse(response.responseText)) : reject(`API 요청 실패: ${response.status}`), onerror: reject }); }); const koTitle = koResponse.title || koResponse.name || '한국어 제목 미기재'; const enTitle = enResponse.title || enResponse.name || '영어 제목 미기재'; const originalTitle = koResponse.original_title || koResponse.original_name || '원어 제목 미기재'; const koreanRating = getKoreanCertification(koResponse, type); const originCountry = getOriginCountry(koResponse); const productionCountry = getProductionCountry(koResponse); const imdbId = koResponse.imdb_id || koResponse.external_ids?.imdb_id; const wikidataId = koResponse.external_ids?.wikidata_id; const tvdbId = koResponse.external_ids?.tvdb_id; const year = new Date(koResponse.release_date || koResponse.first_air_date).getFullYear(); displayTitles(koTitle, enTitle, originalTitle, type, id, koreanRating, originCountry, productionCountry, year, imdbId, wikidataId, tvdbId); displayKoreanRating(koreanRating); displayAdditionalInfo(originCountry, productionCountry, koTitle, enTitle, imdbId, wikidataId, tvdbId, year); } catch (error) { console.error('TMDB API 요청 오류:', error); } }; 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; })();