Files
meta-fuse/packages/meta-fuse-core/src/vfs/defaults/defaultRules.ts
T
Worph d35a64a744
Build and Push Docker Image / build (push) Failing after 15s
align with metadata standard
2026-09-13 15:57:12 +02:00

233 lines
6.8 KiB
TypeScript

/**
* Default Renaming Rules
*
* These rules match the current hardcoded behavior in RenamingRule.ts.
* They serve as the initial configuration when no custom rules are set.
*
* Old behavior:
* - Only processes video, subtitle, torrent files
* - Requires extension
* - Title from title || originalTitle
* - Files without title/extension are hidden (not in Unsorted)
*/
import type { RenamingConfig, RenamingRule } from '../types/RenamingRuleTypes.js';
/**
* Supported file types condition (video, subtitle, torrent)
* Used by all rules to filter out unsupported file types
*/
const supportedFileTypeCondition = {
operator: 'OR' as const,
conditions: [
{ type: 'EQUALS' as const, field: 'fileType', value: 'video' },
{ type: 'EQUALS' as const, field: 'fileType', value: 'subtitle' },
{ type: 'EQUALS' as const, field: 'fileType', value: 'torrent' },
],
};
/**
* TV Series - Extras rule
* Matches files marked as extra/special content
*/
const tvExtrasRule: RenamingRule = {
id: 'tv-extras',
name: 'TV Series - Extras',
description: 'Special episodes, behind the scenes, extras',
enabled: true,
priority: 100,
conditions: {
operator: 'AND',
conditions: [
supportedFileTypeCondition,
{ type: 'EXISTS', field: 'extension' },
{ type: 'EQUALS', field: 'extra', value: true },
],
},
// Old format: TV Shows/{title}/extra/{title}{seasonSpace}{episode}{subtitle}{version}.{extension}
template: 'TV Shows/{title|originalTitle}/extra/{title|originalTitle}{season?( S{season:pad2})}{episode?(E{episode:pad2})}{subtitleLanguage?(.{subtitleLanguage})}{version?( {version})}.{extension}',
fallbackToUnsorted: true, // If template fails (no title), put in Unsorted
};
/**
* TV Series rule
* Matches files with both season and episode numbers
*/
const tvSeriesRule: RenamingRule = {
id: 'tv-series',
name: 'TV Series',
description: 'Regular TV show episodes with season and episode numbers',
enabled: true,
priority: 90,
conditions: {
operator: 'AND',
conditions: [
supportedFileTypeCondition,
{ type: 'EXISTS', field: 'extension' },
{ type: 'EXISTS', field: 'season' },
{ type: 'EXISTS', field: 'episode' },
],
},
// Old format: TV Shows/{title}/S{season}/{title} S{season}E{episode}{subtitle}{version}.{extension}
template: 'TV Shows/{title|originalTitle}/S{season:pad2}/{title|originalTitle} S{season:pad2}E{episode:pad2}{subtitleLanguage?(.{subtitleLanguage})}{version?( {version})}.{extension}',
fallbackToUnsorted: true, // If template fails (no title), put in Unsorted
};
/**
* Movies rule
* Matches files with a title but no season number (assumed to be movies)
*/
const moviesRule: RenamingRule = {
id: 'movies',
name: 'Movies',
description: 'Standalone movies without season/episode numbers',
enabled: true,
priority: 80,
conditions: {
operator: 'AND',
conditions: [
supportedFileTypeCondition,
{ type: 'EXISTS', field: 'extension' },
{
operator: 'OR',
conditions: [
{ type: 'EXISTS', field: 'title' },
{ type: 'EXISTS', field: 'originalTitle' },
],
},
{ type: 'NOT_EXISTS', field: 'season' },
],
},
// Old format: Movies/{title}{year}/{title}{year}{subtitle}{version}.{extension}
template: 'Movies/{title|originalTitle}{movieYear?( ({movieYear}))}/{title|originalTitle}{movieYear?( ({movieYear}))}{subtitleLanguage?(.{subtitleLanguage})}{version?( {version})}.{extension}',
fallbackToUnsorted: true, // If template fails, put in Unsorted
};
/**
* Images rule
* Matches image files (posters, backdrops, etc.)
* Note: Poster images are classified as 'document' type
*/
const imagesRule: RenamingRule = {
id: 'images',
name: 'Images',
description: 'Image files like posters and backdrops',
enabled: true,
priority: 70,
conditions: {
operator: 'AND',
conditions: [
{
operator: 'OR',
conditions: [
{ type: 'EQUALS', field: 'fileType', value: 'image' },
{ type: 'EQUALS', field: 'fileType', value: 'document' },
],
},
],
},
template: 'Images/{fileName}',
fallbackToUnsorted: true,
};
/**
* Default fallback rule
* Used when no other rules match - places files in Unsorted folder
* Note: In old behavior, unsupported files were hidden. This rule provides visibility.
*/
const fallbackRule: RenamingRule = {
id: 'fallback-unsorted',
name: 'Unsorted',
description: 'Files that do not match any other rule',
enabled: true,
priority: 0,
conditions: {
operator: 'AND',
conditions: [], // Always matches
},
template: 'Unsorted/{fileName}',
fallbackToUnsorted: false,
};
/**
* Get the default renaming configuration
*
* @returns The default RenamingConfig matching current hardcoded behavior
*/
export function getDefaultRulesConfig(): RenamingConfig {
return {
version: 1,
rules: [
tvExtrasRule,
tvSeriesRule,
moviesRule,
imagesRule,
],
defaultRule: fallbackRule,
lastModified: new Date().toISOString(),
};
}
/**
* Plex-style naming preset
* Uses Plex's preferred naming conventions
*/
export function getPlexPreset(): RenamingConfig {
return {
version: 1,
rules: [
{
...tvSeriesRule,
id: 'plex-tv',
name: 'TV Series (Plex)',
template: 'TV Shows/{title|originalTitle}/Season {season:pad2}/{title|originalTitle} - S{season:pad2}E{episode:pad2}{subtitleLanguage?(.{subtitleLanguage})}{version?( - {version})}.{extension}',
},
{
...moviesRule,
id: 'plex-movies',
name: 'Movies (Plex)',
template: 'Movies/{title|originalTitle} ({movieYear|Unknown})/{title|originalTitle} ({movieYear|Unknown}){subtitleLanguage?(.{subtitleLanguage})}{version?( - {version})}.{extension}',
},
],
defaultRule: fallbackRule,
lastModified: new Date().toISOString(),
};
}
/**
* Jellyfin-style naming preset
* Uses Jellyfin's preferred naming conventions
*/
export function getJellyfinPreset(): RenamingConfig {
return {
version: 1,
rules: [
{
...tvSeriesRule,
id: 'jellyfin-tv',
name: 'TV Series (Jellyfin)',
template: 'Shows/{title|originalTitle}{year?( ({year}))}/Season {season:pad2}/S{season:pad2}E{episode:pad2}{subtitleLanguage?(.{subtitleLanguage})}.{extension}',
},
{
...moviesRule,
id: 'jellyfin-movies',
name: 'Movies (Jellyfin)',
template: 'Movies/{title|originalTitle} ({movieYear})/{title|originalTitle} ({movieYear}){subtitleLanguage?(.{subtitleLanguage})}.{extension}',
},
],
defaultRule: fallbackRule,
lastModified: new Date().toISOString(),
};
}
/**
* Available presets
*/
export const PRESETS = {
default: getDefaultRulesConfig,
plex: getPlexPreset,
jellyfin: getJellyfinPreset,
} as const;
export type PresetName = keyof typeof PRESETS;