Added basic project structure and experimental entity fetching.

This commit is contained in:
DebaucheryLibrarian 2024-12-23 22:33:54 +01:00
parent b7b3769dc8
commit 85c42f510d
11 changed files with 8802 additions and 20 deletions

14
.editorconfig Normal file
View File

@ -0,0 +1,14 @@
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
indent_style = tab
indent_size = 4
# Matches multiple files with brace expansion notation
# Set default charset
[*.js]
charset = utf-8

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
node_modules/
config/
!config/default.*js
dist/

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
v22.11.0

6
knexfile.js Normal file
View File

@ -0,0 +1,6 @@
import config from 'config';
export default {
client: 'pg',
connection: { ...config.get('database') }, // avoid "cannot redefine password" error
};

8597
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +1,44 @@
{ {
"name": "traxxx-core", "name": "traxxx-core",
"version": "1.0.0", "version": "1.0.0",
"description": "Archiving core of the traxxx project.", "description": "Archiving core of the traxxx project.",
"main": "src/app.js", "main": "src/app.ts",
"scripts": { "type": "module",
"test": "echo \"Error: no test specified\" && exit 1" "scripts": {
}, "postinstall": "node-config-ts",
"repository": { "start": "tsx src/app.ts"
"type": "git", },
"url": "https://gitea.unknown.name/DebaucheryLibrarian/traxxx-core" "repository": {
}, "type": "git",
"keywords": [ "url": "https://gitea.unknown.name/DebaucheryLibrarian/traxxx-core"
"adult", },
"nsfw", "keywords": [
"porn", "adult",
"xxx", "nsfw",
"archive" "porn",
], "xxx",
"author": "DebaucheryLibrarian", "archive"
"license": "ISC" ],
"author": "DebaucheryLibrarian",
"license": "ISC",
"devDependencies": {
"@types/config": "^3.3.5",
"@types/node": "^22.10.0",
"@types/yargs": "^17.0.33",
"eslint": "^9.15.0",
"typescript": "^5.7.2"
},
"dependencies": {
"config": "^3.3.12",
"knex": "^3.1.0",
"kysely": "^0.27.5",
"node-config-ts": "^3.3.1",
"pg": "^8.13.1",
"tsx": "^4.19.2",
"unprint": "^0.14.3",
"yargs": "^17.7.2"
},
"imports": {
"#src/*": "./dist/*.js"
}
} }

50
src/app.ts Normal file
View File

@ -0,0 +1,50 @@
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import { update } from '#src/updates';
console.log(process.argv);
yargs(hideBin(process.argv))
.command('update', 'scan channels for scene and movie', (builder: yargs.Argv) => {
console.log('build update');
builder
.option('latest', {
describe: 'Scrape latest releases if available',
type: 'boolean',
default: true,
})
.option('upcoming', {
describe: 'Scrape upcoming releases if available',
type: 'boolean',
default: true,
})
.option('networks', {
describe: 'Network to scrape all channels from (overrides configuration)',
type: 'array',
alias: ['network'],
})
.option('exclude-networks', {
describe: 'Network not to scrape any channels from (overrides configuration)',
type: 'array',
alias: 'exclude-network',
})
.option('channels', {
describe: 'Channel to scrape (overrides configuration)',
type: 'array',
alias: ['channel'],
})
.option('exclude-channels', {
describe: 'Channel not to scrape (overrides configuration)',
type: 'array',
alias: 'exclude-channel',
})
}, async (argv: any) => update({
latest: argv.latest,
upcoming: argv.upcoming,
includeChannels: argv.channels,
includeNetworks: argv.networks,
excludeChannels: argv.excludeChannels,
excludeNetworks: argv.excludeNetworks,
}))
.parse();

43
src/entities.ts Normal file
View File

@ -0,0 +1,43 @@
import knex from '#src/knex';
type EntityEntry = {
id: number;
parent_id: number;
slug: string;
name: string;
type: string;
};
type BuildOptions = {
includeChannels?: string[];
includeNetworks?: string[];
excludeChannels?: string[];
excludeNetworks?: string[];
};
function buildEntityTree(entities: EntityEntry[], options: BuildOptions, branch: EntityEntry[], parent: EntityEntry | null): EntityEntry[] {
if (branch.length === 0) {
return [];
}
return branch.map((branchEntity) => ({
...branchEntity,
parent,
children: buildEntityTree(entities, options, entities.filter((entity) => entity.parent_id === branchEntity.id), branchEntity),
}));
}
function filterEntityTree(tree: EntityEntry[], options: BuildOptions) {
// travel bottom up, because include Team Skeet should also include its parent Team Skeet Media, but not its siblings
}
export async function buildEntities(options: BuildOptions = {}) {
const entities: EntityEntry[] = await knex<EntityEntry>('entities');
const entityTree = buildEntityTree(entities, options, entities.filter((entity) => entity.parent_id === null), null);
// TODO: possibly easier to filter the raw entities instead of the tree?!
// TODO: but I need to know what channels belong to what networks, include <channel> shouldn't filter its parent network
const filteredEntityTree = filterEntityTree(entityTree);
console.log(options);
console.log(entityTree);
}

10
src/knex.ts Normal file
View File

@ -0,0 +1,10 @@
import { config } from 'node-config-ts';
import knex, { type Knex } from 'knex';
export default knex({
client: 'pg',
connection: config.database,
// performance overhead, don't use asyncStackTraces in production
asyncStackTraces: process.env.NODE_ENV === 'development',
// debug: process.env.NODE_ENV === 'development',
}) as Knex;

19
src/updates.ts Normal file
View File

@ -0,0 +1,19 @@
import { buildEntities } from '#src/entities';
export async function update(options: {
latest: boolean;
upcoming: boolean;
includeChannels?: string[];
includeNetworks?: string[];
excludeChannels?: string[];
excludeNetworks?: string[];
}) {
console.log('UPDATE', options);
await buildEntities({
includeChannels: options.includeChannels,
includeNetworks: options.includeNetworks,
excludeChannels: options.excludeChannels,
excludeNetworks: options.excludeNetworks,
});
}

16
tsconfig.json Normal file
View File

@ -0,0 +1,16 @@
{
"compilerOptions": {
"strict": true,
"target": "es2022",
"module": "esnext",
"outDir": "dist",
"types": ["node"],
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"baseUrl": ".",
"paths": {
"#src/*": ["./src/*"]
}
}
}