Soop 채널(방송국)의 프로필/배너 이미지를 닉네임 파일명으로 다운로드 (커스텀 토스트, 기본이미지 판별, toast-box 내부 append) - 프로필/닉네임 최신 셀렉터 반영 + 파일명 .png 고정 저장
当前为
// ==UserScript==
// @name SOOP 채널 프로필 & 배너 이미지 다운로드
// @namespace http://tampermonkey.net/
// @version 3.2
// @description Soop 채널(방송국)의 프로필/배너 이미지를 닉네임 파일명으로 다운로드 (커스텀 토스트, 기본이미지 판별, toast-box 내부 append) - 프로필/닉네임 최신 셀렉터 반영 + 파일명 .png 고정 저장
// @author WakViewer
// @match https://www.sooplive.co.kr/station/*
// @icon https://res.sooplive.co.kr/afreeca.ico
// @grant GM_download
// @grant unsafeWindow
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @run-at document-end
// @license MIT
// ==/UserScript==
(function() {
'use strict';
let shortcutKey = GM_getValue('shortcutKey', 'Y').toLowerCase();
// ===== 최신/폴백 셀렉터 =====
const SELECTORS = {
bannerDiv : '#layout_container__S7ueh > div.ChannelVisual_channelVisual__a2JA_ > div.TopBanner_TopBannerWrap__1Z87K',
// 프로필 IMG: NEW(사이드바) 1순위 + OLD(상단 비주얼) 폴백
profileImgList: [
'#soop_wrap > div.__soopui__Sidebar-module__Sidebar___CjdhU.__soopui__Sidebar-module__Expanded___DPQe9.ServiceLeftMenu_ChannelServiceLeftMenu__C6J0o.ServiceLeftMenu_expanded__IfzLg > div.__soopui__InnerLnb-module__InnerLnb___qASDV.__soopui__InnerLnb-module__Expanded___hmDdf.ServiceLeftMenu_innerLnb__0hMfP > div.ProfileInfo_streamer__tEqni > div.ProfileInfo_profileImg__mz9Nz > a > span > div > img',
'#layout_container__S7ueh > div.ChannelVisual_channelVisual__a2JA_ > div.ChannelVisual_channelInfoWrapper__ovnvh > div > div.StreamerInfo_streamer__ZhKoB > div.StreamerInfo_profileImg__EC1En > span > div > img'
],
// 닉네임: NEW(사이드바) 1순위 + OLD 폴백
nicknameList: [
'#soop_wrap > div.__soopui__Sidebar-module__Sidebar___CjdhU.__soopui__Sidebar-module__Expanded___DPQe9.ServiceLeftMenu_ChannelServiceLeftMenu__C6J0o.ServiceLeftMenu_expanded__IfzLg > div.__soopui__InnerLnb-module__InnerLnb___qASDV.__soopui__InnerLnb-module__Expanded___hmDdf.ServiceLeftMenu_innerLnb__0hMfP > div.ProfileInfo_streamer__tEqni > div.ProfileInfo_nicknameWrapper__LdDE0 > p', // <p class="ProfileInfo_nick__ZOvu9">마동근</p>
'#layout_container__S7ueh > div.ChannelVisual_channelVisual__a2JA_ > div.ChannelVisual_channelInfoWrapper__ovnvh > div > div.StreamerInfo_streamer__ZhKoB > div:nth-child(2) > div.StreamerInfo_nicknameWrapper__NFtU2 > p'
]
};
// 기본 이미지(있으면 "없음" 처리)
const DEFAULTS = {
profilePrefix: 'https://res.sooplive.co.kr/images/svg/thumb_profile.svg',
bannerLight : 'https://res.sooplive.co.kr/images/channel/ChannelVisualImageLight.jpg',
bannerDark : 'https://res.sooplive.co.kr/images/channel/ChannelVisualImageDark.jpg'
};
// ===== 토스트 =====
function ensureToastBox() {
let box = document.querySelector('body > div.toast-box');
if (!box) {
box = document.createElement('div');
box.className = 'toast-box';
document.body.appendChild(box);
}
return box;
}
function showToast(message) {
const box = ensureToastBox();
const item = document.createElement('div');
const p = document.createElement('p');
p.textContent = message;
item.appendChild(p);
box.appendChild(item);
setTimeout(() => {
if (item && item.parentNode) item.parentNode.removeChild(item);
}, 3000);
}
// ===== 유틸 =====
function parseBgUrlFromStyle(styleStr) {
if (!styleStr) return '';
const m = styleStr.match(/url\((['"]?)(https?:\/\/[^)]+)\1\)/i);
return m ? m[2] : '';
}
// (남겨두지만 현재는 .png 고정 저장을 사용)
function ensureExtByUrl(baseName, url) {
const m = url.match(/\.([a-z0-9]+)(?:[?#]|$)/i);
const ext = (m ? m[1] : 'jpg').toLowerCase();
return baseName.endsWith('.' + ext) ? baseName : `${baseName}.${ext}`;
}
function sanitizeFilenameBase(s) {
// 윈도우 금지문자만 치환
return s.replace(/[\\/:*?"<>|]/g, '_').trim();
}
function ensurePngName(base) {
const safe = sanitizeFilenameBase(base);
return safe.toLowerCase().endsWith('.png') ? safe : `${safe}.png`;
}
function getChannelIdFromUrl() {
const parts = location.pathname.split('/').filter(Boolean); // ["station","<id>"]
return parts[1] || 'streamer';
}
function queryFirst(selectorList) {
for (const sel of selectorList) {
const el = document.querySelector(sel);
if (el) return el;
}
return null;
}
function getNickname() {
// 1) 새 구조(사이드바)
const nickEl = queryFirst(SELECTORS.nicknameList);
const nickTxt = nickEl?.textContent?.trim();
if (nickTxt) return nickTxt;
// 2) 폴백: 프로필 alt
const imgEl = queryFirst(SELECTORS.profileImgList);
const alt = imgEl?.getAttribute('alt')?.trim();
if (alt) return alt;
// 3) 최종: station/<id>
return getChannelIdFromUrl();
}
// ===== 이름을 .png로 고정해서 저장 =====
function downloadImage(url, filenameBase) {
const name = ensurePngName(filenameBase); // 확장자 .png 고정 (내용 포맷 변환 아님)
GM_download({
url,
name,
onerror: (err) => {
console.error(`Failed to download ${name}:`, err);
}
});
}
// ===== 프로필 =====
function downloadProfile() {
const nick = getNickname(); // 예: 마동근
const imgEl = queryFirst(SELECTORS.profileImgList);
const src = imgEl?.src || '';
// 기본 이미지면 없음 처리
if (!src || src.startsWith(DEFAULTS.profilePrefix)) {
showToast('프로필 이미지가 없습니다!');
return;
}
downloadImage(src, `${nick} 프로필`);
showToast('프로필 이미지 다운로드 완료!');
}
// ===== 배너 =====
function downloadBanner() {
const nick = getNickname();
const el = document.querySelector(SELECTORS.bannerDiv);
if (!el) {
showToast('배너 이미지가 없습니다!');
return;
}
const styleBg = el.getAttribute('style') || getComputedStyle(el).backgroundImage || '';
const url = parseBgUrlFromStyle(styleBg);
// 기본 배너면 없음 처리(라이트/다크)
if (!url || url === DEFAULTS.bannerLight || url === DEFAULTS.bannerDark) {
showToast('배너 이미지가 없습니다!');
return;
}
downloadImage(url, `${nick} 배너`);
showToast('배너 이미지 다운로드 완료!');
}
function downloadBoth() {
downloadProfile();
downloadBanner();
}
// ===== 단축키 설정 =====
function setShortcutKey() {
const newKey = prompt('새로운 단축키를 입력하세요!\n\n( 예: Y, Ctrl+Y, Alt+Y, F7 )', shortcutKey);
if (newKey) {
GM_setValue('shortcutKey', newKey.toLowerCase());
shortcutKey = newKey.toLowerCase();
showToast(`단축키 설정 완료: ${newKey}`);
} else {
showToast('단축키 설정이 취소되었습니다.');
}
}
// ===== GM 메뉴 =====
GM_registerMenuCommand('프로필/배너 모두 다운로드', downloadBoth);
GM_registerMenuCommand('프로필 이미지만 다운로드', downloadProfile);
GM_registerMenuCommand('배너 이미지만 다운로드', downloadBanner);
GM_registerMenuCommand('단축키 설정', setShortcutKey);
// ===== 단축키 =====
document.addEventListener('keydown', function(event) {
let keyPressed = '';
if (event.ctrlKey) keyPressed += 'ctrl+';
if (event.shiftKey) keyPressed += 'shift+';
if (event.altKey) keyPressed += 'alt+';
keyPressed += event.key.toLowerCase();
if (keyPressed === shortcutKey) {
downloadBoth();
}
});
})();