// ==UserScript==
// @name instagram.com停用重播 解除靜音 可點擊進度條 下載媒體 開啟媒體
// @namespace Scripts
// @license MIT
// @author huang-wei-lun
// @match https://www.instagram.com/*
// @icon https://www.google.com/s2/favicons?domain=www.instagram.com&sz=32
// @grant none
// @description 停用重播 解除靜音 可點擊進度條 下載媒體 開啟媒體
// @version 1.1a
// ==/UserScript==
(function () {
// 從 localStorage 讀取狀態
let disableLoop = localStorage.getItem("ig_disableLoop") === "true";
let unmuteVideo = localStorage.getItem("ig_unmuteVideo") === "true";
let clickableBar = localStorage.getItem("ig_clickableBar") === "true";
// 主題配色
const themes = {
dark: {
bg: "#262626",
bgHover: "#363636",
border: "#3c3c3c",
text: "#fff",
muted: "#aaa"
},
light: {
bg: "#ffffff",
bgHover: "#f2f2f2",
border: "#dbdbdb",
text: "#000",
muted: "#8e8e8e"
}
};
function getThemeMode() {
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
}
// 建立模組選單 UI
const panel = document.createElement("div");
panel.style.position = "fixed";
panel.style.top = "20px";
panel.style.right = "20px";
panel.style.zIndex = 99999;
panel.style.padding = "10px";
panel.style.borderRadius = "12px";
panel.style.fontSize = "14px";
panel.style.fontFamily = "system-ui, sans-serif";
panel.style.transition = "opacity 0.3s, background 0.3s, color 0.3s";
panel.style.boxShadow = "0 4px 12px rgba(0,0,0,0.4)";
panel.innerHTML = `
<label style="display:block;margin-bottom:6px;">
<input type="checkbox" id="disableLoop"> 停用重播
</label>
<label style="display:block;margin-bottom:6px;">
<input type="checkbox" id="unmuteVideo"> 解除靜音
</label>
<label style="display:block;margin-bottom:6px;">
<input type="checkbox" id="clickableBar"> 可點擊進度條
</label>
<hr id="hr1" style="border:0;margin:8px 0;">
<button id="btnDownload" class="ig-btn">下載媒體</button>
<button id="btnOpen" class="ig-btn">開啟媒體</button>
<hr id="hr2" style="border:0;margin:8px 0;">
<small id="tipText">按 H 顯示/隱藏選單</small>
`;
document.body.appendChild(panel);
// 按鈕樣式套用
function styleButtons(theme) {
document.querySelectorAll(".ig-btn").forEach(btn => {
btn.style.background = theme.bg;
btn.style.color = theme.text;
btn.style.border = `1px solid ${theme.border}`;
btn.style.borderRadius = "8px";
btn.style.padding = "4px 10px";
btn.style.margin = "2px";
btn.style.cursor = "pointer";
btn.style.fontSize = "13px";
btn.onmouseenter = () => btn.style.background = theme.bgHover;
btn.onmouseleave = () => btn.style.background = theme.bg;
});
}
// 更新主題
function applyTheme() {
const mode = getThemeMode();
const theme = themes[mode];
panel.style.background = theme.bg;
panel.style.color = theme.text;
panel.style.border = `1px solid ${theme.border}`;
document.getElementById("hr1").style.borderTop = `1px solid ${theme.border}`;
document.getElementById("hr2").style.borderTop = `1px solid ${theme.border}`;
document.getElementById("tipText").style.color = theme.muted;
styleButtons(theme);
}
// 初始套用主題
applyTheme();
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", applyTheme);
// 初始化 UI 狀態
panel.querySelector("#disableLoop").checked = disableLoop;
panel.querySelector("#unmuteVideo").checked = unmuteVideo;
panel.querySelector("#clickableBar").checked = clickableBar;
// 儲存開關狀態
panel.querySelector("#disableLoop").addEventListener("change", e => {
disableLoop = e.target.checked;
localStorage.setItem("ig_disableLoop", disableLoop);
});
panel.querySelector("#unmuteVideo").addEventListener("change", e => {
unmuteVideo = e.target.checked;
localStorage.setItem("ig_unmuteVideo", unmuteVideo);
});
panel.querySelector("#clickableBar").addEventListener("change", e => {
clickableBar = e.target.checked;
localStorage.setItem("ig_clickableBar", clickableBar);
});
// 顯示 / 隱藏快捷鍵
let panelVisible = true;
document.addEventListener("keydown", e => {
if (e.key.toLowerCase() === "h") {
panelVisible = !panelVisible;
panel.style.opacity = panelVisible ? "1" : "0";
panel.style.pointerEvents = panelVisible ? "auto" : "none";
}
});
// 取得目前頁面主要媒體(圖片或影片)
function getCurrentMedia() {
let video = document.querySelector('video[src]');
let img = document.querySelector('img[srcset], img[src]');
if (video && video.src && !video.src.includes("blob:")) return video.src;
if (img && img.src) return img.src;
if (video && video.src.startsWith("blob:")) {
try {
return video.src;
} catch { }
}
return null;
}
// 下載媒體
panel.querySelector("#btnDownload").addEventListener("click", () => {
const src = getCurrentMedia();
if (!src) return alert("找不到媒體資源");
const a = document.createElement("a");
a.href = src;
a.download = "instagram_media";
document.body.appendChild(a);
a.click();
a.remove();
});
// 開啟媒體
panel.querySelector("#btnOpen").addEventListener("click", () => {
const src = getCurrentMedia();
if (!src) return alert("找不到媒體資源");
window.open(src, "_blank");
});
// 功能監控
setInterval(() => {
const videos = document.querySelectorAll("video");
videos.forEach(v => {
if (disableLoop) v.loop = false;
if (unmuteVideo) {
v.muted = false;
v.volume = 1.0;
}
if (clickableBar && !v.hasClickableBar) {
v.hasClickableBar = true;
const bar = document.createElement("div");
bar.style.position = "absolute";
bar.style.bottom = "5px";
bar.style.left = "0";
bar.style.width = "100%";
bar.style.height = "5px";
bar.style.background = "rgba(255,255,255,0.3)";
bar.style.cursor = "pointer";
bar.style.zIndex = 100000;
const progress = document.createElement("div");
progress.style.height = "100%";
progress.style.background = "rgba(255,255,255,0.8)";
bar.appendChild(progress);
bar.addEventListener("click", e => {
const rect = bar.getBoundingClientRect();
const percent = (e.clientX - rect.left) / rect.width;
v.currentTime = v.duration * percent;
});
setInterval(() => {
progress.style.width = (v.currentTime / v.duration) * 100 + "%";
}, 200);
if (v.parentElement && v.parentElement.style.position !== "absolute") {
v.parentElement.style.position = "relative";
}
v.parentElement.appendChild(bar);
}
});
}, 500);
})();
const themes = {
dark: {
bg: "#262626",
bgHover: "#363636",
border: "#3c3c3c",
text: "#e4e4e4", // 深灰白字色
muted: "#aaa"
},
light: {
bg: "#ffffff",
bgHover: "#f2f2f2",
border: "#dbdbdb",
text: "#000",
muted: "#8e8e8e"
}
};
function applyTheme() {
const mode = getThemeMode();
const theme = themes[mode];
panel.style.background = theme.bg;
panel.style.color = theme.text;
panel.style.border = `1px solid ${theme.border}`;
document.getElementById("hr1").style.borderTop = `1px solid ${theme.border}`;
document.getElementById("hr2").style.borderTop = `1px solid ${theme.border}`;
document.getElementById("tipText").style.color = theme.muted;
// 套用到所有 checkbox 標籤文字
panel.querySelectorAll("label").forEach(label => {
label.style.color = theme.text;
});
styleButtons(theme);
}