lint / go (push) Canceled after 0s
lint / go_mod (push) Canceled after 0s
lint / conf (push) Canceled after 0s
lint / docslinks (push) Canceled after 0s
lint / docsorder (push) Canceled after 0s
lint / apidocs (push) Canceled after 0s
lint / other (push) Canceled after 0s
test / test_64 (push) Canceled after 0s
test / test_32 (push) Canceled after 0s
test / test_e2e (push) Canceled after 0s
183 lines
5.8 KiB
JavaScript
183 lines
5.8 KiB
JavaScript
(function initMediaMTXAdminList(root, factory) {
|
||
"use strict";
|
||
|
||
const api = factory();
|
||
if (typeof module !== "undefined" && module.exports) module.exports = api;
|
||
else root.MediaMTXAdminList = api;
|
||
}(typeof globalThis !== "undefined" ? globalThis : this, () => {
|
||
"use strict";
|
||
|
||
class PageLoadError extends Error {
|
||
constructor(label, page, cause) {
|
||
const detail = cause?.message ? `:${cause.message}` : "。";
|
||
super(`${label}第 ${page + 1} 页加载失败${detail}`);
|
||
this.name = "PageLoadError";
|
||
this.status = Number.isInteger(cause?.status) ? cause.status : 0;
|
||
this.page = page;
|
||
this.cause = cause;
|
||
}
|
||
}
|
||
|
||
function limitConcurrency(fetcher, requestedLimit) {
|
||
const limit = Math.max(1, Number(requestedLimit) || 1);
|
||
let active = 0;
|
||
const queue = [];
|
||
|
||
function runNext() {
|
||
while (active < limit && queue.length > 0) {
|
||
const task = queue.shift();
|
||
active += 1;
|
||
Promise.resolve()
|
||
.then(() => fetcher(...task.args))
|
||
.then(task.resolve, task.reject)
|
||
.finally(() => {
|
||
active -= 1;
|
||
runNext();
|
||
});
|
||
}
|
||
}
|
||
|
||
return (...args) => new Promise((resolve, reject) => {
|
||
queue.push({ args, resolve, reject });
|
||
runNext();
|
||
});
|
||
}
|
||
|
||
function pageURL(endpoint, page, pageSize) {
|
||
const separator = endpoint.includes("?") ? "&" : "?";
|
||
const query = new URLSearchParams({
|
||
itemsPerPage: String(pageSize),
|
||
page: String(page),
|
||
});
|
||
return `${endpoint}${separator}${query}`;
|
||
}
|
||
|
||
function validatePage(payload, label, page) {
|
||
if (!payload || !Array.isArray(payload.items)
|
||
|| !Number.isInteger(payload.itemCount) || payload.itemCount < 0
|
||
|| !Number.isInteger(payload.pageCount) || payload.pageCount < 0) {
|
||
throw new PageLoadError(label, page, new Error("API 返回了无效的分页数据。"));
|
||
}
|
||
return payload;
|
||
}
|
||
|
||
async function fetchPage(fetcher, endpoint, label, page, pageSize) {
|
||
try {
|
||
const payload = await fetcher(pageURL(endpoint, page, pageSize));
|
||
return validatePage(payload, label, page);
|
||
} catch (error) {
|
||
if (error instanceof PageLoadError) throw error;
|
||
throw new PageLoadError(label, page, error);
|
||
}
|
||
}
|
||
|
||
async function fetchAllPages(fetcher, endpoint, options = {}) {
|
||
const label = options.label || "列表";
|
||
const pageSize = options.pageSize || 100;
|
||
const concurrency = Math.max(1, options.concurrency || 4);
|
||
const onProgress = typeof options.onProgress === "function" ? options.onProgress : () => {};
|
||
|
||
const first = await fetchPage(fetcher, endpoint, label, 0, pageSize);
|
||
const pageCount = first.pageCount;
|
||
const pages = new Array(Math.max(1, pageCount));
|
||
pages[0] = first.items;
|
||
let loaded = first.items.length;
|
||
let pagesLoaded = 1;
|
||
let stopped = false;
|
||
onProgress({ label, loaded, total: first.itemCount, pagesLoaded, pageCount });
|
||
|
||
let nextPage = 1;
|
||
const workerCount = Math.min(concurrency, Math.max(0, pageCount - 1));
|
||
const workers = Array.from({ length: workerCount }, async () => {
|
||
while (!stopped && nextPage < pageCount) {
|
||
const page = nextPage;
|
||
nextPage += 1;
|
||
let payload;
|
||
try {
|
||
payload = await fetchPage(fetcher, endpoint, label, page, pageSize);
|
||
} catch (error) {
|
||
stopped = true;
|
||
throw error;
|
||
}
|
||
if (stopped) return;
|
||
pages[page] = payload.items;
|
||
loaded += payload.items.length;
|
||
pagesLoaded += 1;
|
||
onProgress({ label, loaded, total: first.itemCount, pagesLoaded, pageCount });
|
||
}
|
||
});
|
||
await Promise.all(workers);
|
||
|
||
return {
|
||
items: pages.flat(),
|
||
itemCount: first.itemCount,
|
||
pageCount,
|
||
};
|
||
}
|
||
|
||
function uniqueSortedByName(items) {
|
||
const byName = new Map();
|
||
for (const item of items) {
|
||
const name = String(item?.name || "");
|
||
if (name) byName.set(name, item);
|
||
}
|
||
return Array.from(byName.values()).sort((left, right) => {
|
||
const leftName = String(left.name);
|
||
const rightName = String(right.name);
|
||
if (leftName < rightName) return -1;
|
||
if (leftName > rightName) return 1;
|
||
return 0;
|
||
});
|
||
}
|
||
|
||
function filterConfigs(items, query, statusFilter, getStatus) {
|
||
const normalizedQuery = String(query || "").trim().toLocaleLowerCase();
|
||
return items.filter((item) => {
|
||
const matchesQuery = !normalizedQuery
|
||
|| String(item.name).toLocaleLowerCase().includes(normalizedQuery)
|
||
|| String(item.sourceMasked).toLocaleLowerCase().includes(normalizedQuery);
|
||
const matchesStatus = statusFilter === "all" || getStatus(item) === statusFilter;
|
||
return matchesQuery && matchesStatus;
|
||
});
|
||
}
|
||
|
||
function countStatuses(items, getStatus) {
|
||
const result = { total: items.length, online: 0, offline: 0, idle: 0 };
|
||
for (const item of items) {
|
||
const status = getStatus(item);
|
||
if (Object.prototype.hasOwnProperty.call(result, status)) result[status] += 1;
|
||
}
|
||
return result;
|
||
}
|
||
|
||
function paginateItems(items, requestedPage, requestedPageSize) {
|
||
const pageSize = [25, 50, 100].includes(Number(requestedPageSize))
|
||
? Number(requestedPageSize)
|
||
: 50;
|
||
const totalPages = Math.max(1, Math.ceil(items.length / pageSize));
|
||
const page = Math.min(Math.max(1, Number(requestedPage) || 1), totalPages);
|
||
const startIndex = (page - 1) * pageSize;
|
||
const pageItems = items.slice(startIndex, startIndex + pageSize);
|
||
return {
|
||
items: pageItems,
|
||
page,
|
||
pageSize,
|
||
totalItems: items.length,
|
||
totalPages,
|
||
start: pageItems.length === 0 ? 0 : startIndex + 1,
|
||
end: pageItems.length === 0 ? 0 : startIndex + pageItems.length,
|
||
};
|
||
}
|
||
|
||
return {
|
||
PageLoadError,
|
||
countStatuses,
|
||
fetchAllPages,
|
||
filterConfigs,
|
||
limitConcurrency,
|
||
pageURL,
|
||
paginateItems,
|
||
uniqueSortedByName,
|
||
};
|
||
}));
|