refactor: ffmpeg customFormats/containerFormats are now an object
This commit is contained in:
parent
14817a3314
commit
c865b21bf5
3 changed files with 19 additions and 44 deletions
|
|
@ -1,13 +0,0 @@
|
||||||
// Re-exports for backwards compatibility – canonical source is ffmpegFormats.ts
|
|
||||||
export {
|
|
||||||
type ProgressEvent,
|
|
||||||
type CustomFormat,
|
|
||||||
type ContainerFormat,
|
|
||||||
customFormats,
|
|
||||||
containerFormats,
|
|
||||||
isCustomFormat,
|
|
||||||
getCustomFormat,
|
|
||||||
getContainerFormat,
|
|
||||||
transcodeWithCustomFormat,
|
|
||||||
transcodeWithContainerFormat,
|
|
||||||
} from './ffmpegFormats';
|
|
||||||
|
|
@ -12,8 +12,6 @@ export interface ProgressEvent {
|
||||||
export interface CustomFormat {
|
export interface CustomFormat {
|
||||||
/** Human-readable label shown in the UI */
|
/** Human-readable label shown in the UI */
|
||||||
displayName: string;
|
displayName: string;
|
||||||
/** Internal identifier, must start with `FFMPEG_` */
|
|
||||||
internalName: string;
|
|
||||||
/** Arguments passed to ffmpeg (excluding input/output file args) */
|
/** Arguments passed to ffmpeg (excluding input/output file args) */
|
||||||
ffmpegArgs: string[];
|
ffmpegArgs: string[];
|
||||||
/** Output filename used when calling ffmpeg */
|
/** Output filename used when calling ffmpeg */
|
||||||
|
|
@ -40,37 +38,33 @@ export interface ContainerFormat extends Omit<CustomFormat, 'category'> {
|
||||||
needsTranscode: (blob: Blob) => Promise<boolean>;
|
needsTranscode: (blob: Blob) => Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const customFormats: CustomFormat[] = [
|
export const customFormats: Record<string, CustomFormat> = {
|
||||||
{
|
FFMPEG_MP3_320: {
|
||||||
displayName: 'MP3 320kbps',
|
displayName: 'MP3 320kbps',
|
||||||
internalName: 'FFMPEG_MP3_320',
|
|
||||||
ffmpegArgs: ['-map_metadata', '-1', '-c:a', 'libmp3lame', '-b:a', '320k', '-ar', '44100'],
|
ffmpegArgs: ['-map_metadata', '-1', '-c:a', 'libmp3lame', '-b:a', '320k', '-ar', '44100'],
|
||||||
outputFilename: 'output.mp3',
|
outputFilename: 'output.mp3',
|
||||||
outputMime: 'audio/mpeg',
|
outputMime: 'audio/mpeg',
|
||||||
extension: 'mp3',
|
extension: 'mp3',
|
||||||
category: 'MP3',
|
category: 'MP3',
|
||||||
},
|
},
|
||||||
{
|
FFMPEG_MP3_256: {
|
||||||
displayName: 'MP3 256kbps',
|
displayName: 'MP3 256kbps',
|
||||||
internalName: 'FFMPEG_MP3_256',
|
|
||||||
ffmpegArgs: ['-map_metadata', '-1', '-c:a', 'libmp3lame', '-b:a', '256k', '-ar', '44100'],
|
ffmpegArgs: ['-map_metadata', '-1', '-c:a', 'libmp3lame', '-b:a', '256k', '-ar', '44100'],
|
||||||
outputFilename: 'output.mp3',
|
outputFilename: 'output.mp3',
|
||||||
outputMime: 'audio/mpeg',
|
outputMime: 'audio/mpeg',
|
||||||
extension: 'mp3',
|
extension: 'mp3',
|
||||||
category: 'MP3',
|
category: 'MP3',
|
||||||
},
|
},
|
||||||
{
|
FFMPEG_MP3_128: {
|
||||||
displayName: 'MP3 128kbps',
|
displayName: 'MP3 128kbps',
|
||||||
internalName: 'FFMPEG_MP3_128',
|
|
||||||
ffmpegArgs: ['-map_metadata', '-1', '-c:a', 'libmp3lame', '-b:a', '128k', '-ar', '44100'],
|
ffmpegArgs: ['-map_metadata', '-1', '-c:a', 'libmp3lame', '-b:a', '128k', '-ar', '44100'],
|
||||||
outputFilename: 'output.mp3',
|
outputFilename: 'output.mp3',
|
||||||
outputMime: 'audio/mpeg',
|
outputMime: 'audio/mpeg',
|
||||||
extension: 'mp3',
|
extension: 'mp3',
|
||||||
category: 'MP3',
|
category: 'MP3',
|
||||||
},
|
},
|
||||||
{
|
FFMPEG_OGG_320: {
|
||||||
displayName: 'OGG 320kbps',
|
displayName: 'OGG 320kbps',
|
||||||
internalName: 'FFMPEG_OGG_320',
|
|
||||||
ffmpegArgs: [
|
ffmpegArgs: [
|
||||||
'-map_metadata',
|
'-map_metadata',
|
||||||
'-1',
|
'-1',
|
||||||
|
|
@ -88,9 +82,8 @@ export const customFormats: CustomFormat[] = [
|
||||||
extension: 'ogg',
|
extension: 'ogg',
|
||||||
category: 'OGG',
|
category: 'OGG',
|
||||||
},
|
},
|
||||||
{
|
FFMPEG_OGG_256: {
|
||||||
displayName: 'OGG 256kbps',
|
displayName: 'OGG 256kbps',
|
||||||
internalName: 'FFMPEG_OGG_256',
|
|
||||||
ffmpegArgs: [
|
ffmpegArgs: [
|
||||||
'-map_metadata',
|
'-map_metadata',
|
||||||
'-1',
|
'-1',
|
||||||
|
|
@ -108,9 +101,8 @@ export const customFormats: CustomFormat[] = [
|
||||||
extension: 'ogg',
|
extension: 'ogg',
|
||||||
category: 'OGG',
|
category: 'OGG',
|
||||||
},
|
},
|
||||||
{
|
FFMPEG_OGG_128: {
|
||||||
displayName: 'OGG 128kbps',
|
displayName: 'OGG 128kbps',
|
||||||
internalName: 'FFMPEG_OGG_128',
|
|
||||||
ffmpegArgs: [
|
ffmpegArgs: [
|
||||||
'-map_metadata',
|
'-map_metadata',
|
||||||
'-1',
|
'-1',
|
||||||
|
|
@ -128,16 +120,15 @@ export const customFormats: CustomFormat[] = [
|
||||||
extension: 'ogg',
|
extension: 'ogg',
|
||||||
category: 'OGG',
|
category: 'OGG',
|
||||||
},
|
},
|
||||||
{
|
FFMPEG_AAC_256: {
|
||||||
displayName: 'AAC 256kbps',
|
displayName: 'AAC 256kbps',
|
||||||
internalName: 'FFMPEG_AAC_256',
|
|
||||||
ffmpegArgs: ['-map_metadata', '-1', '-c:a', 'aac', '-b:a', '256k'],
|
ffmpegArgs: ['-map_metadata', '-1', '-c:a', 'aac', '-b:a', '256k'],
|
||||||
outputFilename: 'output.m4a',
|
outputFilename: 'output.m4a',
|
||||||
outputMime: 'audio/mp4',
|
outputMime: 'audio/mp4',
|
||||||
extension: 'm4a',
|
extension: 'm4a',
|
||||||
category: 'AAC',
|
category: 'AAC',
|
||||||
},
|
},
|
||||||
];
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Container format definitions for lossless re-muxing. Each entry describes
|
* Container format definitions for lossless re-muxing. Each entry describes
|
||||||
|
|
@ -145,10 +136,9 @@ export const customFormats: CustomFormat[] = [
|
||||||
* `needsTranscode` predicate so callers can skip the ffmpeg step when the
|
* `needsTranscode` predicate so callers can skip the ffmpeg step when the
|
||||||
* source is already in the correct container.
|
* source is already in the correct container.
|
||||||
*/
|
*/
|
||||||
export const containerFormats: ContainerFormat[] = [
|
export const containerFormats: Record<string, ContainerFormat> = {
|
||||||
{
|
flac: {
|
||||||
displayName: 'FLAC',
|
displayName: 'FLAC',
|
||||||
internalName: 'flac',
|
|
||||||
ffmpegArgs: ['-vn', '-map_metadata', '-1', '-map', '0:a', '-c:a', 'flac'],
|
ffmpegArgs: ['-vn', '-map_metadata', '-1', '-map', '0:a', '-c:a', 'flac'],
|
||||||
outputFilename: 'output.flac',
|
outputFilename: 'output.flac',
|
||||||
outputMime: 'audio/flac',
|
outputMime: 'audio/flac',
|
||||||
|
|
@ -156,25 +146,23 @@ export const containerFormats: ContainerFormat[] = [
|
||||||
// Only transcode when the source is NOT already a FLAC file.
|
// Only transcode when the source is NOT already a FLAC file.
|
||||||
needsTranscode: async (blob) => (await getExtensionFromBlob(blob)) !== 'flac',
|
needsTranscode: async (blob) => (await getExtensionFromBlob(blob)) !== 'flac',
|
||||||
},
|
},
|
||||||
{
|
alac: {
|
||||||
displayName: 'Apple Lossless',
|
displayName: 'Apple Lossless',
|
||||||
internalName: 'alac',
|
|
||||||
ffmpegArgs: ['-c:a', 'alac'],
|
ffmpegArgs: ['-c:a', 'alac'],
|
||||||
outputFilename: 'output.m4a',
|
outputFilename: 'output.m4a',
|
||||||
outputMime: 'audio/mp4',
|
outputMime: 'audio/mp4',
|
||||||
extension: 'm4a',
|
extension: 'm4a',
|
||||||
needsTranscode: async () => true,
|
needsTranscode: async () => true,
|
||||||
},
|
},
|
||||||
{
|
nochange: {
|
||||||
displayName: "Don't change",
|
displayName: "Don't change",
|
||||||
internalName: 'nochange',
|
|
||||||
ffmpegArgs: ['-c:a', 'copy', '-strict', '-2'],
|
ffmpegArgs: ['-c:a', 'copy', '-strict', '-2'],
|
||||||
outputFilename: 'output.mp4',
|
outputFilename: 'output.mp4',
|
||||||
outputMime: 'audio/mp4',
|
outputMime: 'audio/mp4',
|
||||||
extension: 'mp4',
|
extension: 'mp4',
|
||||||
needsTranscode: async (blob) => (await getExtensionFromBlob(blob)) == 'm4a',
|
needsTranscode: async (blob) => (await getExtensionFromBlob(blob)) == 'm4a',
|
||||||
},
|
},
|
||||||
];
|
};
|
||||||
|
|
||||||
/** Returns true if the quality string identifies a known custom ffmpeg-transcoded format */
|
/** Returns true if the quality string identifies a known custom ffmpeg-transcoded format */
|
||||||
export function isCustomFormat(quality: string): boolean {
|
export function isCustomFormat(quality: string): boolean {
|
||||||
|
|
@ -183,12 +171,12 @@ export function isCustomFormat(quality: string): boolean {
|
||||||
|
|
||||||
/** Looks up a custom format by its internal name, or returns undefined */
|
/** Looks up a custom format by its internal name, or returns undefined */
|
||||||
export function getCustomFormat(internalName: string): CustomFormat | undefined {
|
export function getCustomFormat(internalName: string): CustomFormat | undefined {
|
||||||
return customFormats.find((f) => f.internalName === internalName);
|
return customFormats[internalName];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Looks up a container format by its internal name, or returns undefined */
|
/** Looks up a container format by its internal name, or returns undefined */
|
||||||
export function getContainerFormat(internalName: string): ContainerFormat | undefined {
|
export function getContainerFormat(internalName: string): ContainerFormat | undefined {
|
||||||
return containerFormats.find((f) => f.internalName === internalName);
|
return containerFormats[internalName];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -816,8 +816,8 @@ export function initializeSettings(scrobbler, player, api, ui) {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Append custom (ffmpeg-transcoded) format options
|
// Append custom (ffmpeg-transcoded) format options
|
||||||
for (const fmt of customFormats) {
|
for (const [key, fmt] of Object.entries(customFormats)) {
|
||||||
allOptions.push({ value: fmt.internalName, text: fmt.displayName, category: fmt.category });
|
allOptions.push({ value: key, text: fmt.displayName, category: fmt.category });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort by category order first, then by bitrate descending within each category
|
// Sort by category order first, then by bitrate descending within each category
|
||||||
|
|
@ -877,7 +877,7 @@ export function initializeSettings(scrobbler, player, api, ui) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (losslessContainerSetting) {
|
if (losslessContainerSetting) {
|
||||||
for (const { internalName, displayName } of containerFormats) {
|
for (const [internalName, { displayName }] of Object.entries(containerFormats)) {
|
||||||
const option = document.createElement('option');
|
const option = document.createElement('option');
|
||||||
option.value = internalName;
|
option.value = internalName;
|
||||||
option.textContent = displayName;
|
option.textContent = displayName;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue