Last active
June 16, 2025 13:21
-
-
Save lunamoth/baba3921931078f3b26411c803bec47e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name 범용 영상 회전기 (단축키 전용) | |
// @version 1.7 | |
// @description 단축키(Ctrl+Shift+Alt+R)로 모든 웹사이트의 HTML5 영상을 90도씩 순차적으로 회전시킵니다. | |
// @author Gemini | |
// @match *://*/* | |
// @grant none | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
const VideoRotator = (function() { | |
const config = { | |
ROTATION_STEPS: 4, | |
ROTATION_ANGLE_DEGREES: 90, | |
SHORTCUT_KEY: 'KeyR', | |
VIDEO_SELECTORS: ['video:hover', 'video:not([paused])', 'video'], | |
INPUT_TAGS: ['INPUT', 'TEXTAREA'], | |
}; | |
const state = new WeakMap(); | |
const findTargetVideo = () => { | |
for (const selector of config.VIDEO_SELECTORS) { | |
const videoElement = document.querySelector(selector); | |
if (videoElement) return videoElement; | |
} | |
return null; | |
}; | |
const isInputActive = (target) => { | |
return target.isContentEditable || config.INPUT_TAGS.includes(target.tagName); | |
}; | |
const getNextRotation = (video) => { | |
const currentRotation = state.get(video)?.rotation ?? 0; | |
return (currentRotation + 1) % config.ROTATION_STEPS; | |
}; | |
const calculateTransformStyle = (rotation, video) => { | |
if (rotation === 0) return ''; | |
const angle = rotation * config.ROTATION_ANGLE_DEGREES; | |
const transforms = [`rotate(${angle}deg)`]; | |
const { clientWidth, clientHeight } = video; | |
if (rotation % 2 !== 0 && clientWidth > 0 && clientHeight > 0) { | |
const scale = Math.min(clientWidth / clientHeight, clientHeight / clientWidth); | |
transforms.push(`scale(${scale})`); | |
} | |
return transforms.join(' '); | |
}; | |
const applyRotation = (video) => { | |
const newRotation = getNextRotation(video); | |
const transformStyle = calculateTransformStyle(newRotation, video); | |
video.style.transform = transformStyle; | |
video.style.willChange = transformStyle ? 'transform' : ''; | |
if (newRotation === 0) { | |
state.delete(video); | |
} else { | |
state.set(video, { rotation: newRotation }); | |
} | |
}; | |
const handleKeyDown = (event) => { | |
const isShortcut = event.ctrlKey && event.shiftKey && event.altKey && event.code === config.SHORTCUT_KEY; | |
if (!isShortcut || isInputActive(event.target)) return; | |
event.preventDefault(); | |
const targetVideo = findTargetVideo(); | |
if (targetVideo) { | |
applyRotation(targetVideo); | |
} | |
}; | |
const init = () => { | |
document.addEventListener('keydown', handleKeyDown, true); | |
}; | |
return { | |
init, | |
}; | |
})(); | |
VideoRotator.init(); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment