identify-media
Version:
Analyse file path and content to make search criteria for media APIs
157 lines (128 loc) • 5.51 kB
text/typescript
import {findEpisode, findSeason, findSeasonEpisode, findYear} from "./analyseString";
import {setSpaces, stripEnd, stripParenthesis, stripSeasons, trimEnd} from "./cleanString";
const parseFilePath = (filePath: string): {fileName: string, dirNames: string[]} => {
const parts = filePath.split(/[/\\]/);
const fileName = parts[parts.length-1];
const dirNames = parts.slice(0, -1);
return {
fileName,
dirNames
}
}
const cleanUpName = (name: string): string => {
return stripParenthesis(trimEnd(setSpaces(name))).trim();
}
export interface AnalysedMovie {
type: 'movie';
year?: number;
name: string;
}
export const isAnalysedMovie = (analysed: AnalysedMedia): analysed is AnalysedMovie => {
return (analysed as AnalysedMovie).type === 'movie';
}
const analyseForMovie = (fileName: string, dirNames: string[]): AnalysedMovie | undefined => {
const yearResult = findYear(fileName) || findYear(dirNames[dirNames.length - 1]);
if (!!yearResult) {
const name = cleanUpName(yearResult.restName || stripEnd(fileName));
return {
type: 'movie',
year: yearResult.year,
name
};
}
return undefined;
}
export interface AnalysedTVShow {
type: 'tv';
name: string;
season?: number;
episodes?: number[];
year?: number;
}
export const isAnalysedTVShow = (analysed: AnalysedMedia): analysed is AnalysedTVShow => {
return (analysed as any).hasOwnProperty('type') && (analysed as AnalysedTVShow).type === 'tv';
}
const searchYear = (fileName: string, dirNames: string[], episodeRestName?: string, seasonRestName?: string): {restName?: string, year: number} | undefined => {
return findYear(episodeRestName || seasonRestName) ||
findYear(dirNames[dirNames.length-2]) ||
findYear(dirNames[dirNames.length-1]) ||
findYear(fileName);
}
interface RestName {
restName?: string;
}
const guessName = (fileName: string, dirNames: string[], seResult?: RestName, yearResult?: RestName): string => { return cleanUpName(
yearResult?.restName && trimEnd(stripSeasons(yearResult.restName)) ||
seResult?.restName ||
(!!yearResult ? dirNames[dirNames.length-3] || dirNames[dirNames.length-2] : dirNames[dirNames.length-2] || dirNames[dirNames.length-1]) ||
stripEnd(fileName)
);
}
const tvBySeasonEpisode = (fileName: string, dirNames: string[]): AnalysedTVShow | undefined => {
const seasonEpisodeResult = findSeasonEpisode(fileName);
if (!!seasonEpisodeResult) {
const seasonResult = findSeason(dirNames[dirNames.length-1]);
const season = seasonEpisodeResult.season || seasonResult?.season;
const yearResult = searchYear(fileName, dirNames, seasonEpisodeResult.restName, seasonResult?.restName);
const name = guessName(fileName, dirNames, seasonEpisodeResult.restName ? seasonEpisodeResult : seasonResult, yearResult);
return {
type: 'tv',
name,
season,
episodes: seasonEpisodeResult.episodes,
year: yearResult?.year,
}
}
return undefined;
}
const tvBySeason = (fileName: string, dirNames: string[]): AnalysedTVShow | undefined => {
const seasonResult = findSeason(dirNames[dirNames.length-1]);
if (!!seasonResult) {
const episodeResult = findEpisode(fileName, seasonResult.season);
const yearResult = searchYear(fileName, dirNames, episodeResult?.restName, seasonResult.restName);
const name = guessName(
fileName,
dirNames,
episodeResult?.restName ? episodeResult : seasonResult,
yearResult);
return {
type: 'tv',
name,
season: seasonResult.season,
episodes: episodeResult?.episodes,
year: yearResult?.year,
};
}
return undefined;
}
const analyseForTv = (fileName: string, dirNames: string[]): AnalysedTVShow | undefined => {
return tvBySeasonEpisode(fileName, dirNames) || tvBySeason(fileName, dirNames);
}
export type AnalysedMedia = AnalysedMovie|AnalysedTVShow|string;
export const analyseFilePath = (filePath: string): AnalysedMedia => {
const {fileName, dirNames} = parseFilePath(filePath);
return analyseForTv(fileName, dirNames) || analyseForMovie(fileName, dirNames) || cleanUpName(stripEnd(fileName));
}
const extrasRegex = /\bextras?\b|\bfeaturettes?\b|\bbonus?\b|\btrailer?\b/i
export const isExtras = (filePath: string): boolean => {
const {dirNames, fileName} = parseFilePath(filePath);
return dirNames.some((dirName) => extrasRegex.test(dirName)) || extrasRegex.test(fileName);
}
const samplesRegex = /^samples?$/i
const sampleRegex = /\bsample\b/i
export const isSample = (filePath: string): boolean => {
const {fileName, dirNames} = parseFilePath(filePath);
return sampleRegex.test(fileName) || dirNames.some((dirName) => samplesRegex.test(dirName));
}
export const isSameRelease = (a: AnalysedMedia, b: AnalysedMedia): boolean => {
if (typeof a === 'string' && typeof b === 'string') {
return a.toLowerCase() === b.toLowerCase();
}
if (isAnalysedMovie(a) && isAnalysedMovie(b)) {
return a.name.toLowerCase() === b.name.toLowerCase() && a.year === b.year;
}
if (isAnalysedTVShow(a) && isAnalysedTVShow(b)) {
return a.name.toLowerCase() === b.name.toLowerCase() && a.year === b.year;
}
return false;
}