gfsd
This commit is contained in:
+595
-102
@@ -701,39 +701,96 @@ html[theme='paper'] .metadata table th {
|
|||||||
border-color: #e2e8f0;
|
border-color: #e2e8f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Icons & GapRight - high contrast black colored */
|
/* Icons & GapRight - aligned with .input-actions button */
|
||||||
html[theme='light'] .gapRight i.iconset,
|
html[theme='light'] .gapRight i.iconset,
|
||||||
html[theme='light'] .gapRight svg.iconset,
|
html[theme='light'] .gapRight svg.iconset,
|
||||||
html[theme='light'] .gapRight > .iconset,
|
html[theme='light'] .gapRight > .iconset,
|
||||||
|
html[theme='light'] .gapRight button,
|
||||||
html[theme='paper'] .gapRight i.iconset,
|
html[theme='paper'] .gapRight i.iconset,
|
||||||
html[theme='paper'] .gapRight svg.iconset,
|
html[theme='paper'] .gapRight svg.iconset,
|
||||||
html[theme='paper'] .gapRight > .iconset {
|
html[theme='paper'] .gapRight > .iconset,
|
||||||
color: #000 !important;
|
html[theme='paper'] .gapRight button,
|
||||||
fill: #000 !important;
|
[theme='light'] .gapRight i.iconset,
|
||||||
background: #f1f5f9 !important;
|
[theme='light'] .gapRight svg.iconset,
|
||||||
|
[theme='light'] .gapRight > .iconset,
|
||||||
|
[theme='light'] .gapRight button,
|
||||||
|
[theme='paper'] .gapRight i.iconset,
|
||||||
|
[theme='paper'] .gapRight svg.iconset,
|
||||||
|
[theme='paper'] .gapRight > .iconset,
|
||||||
|
[theme='paper'] .gapRight button {
|
||||||
|
background: #ffffff !important;
|
||||||
border: 1px solid #cbd5e1 !important;
|
border: 1px solid #cbd5e1 !important;
|
||||||
border-radius: 4px;
|
border-radius: 3px !important;
|
||||||
|
color: #334155 !important;
|
||||||
|
fill: #334155 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
html[theme='light'] .gapRight i.iconset:hover,
|
html[theme='light'] .gapRight i.iconset:hover,
|
||||||
html[theme='light'] .gapRight svg.iconset:hover,
|
html[theme='light'] .gapRight svg.iconset:hover,
|
||||||
|
html[theme='light'] .gapRight > .iconset:hover,
|
||||||
|
html[theme='light'] .gapRight button:hover,
|
||||||
html[theme='paper'] .gapRight i.iconset:hover,
|
html[theme='paper'] .gapRight i.iconset:hover,
|
||||||
html[theme='paper'] .gapRight svg.iconset:hover {
|
html[theme='paper'] .gapRight svg.iconset:hover,
|
||||||
background: #e2e8f0 !important;
|
html[theme='paper'] .gapRight > .iconset:hover,
|
||||||
color: #000 !important;
|
html[theme='paper'] .gapRight button:hover,
|
||||||
fill: #000 !important;
|
[theme='light'] .gapRight i.iconset:hover,
|
||||||
|
[theme='light'] .gapRight svg.iconset:hover,
|
||||||
|
[theme='light'] .gapRight > .iconset:hover,
|
||||||
|
[theme='light'] .gapRight button:hover,
|
||||||
|
[theme='paper'] .gapRight i.iconset:hover,
|
||||||
|
[theme='paper'] .gapRight svg.iconset:hover,
|
||||||
|
[theme='paper'] .gapRight > .iconset:hover,
|
||||||
|
[theme='paper'] .gapRight button:hover,
|
||||||
|
html[theme='light'] .gapRight i.iconset.active,
|
||||||
|
html[theme='light'] .gapRight svg.iconset.active,
|
||||||
|
html[theme='light'] .gapRight > .iconset.active,
|
||||||
|
html[theme='light'] .gapRight button.active,
|
||||||
|
html[theme='paper'] .gapRight i.iconset.active,
|
||||||
|
html[theme='paper'] .gapRight svg.iconset.active,
|
||||||
|
html[theme='paper'] .gapRight > .iconset.active,
|
||||||
|
html[theme='paper'] .gapRight button.active,
|
||||||
|
[theme='light'] .gapRight i.iconset.active,
|
||||||
|
[theme='light'] .gapRight svg.iconset.active,
|
||||||
|
[theme='light'] .gapRight > .iconset.active,
|
||||||
|
[theme='light'] .gapRight button.active,
|
||||||
|
[theme='paper'] .gapRight i.iconset.active,
|
||||||
|
[theme='paper'] .gapRight svg.iconset.active,
|
||||||
|
[theme='paper'] .gapRight > .iconset.active,
|
||||||
|
[theme='paper'] .gapRight button.active,
|
||||||
|
html[theme='light'] .gapRight i.iconset.is-active,
|
||||||
|
html[theme='light'] .gapRight svg.iconset.is-active,
|
||||||
|
html[theme='light'] .gapRight > .iconset.is-active,
|
||||||
|
html[theme='light'] .gapRight button.is-active,
|
||||||
|
html[theme='paper'] .gapRight i.iconset.is-active,
|
||||||
|
html[theme='paper'] .gapRight svg.iconset.is-active,
|
||||||
|
html[theme='paper'] .gapRight > .iconset.is-active,
|
||||||
|
html[theme='paper'] .gapRight button.is-active,
|
||||||
|
[theme='light'] .gapRight i.iconset.is-active,
|
||||||
|
[theme='light'] .gapRight svg.iconset.is-active,
|
||||||
|
[theme='light'] .gapRight > .iconset.is-active,
|
||||||
|
[theme='light'] .gapRight button.is-active,
|
||||||
|
[theme='paper'] .gapRight i.iconset.is-active,
|
||||||
|
[theme='paper'] .gapRight svg.iconset.is-active,
|
||||||
|
[theme='paper'] .gapRight > .iconset.is-active,
|
||||||
|
[theme='paper'] .gapRight button.is-active {
|
||||||
|
background: #f1f5f9 !important;
|
||||||
|
border-color: var(--accent) !important;
|
||||||
|
color: var(--accent) !important;
|
||||||
|
fill: var(--accent) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
html[theme='light'] .gapRight #a_favo,
|
html[theme='light'] .gapRight #a_favo.fa-solid,
|
||||||
html[theme='paper'] .gapRight #a_favo {
|
html[theme='paper'] .gapRight #a_favo.fa-solid,
|
||||||
|
[theme='light'] .gapRight #a_favo.fa-solid,
|
||||||
|
[theme='paper'] .gapRight #a_favo.fa-solid {
|
||||||
color: #e11d48 !important;
|
color: #e11d48 !important;
|
||||||
fill: #e11d48 !important;
|
fill: #e11d48 !important;
|
||||||
|
border-color: rgba(225, 29, 72, 0.4) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
html[theme='light'] i.iconset#a_oc,
|
html[theme='light'] i.iconset#a_oc,
|
||||||
html[theme='paper'] i.iconset#a_oc {
|
html[theme='paper'] i.iconset#a_oc {
|
||||||
color: #000 !important;
|
color: inherit !important;
|
||||||
fill: #000 !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* a_addtag and tag controls */
|
/* a_addtag and tag controls */
|
||||||
@@ -3155,23 +3212,41 @@ body.sidebar-right-hidden #sidebar-drag-zone {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-media-placeholder.audio {
|
.sidebar-media-placeholder.audio {
|
||||||
background: radial-gradient(circle at center, rgba(var(--accent-rgb, 153, 255, 0), 0.22) 0%, rgba(var(--accent-rgb, 153, 255, 0), 0.05) 70%), var(--nav-bg, #1e1e1e);
|
background: #000000;
|
||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
text-shadow: 0 0 16px rgba(var(--accent-rgb, 153, 255, 0), 0.35);
|
text-shadow: 0 0 16px color-mix(in srgb, var(--accent, #99ff00) 35%, transparent);
|
||||||
|
perspective: 800px;
|
||||||
|
overflow: hidden;
|
||||||
|
outline: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-media-placeholder.audio.has-cover {
|
.sidebar-media-placeholder.audio.has-cover {
|
||||||
background: radial-gradient(circle at center, rgba(0, 0, 0, 0.45) 0%, rgba(0, 0, 0, 0.82) 85%), var(--nav-bg, #1e1e1e);
|
background: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-reactive-bg {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 0;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
will-change: background, opacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
.audio-cover-circle {
|
.audio-cover-circle {
|
||||||
display: none;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
width: 155px;
|
width: 125px;
|
||||||
height: 155px;
|
height: 125px;
|
||||||
max-width: calc(100% - 40px);
|
max-width: calc(100% - 40px);
|
||||||
max-height: calc(100% - 40px);
|
max-height: calc(100% - 40px);
|
||||||
aspect-ratio: 1 / 1;
|
aspect-ratio: 1 / 1;
|
||||||
@@ -3179,31 +3254,63 @@ body.sidebar-right-hidden #sidebar-drag-zone {
|
|||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.88), 0 0 35px rgba(var(--accent-rgb, 153, 255, 0), 0.28), inset 0 0 20px rgba(0, 0, 0, 0.65);
|
background-color: #000000;
|
||||||
border: 2px solid rgba(var(--accent-rgb, 153, 255, 0), 0.4);
|
background-image: none;
|
||||||
overflow: hidden;
|
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.88), 0 0 150px color-mix(in srgb, var(--accent, #99ff00) 45%, transparent);
|
||||||
|
border: 2px solid color-mix(in srgb, var(--accent, #99ff00) 40%, transparent);
|
||||||
|
outline: none !important;
|
||||||
|
overflow: visible;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
will-change: transform, box-shadow, border-color;
|
will-change: transform, box-shadow, border-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.audio-cover-circle::after {
|
.audio-eye-inner-canvas {
|
||||||
content: '';
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: radial-gradient(circle at center, transparent 72%, rgba(0, 0, 0, 0.35) 92%, rgba(0, 0, 0, 0.75) 100%);
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-media-placeholder.audio.has-cover .audio-cover-circle {
|
.audio-cover-circle i {
|
||||||
display: block;
|
font-size: 3.4rem;
|
||||||
|
color: var(--accent);
|
||||||
|
text-shadow: 0 0 16px color-mix(in srgb, var(--accent, #99ff00) 75%, transparent), 0 0 35px color-mix(in srgb, var(--accent, #99ff00) 40%, transparent);
|
||||||
|
z-index: 2;
|
||||||
|
position: relative;
|
||||||
|
pointer-events: none;
|
||||||
|
display: inline-block;
|
||||||
|
will-change: transform, filter;
|
||||||
|
transition: transform 0.04s linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-media-placeholder.audio.has-cover i {
|
.sidebar-media-placeholder.audio.has-cover .audio-cover-circle i {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-media-placeholder.audio:has(.audio-cover-circle) > i {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-media-placeholder.audio .audio-cover-circle {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumb > .sidebar-media-placeholder .audio-cover-circle,
|
||||||
|
.album-thumb-item > .sidebar-media-placeholder .audio-cover-circle,
|
||||||
|
.item-preview .sidebar-media-placeholder .audio-cover-circle {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumb > .sidebar-media-placeholder > i,
|
||||||
|
.album-thumb-item > .sidebar-media-placeholder > i,
|
||||||
|
.item-preview .sidebar-media-placeholder > i {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-media-placeholder.hidden {
|
.sidebar-media-placeholder.hidden {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
@@ -3233,6 +3340,13 @@ body.sidebar-right-hidden #sidebar-drag-zone {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
font-size: clamp(2rem, 5.5vw, 3.8rem);
|
font-size: clamp(2rem, 5.5vw, 3.8rem);
|
||||||
border-radius: inherit;
|
border-radius: inherit;
|
||||||
|
background: #000000 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.album-gallery-container.is-audio-active,
|
||||||
|
.album-audio-wrapper,
|
||||||
|
.embed-responsive:has(.sidebar-media-placeholder.audio) {
|
||||||
|
background: #000000 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v0ck:fullscreen > .sidebar-media-placeholder.audio,
|
.v0ck:fullscreen > .sidebar-media-placeholder.audio,
|
||||||
@@ -3252,6 +3366,13 @@ body.sidebar-right-hidden #sidebar-drag-zone {
|
|||||||
max-height: calc(100% - 100px);
|
max-height: calc(100% - 100px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.v0ck:fullscreen .audio-cover-circle i,
|
||||||
|
.v0ck.v0ck_fullscreen .audio-cover-circle i,
|
||||||
|
#main:fullscreen .v0ck.v0ck_fullscreen .audio-cover-circle i,
|
||||||
|
#onara-modal:fullscreen .v0ck.v0ck_fullscreen .audio-cover-circle i {
|
||||||
|
font-size: clamp(5rem, 14vmin, 12rem);
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-media-placeholder.audio i {
|
.sidebar-media-placeholder.audio i {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
@@ -3311,6 +3432,50 @@ body.sidebar-right-hidden #sidebar-drag-zone {
|
|||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Embedded in Right Sidebar */
|
||||||
|
.sidebar-tuner-container {
|
||||||
|
display: none;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: rgba(255, 255, 255, 0.15) transparent;
|
||||||
|
padding: 0;
|
||||||
|
touch-action: pan-y;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-tuner-container::-webkit-scrollbar {
|
||||||
|
width: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-tuner-container::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-tuner-container.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.f0ck-audio-tuner-panel.in-sidebar {
|
||||||
|
position: static;
|
||||||
|
width: 100%;
|
||||||
|
max-height: none;
|
||||||
|
overflow-y: visible;
|
||||||
|
background: transparent;
|
||||||
|
backdrop-filter: none;
|
||||||
|
-webkit-backdrop-filter: none;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
padding: 14px 14px 40px 14px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.f0ck-audio-tuner-panel.in-sidebar .f0ck-tuner-close {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.f0ck-tuner-header {
|
.f0ck-tuner-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -3431,6 +3596,126 @@ body.sidebar-right-hidden #sidebar-drag-zone {
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.f0ck-tuner-presets-bar {
|
||||||
|
background: rgba(255, 255, 255, 0.04);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.10);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.f0ck-tuner-presets-select-wrap {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.f0ck-tuner-preset-select {
|
||||||
|
flex: 1;
|
||||||
|
background: rgba(0, 0, 0, 0.45);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: #fff;
|
||||||
|
padding: 6px 8px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-family: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.f0ck-tuner-preset-select:focus {
|
||||||
|
border-color: var(--accent, #99ff00);
|
||||||
|
}
|
||||||
|
|
||||||
|
.f0ck-tuner-preset-select optgroup {
|
||||||
|
color: var(--accent, #99ff00);
|
||||||
|
background: #111;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.f0ck-tuner-preset-select option {
|
||||||
|
color: #eee;
|
||||||
|
background: #181818;
|
||||||
|
}
|
||||||
|
|
||||||
|
.f0ck-tuner-btn-icon {
|
||||||
|
background: rgba(255, 255, 255, 0.06);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.14);
|
||||||
|
color: #bbb;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 6px 9px;
|
||||||
|
font-size: 11px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.f0ck-tuner-btn-icon:disabled {
|
||||||
|
opacity: 0.3;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.f0ck-tuner-btn-icon:not(:disabled):hover {
|
||||||
|
background: rgba(255, 50, 50, 0.25);
|
||||||
|
border-color: rgba(255, 50, 50, 0.5);
|
||||||
|
color: #ff5555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.f0ck-tuner-presets-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.f0ck-tuner-preset-input {
|
||||||
|
flex: 1;
|
||||||
|
background: rgba(0, 0, 0, 0.35);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.14);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: #fff;
|
||||||
|
padding: 5px 8px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-family: inherit;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.f0ck-tuner-preset-input:focus {
|
||||||
|
border-color: var(--accent, #99ff00);
|
||||||
|
}
|
||||||
|
|
||||||
|
.f0ck-tuner-btn-save-preset,
|
||||||
|
.f0ck-tuner-btn-import-preset {
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||||
|
color: #eee;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
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-preset:hover {
|
||||||
|
background: var(--accent, #99ff00);
|
||||||
|
border-color: var(--accent, #99ff00);
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.f0ck-tuner-btn-import-preset:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-video-details {
|
.sidebar-video-details {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
@@ -6725,8 +7010,10 @@ body.layout-modern.sidebar-right-hidden .pagination-container-fluid {
|
|||||||
|
|
||||||
.gapRight {
|
.gapRight {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: inline-flex;
|
||||||
|
gap: 6px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-lock-btn {
|
.admin-lock-btn {
|
||||||
@@ -6938,7 +7225,8 @@ body:has(.index-container) > .pagewrapper > #main {
|
|||||||
overflow-y: auto !important;
|
overflow-y: auto !important;
|
||||||
overflow-x: hidden !important;
|
overflow-x: hidden !important;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
overscroll-behavior-y: contain;
|
overscroll-behavior-y: auto;
|
||||||
|
overscroll-behavior-x: contain;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 5px 20px 75px 20px;
|
padding: 5px 20px 75px 20px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -7772,7 +8060,7 @@ a.removetag i {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0 0 0 5px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
@@ -7783,44 +8071,15 @@ a.removetag i {
|
|||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
opacity: 0.85;
|
||||||
|
width: 14px;
|
||||||
|
max-width: 14px;
|
||||||
|
overflow: visible;
|
||||||
|
pointer-events: auto;
|
||||||
|
transform: scale(1);
|
||||||
transition: all 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);
|
transition: all 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (hover: hover) {
|
|
||||||
.tag-badge:not(.tag-is-excluded) .tag-exclude-btn {
|
|
||||||
opacity: 0;
|
|
||||||
width: 0;
|
|
||||||
max-width: 0;
|
|
||||||
margin-left: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
transform: scale(0.5);
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag-badge:hover .tag-exclude-btn,
|
|
||||||
.tag-badge:focus-within .tag-exclude-btn {
|
|
||||||
opacity: 0.85;
|
|
||||||
width: 14px;
|
|
||||||
max-width: 14px;
|
|
||||||
margin-left: 5px;
|
|
||||||
overflow: visible;
|
|
||||||
transform: scale(1);
|
|
||||||
pointer-events: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (hover: none) {
|
|
||||||
.tag-badge .tag-exclude-btn {
|
|
||||||
opacity: 0.7;
|
|
||||||
width: 14px;
|
|
||||||
max-width: 14px;
|
|
||||||
margin-left: 4px;
|
|
||||||
overflow: visible;
|
|
||||||
pointer-events: auto;
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag-badge .tag-exclude-btn:hover,
|
.tag-badge .tag-exclude-btn:hover,
|
||||||
.tag-badge .tag-exclude-btn:focus-visible {
|
.tag-badge .tag-exclude-btn:focus-visible {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@@ -7845,12 +8104,6 @@ a.removetag i {
|
|||||||
|
|
||||||
.tag-badge.tag-is-excluded .tag-exclude-btn {
|
.tag-badge.tag-is-excluded .tag-exclude-btn {
|
||||||
opacity: 0.95;
|
opacity: 0.95;
|
||||||
width: 14px;
|
|
||||||
max-width: 14px;
|
|
||||||
margin-left: 5px;
|
|
||||||
overflow: visible;
|
|
||||||
pointer-events: auto;
|
|
||||||
transform: scale(1);
|
|
||||||
color: #ff4757;
|
color: #ff4757;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9674,15 +9927,18 @@ table img {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* gapRight icon colors: white for all, pink for favorites */
|
/* gapRight icon colors & active states */
|
||||||
.gapRight i.iconset {
|
.gapRight i.iconset,
|
||||||
color: #fff !important;
|
.gapRight svg.iconset,
|
||||||
fill: #fff !important;
|
.gapRight button {
|
||||||
|
color: var(--white, #fff);
|
||||||
|
fill: var(--white, #fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
.gapRight #a_favo {
|
.gapRight #a_favo.fa-solid {
|
||||||
color: #ff6b9d !important;
|
color: #ff6b9d !important;
|
||||||
fill: #ff6b9d !important;
|
fill: #ff6b9d !important;
|
||||||
|
border-color: rgba(255, 107, 157, 0.4) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* pin icon */
|
/* pin icon */
|
||||||
@@ -9701,10 +9957,6 @@ table img {
|
|||||||
transform: rotate(-45deg);
|
transform: rotate(-45deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconset#subscribe-btn {
|
|
||||||
border-style: outset;
|
|
||||||
}
|
|
||||||
|
|
||||||
.imageDoor:hover:after {
|
.imageDoor:hover:after {
|
||||||
content: "";
|
content: "";
|
||||||
background-image: var(--hover-image);
|
background-image: var(--hover-image);
|
||||||
@@ -14695,6 +14947,42 @@ body.layout-modern .tag-ac-input {
|
|||||||
margin: 5px;
|
margin: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.item-preview .sidebar-thumb-link {
|
||||||
|
position: relative;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin: 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
flex-shrink: 0;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-preview .sidebar-media-placeholder {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
font-size: 14px;
|
||||||
|
border-radius: inherit;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-preview .sidebar-media-placeholder.audio {
|
||||||
|
background: #000000;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-preview .sidebar-media-placeholder.audio > i {
|
||||||
|
display: flex !important;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
/* ═══════════════════════════════════════════════════════════
|
/* ═══════════════════════════════════════════════════════════
|
||||||
PRIVATE MESSAGES / DM SYSTEM
|
PRIVATE MESSAGES / DM SYSTEM
|
||||||
═══════════════════════════════════════════════════════════ */
|
═══════════════════════════════════════════════════════════ */
|
||||||
@@ -16347,37 +16635,109 @@ span.gchat-post-card--loading {
|
|||||||
.gapRight,
|
.gapRight,
|
||||||
body.layout-modern .gapRight,
|
body.layout-modern .gapRight,
|
||||||
body.layout-legacy .gapRight {
|
body.layout-legacy .gapRight {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
gap: 6px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gapRight>svg.iconset,
|
.gapRight>svg.iconset,
|
||||||
.gapRight>i.iconset,
|
.gapRight>i.iconset,
|
||||||
|
.gapRight>button,
|
||||||
|
.gapRight>.iconset,
|
||||||
|
.gapRight i.iconset,
|
||||||
|
.gapRight svg.iconset,
|
||||||
|
.gapRight button,
|
||||||
body.layout-modern .gapRight>svg.iconset,
|
body.layout-modern .gapRight>svg.iconset,
|
||||||
body.layout-modern .gapRight>i.iconset,
|
body.layout-modern .gapRight>i.iconset,
|
||||||
|
body.layout-modern .gapRight>button,
|
||||||
|
body.layout-modern .gapRight>.iconset,
|
||||||
body.layout-legacy .gapRight>svg.iconset,
|
body.layout-legacy .gapRight>svg.iconset,
|
||||||
body.layout-legacy .gapRight>i.iconset {
|
body.layout-legacy .gapRight>i.iconset,
|
||||||
|
body.layout-legacy .gapRight>button,
|
||||||
|
body.layout-legacy .gapRight>.iconset {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
|
min-width: 30px;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
margin: 5px;
|
padding: 0;
|
||||||
padding: 5px;
|
box-sizing: border-box;
|
||||||
right: 0px;
|
|
||||||
background: var(--badge-tag);
|
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: 18px;
|
gap: 5px;
|
||||||
box-sizing: border-box;
|
border-radius: 3px;
|
||||||
|
border: 1px solid var(--nav-border-color, rgba(255, 255, 255, 0.15));
|
||||||
|
background: var(--navigation-links-bg, rgba(255, 255, 255, 0.08));
|
||||||
|
color: var(--white, #fff);
|
||||||
|
fill: var(--white, #fff);
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 0;
|
||||||
|
position: static;
|
||||||
|
right: auto;
|
||||||
|
vertical-align: middle;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease, opacity 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gapRight>svg.iconset:hover,
|
||||||
|
.gapRight>i.iconset:hover,
|
||||||
|
.gapRight>button:hover,
|
||||||
|
.gapRight>.iconset:hover,
|
||||||
|
.gapRight i.iconset:hover,
|
||||||
|
.gapRight svg.iconset:hover,
|
||||||
|
.gapRight button:hover,
|
||||||
|
.gapRight>svg.iconset.active,
|
||||||
|
.gapRight>i.iconset.active,
|
||||||
|
.gapRight>button.active,
|
||||||
|
.gapRight>.iconset.active,
|
||||||
|
.gapRight i.iconset.active,
|
||||||
|
.gapRight svg.iconset.active,
|
||||||
|
.gapRight button.active,
|
||||||
|
.gapRight>svg.iconset.is-active,
|
||||||
|
.gapRight>i.iconset.is-active,
|
||||||
|
.gapRight>button.is-active,
|
||||||
|
.gapRight>.iconset.is-active,
|
||||||
|
.gapRight i.iconset.is-active,
|
||||||
|
.gapRight svg.iconset.is-active,
|
||||||
|
.gapRight button.is-active,
|
||||||
|
body.layout-modern .gapRight>svg.iconset:hover,
|
||||||
|
body.layout-modern .gapRight>i.iconset:hover,
|
||||||
|
body.layout-modern .gapRight>button:hover,
|
||||||
|
body.layout-modern .gapRight>.iconset:hover,
|
||||||
|
body.layout-modern .gapRight>svg.iconset.active,
|
||||||
|
body.layout-modern .gapRight>i.iconset.active,
|
||||||
|
body.layout-modern .gapRight>button.active,
|
||||||
|
body.layout-modern .gapRight>.iconset.active,
|
||||||
|
body.layout-legacy .gapRight>svg.iconset:hover,
|
||||||
|
body.layout-legacy .gapRight>i.iconset:hover,
|
||||||
|
body.layout-legacy .gapRight>button:hover,
|
||||||
|
body.layout-legacy .gapRight>.iconset:hover,
|
||||||
|
body.layout-legacy .gapRight>svg.iconset.active,
|
||||||
|
body.layout-legacy .gapRight>i.iconset.active,
|
||||||
|
body.layout-legacy .gapRight>button.active,
|
||||||
|
body.layout-legacy .gapRight>.iconset.active {
|
||||||
|
background: var(--nav-link-hover-bg, rgba(255, 255, 255, 0.15)) !important;
|
||||||
|
border-color: var(--accent) !important;
|
||||||
|
color: var(--accent) !important;
|
||||||
|
fill: var(--accent) !important;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gapRight>.iconset#a_favo,
|
.gapRight>.iconset#a_favo,
|
||||||
body.layout-legacy .gapRight>.iconset#a_favo {
|
body.layout-legacy .gapRight>.iconset#a_favo {
|
||||||
fill: #ff009b;
|
fill: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* OC Toggle Icon - now i element, color inherited from .gapRight i.iconset */
|
/* OC Toggle Icon - inherits from .gapRight i.iconset */
|
||||||
i.iconset#a_oc {
|
i.iconset#a_oc {
|
||||||
color: #fff !important;
|
color: inherit;
|
||||||
fill: #fff !important;
|
fill: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* OC Feature */
|
/* OC Feature */
|
||||||
@@ -20246,7 +20606,8 @@ body.onara-modal-open .admin-bar.open {
|
|||||||
overflow-x: hidden !important;
|
overflow-x: hidden !important;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
scrollbar-width: none !important;
|
scrollbar-width: none !important;
|
||||||
overscroll-behavior: contain !important;
|
overscroll-behavior-y: auto !important;
|
||||||
|
overscroll-behavior-x: contain !important;
|
||||||
box-sizing: border-box !important;
|
box-sizing: border-box !important;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: scale(0.99);
|
transform: scale(0.99);
|
||||||
@@ -21026,6 +21387,8 @@ div#flash,
|
|||||||
.album-gallery-container {
|
.album-gallery-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.album-active-image {
|
.album-active-image {
|
||||||
@@ -21082,7 +21445,8 @@ div#flash,
|
|||||||
top: 14px;
|
top: 14px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
pointer-events: auto;
|
pointer-events: none;
|
||||||
|
opacity: 0;
|
||||||
background: rgba(16, 16, 20, 0.75);
|
background: rgba(16, 16, 20, 0.75);
|
||||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
backdrop-filter: blur(12px);
|
backdrop-filter: blur(12px);
|
||||||
@@ -21098,6 +21462,24 @@ div#flash,
|
|||||||
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
|
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
|
||||||
letter-spacing: 0.03em;
|
letter-spacing: 0.03em;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
transition: opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1), transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (hover: hover) {
|
||||||
|
.album-gallery-container:hover .album-counter-pill,
|
||||||
|
.album-gallery-container:focus-within .album-counter-pill {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.album-gallery-container.v0ck_hover .album-counter-pill,
|
||||||
|
.album-gallery-container.strip-peek .album-counter-pill,
|
||||||
|
.album-gallery-container:has(.v0ck.v0ck_hover) .album-counter-pill,
|
||||||
|
.album-counter-pill.is-visible,
|
||||||
|
.album-counter-pill.strip-peek {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.album-counter-pill i {
|
.album-counter-pill i {
|
||||||
@@ -21111,6 +21493,7 @@ div#flash,
|
|||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%) translateY(10px);
|
transform: translateX(-50%) translateY(10px);
|
||||||
max-width: calc(100% - 32px);
|
max-width: calc(100% - 32px);
|
||||||
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
@@ -21122,16 +21505,101 @@ div#flash,
|
|||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
z-index: 22;
|
z-index: 22;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
overscroll-behavior: contain;
|
||||||
|
overscroll-behavior-x: contain;
|
||||||
|
overscroll-behavior-y: none;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
touch-action: pan-x;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
|
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
cursor: grab;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
scroll-behavior: smooth;
|
||||||
transition: opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1), transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1), transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.album-gallery-container:hover .album-thumbnails-strip,
|
.album-thumbnails-strip.is-dragging {
|
||||||
.album-gallery-container:focus-within .album-thumbnails-strip,
|
cursor: grabbing !important;
|
||||||
.album-thumbnails-strip.is-visible {
|
scroll-behavior: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.album-thumb-item {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.album-thumb-item img {
|
||||||
|
-webkit-user-drag: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Audio Track Info Badge (Artist & Song Title on top-left) */
|
||||||
|
.audio-track-info-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
left: 10px;
|
||||||
|
text-align: left;
|
||||||
|
z-index: 25;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-track-info-badge.is-visible {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-track-info-details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
text-align: left;
|
||||||
|
min-width: 0;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-track-artist {
|
||||||
|
font-size: 0.72rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.07em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--accent);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-align: left;
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-track-title {
|
||||||
|
font-size: 0.88rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #fff;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);
|
||||||
|
text-align: left;
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (hover: hover) {
|
||||||
|
.album-gallery-container:hover .album-thumbnails-strip,
|
||||||
|
.album-gallery-container:focus-within .album-thumbnails-strip {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
transform: translateX(-50%) translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.album-gallery-container.v0ck_hover .album-thumbnails-strip,
|
||||||
|
.album-gallery-container.strip-peek .album-thumbnails-strip,
|
||||||
|
.album-gallery-container:has(.v0ck.v0ck_hover) .album-thumbnails-strip,
|
||||||
|
.album-thumbnails-strip.is-visible,
|
||||||
|
.album-thumbnails-strip.strip-peek {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
transform: translateX(-50%) translateY(0);
|
transform: translateX(-50%) translateY(0);
|
||||||
@@ -21141,8 +21609,7 @@ div#flash,
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.album-video-wrapper,
|
.album-video-wrapper {
|
||||||
.album-audio-wrapper {
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -21150,6 +21617,14 @@ div#flash,
|
|||||||
background: #000;
|
background: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.album-audio-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
.album-gallery-container.is-video-active .album-thumbnails-strip,
|
.album-gallery-container.is-video-active .album-thumbnails-strip,
|
||||||
.album-gallery-container.is-audio-active .album-thumbnails-strip {
|
.album-gallery-container.is-audio-active .album-thumbnails-strip {
|
||||||
bottom: 58px;
|
bottom: 58px;
|
||||||
@@ -21180,6 +21655,9 @@ div#flash,
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background: #000;
|
background: #000;
|
||||||
|
touch-action: pan-x;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21261,5 +21739,20 @@ div#flash,
|
|||||||
bottom: 8px;
|
bottom: 8px;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
max-width: calc(100% - 16px);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.audio-track-info-badge {
|
||||||
|
top: 8px;
|
||||||
|
left: 8px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
gap: 6px;
|
||||||
|
max-width: calc(100% - 70px);
|
||||||
|
}
|
||||||
|
.audio-track-title {
|
||||||
|
font-size: 0.78rem;
|
||||||
|
}
|
||||||
|
.audio-track-artist {
|
||||||
|
font-size: 0.68rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
background-size: contain;
|
background-size: contain;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center center;
|
background-position: center center;
|
||||||
touch-action: none; /* Prevent pull-to-refresh and scroll while interacting */
|
touch-action: pan-y;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -556,8 +556,8 @@
|
|||||||
transform: translate(-50%, 0);
|
transform: translate(-50%, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hide mouse cursor on inactivity in fullscreen */
|
/* Hide mouse cursor on inactivity when player controls auto-hide */
|
||||||
.v0ck.v0ck_fullscreen:not(.v0ck_hover),
|
.v0ck:not(.v0ck_hover),
|
||||||
.v0ck.v0ck_fullscreen:not(.v0ck_hover) * {
|
.v0ck:not(.v0ck_hover) * {
|
||||||
cursor: none !important;
|
cursor: none !important;
|
||||||
}
|
}
|
||||||
+54
-8
@@ -27,6 +27,8 @@ class CommentSystem {
|
|||||||
if (this.displayMode === 1) this.sort = 'old';
|
if (this.displayMode === 1) this.sort = 'old';
|
||||||
|
|
||||||
this.customEmojis = CommentSystem.emojiCache || {};
|
this.customEmojis = CommentSystem.emojiCache || {};
|
||||||
|
this._selfPostedCommentIds = new Set();
|
||||||
|
this._pendingSelfCommentTexts = new Set();
|
||||||
|
|
||||||
this.icons = {
|
this.icons = {
|
||||||
reply: `<i class="fa-solid fa-reply"></i>`,
|
reply: `<i class="fa-solid fa-reply"></i>`,
|
||||||
@@ -472,6 +474,34 @@ class CommentSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_isSelfComment(data) {
|
||||||
|
if (!data) return false;
|
||||||
|
if (data.id && this._selfPostedCommentIds?.has(data.id)) return true;
|
||||||
|
if (data.body && this._pendingSelfCommentTexts?.has(data.body)) return true;
|
||||||
|
|
||||||
|
const session = window.f0ckSession || {};
|
||||||
|
const currentUserId = session.id || session.user_id;
|
||||||
|
if (currentUserId && data.user_id && parseInt(data.user_id, 10) === parseInt(currentUserId, 10)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentUsername = session.user || this.user;
|
||||||
|
if (currentUsername && data.username && currentUsername.toLowerCase() === data.username.toLowerCase()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session.is_anon && data.is_anon) {
|
||||||
|
if (data.anon_fingerprint && session.fingerprint && data.anon_fingerprint === session.fingerprint) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (data.anon_short_fingerprint && session.fingerprint && session.fingerprint.includes(data.anon_short_fingerprint)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
handleLiveComment(data) {
|
handleLiveComment(data) {
|
||||||
if (!this.container || !this.itemId) return;
|
if (!this.container || !this.itemId) return;
|
||||||
// 1. Check if comment belongs to this item
|
// 1. Check if comment belongs to this item
|
||||||
@@ -537,14 +567,20 @@ class CommentSystem {
|
|||||||
|
|
||||||
// Danmaku: fire one-shot for other users' comments.
|
// Danmaku: fire one-shot for other users' comments.
|
||||||
// Own comment is handled by the optimistic submit path (fire + addItem).
|
// Own comment is handled by the optimistic submit path (fire + addItem).
|
||||||
// The _loadDanmaku re-render will add this comment to the items rotation.
|
const isSelf = this._isSelfComment(data);
|
||||||
const currentUser = window.f0ckSession?.user;
|
if (window.danmakuInstance && !isSelf) {
|
||||||
if (window.danmakuInstance && data.username !== currentUser) {
|
|
||||||
window.danmakuInstance.fire(
|
window.danmakuInstance.fire(
|
||||||
data.body,
|
data.body,
|
||||||
data.display_name || data.username || '?',
|
data.display_name || data.username || '?',
|
||||||
data.username_color || null
|
data.username_color || null
|
||||||
);
|
);
|
||||||
|
window.danmakuInstance.addItem({
|
||||||
|
id: data.id,
|
||||||
|
content: data.body,
|
||||||
|
video_time: data.video_time ?? null,
|
||||||
|
display_name: data.display_name || data.username || '?',
|
||||||
|
username_color: data.username_color || null
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update backlinks for live comment
|
// Update backlinks for live comment
|
||||||
@@ -3448,6 +3484,7 @@ class CommentSystem {
|
|||||||
} else {
|
} else {
|
||||||
this.isMainSubmitting = true;
|
this.isMainSubmitting = true;
|
||||||
}
|
}
|
||||||
|
this._pendingSelfCommentTexts.add(text);
|
||||||
|
|
||||||
let retryCount = 0;
|
let retryCount = 0;
|
||||||
const maxRetries = 20; // Allow several minutes of retrying during restart
|
const maxRetries = 20; // Allow several minutes of retrying during restart
|
||||||
@@ -3534,7 +3571,7 @@ class CommentSystem {
|
|||||||
// For 4xx errors, we stop and show the error to user (likely validation or auth)
|
// For 4xx errors, we stop and show the error to user (likely validation or auth)
|
||||||
const json = await res.json().catch(() => ({}));
|
const json = await res.json().catch(() => ({}));
|
||||||
alert('Error: ' + (json.message || `Status ${res.status}`));
|
alert('Error: ' + (json.message || `Status ${res.status}`));
|
||||||
this._finishSubmit(submitBtn, originalBtnHtml, parentId);
|
this._finishSubmit(submitBtn, originalBtnHtml, parentId, text);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3692,6 +3729,11 @@ class CommentSystem {
|
|||||||
poll: null
|
poll: null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (json.comment?.id) {
|
||||||
|
this._selfPostedCommentIds.add(json.comment.id);
|
||||||
|
}
|
||||||
|
this._pendingSelfCommentTexts.delete(text);
|
||||||
|
|
||||||
// Danmaku: fire immediately (one-shot) + add to future rotation
|
// Danmaku: fire immediately (one-shot) + add to future rotation
|
||||||
if (window.danmakuInstance) {
|
if (window.danmakuInstance) {
|
||||||
window.danmakuInstance.fire(
|
window.danmakuInstance.fire(
|
||||||
@@ -3700,6 +3742,7 @@ class CommentSystem {
|
|||||||
session.username_color || null
|
session.username_color || null
|
||||||
);
|
);
|
||||||
window.danmakuInstance.addItem({
|
window.danmakuInstance.addItem({
|
||||||
|
id: newComment.id,
|
||||||
content: text,
|
content: text,
|
||||||
video_time: newComment.video_time ?? null,
|
video_time: newComment.video_time ?? null,
|
||||||
display_name: session.display_name || currentUsername || '?',
|
display_name: session.display_name || currentUsername || '?',
|
||||||
@@ -3810,10 +3853,10 @@ class CommentSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._silentSync();
|
this._silentSync();
|
||||||
this._finishSubmit(submitBtn, originalBtnHtml, parentId);
|
this._finishSubmit(submitBtn, originalBtnHtml, parentId, text);
|
||||||
} else {
|
} else {
|
||||||
alert('Error: ' + json.message);
|
alert('Error: ' + json.message);
|
||||||
this._finishSubmit(submitBtn, originalBtnHtml, parentId);
|
this._finishSubmit(submitBtn, originalBtnHtml, parentId, text);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(`[CommentSystem] Submit attempt ${retryCount + 1} failed:`, err);
|
console.warn(`[CommentSystem] Submit attempt ${retryCount + 1} failed:`, err);
|
||||||
@@ -3825,7 +3868,7 @@ class CommentSystem {
|
|||||||
setTimeout(attemptSubmit, delay);
|
setTimeout(attemptSubmit, delay);
|
||||||
} else {
|
} else {
|
||||||
alert('Failed to send comment after multiple attempts. Please check your connection.');
|
alert('Failed to send comment after multiple attempts. Please check your connection.');
|
||||||
this._finishSubmit(submitBtn, originalBtnHtml, parentId);
|
this._finishSubmit(submitBtn, originalBtnHtml, parentId, text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -3857,7 +3900,10 @@ class CommentSystem {
|
|||||||
console.log('[ensureBtn] done, button in DOM:', !!contentEl.querySelector('.load-full-comment-btn'));
|
console.log('[ensureBtn] done, button in DOM:', !!contentEl.querySelector('.load-full-comment-btn'));
|
||||||
}
|
}
|
||||||
|
|
||||||
_finishSubmit(btn, originalHtml, parentId) {
|
_finishSubmit(btn, originalHtml, parentId, submittedText = null) {
|
||||||
|
if (submittedText) {
|
||||||
|
this._pendingSelfCommentTexts?.delete(submittedText);
|
||||||
|
}
|
||||||
if (parentId) {
|
if (parentId) {
|
||||||
this.pendingSubmissions.delete(parentId);
|
this.pendingSubmissions.delete(parentId);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -195,6 +195,7 @@ class Danmaku {
|
|||||||
const mapped = comments
|
const mapped = comments
|
||||||
.filter(c => !c.is_deleted && c.content)
|
.filter(c => !c.is_deleted && c.content)
|
||||||
.map(c => ({
|
.map(c => ({
|
||||||
|
id: c.id || null,
|
||||||
text: this._prepareText(c.content),
|
text: this._prepareText(c.content),
|
||||||
username: c.display_name || c.username || '?',
|
username: c.display_name || c.username || '?',
|
||||||
color: c.username_color || null,
|
color: c.username_color || null,
|
||||||
@@ -288,6 +289,7 @@ class Danmaku {
|
|||||||
*/
|
*/
|
||||||
addItem(comment) {
|
addItem(comment) {
|
||||||
if (!comment || !comment.content) return;
|
if (!comment || !comment.content) return;
|
||||||
|
if (comment.id && this.items && this.items.some(i => i.id === comment.id)) return;
|
||||||
|
|
||||||
const duration = this.media.duration;
|
const duration = this.media.duration;
|
||||||
const hasDuration = isFinite(duration) && duration > 0;
|
const hasDuration = isFinite(duration) && duration > 0;
|
||||||
@@ -307,6 +309,7 @@ class Danmaku {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const item = {
|
const item = {
|
||||||
|
id: comment.id || null,
|
||||||
video_time: t,
|
video_time: t,
|
||||||
text: this._prepareText(comment.content),
|
text: this._prepareText(comment.content),
|
||||||
username: comment.display_name || comment.username || '?',
|
username: comment.display_name || comment.username || '?',
|
||||||
@@ -314,6 +317,13 @@ class Danmaku {
|
|||||||
fired: true // mark as already fired — caller handles any immediate one-shot
|
fired: true // mark as already fired — caller handles any immediate one-shot
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (this._synthClock && this._flashPool) {
|
||||||
|
if (!this._flashPool.some(i => (comment.id && i.id === comment.id) || (i.text === item.text && i.username === item.username))) {
|
||||||
|
this._flashPool.push(item);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Insert in sorted order
|
// Insert in sorted order
|
||||||
const idx = this.items.findIndex(i => i.video_time > t);
|
const idx = this.items.findIndex(i => i.video_time > t);
|
||||||
if (idx === -1) this.items.push(item);
|
if (idx === -1) this.items.push(item);
|
||||||
|
|||||||
+2255
-150
File diff suppressed because it is too large
Load Diff
@@ -532,14 +532,32 @@
|
|||||||
else if (rClass === 'sfw' && blurSfw) isBlurred = true;
|
else if (rClass === 'sfw' && blurSfw) isBlurred = true;
|
||||||
else if (rClass === 'untagged' && blurUntagged) isBlurred = true;
|
else if (rClass === 'untagged' && blurUntagged) isBlurred = true;
|
||||||
|
|
||||||
let thumbUrl = `/t/${c.item_id}.webp`;
|
const mime = c.mime || c.item_mime || '';
|
||||||
|
const activeDest = c.item_dest || c.dest || '';
|
||||||
|
const isAudio = mime.startsWith('audio/') || (!mime && activeDest.match(/\.(mp3|wav|ogg|flac|m4a|aac|opus)$/i));
|
||||||
|
|
||||||
|
let thumbUrl = c.thumb || `/t/${c.item_id}.webp`;
|
||||||
if (isBlurred) {
|
if (isBlurred) {
|
||||||
thumbUrl = `/t/${c.item_id}_blur.webp`;
|
const baseThumb = thumbUrl.replace(/\.webp$/, '');
|
||||||
|
thumbUrl = `${baseThumb}_blur.webp`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.applyThumbCacheBust) thumbUrl = window.applyThumbCacheBust(thumbUrl);
|
if (window.applyThumbCacheBust) thumbUrl = window.applyThumbCacheBust(thumbUrl);
|
||||||
|
|
||||||
mediaHtml = `<img src="${thumbUrl}" style="width: 32px; height: 32px; object-fit: cover; border-radius: 2px;" loading="lazy" onerror="this.style.display='none'" />`;
|
if (isAudio && !c.has_coverart) {
|
||||||
|
mediaHtml = `
|
||||||
|
<div class="sidebar-media-placeholder audio">
|
||||||
|
<i class="fa-solid fa-music"></i>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
mediaHtml = `
|
||||||
|
<img src="${thumbUrl}" style="width: 32px; height: 32px; object-fit: cover; border-radius: 2px;" loading="lazy" onerror="this.style.display='none'; if(this.nextElementSibling) this.nextElementSibling.classList.remove('hidden');" />
|
||||||
|
<div class="sidebar-media-placeholder ${isAudio ? 'audio' : ''} hidden">
|
||||||
|
<i class="${isAudio ? 'fa-solid fa-music' : 'fa-solid fa-image'}"></i>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
itemPreview = `
|
itemPreview = `
|
||||||
<div class="item-preview">
|
<div class="item-preview">
|
||||||
@@ -1869,6 +1887,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
window.switchSidebarTab = switchSidebarTab;
|
||||||
|
|
||||||
const initSidebarTabs = () => {
|
const initSidebarTabs = () => {
|
||||||
const tabsContainer = document.querySelector('.sidebar-tabs');
|
const tabsContainer = document.querySelector('.sidebar-tabs');
|
||||||
|
|||||||
+39
-8
@@ -88,6 +88,8 @@ const updateHoverStates = () => {
|
|||||||
const isOver = mouseX >= rect.left && mouseX <= rect.right &&
|
const isOver = mouseX >= rect.left && mouseX <= rect.right &&
|
||||||
mouseY >= rect.top && mouseY <= rect.bottom;
|
mouseY >= rect.top && mouseY <= rect.bottom;
|
||||||
p.classList.toggle("v0ck_hover", isOver);
|
p.classList.toggle("v0ck_hover", isOver);
|
||||||
|
const gal = p.closest('.album-gallery-container');
|
||||||
|
if (gal) gal.classList.toggle("v0ck_hover", isOver);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -117,6 +119,8 @@ class v0ck {
|
|||||||
if (mouseX >= rect.left && mouseX <= rect.right &&
|
if (mouseX >= rect.left && mouseX <= rect.right &&
|
||||||
mouseY >= rect.top && mouseY <= rect.bottom) {
|
mouseY >= rect.top && mouseY <= rect.bottom) {
|
||||||
parent.classList.add("v0ck_hover", "v0ck_no_transition");
|
parent.classList.add("v0ck_hover", "v0ck_no_transition");
|
||||||
|
const gal = parent.closest('.album-gallery-container');
|
||||||
|
if (gal) gal.classList.add("v0ck_hover");
|
||||||
// Remove no-transition after a frame
|
// Remove no-transition after a frame
|
||||||
setTimeout(() => parent.classList.remove("v0ck_no_transition"), 50);
|
setTimeout(() => parent.classList.remove("v0ck_no_transition"), 50);
|
||||||
}
|
}
|
||||||
@@ -142,14 +146,24 @@ class v0ck {
|
|||||||
if (!ph) {
|
if (!ph) {
|
||||||
ph = document.createElement('div');
|
ph = document.createElement('div');
|
||||||
ph.className = 'sidebar-media-placeholder audio';
|
ph.className = 'sidebar-media-placeholder audio';
|
||||||
ph.innerHTML = '<div class="audio-cover-circle"></div><i class="fa-solid fa-music"></i>';
|
ph.innerHTML = '<div class="audio-cover-circle"><i class="fa-solid fa-music"></i></div>';
|
||||||
player.prepend(ph);
|
player.prepend(ph);
|
||||||
}
|
}
|
||||||
let coverCircle = ph.querySelector('.audio-cover-circle');
|
let coverCircle = ph.querySelector('.audio-cover-circle');
|
||||||
if (!coverCircle) {
|
if (!coverCircle) {
|
||||||
coverCircle = document.createElement('div');
|
coverCircle = document.createElement('div');
|
||||||
coverCircle.className = 'audio-cover-circle';
|
coverCircle.className = 'audio-cover-circle';
|
||||||
|
coverCircle.innerHTML = '<i class="fa-solid fa-music"></i>';
|
||||||
ph.insertBefore(coverCircle, ph.firstChild);
|
ph.insertBefore(coverCircle, ph.firstChild);
|
||||||
|
} else {
|
||||||
|
if (!coverCircle.querySelector('i')) {
|
||||||
|
const existingI = ph.querySelector(':scope > i');
|
||||||
|
if (existingI) {
|
||||||
|
coverCircle.appendChild(existingI);
|
||||||
|
} else {
|
||||||
|
coverCircle.insertAdjacentHTML('beforeend', '<i class="fa-solid fa-music"></i>');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
player.style.backgroundImage = 'none';
|
player.style.backgroundImage = 'none';
|
||||||
player.style.backgroundColor = 'transparent';
|
player.style.backgroundColor = 'transparent';
|
||||||
@@ -157,7 +171,7 @@ class v0ck {
|
|||||||
coverCircle.style.backgroundImage = `url('${poster}')`;
|
coverCircle.style.backgroundImage = `url('${poster}')`;
|
||||||
ph.classList.add('has-cover');
|
ph.classList.add('has-cover');
|
||||||
} else {
|
} else {
|
||||||
coverCircle.style.backgroundImage = 'none';
|
coverCircle.style.backgroundImage = '';
|
||||||
ph.classList.remove('has-cover');
|
ph.classList.remove('has-cover');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -203,6 +217,23 @@ class v0ck {
|
|||||||
let wasPausedWhenStarted = false;
|
let wasPausedWhenStarted = false;
|
||||||
// Mobile tap-to-show-controls: true when this touch revealed the controls bar
|
// Mobile tap-to-show-controls: true when this touch revealed the controls bar
|
||||||
let controlsJustShown = false;
|
let controlsJustShown = false;
|
||||||
|
|
||||||
|
const setHover = (active) => {
|
||||||
|
if (active) {
|
||||||
|
player.classList.add('v0ck_hover');
|
||||||
|
const gal = player.closest('.album-gallery-container');
|
||||||
|
if (gal) gal.classList.add('v0ck_hover');
|
||||||
|
} else {
|
||||||
|
player.classList.remove('v0ck_hover');
|
||||||
|
const gal = player.closest('.album-gallery-container');
|
||||||
|
if (gal) {
|
||||||
|
gal.classList.remove('v0ck_hover');
|
||||||
|
gal.classList.remove('strip-peek');
|
||||||
|
const strip = gal.querySelector('.album-thumbnails-strip');
|
||||||
|
if (strip) strip.classList.remove('strip-peek');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
const speedIndicator = player.querySelector('.v0ck_speed_indicator');
|
const speedIndicator = player.querySelector('.v0ck_speed_indicator');
|
||||||
|
|
||||||
// (mouse position is now tracked via docMouseX/docMouseY in resetControlsTimer block)
|
// (mouse position is now tracked via docMouseX/docMouseY in resetControlsTimer block)
|
||||||
@@ -343,7 +374,7 @@ class v0ck {
|
|||||||
if (isMobile && controlsJustShown) {
|
if (isMobile && controlsJustShown) {
|
||||||
// First tap: controls were just revealed by this touch — don't toggle play
|
// First tap: controls were just revealed by this touch — don't toggle play
|
||||||
controlsJustShown = false;
|
controlsJustShown = false;
|
||||||
player.classList.add('v0ck_hover');
|
setHover(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
controlsJustShown = false;
|
controlsJustShown = false;
|
||||||
@@ -353,7 +384,7 @@ class v0ck {
|
|||||||
toggle.addEventListener('click', togglePlay);
|
toggle.addEventListener('click', togglePlay);
|
||||||
overlay.addEventListener('click', e => {
|
overlay.addEventListener('click', e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
player.classList.add('v0ck_hover');
|
setHover(true);
|
||||||
togglePlay();
|
togglePlay();
|
||||||
});
|
});
|
||||||
video.addEventListener('play', updatePlayIcon);
|
video.addEventListener('play', updatePlayIcon);
|
||||||
@@ -831,7 +862,7 @@ class v0ck {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isMobile && !isInsidePlayer && !isFlashYankUI) {
|
if (isMobile && !isInsidePlayer && !isFlashYankUI) {
|
||||||
player.classList.remove('v0ck_hover');
|
setHover(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -928,7 +959,7 @@ class v0ck {
|
|||||||
const isFullscreen = player.classList.contains('v0ck_fullscreen');
|
const isFullscreen = player.classList.contains('v0ck_fullscreen');
|
||||||
if (!video.paused || isFullscreen) {
|
if (!video.paused || isFullscreen) {
|
||||||
controlsTimer = setTimeout(() => {
|
controlsTimer = setTimeout(() => {
|
||||||
player.classList.remove('v0ck_hover');
|
setHover(false);
|
||||||
if (settingsMenu && !settingsMenu.classList.contains('v0ck_hidden')) {
|
if (settingsMenu && !settingsMenu.classList.contains('v0ck_hidden')) {
|
||||||
settingsMenu.classList.add('v0ck_hidden');
|
settingsMenu.classList.add('v0ck_hidden');
|
||||||
document.dispatchEvent(new CustomEvent('v0ck_settings_closed'));
|
document.dispatchEvent(new CustomEvent('v0ck_settings_closed'));
|
||||||
@@ -950,7 +981,7 @@ class v0ck {
|
|||||||
docMouseX = e.clientX;
|
docMouseX = e.clientX;
|
||||||
docMouseY = e.clientY;
|
docMouseY = e.clientY;
|
||||||
}
|
}
|
||||||
player.classList.add('v0ck_hover');
|
setHover(true);
|
||||||
resetControlsTimer();
|
resetControlsTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -998,7 +1029,7 @@ class v0ck {
|
|||||||
}
|
}
|
||||||
docMouseX = -1;
|
docMouseX = -1;
|
||||||
docMouseY = -1;
|
docMouseY = -1;
|
||||||
player.classList.remove('v0ck_hover');
|
setHover(false);
|
||||||
clearTimeout(controlsTimer);
|
clearTimeout(controlsTimer);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+12
-1
@@ -49,7 +49,18 @@ export default new class {
|
|||||||
return slug;
|
return slug;
|
||||||
}
|
}
|
||||||
|
|
||||||
formatSize(size, i = ~~(Math.log(size) / Math.log(1024))) {
|
formatSize(size) {
|
||||||
|
if (size === undefined || size === null || size === '') return '0 B';
|
||||||
|
if (typeof size === 'string') {
|
||||||
|
if (/^\d+(\.\d+)?\s*(B|kB|KB|MB|GB|TB)$/i.test(size.trim())) {
|
||||||
|
return size.trim();
|
||||||
|
}
|
||||||
|
const num = Number(size);
|
||||||
|
if (isNaN(num)) return '0 B';
|
||||||
|
size = num;
|
||||||
|
}
|
||||||
|
if (isNaN(size) || size <= 0) return '0 B';
|
||||||
|
const i = Math.min(4, Math.max(0, ~~(Math.log(size) / Math.log(1024))));
|
||||||
return (size / Math.pow(1024, i)).toFixed(2) * 1 + " " + ["B", "kB", "MB", "GB", "TB"][i];
|
return (size / Math.pow(1024, i)).toFixed(2) * 1 + " " + ["B", "kB", "MB", "GB", "TB"][i];
|
||||||
};
|
};
|
||||||
calcSpeed(b, s) {
|
calcSpeed(b, s) {
|
||||||
|
|||||||
@@ -1492,6 +1492,16 @@ const f0cklib = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let subSizeNum = r.size;
|
||||||
|
if (!subSizeNum || isNaN(subSizeNum) || Number(subSizeNum) <= 0) {
|
||||||
|
try {
|
||||||
|
const bFile = path.join(cfg.paths.b, r.dest);
|
||||||
|
if (fs.existsSync(bFile)) {
|
||||||
|
subSizeNum = fs.statSync(bFile).size;
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
let subTags = tagsBySubId.get(r.id) || [];
|
let subTags = tagsBySubId.get(r.id) || [];
|
||||||
if (subTags.length === 0 && (r.order_index === 0 || idx === 0)) {
|
if (subTags.length === 0 && (r.order_index === 0 || idx === 0)) {
|
||||||
subTags = tags;
|
subTags = tags;
|
||||||
@@ -1505,7 +1515,7 @@ const f0cklib = {
|
|||||||
src: `${cfg.websrv.paths.images}/${r.dest}`,
|
src: `${cfg.websrv.paths.images}/${r.dest}`,
|
||||||
filename: r.dest,
|
filename: r.dest,
|
||||||
mime: r.mime,
|
mime: r.mime,
|
||||||
size: lib.formatSize(r.size),
|
size: lib.formatSize(subSizeNum),
|
||||||
width: r.width,
|
width: r.width,
|
||||||
height: r.height,
|
height: r.height,
|
||||||
checksum: r.checksum,
|
checksum: r.checksum,
|
||||||
|
|||||||
@@ -348,6 +348,131 @@ export default router => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const audioMetaCache = new Map();
|
||||||
|
|
||||||
|
group.get(/\/audio-metadata$/, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const rawSrc = req.url.qs?.src || req.url.qs?.file || '';
|
||||||
|
const itemId = req.url.qs?.id || '';
|
||||||
|
if (!rawSrc && !itemId) {
|
||||||
|
return res.json({ success: false, msg: 'Missing src or id' }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
let filename = '';
|
||||||
|
if (rawSrc) {
|
||||||
|
filename = path.basename(rawSrc.split('?')[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filename && (filename.includes('/') || filename.includes('\\') || filename.includes('..'))) {
|
||||||
|
return res.json({ success: false, msg: 'Invalid filename' }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cacheKey = filename || `item_${itemId}`;
|
||||||
|
if (audioMetaCache.has(cacheKey)) {
|
||||||
|
return res.json(audioMetaCache.get(cacheKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
let fullPath = filename ? path.join(cfg.paths.b, filename) : null;
|
||||||
|
let originalFilename = '';
|
||||||
|
|
||||||
|
if (itemId) {
|
||||||
|
const isNumeric = /^\d+$/.test(itemId);
|
||||||
|
const rows = isNumeric
|
||||||
|
? await db`SELECT dest, original_filename, title FROM items WHERE id = ${itemId}`
|
||||||
|
: await db`SELECT dest, original_filename, title FROM items WHERE slug = ${itemId}`;
|
||||||
|
if (rows && rows[0]) {
|
||||||
|
if (!fullPath) fullPath = path.join(cfg.paths.b, rows[0].dest);
|
||||||
|
originalFilename = rows[0].original_filename || '';
|
||||||
|
} else {
|
||||||
|
const subRows = isNumeric
|
||||||
|
? await db`SELECT dest, original_filename FROM sub_items WHERE id = ${itemId}`
|
||||||
|
: await db`SELECT dest, original_filename FROM sub_items WHERE slug = ${itemId}`;
|
||||||
|
if (subRows && subRows[0]) {
|
||||||
|
if (!fullPath) fullPath = path.join(cfg.paths.b, subRows[0].dest);
|
||||||
|
originalFilename = subRows[0].original_filename || '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fullPath) {
|
||||||
|
return res.json({ success: false, msg: 'File not found' }, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fs.access(fullPath);
|
||||||
|
} catch {
|
||||||
|
return res.json({ success: false, msg: 'File not found on disk' }, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const metadata = await queue.getVideoMetadata(fullPath);
|
||||||
|
const tags = metadata || {};
|
||||||
|
|
||||||
|
// Normalize all tag keys to lowercase without punctuation (e.g. 'Title' -> 'title', 'ALBUM_ARTIST' -> 'albumartist')
|
||||||
|
const lowerTags = {};
|
||||||
|
for (const [k, v] of Object.entries(tags)) {
|
||||||
|
if (v != null && v !== '') {
|
||||||
|
const normKey = k.toLowerCase().replace(/[-_\s]/g, '');
|
||||||
|
lowerTags[normKey] = typeof v === 'string' ? v.trim() : String(v).trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let title = lowerTags.title ||
|
||||||
|
lowerTags.tracktitle ||
|
||||||
|
lowerTags.song ||
|
||||||
|
lowerTags.songname ||
|
||||||
|
lowerTags.name ||
|
||||||
|
'';
|
||||||
|
|
||||||
|
let artist = lowerTags.artist ||
|
||||||
|
lowerTags.albumartist ||
|
||||||
|
lowerTags.author ||
|
||||||
|
lowerTags.performer ||
|
||||||
|
lowerTags.creator ||
|
||||||
|
lowerTags.composer ||
|
||||||
|
lowerTags.byline ||
|
||||||
|
'';
|
||||||
|
|
||||||
|
let album = lowerTags.album ||
|
||||||
|
lowerTags.albumtitle ||
|
||||||
|
'';
|
||||||
|
|
||||||
|
let genre = lowerTags.genre || '';
|
||||||
|
let date = lowerTags.date || lowerTags.year || '';
|
||||||
|
|
||||||
|
if (!title && !artist) {
|
||||||
|
const nameToParse = originalFilename || path.parse(fullPath).name;
|
||||||
|
const match = nameToParse.match(/^(.+?)\s*[-–—_]\s*(.+)$/);
|
||||||
|
if (match) {
|
||||||
|
artist = match[1].replace(/^[0-9]+[\s._-]+/, '').trim();
|
||||||
|
title = match[2].trim();
|
||||||
|
} else if (nameToParse && !nameToParse.startsWith('subf0ck') && nameToParse.length < 60) {
|
||||||
|
title = nameToParse.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
success: true,
|
||||||
|
artist: String(artist || '').trim(),
|
||||||
|
title: String(title || '').trim(),
|
||||||
|
album: String(album || '').trim(),
|
||||||
|
genre: String(genre || '').trim(),
|
||||||
|
date: String(date || '').trim(),
|
||||||
|
has_meta: !!(artist || title)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (audioMetaCache.size > 1000) {
|
||||||
|
const firstKey = audioMetaCache.keys().next().value;
|
||||||
|
audioMetaCache.delete(firstKey);
|
||||||
|
}
|
||||||
|
audioMetaCache.set(cacheKey, result);
|
||||||
|
|
||||||
|
return res.json(result);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[API-V2-AUDIO-METADATA] Error:', err);
|
||||||
|
return res.json({ success: false, msg: 'Failed to extract audio metadata' }, 500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// F-002 Security: Require authentication to prevent SSRF via arbitrary URL fetching.
|
// F-002 Security: Require authentication to prevent SSRF via arbitrary URL fetching.
|
||||||
// Guests may read from the cache (in-memory or DB); only authenticated users trigger real outbound fetches.
|
// Guests may read from the cache (in-memory or DB); only authenticated users trigger real outbound fetches.
|
||||||
group.get(/\/meta\/fetch$/, async (req, res) => {
|
group.get(/\/meta\/fetch$/, async (req, res) => {
|
||||||
|
|||||||
@@ -624,6 +624,9 @@ export default (router, tpl) => {
|
|||||||
const itemQuery = await db`
|
const itemQuery = await db`
|
||||||
SELECT
|
SELECT
|
||||||
i.slug,
|
i.slug,
|
||||||
|
i.mime,
|
||||||
|
i.dest,
|
||||||
|
i.has_coverart,
|
||||||
i.xd_score,
|
i.xd_score,
|
||||||
COALESCE(i.visibility, 0) as visibility,
|
COALESCE(i.visibility, 0) as visibility,
|
||||||
(SELECT ta.tag_id FROM tags_assign ta
|
(SELECT ta.tag_id FROM tags_assign ta
|
||||||
@@ -684,6 +687,10 @@ export default (router, tpl) => {
|
|||||||
db.notify('activity', JSON.stringify({
|
db.notify('activity', JSON.stringify({
|
||||||
user_id: req.session.id,
|
user_id: req.session.id,
|
||||||
item_id: item_id,
|
item_id: item_id,
|
||||||
|
item_slug: (getEnableItemSlugs() && itemQuery[0]?.slug) ? itemQuery[0].slug : null,
|
||||||
|
mime: itemQuery[0]?.mime || null,
|
||||||
|
dest: itemQuery[0]?.dest || null,
|
||||||
|
has_coverart: !!itemQuery[0]?.has_coverart,
|
||||||
type: 'comment',
|
type: 'comment',
|
||||||
body: notifyBody,
|
body: notifyBody,
|
||||||
id: commentId,
|
id: commentId,
|
||||||
@@ -1104,6 +1111,7 @@ export default (router, tpl) => {
|
|||||||
i.id as item_id,
|
i.id as item_id,
|
||||||
i.slug as item_slug,
|
i.slug as item_slug,
|
||||||
i.dest as item_dest,
|
i.dest as item_dest,
|
||||||
|
i.has_coverart,
|
||||||
(SELECT ta.tag_id FROM tags_assign ta
|
(SELECT ta.tag_id FROM tags_assign ta
|
||||||
WHERE ta.item_id = i.id AND ta.tag_id = ANY(${[1, 2, cfg.nsfl_tag_id || 3]}::int[])
|
WHERE ta.item_id = i.id AND ta.tag_id = ANY(${[1, 2, cfg.nsfl_tag_id || 3]}::int[])
|
||||||
ORDER BY ta.tag_id LIMIT 1) AS rating_tag_id,
|
ORDER BY ta.tag_id LIMIT 1) AS rating_tag_id,
|
||||||
|
|||||||
@@ -131,6 +131,9 @@
|
|||||||
<button type="button" class="sidebar-tab" id="sidebar-tab-tag" data-tab="tag" title="Tag Feed" aria-label="Tag Feed" style="display: none;">
|
<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>
|
<i class="fa-solid fa-hashtag"></i>
|
||||||
</button>
|
</button>
|
||||||
|
<button type="button" class="sidebar-tab" id="sidebar-tab-tuner" data-tab="tuner" title="Audio Reactivity Tuner" aria-label="Audio Reactivity Tuner">
|
||||||
|
<i class="fa-solid fa-sliders"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="sidebar-activity-container" class="sidebar-comments-list sidebar-tab-content active" data-tab-content="comments">
|
<div id="sidebar-activity-container" class="sidebar-comments-list sidebar-tab-content active" data-tab-content="comments">
|
||||||
<div class="sidebar-loading-state">
|
<div class="sidebar-loading-state">
|
||||||
@@ -162,6 +165,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="sidebar-tuner-container" class="sidebar-tuner-container sidebar-tab-content" data-tab-content="tuner" style="display: none;">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="global-sidebar-right-footer">
|
<div class="global-sidebar-right-footer">
|
||||||
<a href="/ranking">{{ t('footer.ranking') }}</a>
|
<a href="/ranking">{{ t('footer.ranking') }}</a>
|
||||||
|
|||||||
@@ -8,12 +8,19 @@
|
|||||||
<div id="f0ck-album-video-wrapper" class="album-video-wrapper embed-responsive embed-responsive-16by9" style="display: none; position: absolute; inset: 0; width: 100%; height: 100%;">
|
<div id="f0ck-album-video-wrapper" class="album-video-wrapper embed-responsive embed-responsive-16by9" style="display: none; position: absolute; inset: 0; width: 100%; height: 100%;">
|
||||||
<video id="f0ck-album-video" class="embed-responsive-item" preload="auto" loop playsinline></video>
|
<video id="f0ck-album-video" class="embed-responsive-item" preload="auto" loop playsinline></video>
|
||||||
</div>
|
</div>
|
||||||
<div id="f0ck-album-audio-wrapper" class="album-audio-wrapper embed-responsive embed-responsive-16by9" style="display: none; position: absolute; inset: 0; width: 100%; height: 100%;">
|
<div id="f0ck-album-audio-wrapper" class="album-audio-wrapper embed-responsive embed-responsive-16by9" style="display: none; position: absolute; inset: 0; width: 100%; height: 100%; background: #000;">
|
||||||
<div class="sidebar-media-placeholder audio">
|
<div class="sidebar-media-placeholder audio">
|
||||||
<div class="audio-cover-circle"></div>
|
<div class="audio-track-info-badge">
|
||||||
<i class="fa-solid fa-music"></i>
|
<div class="audio-track-info-details">
|
||||||
|
<span class="audio-track-artist"></span>
|
||||||
|
<span class="audio-track-title"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="audio-cover-circle">
|
||||||
|
<i class="fa-solid fa-music"></i>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<audio id="f0ck-album-audio" class="embed-responsive-item" preload="auto" loop></audio>
|
<audio id="f0ck-album-audio" class="embed-responsive-item" preload="auto" loop crossorigin="anonymous"></audio>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Album Gallery Controls Inside View -->
|
<!-- Album Gallery Controls Inside View -->
|
||||||
@@ -59,10 +66,18 @@
|
|||||||
<video id="my-video" class="embed-responsive-item" width="640" height="360" src="{{ item.dest }}" preload="auto" data-size="{{ item.size }}" loop playsinline></video>
|
<video id="my-video" class="embed-responsive-item" width="640" height="360" src="{{ item.dest }}" preload="auto" data-size="{{ item.size }}" loop playsinline></video>
|
||||||
</div>
|
</div>
|
||||||
@elseif(item.mime.startsWith("audio"))
|
@elseif(item.mime.startsWith("audio"))
|
||||||
<div class="embed-responsive embed-responsive-16by9">
|
<div class="embed-responsive embed-responsive-16by9" style="background: #000;">
|
||||||
<div class="sidebar-media-placeholder audio @if(item.coverart)has-cover@endif">
|
<div class="sidebar-media-placeholder audio @if(item.coverart)has-cover@endif">
|
||||||
<div class="audio-cover-circle" @if(item.coverart)style="background-image: url('{{ item.coverart }}');"@endif></div>
|
<div class="audio-reactive-bg"></div>
|
||||||
<i class="fa-solid fa-music"></i>
|
<div class="audio-track-info-badge">
|
||||||
|
<div class="audio-track-info-details">
|
||||||
|
<span class="audio-track-artist"></span>
|
||||||
|
<span class="audio-track-title"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="audio-cover-circle" @if(item.coverart)style="background-image: url('{{ item.coverart }}');"@endif>
|
||||||
|
<i class="fa-solid fa-music"></i>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<audio id="my-video" class="embed-responsive-item" preload="auto" loop src="{{ item.dest }}" data-setup="{}" data-size="{{ item.size }}" @if(item.coverart)poster="{{ item.coverart }}"@endif type="{{ item.mime }}"></audio>
|
<audio id="my-video" class="embed-responsive-item" preload="auto" loop src="{{ item.dest }}" data-setup="{}" data-size="{{ item.size }}" @if(item.coverart)poster="{{ item.coverart }}"@endif type="{{ item.mime }}"></audio>
|
||||||
<img id="f0ck-audio-cover" @if(item.coverart)src="{{ item.coverart }}"@endif style="display: none;">
|
<img id="f0ck-audio-cover" @if(item.coverart)src="{{ item.coverart }}"@endif style="display: none;">
|
||||||
|
|||||||
Reference in New Issue
Block a user