mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-29 11:36:36 +08:00
38b85bb487
* feat(strm): 同名多版本择优/keep_ext,并支持合并展示与播放切换 默认对同名不同扩展(如 .mkv/.mp4)按体积→mtime→扩展名优先级择优生成一条 strm,冲突打日志;开启 keep_ext 则分别生成 name.ext.strm。媒体详情与播放器可切换版本,列表继续合并同片多版本,Emby MediaSources 名称区分容器/分辨率。 Co-authored-by: truewhile <truewhile@users.noreply.github.com> * fix(meta): 边车匹配忽略 .mkv.strm 中间扩展,同片共享 NFO/海报/字幕 NFO、海报、字幕与刮削写出统一使用 mediaSidecarBase 词干,使竞女01.mkv.strm 与竞女01.mp4.strm 配对同一套 竞女01.nfo / 竞女01-poster / 竞女01.srt; 仍兼容历史上的 name.mkv.nfo 命名。 Co-authored-by: truewhile <truewhile@users.noreply.github.com> * fix(emby): People NFO 按共享词干匹配 keep_ext strm resolveMediaPeople 改为查找竞女01.nfo(并兼容旧的竞女01.mkv.nfo), 同时修正把 decodeNFOFile 的 partial 误当 ok 导致完整 NFO 被跳过的问题。 Co-authored-by: truewhile <truewhile@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: truewhile <truewhile@users.noreply.github.com>
61 lines
2.2 KiB
TypeScript
61 lines
2.2 KiB
TypeScript
import type { Media } from '../types'
|
|
|
|
/** 从路径 / strm URL 推断容器扩展名(mkv/mp4…)。 */
|
|
export function mediaVersionContainer(media: Media): string {
|
|
const path = (media.path || '').replace(/\\/g, '/')
|
|
const base = path.split('/').pop() || ''
|
|
const ext = base.includes('.') ? base.slice(base.lastIndexOf('.')).toLowerCase() : ''
|
|
let name = ext ? base.slice(0, -ext.length) : base
|
|
if (ext === '.strm') {
|
|
const second = name.includes('.') ? name.slice(name.lastIndexOf('.')).toLowerCase() : ''
|
|
if (second && second !== '.strm') {
|
|
return second.replace(/^\./, '')
|
|
}
|
|
const strm = (media.strm_url || '').toLowerCase()
|
|
const marker = '/video.'
|
|
const idx = strm.lastIndexOf(marker)
|
|
if (idx >= 0) {
|
|
let rest = strm.slice(idx + marker.length)
|
|
const end = rest.search(/[?#&/]/)
|
|
if (end >= 0) rest = rest.slice(0, end)
|
|
rest = rest.replace(/^\./, '').trim()
|
|
if (rest) return rest
|
|
}
|
|
return 'strm'
|
|
}
|
|
return ext.replace(/^\./, '')
|
|
}
|
|
|
|
function formatSize(bytes: number): string {
|
|
if (!bytes || bytes < 1024) return bytes ? `${bytes} B` : ''
|
|
const units = ['KB', 'MB', 'GB', 'TB']
|
|
let value = bytes
|
|
let unit = -1
|
|
do {
|
|
value /= 1024
|
|
unit += 1
|
|
} while (value >= 1024 && unit < units.length - 1)
|
|
return `${value.toFixed(1)} ${units[unit]}`
|
|
}
|
|
|
|
/** 版本切换展示名:分辨率 · 容器 · 编码 · 体积,缺省回退文件名。 */
|
|
export function mediaVersionLabel(media: Media): string {
|
|
const parts: string[] = []
|
|
if (media.height > 0) parts.push(`${media.height}p`)
|
|
else if (media.width > 0) parts.push(`${media.width}w`)
|
|
const container = mediaVersionContainer(media)
|
|
if (container && container !== 'strm') parts.push(container.toUpperCase())
|
|
if (media.video_codec?.trim()) parts.push(media.video_codec.trim().toUpperCase())
|
|
const size = formatSize(media.size_bytes)
|
|
if (size) parts.push(size)
|
|
if (parts.length > 0) return parts.join(' · ')
|
|
const base = (media.path || '').replace(/\\/g, '/').split('/').pop()
|
|
return base || media.title || media.id
|
|
}
|
|
|
|
export function mediaVersionsOf(media: Media | null | undefined): Media[] {
|
|
if (!media) return []
|
|
if (media.versions && media.versions.length > 1) return media.versions
|
|
return [media]
|
|
}
|