初始提交:短剧库源码(含转码优化 ultrafast)

This commit is contained in:
yvan
2026-09-17 19:09:08 +08:00
commit 07dc0f1059
247 changed files with 43242 additions and 0 deletions
+78
View File
@@ -0,0 +1,78 @@
(function (root) {
'use strict';
const modes = ['default', 'newest', 'oldest', 'heat', 'views', 'title'];
const collator = new Intl.Collator('zh-Hans-CN', {numeric: true, sensitivity: 'base'});
function chineseNumber(text) {
const digits = {零:0,〇:0,一:1,二:2,两:2,兩:2,三:3,四:4,五:5,六:6,七:7,八:8,九:9};
const units = {十:10,百:100,千:1000,万:10000,萬:10000};
if ([...text].every(character => character in digits)) return Number([...text].map(character => digits[character]).join(''));
let total=0, section=0, digit=0;
for (const character of text) {
if (character in digits) digit=digits[character];
else if (units[character]===10000) {total+=(section+digit||1)*10000;section=0;digit=0;}
else {section+=(digit||1)*units[character];digit=0;}
}
return total+section+digit;
}
function titleKey(drama) {
return String(drama.title || drama.name || '').normalize('NFKC').trim()
.replace(/第\s*([零〇一二两兩三四五六七八九十百千万萬]+)\s*(?=[季部集章卷篇册冊辑輯])/g, (_, number) => '第'+chineseNumber(number));
}
function metricValue(value) {
if (typeof value === 'number') return Number.isFinite(value) && value >= 0 ? value : null;
const text = String(value ?? '').replace(/[,,\s]/g, '');
const match = /^(\d+(?:\.\d+)?)(亿|万|千|w|k|m|b)?\+?(?:次播放|次观看|人看过|热度|播放|观看|次)?\+?$/i.exec(text);
if (!match) return null;
const unit = (match[2] || '').toLowerCase();
const number = Number(match[1]) * ({亿: 1e8, 万: 1e4, 千: 1e3, w: 1e4, k: 1e3, m: 1e6, b: 1e9}[unit] || 1);
return Number.isFinite(number) ? number : null;
}
function dateValue(value) {
const match = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(String(value || '').trim());
if (!match) return null;
const year = Number(match[1]), month = Number(match[2]), day = Number(match[3]);
const date = new Date(Date.UTC(year, month - 1, day));
if (year < 2000 || year > 2100 || date.getUTCFullYear() !== year || date.getUTCMonth() !== month - 1 || date.getUTCDate() !== day) return null;
return date.getTime();
}
function sortValue(drama, mode) {
if (mode === 'newest' || mode === 'oldest') return dateValue(drama.onlineDate);
if (mode === 'heat') return metricValue(drama.heat);
if (mode === 'views') return metricValue(drama.views);
return null;
}
function sortDramas(list, mode) {
if (!modes.includes(mode) || mode === 'default') return list.slice();
return list.map((drama, index) => ({drama, index, title: titleKey(drama), value: sortValue(drama, mode)})).sort((left, right) => {
if (mode !== 'title') {
if (left.value === null && right.value === null) return left.index - right.index;
if (left.value === null && right.value !== null) return 1;
if (left.value !== null && right.value === null) return -1;
if (left.value !== right.value) return mode === 'oldest' ? left.value - right.value : right.value - left.value;
}
return collator.compare(left.title, right.title) || left.index - right.index;
}).map(item => item.drama);
}
function summary(list, mode) {
if (!['newest', 'oldest', 'heat', 'views'].includes(mode)) return '';
const label = {newest: '上线日期', oldest: '上线日期', heat: '热度', views: '播放量'}[mode];
const known = list.filter(drama => sortValue(drama, mode) !== null).length;
if (!list.length) return '';
if (!known) return '当前结果缺少' + label + ',无法按此排序';
return known < list.length ? label + '未知 ' + (list.length - known) + ' 部,已置后' : '';
}
const librarySort = {modes, metricValue, dateValue, sortDramas, summary, titleKey};
if (typeof module !== 'undefined' && module.exports) module.exports = librarySort;
if (root) root.JukuLibrarySort = librarySort;
})(typeof window !== 'undefined' ? window : null);