Merge pull request #327 from ntsrihari/fix/issues-322-313-278
fix(ui): fullscreen volume above taskbar, settings overflow, download…
This commit is contained in:
commit
4277e6db4b
3 changed files with 51 additions and 3 deletions
22
js/api.js
22
js/api.js
|
|
@ -20,8 +20,10 @@ import { loadFfmpeg, FfmpegError } from './ffmpeg.js';
|
||||||
import { triggerDownload, applyAudioPostProcessing } from './download-utils.ts';
|
import { triggerDownload, applyAudioPostProcessing } from './download-utils.ts';
|
||||||
import { isCustomFormat } from './ffmpegFormats.ts';
|
import { isCustomFormat } from './ffmpegFormats.ts';
|
||||||
import { DownloadProgress } from './progressEvents.js';
|
import { DownloadProgress } from './progressEvents.js';
|
||||||
|
import { resolveDownloadTotalBytes } from './downloadProgressUtils.js';
|
||||||
|
|
||||||
export const DASH_MANIFEST_UNAVAILABLE_CODE = 'DASH_MANIFEST_UNAVAILABLE';
|
export const DASH_MANIFEST_UNAVAILABLE_CODE = 'DASH_MANIFEST_UNAVAILABLE';
|
||||||
|
export { resolveDownloadTotalBytes };
|
||||||
const TIDAL_V2_TOKEN = 'txNoH4kkV41MfH25';
|
const TIDAL_V2_TOKEN = 'txNoH4kkV41MfH25';
|
||||||
|
|
||||||
export class LosslessAPI {
|
export class LosslessAPI {
|
||||||
|
|
@ -1398,6 +1400,22 @@ export class LosslessAPI {
|
||||||
throw hlsError;
|
throw hlsError;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Try HEAD first to get Content-Length when GET uses chunked encoding (fixes #278)
|
||||||
|
let headContentLength = null;
|
||||||
|
try {
|
||||||
|
const headResponse = await fetch(streamUrl, {
|
||||||
|
method: 'HEAD',
|
||||||
|
cache: 'no-store',
|
||||||
|
signal: options.signal,
|
||||||
|
});
|
||||||
|
if (headResponse.ok) {
|
||||||
|
const cl = headResponse.headers.get('Content-Length');
|
||||||
|
if (cl) headContentLength = parseInt(cl, 10);
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
/* ignore HEAD failure; proceed with GET */
|
||||||
|
}
|
||||||
|
|
||||||
const response = await fetch(streamUrl, {
|
const response = await fetch(streamUrl, {
|
||||||
cache: 'no-store',
|
cache: 'no-store',
|
||||||
signal: options.signal,
|
signal: options.signal,
|
||||||
|
|
@ -1407,8 +1425,8 @@ export class LosslessAPI {
|
||||||
throw new Error(`Fetch failed: ${response.status}`);
|
throw new Error(`Fetch failed: ${response.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const contentLength = response.headers.get('Content-Length');
|
const contentLengthHeader = response.headers.get('Content-Length');
|
||||||
const totalBytes = contentLength ? parseInt(contentLength, 10) : 0;
|
const totalBytes = resolveDownloadTotalBytes(contentLengthHeader, headContentLength);
|
||||||
|
|
||||||
let receivedBytes = 0;
|
let receivedBytes = 0;
|
||||||
|
|
||||||
|
|
|
||||||
14
js/downloadProgressUtils.js
Normal file
14
js/downloadProgressUtils.js
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
/**
|
||||||
|
* Helpers for download progress. Extracted for testability (fixes #278).
|
||||||
|
* Resolve total byte count from GET and optional HEAD Content-Length.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string | null} contentLengthFromGet - Content-Length header from GET response
|
||||||
|
* @param {number | null} headContentLength - Content-Length from prior HEAD request
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
export function resolveDownloadTotalBytes(contentLengthFromGet, headContentLength) {
|
||||||
|
const fromGet = contentLengthFromGet ? parseInt(contentLengthFromGet, 10) : null;
|
||||||
|
return fromGet ?? headContentLength ?? 0;
|
||||||
|
}
|
||||||
18
styles.css
18
styles.css
|
|
@ -1733,6 +1733,14 @@ input[type='search']::-webkit-search-cancel-button {
|
||||||
animation: fade-in-slide-up 0.4s var(--ease-out-back);
|
animation: fade-in-slide-up 0.4s var(--ease-out-back);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Prevent settings tab content from overflowing on small displays (fixes #313) */
|
||||||
|
.settings-tab-content {
|
||||||
|
max-width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: visible;
|
||||||
|
}
|
||||||
|
|
||||||
.card-grid {
|
.card-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||||
|
|
@ -2571,6 +2579,9 @@ input[type='search']::-webkit-search-cancel-button {
|
||||||
|
|
||||||
.settings-list {
|
.settings-list {
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-group {
|
.settings-group {
|
||||||
|
|
@ -2615,6 +2626,7 @@ input[type='search']::-webkit-search-cancel-button {
|
||||||
padding: var(--spacing-lg) 0;
|
padding: var(--spacing-lg) 0;
|
||||||
border-bottom: 1px solid var(--border);
|
border-bottom: 1px solid var(--border);
|
||||||
gap: var(--spacing-lg);
|
gap: var(--spacing-lg);
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.setting-item.sidebar-setting-item {
|
.setting-item.sidebar-setting-item {
|
||||||
|
|
@ -2654,6 +2666,7 @@ input[type='search']::-webkit-search-cancel-button {
|
||||||
.setting-item .info {
|
.setting-item .info {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.setting-item .label {
|
.setting-item .label {
|
||||||
|
|
@ -3641,7 +3654,8 @@ input:checked + .slider::before {
|
||||||
/* Use a CSS variable for the image so we can set it in JS */
|
/* Use a CSS variable for the image so we can set it in JS */
|
||||||
--bg-image: none;
|
--bg-image: none;
|
||||||
|
|
||||||
padding-bottom: 0;
|
/* Reserve space above taskbar / system UI so volume controls stay visible (fixes #322) */
|
||||||
|
padding-bottom: max(env(safe-area-inset-bottom), 1.5rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
#fullscreen-cover-overlay::before {
|
#fullscreen-cover-overlay::before {
|
||||||
|
|
@ -5390,11 +5404,13 @@ img[src=''] {
|
||||||
|
|
||||||
.fullscreen-main-view {
|
.fullscreen-main-view {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 6rem 2rem 2rem;
|
padding: 6rem 2rem 2rem;
|
||||||
|
overflow-y: auto;
|
||||||
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue