This commit is contained in:
2026-09-15 20:47:12 +02:00
parent 912deeb28e
commit 20470435ea
10 changed files with 964 additions and 143 deletions
+62 -13
View File
@@ -41,10 +41,10 @@
--posts-meta-bg: #000000b8;
--x2154: #215483;
--bg-canvas-opacity: 1.0;
--bg-canvas-blur: 300px;
--onara-bg-opacity: 0.84;
--bg-canvas-blur: 30px;
--onara-bg-opacity: 0.7;
--onara-backdrop-blur: 5px;
--onara-grid-dim-opacity: 0.15;
--onara-grid-dim-opacity: 0.38;
}
html:not([theme]), html[theme=""] {
@@ -3716,6 +3716,50 @@ body.sidebar-right-hidden #sidebar-drag-zone {
color: #000;
}
.f0ck-tuner-btn-save-default {
background: color-mix(in srgb, var(--accent, #99ff00) 18%, rgba(0, 0, 0, 0.4));
border: 1px solid color-mix(in srgb, var(--accent, #99ff00) 45%, transparent);
color: var(--accent, #99ff00);
border-radius: 6px;
padding: 5px 8px;
font-size: 11px;
font-weight: 600;
cursor: pointer;
transition: all 0.15s ease;
display: flex;
align-items: center;
gap: 4px;
white-space: nowrap;
}
.f0ck-tuner-btn-save-default:hover {
background: var(--accent, #99ff00);
border-color: var(--accent, #99ff00);
color: #000;
box-shadow: 0 2px 8px color-mix(in srgb, var(--accent, #99ff00) 35%, transparent);
}
.f0ck-tuner-btn-factory-reset {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.15);
color: #aaa;
border-radius: 6px;
padding: 5px 8px;
font-size: 11px;
cursor: pointer;
transition: all 0.15s ease;
display: flex;
align-items: center;
gap: 4px;
white-space: nowrap;
}
.f0ck-tuner-btn-factory-reset:hover {
background: rgba(255, 100, 100, 0.18);
border-color: rgba(255, 100, 100, 0.4);
color: #ff7777;
}
.f0ck-tuner-btn-import-preset:hover {
background: rgba(255, 255, 255, 0.2);
color: #fff;
@@ -3738,13 +3782,13 @@ body.sidebar-right-hidden #sidebar-drag-zone {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 5px;
gap: 4px;
background: transparent;
border: none;
color: #8e8e93;
font-size: 11px;
font-size: 10.5px;
font-weight: 600;
padding: 5px 8px;
padding: 5px 5px;
border-radius: 6px;
cursor: pointer;
transition: all 0.15s ease;
@@ -10314,14 +10358,13 @@ div.favs div.posts {
right: 0;
width: 100%;
height: 100%;
/* blur is applied in canvas 2D context at draw time no CSS filter needed.
CSS filter: blur() on a full-screen fixed element forces Chromium to run
a GPU blur pass on every compositor frame (incl. during the fade transition),
which is extremely expensive. Firefox uses a cheaper path for this. */
transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0) scale(var(--bg-canvas-zoom, 1.12));
transform-origin: center center;
z-index: -2;
transition: opacity 1.5s cubic-bezier(0.4, 0, 0.2, 1);
opacity: var(--bg-canvas-opacity, 1.0);
filter: var(--bg-canvas-filter, none);
-webkit-filter: var(--bg-canvas-filter, none);
}
button#togglebg {
@@ -20624,9 +20667,14 @@ a.lazy-thumb.filtered-upload-ghost:hover::after {
}
.user-infobox-username[contenteditable="true"] {
border-bottom: 1px dashed rgba(255, 255, 255, 0.3);
padding: 1px 4px;
padding: 1px 6px;
border-radius: 3px;
transition: all 0.2s ease;
min-width: 80px;
max-width: 320px;
white-space: nowrap;
overflow: visible;
cursor: text;
}
.user-infobox-username[contenteditable="true"]:hover {
border-bottom-color: var(--accent);
@@ -20636,6 +20684,7 @@ a.lazy-thumb.filtered-upload-ghost:hover::after {
outline: none;
border-bottom: 2px solid var(--accent);
background: rgba(0, 0, 0, 0.3);
min-width: 120px;
}
.color-picker-badge {
@@ -20821,7 +20870,7 @@ body.onara-modal-open .admin-bar.open {
z-index: 10050 !important;
display: none;
flex-direction: column !important;
background: rgba(0, 0, 0, var(--onara-bg-opacity, 0.84)) !important;
background: rgba(0, 0, 0, var(--onara-bg-opacity, 0.7)) !important;
backdrop-filter: blur(var(--onara-backdrop-blur, 5px)) saturate(130%) !important;
-webkit-backdrop-filter: blur(var(--onara-backdrop-blur, 5px)) saturate(130%) !important;
overflow-y: auto !important;
+630 -62
View File
File diff suppressed because it is too large Load Diff
+13 -6
View File
@@ -1561,10 +1561,10 @@
const valSpan = document.getElementById(valId);
if (!slider) return;
let currentTuning = {};
let currentTuning = (window.audioVisualizerTuning && Object.keys(window.audioVisualizerTuning).length > 0) ? window.audioVisualizerTuning : {};
try {
const raw = localStorage.getItem('f0ck_audio_tuning');
if (raw) currentTuning = JSON.parse(raw);
if (raw) currentTuning = Object.assign({}, currentTuning, JSON.parse(raw));
} catch (e) {}
const initialVal = currentTuning[tuningKey] !== undefined ? Number(currentTuning[tuningKey]) : defaultVal;
@@ -1585,12 +1585,19 @@
});
};
setupAudioTuningSlider('onara_bg_opacity_slider', 'onara_bg_opacity_val', 'onaraBgOpacity', 0.84, v => Math.round(v * 100) + '%');
setupAudioTuningSlider('onara_bg_opacity_slider', 'onara_bg_opacity_val', 'onaraBgOpacity', 0.70, v => Math.round(v * 100) + '%');
setupAudioTuningSlider('onara_backdrop_blur_slider', 'onara_backdrop_blur_val', 'onaraBackdropBlur', 5, v => Math.round(v) + 'px');
setupAudioTuningSlider('onara_grid_dim_slider', 'onara_grid_dim_val', 'onaraGridDim', 0.15, v => Math.round(v * 100) + '%');
setupAudioTuningSlider('onara_grid_dim_slider', 'onara_grid_dim_val', 'onaraGridDim', 0.38, v => Math.round(v * 100) + '%');
setupAudioTuningSlider('bg_canvas_opacity_slider', 'bg_canvas_opacity_val', 'bgCanvasOpacity', 1.00, v => Math.round(v * 100) + '%');
setupAudioTuningSlider('bg_canvas_blur_slider', 'bg_canvas_blur_val', 'bgCanvasBlur', 300, v => Math.round(v) + 'px');
setupAudioTuningSlider('bg_canvas_brightness_slider', 'bg_canvas_brightness_val', 'bgCanvasBrightness', 1.00, v => Math.round(v * 100) + '%');
setupAudioTuningSlider('bg_canvas_blur_slider', 'bg_canvas_blur_val', 'bgCanvasBlur', 30, v => Math.round(v) + 'px');
setupAudioTuningSlider('bg_canvas_brightness_slider', 'bg_canvas_brightness_val', 'bgCanvasBrightness', 0.45, v => Number(v).toFixed(2) + 'x');
setupAudioTuningSlider('bg_canvas_saturate_slider', 'bg_canvas_saturate_val', 'bgCanvasSaturate', 1.8, v => Number(v).toFixed(2) + 'x');
setupAudioTuningSlider('bg_canvas_contrast_slider', 'bg_canvas_contrast_val', 'bgCanvasContrast', 1.1, v => Number(v).toFixed(2) + 'x');
setupAudioTuningSlider('bg_canvas_zoom_slider', 'bg_canvas_zoom_val', 'bgCanvasZoom', 1.12, v => Number(v).toFixed(2) + 'x');
// Blur Engine slider — shows friendly label text instead of raw number
const blurMethodNames = ['Canvas 2D', 'GPU CSS Filter', 'Hybrid'];
setupAudioTuningSlider('bg_blur_method_slider', 'bg_blur_method_val', 'bgBlurMethod', 0, v => blurMethodNames[Math.round(v)] || String(Math.round(v)), v => Math.round(Number(v)));
// Quote Emojis Toggle
const quoteEmojisToggle = document.getElementById('quote_emojis_toggle');
+5
View File
@@ -411,6 +411,11 @@ export default new class queue {
}
if (!fetched) {
console.error(`[QUEUE] YouTube thumbnail extraction failed for ${itemid}: all quality levels failed or returned placeholder`);
} else {
// Pre-warm dynamic ambient timeline in background
import('./routes/apiv2/ambient.mjs')
.then(m => m.getOrExtractYoutubeAmbient(videoId))
.catch(() => {});
}
}
}
+155
View File
@@ -0,0 +1,155 @@
import { promises as fs } from "fs";
import path from "path";
import os from "os";
import { execFile } from "child_process";
import { promisify } from "util";
import cfg from "../../config.mjs";
const execFileAsync = promisify(execFile);
// Memory cache for in-flight requests to avoid concurrent yt-dlp calls for the same video
const pendingRequests = new Map();
export async function getOrExtractYoutubeAmbient(videoId) {
// Validate videoId: YouTube IDs are 11 chars containing [a-zA-Z0-9_-]
if (!videoId || !/^[a-zA-Z0-9_-]{6,15}$/.test(videoId)) {
throw new Error("Invalid YouTube video ID");
}
let tDir = cfg.paths?.t || 'public/t';
try { tDir = await fs.realpath(tDir); } catch (_) {}
const cacheFile = path.join(tDir, `ambient_${videoId}.json`);
// 1. Check disk cache
try {
const cached = await fs.readFile(cacheFile, 'utf8');
return JSON.parse(cached);
} catch (_) {
// Not cached yet
}
// 2. De-duplicate concurrent requests
if (pendingRequests.has(videoId)) {
return pendingRequests.get(videoId);
}
const promise = (async () => {
try {
const ytUrl = `https://www.youtube.com/watch?v=${videoId}`;
const proxyArgs = (cfg.main?.socks && cfg.main.socks !== 'undefined' && cfg.main.socks !== '')
? ['--proxy', cfg.main.socks.includes('://') ? cfg.main.socks : `socks5h://${cfg.main.socks}`]
: [];
// Run yt-dlp to inspect formats
const { stdout } = await execFileAsync('yt-dlp', [
'-j',
'--skip-download',
'--no-playlist',
...proxyArgs,
ytUrl
], { timeout: 15000 });
const info = JSON.parse(stdout);
const duration = Number(info.duration) || 0;
// Find storyboard format (prefer sb3, fallback to sb2/sb1/sb0)
const sbFormats = (info.formats || []).filter(f => f.format_id && f.format_id.startsWith('sb'));
const sb = sbFormats.find(f => f.format_id === 'sb3') ||
sbFormats.find(f => f.format_id === 'sb2') ||
sbFormats[sbFormats.length - 1];
let colors = [];
if (sb && (sb.url || (sb.fragments && sb.fragments[0]?.url))) {
const imgUrl = (sb.fragments && sb.fragments[0]?.url)
? sb.fragments[0].url
: sb.url.replace('$M', '0');
const tmpImg = path.join(os.tmpdir(), `sb_${videoId}_${Date.now()}.jpg`);
const tmpRgb = path.join(os.tmpdir(), `sb_${videoId}_${Date.now()}.rgb`);
try {
const curlProxyArgs = (cfg.main?.socks && cfg.main.socks !== 'undefined' && cfg.main.socks !== '')
? ['--proxy', cfg.main.socks.includes('://') ? cfg.main.socks : `socks5h://${cfg.main.socks}`]
: [];
await execFileAsync('curl', ['-s', '-L', imgUrl, '-o', tmpImg, ...curlProxyArgs], { timeout: 10000 });
await execFileAsync('magick', [tmpImg, '-scale', '10x10!', `rgb:${tmpRgb}`], { timeout: 5000 });
const buf = await fs.readFile(tmpRgb);
for (let i = 0; i < buf.length; i += 3) {
colors.push([buf[i], buf[i + 1], buf[i + 2]]);
}
} finally {
await fs.unlink(tmpImg).catch(() => {});
await fs.unlink(tmpRgb).catch(() => {});
}
}
// If storyboard wasn't available or yielded no colors, fallback to thumbnail sampling
if (!colors.length) {
// Fallback: download thumbnail and sample a 3x3 palette
const thumbUrl = `https://img.youtube.com/vi/${videoId}/hqdefault.jpg`;
const tmpImg = path.join(os.tmpdir(), `sb_thumb_${videoId}_${Date.now()}.jpg`);
const tmpRgb = path.join(os.tmpdir(), `sb_thumb_${videoId}_${Date.now()}.rgb`);
try {
await execFileAsync('curl', ['-s', '-L', thumbUrl, '-o', tmpImg], { timeout: 10000 });
await execFileAsync('magick', [tmpImg, '-scale', '3x3!', `rgb:${tmpRgb}`], { timeout: 5000 });
const buf = await fs.readFile(tmpRgb);
for (let i = 0; i < buf.length; i += 3) {
colors.push([buf[i], buf[i + 1], buf[i + 2]]);
}
} catch (_) {
// Minimal fallback
colors = [[30, 30, 35]];
} finally {
await fs.unlink(tmpImg).catch(() => {});
await fs.unlink(tmpRgb).catch(() => {});
}
}
const result = {
videoId,
duration,
colors
};
// Cache to disk
try {
await fs.writeFile(cacheFile, JSON.stringify(result), 'utf8');
} catch (err) {
console.warn(`[AMBIENT] Failed to write cache for ${videoId}:`, err.message);
}
return result;
} finally {
pendingRequests.delete(videoId);
}
})();
pendingRequests.set(videoId, promise);
return promise;
}
export default (router) => {
router.get(/^\/api\/v2\/ambient\/yt\/([a-zA-Z0-9_-]+)/, async (req, res) => {
const videoId = req.url.pathname.split('/')[5];
if (!videoId) {
return res.writeHead(400, { 'Content-Type': 'application/json' })
.end(JSON.stringify({ error: 'Missing video ID' }));
}
try {
const data = await getOrExtractYoutubeAmbient(videoId);
return res.writeHead(200, {
'Content-Type': 'application/json',
'Cache-Control': 'public, max-age=86400, stale-while-revalidate=604800'
}).end(JSON.stringify(data));
} catch (err) {
console.error(`[AMBIENT ERROR] Failed for ${videoId}:`, err.message);
return res.writeHead(500, { 'Content-Type': 'application/json' })
.end(JSON.stringify({ error: err.message }));
}
});
return router;
};
+1
View File
@@ -1837,6 +1837,7 @@ process.on('uncaughtException', err => {
nsfl_tag_id: cfg.nsfl_tag_id || 3,
scroller_mime_cats: Array.isArray(cfg.allowedMimes) ? cfg.allowedMimes.filter(c => ['video','image','audio'].includes(c)) : ['video','image','audio'],
themes_json: JSON.stringify(cfg.websrv.themes || []),
audio_tuner_json: JSON.stringify(cfg.audio_tuner || cfg.audio_visualizer || cfg.websrv?.audio_tuner || null),
enable_profile_description: !!cfg.websrv.enable_profile_description,
expose_repost_links_to_guests: !!(cfg.websrv.expose_repost_links_to_guests || cfg.websrv.expose_repost_links),
get private_messages() { return getPrivateMessages(); },
+48 -12
View File
@@ -255,9 +255,9 @@
<div class="setting-item" style="margin-top: 15px; padding-left: 25px;">
<div style="display: flex; align-items: center; justify-content: space-between; max-width: 380px;">
<label for="onara_bg_opacity_slider" style="font-size: 0.9em; margin-bottom: 0; font-weight: bold;">Onara Backdrop Darkness</label>
<span id="onara_bg_opacity_val" style="font-size: 0.85em; opacity: 0.8;">84%</span>
<span id="onara_bg_opacity_val" style="font-size: 0.85em; opacity: 0.8;">70%</span>
</div>
<input type="range" id="onara_bg_opacity_slider" min="0" max="1" step="0.01" value="0.84" style="max-width: 380px; width: 100%; margin-top: 4px;">
<input type="range" id="onara_bg_opacity_slider" min="0" max="1" step="0.01" value="0.70" style="max-width: 380px; width: 100%; margin-top: 4px;">
<small class="text-muted" style="display: block;">Overlay darkness in Onara mode (lower values allow background canvas to shine through).</small>
</div>
@@ -273,9 +273,9 @@
<div class="setting-item" style="margin-top: 15px; padding-left: 25px;">
<div style="display: flex; align-items: center; justify-content: space-between; max-width: 380px;">
<label for="onara_grid_dim_slider" style="font-size: 0.9em; margin-bottom: 0; font-weight: bold;">Onara Background Grid Opacity</label>
<span id="onara_grid_dim_val" style="font-size: 0.85em; opacity: 0.8;">15%</span>
<span id="onara_grid_dim_val" style="font-size: 0.85em; opacity: 0.8;">38%</span>
</div>
<input type="range" id="onara_grid_dim_slider" min="0" max="1" step="0.01" value="0.15" style="max-width: 380px; width: 100%; margin-top: 4px;">
<input type="range" id="onara_grid_dim_slider" min="0" max="1" step="0.01" value="0.38" style="max-width: 380px; width: 100%; margin-top: 4px;">
<small class="text-muted" style="display: block;">Control how clearly the background thumbnail grid shines through over the ambient canvas in Onara mode.</small>
</div>
@@ -290,20 +290,56 @@
<div class="setting-item" style="margin-top: 15px; padding-left: 25px;">
<div style="display: flex; align-items: center; justify-content: space-between; max-width: 380px;">
<label for="bg_canvas_blur_slider" style="font-size: 0.9em; margin-bottom: 0; font-weight: bold;">Background Canvas Blur</label>
<span id="bg_canvas_blur_val" style="font-size: 0.85em; opacity: 0.8;">300px</span>
<label for="bg_canvas_blur_slider" style="font-size: 0.9em; margin-bottom: 0; font-weight: bold;">Background Blur Radius</label>
<span id="bg_canvas_blur_val" style="font-size: 0.85em; opacity: 0.8;">30px</span>
</div>
<input type="range" id="bg_canvas_blur_slider" min="0" max="300" step="1" value="300" style="max-width: 380px; width: 100%; margin-top: 4px;">
<small class="text-muted" style="display: block;">Adjust the ambient blur of the background canvas (higher values create a soft, dreamy ambient glow).</small>
<input type="range" id="bg_canvas_blur_slider" min="0" max="300" step="1" value="30" style="max-width: 380px; width: 100%; margin-top: 4px;">
<small class="text-muted" style="display: block;">Adjust the ambient blur radius. Higher values = softer, dreamier glow. In GPU CSS mode this goes up to 300px at full resolution.</small>
</div>
<div class="setting-item" style="margin-top: 15px; padding-left: 25px;">
<div style="display: flex; align-items: center; justify-content: space-between; max-width: 380px;">
<label for="bg_canvas_brightness_slider" style="font-size: 0.9em; margin-bottom: 0; font-weight: bold;">Background Brightness Boost</label>
<span id="bg_canvas_brightness_val" style="font-size: 0.85em; opacity: 0.8;">100%</span>
<label for="bg_canvas_brightness_slider" style="font-size: 0.9em; margin-bottom: 0; font-weight: bold;">Background Brightness</label>
<span id="bg_canvas_brightness_val" style="font-size: 0.85em; opacity: 0.8;">0.45x</span>
</div>
<input type="range" id="bg_canvas_brightness_slider" min="0.5" max="2.5" step="0.05" value="1.00" style="max-width: 380px; width: 100%; margin-top: 4px;">
<small class="text-muted" style="display: block;">Boost background canvas brightness so visuals pop through clearly.</small>
<input type="range" id="bg_canvas_brightness_slider" min="0.1" max="3.0" step="0.05" value="0.45" style="max-width: 380px; width: 100%; margin-top: 4px;">
<small class="text-muted" style="display: block;">Boost brightness so colors pop. Values &lt; 1 darken, &gt; 1 amplify.</small>
</div>
<div class="setting-item" style="margin-top: 15px; padding-left: 25px;">
<div style="display: flex; align-items: center; justify-content: space-between; max-width: 380px;">
<label for="bg_blur_method_slider" style="font-size: 0.9em; margin-bottom: 0; font-weight: bold;">Blur Engine</label>
<span id="bg_blur_method_val" style="font-size: 0.85em; opacity: 0.8;">Canvas 2D</span>
</div>
<input type="range" id="bg_blur_method_slider" min="0" max="2" step="1" value="0" style="max-width: 380px; width: 100%; margin-top: 4px;">
<small class="text-muted" style="display: block;"><strong>0 = Canvas 2D</strong> (downsampled, fast) &nbsp;|&nbsp; <strong>1 = GPU CSS Filter</strong> (classic look, full-res compositor blur like older versions) &nbsp;|&nbsp; <strong>2 = Hybrid</strong> (CSS blur + canvas color grading)</small>
</div>
<div class="setting-item" style="margin-top: 15px; padding-left: 25px;">
<div style="display: flex; align-items: center; justify-content: space-between; max-width: 380px;">
<label for="bg_canvas_saturate_slider" style="font-size: 0.9em; margin-bottom: 0; font-weight: bold;">Color Saturation</label>
<span id="bg_canvas_saturate_val" style="font-size: 0.85em; opacity: 0.8;">1.80x</span>
</div>
<input type="range" id="bg_canvas_saturate_slider" min="0.5" max="4.0" step="0.05" value="1.8" style="max-width: 380px; width: 100%; margin-top: 4px;">
<small class="text-muted" style="display: block;">Boost color saturation of the background. 1.0 = original, higher = more vivid / neon.</small>
</div>
<div class="setting-item" style="margin-top: 15px; padding-left: 25px;">
<div style="display: flex; align-items: center; justify-content: space-between; max-width: 380px;">
<label for="bg_canvas_contrast_slider" style="font-size: 0.9em; margin-bottom: 0; font-weight: bold;">Contrast Depth</label>
<span id="bg_canvas_contrast_val" style="font-size: 0.85em; opacity: 0.8;">1.10x</span>
</div>
<input type="range" id="bg_canvas_contrast_slider" min="0.5" max="3.0" step="0.05" value="1.1" style="max-width: 380px; width: 100%; margin-top: 4px;">
<small class="text-muted" style="display: block;">Contrast punch. Values &gt; 1.0 darken blacks and pop highlights.</small>
</div>
<div class="setting-item" style="margin-top: 15px; padding-left: 25px;">
<div style="display: flex; align-items: center; justify-content: space-between; max-width: 380px;">
<label for="bg_canvas_zoom_slider" style="font-size: 0.9em; margin-bottom: 0; font-weight: bold;">Edge Overscan Zoom</label>
<span id="bg_canvas_zoom_val" style="font-size: 0.85em; opacity: 0.8;">1.12x</span>
</div>
<input type="range" id="bg_canvas_zoom_slider" min="1.0" max="1.8" step="0.01" value="1.12" style="max-width: 380px; width: 100%; margin-top: 4px;">
<small class="text-muted" style="display: block;">Zoom the background canvas slightly to hide blurry edge bleeding. 1.12 eliminates edge artifacts at typical blur values.</small>
</div>
<div class="setting-item" style="margin-top: 15px;">
+1 -1
View File
@@ -131,7 +131,7 @@
<button type="button" class="sidebar-tab" id="sidebar-tab-tag" data-tab="tag" title="Tag Feed" aria-label="Tag Feed" style="display: none;">
<i class="fa-solid fa-hashtag"></i>
</button>
<button type="button" class="sidebar-tab" id="sidebar-tab-tuner" data-tab="tuner" title="Tuner & Debug (Audio & Danmaku)" aria-label="Tuner & Debug (Audio & Danmaku)">
<button type="button" class="sidebar-tab" id="sidebar-tab-tuner" data-tab="tuner" title="Tuner & Debug (Audio, Danmaku & Background)" aria-label="Tuner & Debug (Audio, Danmaku & Background)">
<i class="fa-solid fa-sliders"></i>
</button>
</div>
+1 -1
View File
@@ -67,7 +67,7 @@
@endif
<link rel="stylesheet" href="/s/css/upload.css?v={{ ts }}">
@endif
<script>window.f0ckThemes = {{ themes_json }}; window.f0ckDefaultTheme = "{{ default_theme }}"; window.f0ckDefaultLayout = "{{ default_layout }}"; window.f0ckDomain = "{{ domain }}"; window.f0ckGitHash = "{{ git_hash }}"; window.f0ckAllowedImages = {{ allowed_comment_images_json }}; window.f0ckEmbedYoutubeInComments = {{ embed_youtube_in_comments ? 'true' : 'false' }}; window.f0ckEnableYoutubeUpload = {{ enable_youtube_upload ? 'true' : 'false' }}; window.f0ckBrandImages = {{ custom_brand_images_json }}; window.f0ckMediaBase = "{{ paths_images }}"; window.f0ckShitpostMode = {{ shitpost_mode ? 'true' : 'false' }}; window.f0ckShitpostRequireRating = {{ shitpost_require_rating ? 'true' : 'false' }}; window.f0ckShitpostMinTags = {{ shitpost_min_tags || 0 }}; window.f0ckEnableItemTitle = {{ enable_item_title ? 'true' : 'false' }}; window.f0ckCommentBannerEnabled = @if(comment_banner_enabled) true @else false @endif;</script>
<script>window.f0ckThemes = {{ themes_json }}; window.f0ckDefaultTheme = "{{ default_theme }}"; window.f0ckDefaultLayout = "{{ default_layout }}"; window.f0ckDomain = "{{ domain }}"; window.f0ckGitHash = "{{ git_hash }}"; window.f0ckAllowedImages = {{ allowed_comment_images_json }}; window.f0ckEmbedYoutubeInComments = {{ embed_youtube_in_comments ? 'true' : 'false' }}; window.f0ckEnableYoutubeUpload = {{ enable_youtube_upload ? 'true' : 'false' }}; window.f0ckBrandImages = {{ custom_brand_images_json }}; window.f0ckMediaBase = "{{ paths_images }}"; window.f0ckShitpostMode = {{ shitpost_mode ? 'true' : 'false' }}; window.f0ckShitpostRequireRating = {{ shitpost_require_rating ? 'true' : 'false' }}; window.f0ckShitpostMinTags = {{ shitpost_min_tags || 0 }}; window.f0ckEnableItemTitle = {{ enable_item_title ? 'true' : 'false' }}; window.f0ckCommentBannerEnabled = @if(comment_banner_enabled) true @else false @endif; window.f0ckServerAudioTuner = {{ audio_tuner_json || 'null' }};</script>
@if(!private_society || session)
<script src="/s/js/marked.min.js" defer></script>
<script src="/s/js/comments.js?v={{ ts }}" defer></script>
+1 -1
View File
@@ -59,7 +59,7 @@
</div>
@elseif(item.mime === 'video/youtube')
<div class="embed-responsive embed-responsive-16by9">
<iframe id="yt-embed" class="embed-responsive-item" width="640" height="360" src="https://www.youtube.com/embed/{!! item.dest.replace('yt:', '') !!}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<iframe id="yt-embed" class="embed-responsive-item" width="640" height="360" data-yt-id="{!! item.dest.replace('yt:', '') !!}" src="https://www.youtube.com/embed/{!! item.dest.replace('yt:', '') !!}?enablejsapi=1" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div>
@elseif(item.mime.startsWith("video"))
<div class="embed-responsive embed-responsive-16by9">