move script into src

This commit is contained in:
Jonathan Barrow
2023-09-26 14:10:02 -04:00
parent e008ebd019
commit b70a27a0c8
2 changed files with 64 additions and 57 deletions

View File

@@ -36,7 +36,7 @@
"scripts": { "scripts": {
"build": "vite build", "build": "vite build",
"test": "vitest run", "test": "vitest run",
"test:dev": "ts-node ./run-source.ts", "test:dev": "npx ts-node ./src/dev-cli.ts",
"test:watch": "vitest", "test:watch": "vitest",
"test:coverage": "vitest run --coverage", "test:coverage": "vitest run --coverage",
"lint": "eslint --ext .ts,.js src/", "lint": "eslint --ext .ts,.js src/",

View File

@@ -1,9 +1,14 @@
import nodeFetch from 'node-fetch'; // eslint-disable-next-line import/no-extraneous-dependencies
import { prompt } from 'enquirer';
import Spinnies from 'spinnies';
import { program } from 'commander'; import { program } from 'commander';
// eslint-disable-next-line import/no-extraneous-dependencies
import dotenv from 'dotenv'; import dotenv from 'dotenv';
import { makeProviders, targets, makeStandardFetcher, MovieMedia, ShowMedia, ProviderControls, MetaOutput } from '.'; // eslint-disable-next-line import/no-extraneous-dependencies
import { prompt } from 'enquirer';
import nodeFetch from 'node-fetch';
// eslint-disable-next-line import/no-extraneous-dependencies
import Spinnies from 'spinnies';
import { MetaOutput, MovieMedia, ProviderControls, ShowMedia, makeProviders, makeStandardFetcher, targets } from '..';
dotenv.config(); dotenv.config();
@@ -49,8 +54,8 @@ function getAllSources() {
// * create all these things. Maybe this should change // * create all these things. Maybe this should change
const providers = makeProviders({ const providers = makeProviders({
fetcher: makeStandardFetcher(nodeFetch), fetcher: makeStandardFetcher(nodeFetch),
target: targets.NATIVE target: targets.NATIVE,
}) });
const sources = providers.listSources(); const sources = providers.listSources();
const embeds = providers.listEmbeds(); const embeds = providers.listEmbeds();
@@ -58,9 +63,9 @@ function getAllSources() {
const combined = [...sources, ...embeds]; const combined = [...sources, ...embeds];
// * Remove dupes // * Remove dupes
const map = new Map(combined.map(source => [source.id, source])); const map = new Map(combined.map((source) => [source.id, source]));
return [...map.values()] return [...map.values()];
} }
async function makeTMDBRequest(url: string): Promise<Response> { async function makeTMDBRequest(url: string): Promise<Response> {
@@ -68,7 +73,7 @@ async function makeTMDBRequest(url: string): Promise<Response> {
accept: 'application/json'; accept: 'application/json';
authorization?: string; authorization?: string;
} = { } = {
accept: 'application/json' accept: 'application/json',
}; };
// * JWT keys always start with ey and are ONLY valid as a header. // * JWT keys always start with ey and are ONLY valid as a header.
@@ -82,7 +87,7 @@ async function makeTMDBRequest(url: string): Promise<Response> {
return fetch(url, { return fetch(url, {
method: 'GET', method: 'GET',
headers: headers headers,
}); });
} }
@@ -102,7 +107,7 @@ async function getMovieMediaDetails(id: string): Promise<MovieMedia> {
type: 'movie', type: 'movie',
title: movie.title, title: movie.title,
releaseYear: Number(movie.release_date.split('-')[0]), releaseYear: Number(movie.release_date.split('-')[0]),
tmdbId: id tmdbId: id,
}; };
} }
@@ -127,7 +132,9 @@ async function getShowMediaDetails(id: string, seasonNumber: string, episodeNumb
throw new Error(season.status_message); throw new Error(season.status_message);
} }
response = await makeTMDBRequest(`https://api.themoviedb.org/3/tv/${id}/season/${seasonNumber}/episode/${episodeNumber}`); response = await makeTMDBRequest(
`https://api.themoviedb.org/3/tv/${id}/season/${seasonNumber}/episode/${episodeNumber}`,
);
const episode = await response.json(); const episode = await response.json();
if (episode.success === false) { if (episode.success === false) {
@@ -141,26 +148,27 @@ async function getShowMediaDetails(id: string, seasonNumber: string, episodeNumb
tmdbId: id, tmdbId: id,
episode: { episode: {
number: episode.episode_number, number: episode.episode_number,
tmdbId: episode.id tmdbId: episode.id,
}, },
season: { season: {
number: season.season_number, number: season.season_number,
tmdbId: season.id tmdbId: season.id,
} },
}; };
} }
function joinMediaTypes(mediaTypes: string[] | undefined) { function joinMediaTypes(mediaTypes: string[] | undefined) {
if (mediaTypes) { if (mediaTypes) {
const formatted = mediaTypes.map((type: string) => { const formatted = mediaTypes
.map((type: string) => {
type = type[0].toUpperCase() + type.substring(1).toLowerCase(); type = type[0].toUpperCase() + type.substring(1).toLowerCase();
return `${type}s`; return `${type}s`;
}).join(' / '); })
.join(' / ');
return `(${formatted})`; return `(${formatted})`;
} else {
return ''; // * Embed sources pass through here too
} }
return ''; // * Embed sources pass through here too
} }
async function runQuestions() { async function runQuestions() {
@@ -171,7 +179,7 @@ async function runQuestions() {
type: 'movie', type: 'movie',
season: '0', season: '0',
episode: '0', episode: '0',
url: '' url: '',
}; };
const answers = await prompt<CommonAnswers>([ const answers = await prompt<CommonAnswers>([
@@ -181,39 +189,38 @@ async function runQuestions() {
message: 'Select a fetcher', message: 'Select a fetcher',
choices: [ choices: [
{ {
message: 'Native', message: 'Native',
name: 'native' name: 'native',
}, },
{ {
message: 'Node fetch', message: 'Node fetch',
name: 'node-fetch' name: 'node-fetch',
} },
] ],
}, },
{ {
type: 'select', type: 'select',
name: 'source', name: 'source',
message: 'Select a source', message: 'Select a source',
choices: sources.map(source => ({ choices: sources.map((source) => ({
message: `[${source.type.toLocaleUpperCase()}] ${source.name} ${joinMediaTypes(source.mediaTypes)}`.trim(), message: `[${source.type.toLocaleUpperCase()}] ${source.name} ${joinMediaTypes(source.mediaTypes)}`.trim(),
name: source.id name: source.id,
})) })),
} },
]); ]);
options.fetcher = answers.fetcher; options.fetcher = answers.fetcher;
options.sourceId = answers.source; options.sourceId = answers.source;
const source = sources.find(source => source.id === answers.source)!; const source = sources.find((source) => source.id === answers.source)!;
if (source.type === 'embed') { if (source.type === 'embed') {
const sourceAnswers = await prompt<EmbedSourceAnswers>([ const sourceAnswers = await prompt<EmbedSourceAnswers>([
{ {
type: 'input', type: 'input',
name: 'url', name: 'url',
message: 'Embed URL' message: 'Embed URL',
} },
]); ]);
options.url = sourceAnswers.url; options.url = sourceAnswers.url;
@@ -222,7 +229,7 @@ async function runQuestions() {
{ {
type: 'input', type: 'input',
name: 'id', name: 'id',
message: 'TMDB ID' message: 'TMDB ID',
}, },
{ {
type: 'select', type: 'select',
@@ -231,14 +238,14 @@ async function runQuestions() {
choices: [ choices: [
{ {
message: 'Movie', message: 'Movie',
name: 'movie' name: 'movie',
}, },
{ {
message: 'TV Show', message: 'TV Show',
name: 'show' name: 'show',
} },
] ],
} },
]); ]);
options.tmdbId = sourceAnswers.id; options.tmdbId = sourceAnswers.id;
@@ -249,13 +256,13 @@ async function runQuestions() {
{ {
type: 'input', type: 'input',
name: 'season', name: 'season',
message: 'Season' message: 'Season',
}, },
{ {
type: 'input', type: 'input',
name: 'episode', name: 'episode',
message: 'Episode' message: 'Episode',
} },
]); ]);
options.season = seriesAnswers.season; options.season = seriesAnswers.season;
@@ -268,13 +275,13 @@ async function runQuestions() {
async function runCommandLine() { async function runCommandLine() {
program program
.option('-f, --fetcher <fetcher>', 'Fetcher to use. Either \'native\' or \'node-fetch\'', 'node-fetch') .option('-f, --fetcher <fetcher>', "Fetcher to use. Either 'native' or 'node-fetch'", 'node-fetch')
.option('-sid, --source-id <id>', 'ID for the source to use. Either an embed or provider', '') .option('-sid, --source-id <id>', 'ID for the source to use. Either an embed or provider', '')
.option('-tid, --tmdb-id <id>', 'TMDB ID for the media to scrape. Only used if source is a provider', '') .option('-tid, --tmdb-id <id>', 'TMDB ID for the media to scrape. Only used if source is a provider', '')
.option('-t, --type <type>', 'Media type. Either \'movie\' or \'show\'. Only used if source is a provider', 'movie') .option('-t, --type <type>', "Media type. Either 'movie' or 'show'. Only used if source is a provider", 'movie')
.option('-s, --season <number>', 'Season number. Only used if type is \'show\'', '0') .option('-s, --season <number>', "Season number. Only used if type is 'show'", '0')
.option('-e, --episode <number>', 'Episode number. Only used if type is \'show\'', '0') .option('-e, --episode <number>', "Episode number. Only used if type is 'show'", '0')
.option('-u, --url <embed URL>', 'URL to a video embed. Only used if source is an embed', '') .option('-u, --url <embed URL>', 'URL to a video embed. Only used if source is an embed', '');
program.parse(); program.parse();
@@ -283,14 +290,14 @@ async function runCommandLine() {
async function processOptions(options: CommandLineArguments) { async function processOptions(options: CommandLineArguments) {
if (options.fetcher !== 'node-fetch' && options.fetcher !== 'native') { if (options.fetcher !== 'node-fetch' && options.fetcher !== 'native') {
throw new Error('Fetcher must be either \'native\' or \'node-fetch\''); throw new Error("Fetcher must be either 'native' or 'node-fetch'");
} }
if (!options.sourceId.trim()) { if (!options.sourceId.trim()) {
throw new Error('Source ID must be provided'); throw new Error('Source ID must be provided');
} }
const source = sources.find(source => source.id === options.sourceId); const source = sources.find((source) => source.id === options.sourceId);
if (!source) { if (!source) {
throw new Error('Invalid source ID. No source found'); throw new Error('Invalid source ID. No source found');
@@ -314,7 +321,7 @@ async function processOptions(options: CommandLineArguments) {
} }
if (options.type !== 'movie' && options.type !== 'show') { if (options.type !== 'movie' && options.type !== 'show') {
throw new Error('Invalid media type. Must be either \'movie\' or \'show\''); throw new Error("Invalid media type. Must be either 'movie' or 'show'");
} }
if (options.type === 'show') { if (options.type === 'show') {
@@ -345,8 +352,8 @@ async function processOptions(options: CommandLineArguments) {
} }
const providers = makeProviders({ const providers = makeProviders({
fetcher: fetcher, fetcher,
target: targets.NATIVE target: targets.NATIVE,
}); });
await runScraper(providers, source, options); await runScraper(providers, source, options);
@@ -360,7 +367,7 @@ async function runScraper(providers: ProviderControls, source: MetaOutput, optio
try { try {
const result = await providers.runEmbedScraper({ const result = await providers.runEmbedScraper({
url: options.url, url: options.url,
id: source.id id: source.id,
}); });
spinnies.succeed('scrape', { text: 'Done!' }); spinnies.succeed('scrape', { text: 'Done!' });
console.log(result); console.log(result);
@@ -384,8 +391,8 @@ async function runScraper(providers: ProviderControls, source: MetaOutput, optio
spinnies.add('scrape', { text: `Running ${source.name} scraper on ${media.title}` }); spinnies.add('scrape', { text: `Running ${source.name} scraper on ${media.title}` });
try { try {
const result = await providers.runSourceScraper({ const result = await providers.runSourceScraper({
media: media, media,
id: source.id id: source.id,
}); });
spinnies.succeed('scrape', { text: 'Done!' }); spinnies.succeed('scrape', { text: 'Done!' });
console.log(result); console.log(result);