Sleazy Fork is available in English.
Set custom background from local PC (PNG, GIF, MP4). Saved in browser.
// ==UserScript==
// @name Furry34 Local Background
// @name:ru Furry34 Локальный фон
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Set custom background from local PC (PNG, GIF, MP4). Saved in browser.
// @description:ru Установка кастомного фона с ПК (PNG, GIF, MP4). Сохраняется в браузере.
// @author scody
// @license MIT
// @match *://furry34.com/*
// @grant GM_addStyle
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
const DB_NAME = 'Furry34BgDB';
const STORE_NAME = 'bg';
let settingsInjected = false;
function openDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, 1);
request.onupgradeneeded = function(e) {
const db = e.target.result;
if (!db.objectStoreNames.contains(STORE_NAME)) {
db.createObjectStore(STORE_NAME, { keyPath: 'id' });
}
};
request.onsuccess = function() { resolve(request.result); };
request.onerror = function() { reject(request.error); };
});
}
async function saveBg(file) {
const db = await openDB();
return new Promise(function(resolve, reject) {
const reader = new FileReader();
reader.onload = function(e) {
const data = {
id: 'bg',
type: file.type.startsWith('video/') ? 'video' : 'image',
mime: file.type,
data: e.target.result,
name: file.name,
size: file.size
};
const tx = db.transaction(STORE_NAME, 'readwrite');
const store = tx.objectStore(STORE_NAME);
const req = store.put(data);
req.onsuccess = function() { resolve(); };
req.onerror = function() { reject(req.error); };
};
reader.onerror = function() { reject(reader.error); };
reader.readAsDataURL(file);
});
}
async function loadBg() {
try {
const db = await openDB();
return new Promise(function(resolve) {
const tx = db.transaction(STORE_NAME, 'readonly');
const store = tx.objectStore(STORE_NAME);
const req = store.get('bg');
req.onsuccess = function() { resolve(req.result || null); };
req.onerror = function() { resolve(null); };
});
} catch (e) {
return null;
}
}
async function deleteBg() {
const db = await openDB();
return new Promise(function(resolve, reject) {
const tx = db.transaction(STORE_NAME, 'readwrite');
const store = tx.objectStore(STORE_NAME);
const req = store.delete('bg');
req.onsuccess = function() { resolve(); };
req.onerror = function() { reject(req.error); };
});
}
function showBg(data) {
const old = document.getElementById('custom-bg-layer');
if (old) old.remove();
const layer = document.createElement('div');
layer.id = 'custom-bg-layer';
layer.style.cssText =
'position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:-1;overflow:hidden;pointer-events:none;background:#001526;';
if (data.type === 'video') {
const video = document.createElement('video');
video.autoplay = true;
video.loop = true;
video.muted = true;
video.playsInline = true;
video.style.cssText = 'width:100%;height:100%;object-fit:cover;';
video.src = data.data;
layer.appendChild(video);
video.play().catch(function() {});
} else {
const img = document.createElement('img');
img.style.cssText = 'width:100%;height:100%;object-fit:cover;';
img.src = data.data;
layer.appendChild(img);
}
document.documentElement.prepend(layer);
}
GM_addStyle(
'body,.root,.sidenav-container,.mat-drawer-container,.mat-drawer-content,.page-content,.container-center{background:transparent !important;}' +
'.bg-settings-card{background:var(--mat-sys-surface-container-high) !important;border:1px solid var(--mat-sys-outline-variant) !important;border-radius:12px !important;padding:16px 20px !important;margin-top:16px !important;}' +
'.bg-settings-card .bg-title{font-size:16px;font-weight:500;color:var(--mat-sys-on-surface);margin:0 0 12px 0;}' +
'.bg-settings-card .bg-content{display:flex;flex-direction:column;gap:12px;}' +
'.bg-settings-card .bg-content .bg-row{display:flex;gap:12px;align-items:center;}' +
'.bg-settings-card .bg-content .bg-row input[type="file"]{flex:1;background:var(--mat-sys-surface);color:var(--mat-sys-on-surface);border:1px solid var(--mat-sys-outline-variant);border-radius:8px;padding:8px 12px;font-size:14px;cursor:pointer;font-family:inherit;}' +
'.bg-settings-card .bg-content .bg-row input[type="file"]::-webkit-file-upload-button{background:var(--mat-sys-primary-container);color:var(--mat-sys-on-primary-container);border:none;border-radius:6px;padding:4px 12px;cursor:pointer;font-family:inherit;}' +
'.bg-settings-card .bg-content .bg-info{color:var(--mat-sys-on-surface-variant);font-size:13px;padding:6px 10px;border-radius:6px;background:var(--mat-sys-surface);min-height:20px;}' +
'.bg-settings-card .bg-content .bg-buttons{display:flex;gap:8px;}' +
'.bg-settings-card .bg-content .bg-btn{padding:8px 20px;border:none;border-radius:8px;font-size:14px;cursor:pointer;font-weight:500;font-family:inherit;transition:opacity 0.2s;}' +
'.bg-settings-card .bg-content .bg-btn:hover{opacity:0.85;}' +
'.bg-settings-card .bg-content .bg-upload{background:var(--mat-sys-primary);color:var(--mat-sys-on-primary);}' +
'.bg-settings-card .bg-content .bg-remove{background:transparent;color:var(--mat-sys-error);border:1px solid var(--mat-sys-error);}' +
'.bg-settings-card .bg-content .bg-remove:hover{background:var(--mat-sys-error-container);}' +
'.bg-settings-card .bg-content .bg-status{color:var(--mat-sys-on-surface-variant);font-size:13px;text-align:center;padding:6px 0;border-radius:6px;background:var(--mat-sys-surface);}'
);
function injectSettings() {
if (settingsInjected) return;
const target = document.querySelector('app-edit-account-content .edit-container');
if (!target) {
setTimeout(injectSettings, 500);
return;
}
if (document.getElementById('bg-settings-card')) return;
const card = document.createElement('div');
card.id = 'bg-settings-card';
card.className = 'bg-settings-card';
card.innerHTML =
'<div class="bg-title">Background</div>' +
'<div class="bg-content">' +
'<div class="bg-row"><input id="bg-file" type="file" accept="image/*,video/*"></div>' +
'<div class="bg-info" id="bg-info">No file selected</div>' +
'<div class="bg-buttons">' +
'<button class="bg-btn bg-upload" id="bg-upload">Upload</button>' +
'<button class="bg-btn bg-remove" id="bg-remove">Delete</button>' +
'</div>' +
'<div class="bg-status" id="bg-status">Waiting...</div>' +
'</div>';
const editContainer = document.querySelector('app-edit-account-content .edit-container');
const privacyCard = editContainer.querySelector('app-edit-privacy');
if (privacyCard) {
privacyCard.parentNode.insertBefore(card, privacyCard.nextSibling);
} else {
editContainer.appendChild(card);
}
settingsInjected = true;
const fileInput = document.getElementById('bg-file');
const info = document.getElementById('bg-info');
const status = document.getElementById('bg-status');
const uploadBtn = document.getElementById('bg-upload');
const removeBtn = document.getElementById('bg-remove');
let selectedFile = null;
fileInput.onchange = function() {
const f = fileInput.files[0];
if (f) {
selectedFile = f;
const type = f.type.startsWith('video/') ? 'Video' : 'Image';
const size = (f.size / 1024 / 1024).toFixed(2);
info.textContent = type + ' | ' + f.name + ' | ' + size + ' MB';
info.style.color = 'var(--mat-sys-on-surface)';
status.textContent = 'File selected';
status.style.color = 'var(--mat-sys-primary)';
}
};
uploadBtn.onclick = async function() {
if (!selectedFile) {
status.textContent = 'Select a file';
status.style.color = 'var(--mat-sys-tertiary)';
return;
}
if (selectedFile.size > 50 * 1024 * 1024) {
status.textContent = 'File exceeds 50MB limit';
status.style.color = 'var(--mat-sys-error)';
return;
}
status.textContent = 'Saving...';
status.style.color = 'var(--mat-sys-tertiary)';
uploadBtn.disabled = true;
try {
await saveBg(selectedFile);
status.textContent = 'Saved! Reloading...';
status.style.color = 'var(--mat-sys-primary)';
setTimeout(function() { location.reload(); }, 600);
} catch (e) {
status.textContent = 'Error: ' + e.message;
status.style.color = 'var(--mat-sys-error)';
uploadBtn.disabled = false;
}
};
removeBtn.onclick = async function() {
if (!confirm('Delete saved background?')) return;
try {
await deleteBg();
status.textContent = 'Deleted! Reloading...';
status.style.color = 'var(--mat-sys-tertiary)';
setTimeout(function() { location.reload(); }, 600);
} catch (e) {
status.textContent = 'Delete error';
status.style.color = 'var(--mat-sys-error)';
}
};
loadBg().then(function(data) {
if (data) {
status.textContent = 'Background loaded from browser';
status.style.color = 'var(--mat-sys-primary)';
info.textContent = (data.type === 'video' ? 'Video' : 'Image') + ' | ' + data.name + ' | ' + (data.size / 1024 / 1024).toFixed(2) + ' MB (saved)';
info.style.color = 'var(--mat-sys-primary)';
}
});
}
function checkAndInject() {
if (window.location.pathname === '/account/edit') {
injectSettings();
}
}
async function init() {
const data = await loadBg();
if (data) {
showBg(data);
}
checkAndInject();
const observer = new MutationObserver(function() {
checkAndInject();
});
observer.observe(document.body, { childList: true, subtree: true });
let lastUrl = window.location.href;
setInterval(function() {
if (window.location.href !== lastUrl) {
lastUrl = window.location.href;
settingsInjected = false;
setTimeout(checkAndInject, 500);
}
}, 500);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();