feat: more nsfw flag (#20184)

* feat: add more nsfw flag

* update
This commit is contained in:
Stephen Zhou 2025-09-29 17:41:45 +08:00 committed by GitHub
parent af95d7577d
commit add932bf38
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
136 changed files with 698 additions and 241 deletions

211
eslint-plugins/nsfw-flag.js Normal file
View File

@ -0,0 +1,211 @@
/**
* ESLint 9 plugin to automatically mark NSFW routes with the nsfw flag
*/
const nsfwRoutes = [
'141jav',
'141ppv',
'18comic',
'2048',
'7mmtv',
'8kcos',
'91porn',
'95mm',
'abskoop',
'asiantolick',
'asmr-200',
'booru',
'chikubi',
'chub',
'civitai',
'cool18',
'coomer',
'copymanga',
'cosplaytele',
'dlsite',
'e-hentai',
'ehentai',
'everia',
'fanbox',
'fansly',
'fantia',
'freexcomic',
'furaffinity',
'gelbooru',
'hanime1',
'iwara',
'javbus',
'javdb',
'javlibrary',
'javtiful',
'javtrailers',
'jpxgmn',
'kemono',
'kisskiss',
'komiic',
'konachan',
'koyso',
'laimanhua',
'literotica',
'mangadex',
'manhuagui',
'manyvids',
'missav',
'netflav',
'nhentai',
'olevod',
'oreno3d',
'patreon',
'pixiv',
'pornhub',
'rawkuma',
'sehuatang',
'shuiguopai',
'sis001',
'skeb',
'skebetter',
'spankbang',
't66y',
'uraaka-joshi',
'wnacg',
'xbookcn',
'xmanhua',
'xsijishe',
'yande',
'zaimanhua',
'zodgame',
];
// 检查是否是 NSFW 路由文件
function isNsfwRoute(filePath) {
const normalizedPath = filePath.replaceAll('\\', '/');
return nsfwRoutes.some((nsfwKey) => {
const routePattern = `/lib/routes/${nsfwKey}/`;
return normalizedPath.includes(routePattern);
});
}
export default {
meta: {
name: '@rsshub/nsfw-flag',
version: '1.0.0',
},
configs: {
recommended: {
plugins: {
'@rsshub/nsfw-flag': 'self',
},
rules: {
'@rsshub/nsfw-flag/add-nsfw-flag': 'error',
},
},
},
rules: {
'add-nsfw-flag': {
meta: {
type: 'problem',
docs: {
description: 'Automatically add nsfw flag to NSFW routes',
category: 'Best Practices',
recommended: true,
},
fixable: 'code',
schema: [],
messages: {
missingNsfwFlag: 'NSFW route is missing the nsfw flag in features',
},
},
create(context) {
const filename = context.filename || context.getFilename();
// 如果不是 NSFW 路由,跳过检查
if (!isNsfwRoute(filename)) {
return {};
}
return {
ExportNamedDeclaration(node) {
// 查找 export const route: Route = {...}
if (
node.declaration &&
node.declaration.type === 'VariableDeclaration' &&
node.declaration.declarations &&
node.declaration.declarations[0] &&
node.declaration.declarations[0].id &&
node.declaration.declarations[0].id.name === 'route'
) {
const routeDeclaration = node.declaration.declarations[0];
const routeObject = routeDeclaration.init;
if (routeObject && routeObject.type === 'ObjectExpression') {
let featuresProperty = null;
let nsfwProperty = null;
// 查找 features 属性
for (const prop of routeObject.properties) {
if (prop.type === 'Property' && prop.key && prop.key.name === 'features') {
featuresProperty = prop;
// 在 features 中查找 nsfw 属性
if (prop.value && prop.value.type === 'ObjectExpression') {
for (const featureProp of prop.value.properties) {
if (featureProp.type === 'Property' && featureProp.key && featureProp.key.name === 'nsfw') {
nsfwProperty = featureProp;
break;
}
}
}
break;
}
}
// 检查是否需要添加或修复 nsfw 标志
if (!featuresProperty) {
// 没有 features 属性,需要添加整个 features 对象
context.report({
node: routeObject,
messageId: 'missingNsfwFlag',
fix(fixer) {
// 在对象的最后添加 features 属性
const lastProperty = routeObject.properties.at(-1);
return lastProperty
? fixer.insertTextAfter(lastProperty, ',\n features: {\n nsfw: true,\n }')
: // 空对象的情况
fixer.insertTextAfter(routeObject.properties.length > 0 ? routeObject.properties.at(-1) : routeObject, '\n features: {\n nsfw: true,\n }\n');
},
});
} else if (!nsfwProperty) {
// 有 features 属性但没有 nsfw 属性
context.report({
node: featuresProperty.value,
messageId: 'missingNsfwFlag',
fix(fixer) {
const featuresObject = featuresProperty.value;
if (featuresObject.properties.length > 0) {
const lastFeatureProp = featuresObject.properties.at(-1);
return fixer.insertTextAfter(lastFeatureProp, ',\n nsfw: true');
} else {
// features 是空对象
return fixer.replaceTextRange([featuresObject.range[0] + 1, featuresObject.range[1] - 1], '\n nsfw: true,\n ');
}
},
});
} else if (nsfwProperty.value && (nsfwProperty.value.type !== 'Literal' || nsfwProperty.value.value !== true)) {
// nsfw 属性存在但不是 true
context.report({
node: nsfwProperty.value,
messageId: 'missingNsfwFlag',
fix(fixer) {
return fixer.replaceText(nsfwProperty.value, 'true');
},
});
}
}
}
},
};
},
},
},
};

View File

@ -8,6 +8,7 @@ import tsParser from '@typescript-eslint/parser';
import yamlParser from 'yaml-eslint-parser';
import js from '@eslint/js';
import { FlatCompat } from '@eslint/eslintrc';
// import nsfwFlagPlugin from './eslint-plugins/nsfw-flag.js';
const __dirname = import.meta.dirname;
const compat = new FlatCompat({
@ -15,251 +16,294 @@ const compat = new FlatCompat({
recommendedConfig: js.configs.recommended,
});
export default [{
ignores: [
'**/coverage',
'**/.vscode',
'**/docker-compose.yml',
'!.github',
'assets/build',
'lib/routes-deprecated',
'lib/router.js',
'**/babel.config.js',
'scripts/docker/minify-docker.js',
'dist',
],
}, ...compat.extends(
'eslint:recommended',
'plugin:prettier/recommended',
'plugin:yml/recommended',
'plugin:@typescript-eslint/recommended',
),
n.configs['flat/recommended-script'],
unicorn.configs.recommended,
{
plugins: {
prettier,
'@stylistic': stylistic,
'@typescript-eslint': typescriptEslint,
export default [
// {
// plugins: {
// '@rsshub/nsfw-flag': nsfwFlagPlugin,
// },
// rules: {
// '@rsshub/nsfw-flag/add-nsfw-flag': 'error',
// },
// },
{
ignores: ['**/coverage', '**/.vscode', '**/docker-compose.yml', '!.github', 'assets/build', 'lib/routes-deprecated', 'lib/router.js', '**/babel.config.js', 'scripts/docker/minify-docker.js', 'dist'],
},
languageOptions: {
globals: {
...globals.node,
...globals.browser,
...compat.extends('eslint:recommended', 'plugin:prettier/recommended', 'plugin:yml/recommended', 'plugin:@typescript-eslint/recommended'),
n.configs['flat/recommended-script'],
unicorn.configs.recommended,
{
plugins: {
prettier,
'@stylistic': stylistic,
'@typescript-eslint': typescriptEslint,
},
parser: tsParser,
ecmaVersion: 'latest',
sourceType: 'module',
languageOptions: {
globals: {
...globals.node,
...globals.browser,
},
parser: tsParser,
ecmaVersion: 'latest',
sourceType: 'module',
},
rules: {
// possible problems
'array-callback-return': [
'error',
{
allowImplicit: true,
},
],
'no-await-in-loop': 'error',
'no-control-regex': 'off',
'no-duplicate-imports': 'error',
'no-prototype-builtins': 'off',
// suggestions
'arrow-body-style': 'error',
'block-scoped-var': 'error',
curly: 'error',
'dot-notation': 'error',
eqeqeq: 'error',
'default-case': [
'warn',
{
commentPattern: '^no default$',
},
],
'default-case-last': 'error',
'no-console': 'error',
'no-eval': 'error',
'no-extend-native': 'error',
'no-extra-label': 'error',
'no-implicit-coercion': [
'error',
{
boolean: false,
number: false,
string: false,
disallowTemplateShorthand: true,
},
],
'no-implicit-globals': 'error',
'no-labels': 'error',
'no-multi-str': 'error',
'no-new-func': 'error',
'no-restricted-imports': 'error',
'no-restricted-syntax': [
'error',
{
selector: "CallExpression[callee.property.name='get'][arguments.length=0]",
message: 'Please use .toArray() instead.',
},
{
selector: "CallExpression[callee.property.name='toArray'] MemberExpression[object.callee.property.name='map']",
message: 'Please use .toArray() before .map().',
},
{
selector: 'CallExpression[callee.property.name="catch"] > ArrowFunctionExpression[params.length<=1][body.value=null]',
message: 'Usage of .catch(() => null) is not allowed. Please handle the error appropriately.',
},
],
'no-unneeded-ternary': 'error',
'no-useless-computed-key': 'error',
'no-useless-concat': 'warn',
'no-useless-rename': 'error',
'no-var': 'error',
'object-shorthand': 'error',
'prefer-arrow-callback': 'error',
'prefer-const': 'error',
'prefer-object-has-own': 'error',
'no-useless-escape': 'warn',
'prefer-regex-literals': [
'error',
{
disallowRedundantWrapping: true,
},
],
'require-await': 'error',
// typescript
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-unused-expressions': [
'error',
{
allowShortCircuit: true,
allowTernary: true,
},
],
// unicorn
'unicorn/consistent-function-scoping': 'warn',
'unicorn/explicit-length-check': 'off',
'unicorn/filename-case': [
'error',
{
case: 'kebabCase',
ignore: [String.raw`.*\.(yaml|yml)$`, String.raw`RequestInProgress\.js$`],
},
],
'unicorn/no-array-callback-reference': 'warn',
'unicorn/no-array-reduce': 'warn',
'unicorn/no-array-sort': 'warn',
'unicorn/no-await-expression-member': 'off',
'unicorn/no-empty-file': 'warn',
'unicorn/no-hex-escape': 'warn',
'unicorn/no-null': 'off',
'unicorn/no-object-as-default-parameter': 'warn',
'unicorn/no-nested-ternary': 'off',
'unicorn/no-process-exit': 'off',
'unicorn/no-useless-switch-case': 'off',
'unicorn/no-useless-undefined': [
'error',
{
checkArguments: false,
},
],
'unicorn/numeric-separators-style': [
'warn',
{
onlyIfContainsSeparator: false,
number: {
minimumDigits: 7,
groupLength: 3,
},
binary: {
minimumDigits: 9,
groupLength: 4,
},
octal: {
minimumDigits: 9,
groupLength: 4,
},
hexadecimal: {
minimumDigits: 5,
groupLength: 2,
},
},
],
'unicorn/prefer-code-point': 'warn',
'unicorn/prefer-global-this': 'off',
'unicorn/prefer-import-meta-properties': 'warn',
'unicorn/prefer-module': 'off',
'unicorn/prefer-number-properties': [
'error',
{
checkInfinity: false,
checkNaN: false,
},
],
'unicorn/prefer-spread': 'warn',
'unicorn/prefer-string-slice': 'warn',
'unicorn/prefer-switch': [
'warn',
{
emptyDefaultCase: 'do-nothing-comment',
},
],
'unicorn/prefer-top-level-await': 'off',
'unicorn/prevent-abbreviations': 'off',
'unicorn/switch-case-braces': ['error', 'avoid'],
'unicorn/text-encoding-identifier-case': 'off',
// formatting rules
'@stylistic/arrow-parens': 'error',
'@stylistic/arrow-spacing': 'error',
'@stylistic/comma-spacing': 'error',
'@stylistic/comma-style': 'error',
'@stylistic/function-call-spacing': 'error',
'@stylistic/keyword-spacing': 'error',
'@stylistic/linebreak-style': 'error',
'@stylistic/lines-around-comment': [
'error',
{
beforeBlockComment: false,
},
],
'@stylistic/no-multiple-empty-lines': 'error',
'@stylistic/no-trailing-spaces': 'error',
'@stylistic/rest-spread-spacing': 'error',
'@stylistic/semi': 'error',
'@stylistic/space-before-blocks': 'error',
'@stylistic/space-in-parens': 'error',
'@stylistic/space-infix-ops': 'error',
'@stylistic/space-unary-ops': 'error',
'@stylistic/spaced-comment': 'error',
// https://github.com/eslint-community/eslint-plugin-n
// node specific rules
'n/no-extraneous-require': 'error',
'n/no-deprecated-api': 'warn',
'n/no-missing-import': 'off',
'n/no-missing-require': 'off',
'n/no-process-exit': 'off',
'n/no-unpublished-import': 'off',
'n/no-unpublished-require': [
'error',
{
allowModules: ['tosource'],
},
],
'n/no-unsupported-features/node-builtins': [
'error',
{
version: '>=22.16.0',
ignores: [],
},
],
'prettier/prettier': 'off',
'yml/quotes': [
'error',
{
prefer: 'single',
},
],
'yml/no-empty-mapping-value': 'off',
},
},
rules: {
// possible problems
'array-callback-return': ['error', {
allowImplicit: true,
}],
'no-await-in-loop': 'error',
'no-control-regex': 'off',
'no-duplicate-imports': 'error',
'no-prototype-builtins': 'off',
// suggestions
'arrow-body-style': 'error',
'block-scoped-var': 'error',
'curly': 'error',
'dot-notation': 'error',
'eqeqeq': 'error',
'default-case': ['warn', {
commentPattern: '^no default$',
}],
'default-case-last': 'error',
'no-console': 'error',
'no-eval': 'error',
'no-extend-native': 'error',
'no-extra-label': 'error',
'no-implicit-coercion': ['error', {
boolean: false,
number: false,
string: false,
disallowTemplateShorthand: true,
}],
'no-implicit-globals': 'error',
'no-labels': 'error',
'no-multi-str': 'error',
'no-new-func': 'error',
'no-restricted-imports': 'error',
'no-restricted-syntax': ['error', {
selector: "CallExpression[callee.property.name='get'][arguments.length=0]",
message: "Please use .toArray() instead.",
}, {
selector: "CallExpression[callee.property.name='toArray'] MemberExpression[object.callee.property.name='map']",
message: "Please use .toArray() before .map().",
}, {
selector: 'CallExpression[callee.property.name="catch"] > ArrowFunctionExpression[params.length<=1][body.value=null]',
message: "Usage of .catch(() => null) is not allowed. Please handle the error appropriately."
}],
'no-unneeded-ternary': 'error',
'no-useless-computed-key': 'error',
'no-useless-concat': 'warn',
'no-useless-rename': 'error',
'no-var': 'error',
'object-shorthand': 'error',
'prefer-arrow-callback': 'error',
'prefer-const': 'error',
'prefer-object-has-own': 'error',
'no-useless-escape': 'warn',
'prefer-regex-literals': ['error', {
disallowRedundantWrapping: true,
}],
'require-await': 'error',
// typescript
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-unused-expressions': ['error', {
allowShortCircuit: true,
allowTernary: true,
}],
// unicorn
'unicorn/consistent-function-scoping': 'warn',
'unicorn/explicit-length-check': 'off',
'unicorn/filename-case': ['error', {
case: 'kebabCase',
ignore: [String.raw`.*\.(yaml|yml)$`, String.raw`RequestInProgress\.js$`],
}],
'unicorn/no-array-callback-reference': 'warn',
'unicorn/no-array-reduce': 'warn',
'unicorn/no-array-sort': 'warn',
'unicorn/no-await-expression-member': 'off',
'unicorn/no-empty-file': 'warn',
'unicorn/no-hex-escape': 'warn',
'unicorn/no-null': 'off',
'unicorn/no-object-as-default-parameter': 'warn',
'unicorn/no-nested-ternary': 'off',
'unicorn/no-process-exit': 'off',
'unicorn/no-useless-switch-case': 'off',
'unicorn/no-useless-undefined': ['error', {
checkArguments: false,
}],
'unicorn/numeric-separators-style': ['warn', {
onlyIfContainsSeparator: false,
number: {
minimumDigits: 7,
groupLength: 3,
},
binary: {
minimumDigits: 9,
groupLength: 4,
},
octal: {
minimumDigits: 9,
groupLength: 4,
},
hexadecimal: {
minimumDigits: 5,
groupLength: 2,
},
}],
'unicorn/prefer-code-point': 'warn',
'unicorn/prefer-global-this': 'off',
'unicorn/prefer-import-meta-properties': 'warn',
'unicorn/prefer-module': 'off',
'unicorn/prefer-number-properties': ['error', {
checkInfinity: false,
checkNaN: false,
}],
'unicorn/prefer-spread': 'warn',
'unicorn/prefer-string-slice': 'warn',
'unicorn/prefer-switch': ['warn', {
emptyDefaultCase: 'do-nothing-comment',
}],
'unicorn/prefer-top-level-await': 'off',
'unicorn/prevent-abbreviations': 'off',
'unicorn/switch-case-braces': ['error', 'avoid'],
'unicorn/text-encoding-identifier-case': 'off',
// formatting rules
'@stylistic/arrow-parens': 'error',
'@stylistic/arrow-spacing': 'error',
'@stylistic/comma-spacing': 'error',
'@stylistic/comma-style': 'error',
'@stylistic/function-call-spacing': 'error',
'@stylistic/keyword-spacing': 'error',
'@stylistic/linebreak-style': 'error',
'@stylistic/lines-around-comment': ['error', {
beforeBlockComment: false,
}],
'@stylistic/no-multiple-empty-lines': 'error',
'@stylistic/no-trailing-spaces': 'error',
'@stylistic/rest-spread-spacing': 'error',
'@stylistic/semi': 'error',
'@stylistic/space-before-blocks': 'error',
'@stylistic/space-in-parens': 'error',
'@stylistic/space-infix-ops': 'error',
'@stylistic/space-unary-ops': 'error',
'@stylistic/spaced-comment': 'error',
// https://github.com/eslint-community/eslint-plugin-n
// node specific rules
'n/no-extraneous-require': 'error',
'n/no-deprecated-api': 'warn',
'n/no-missing-import': 'off',
'n/no-missing-require': 'off',
'n/no-process-exit': 'off',
'n/no-unpublished-import': 'off',
'n/no-unpublished-require': ['error', {
allowModules: ['tosource'],
}],
"n/no-unsupported-features/node-builtins": ["error", {
"version": ">=22.16.0",
"ignores": []
}],
'prettier/prettier': 'off',
'yml/quotes': ['error', {
prefer: 'single',
}],
'yml/no-empty-mapping-value': 'off',
},
}, {
{
files: ['.puppeteerrc.cjs'],
rules: {
'@typescript-eslint/no-require-imports': 'off',
},
}, {
},
{
files: ['**/*.yaml', '**/*.yml'],
languageOptions: {
@ -267,8 +311,12 @@ unicorn.configs.recommended,
},
rules: {
'lines-around-comment': ['error', {
beforeBlockComment: false,
}],
'lines-around-comment': [
'error',
{
beforeBlockComment: false,
},
],
},
}];
},
];

View File

@ -16,6 +16,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -18,6 +18,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -23,6 +23,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -16,6 +16,9 @@ export const route: Route = {
maintainers: [],
handler,
url: '8kcosplay.com/',
features: {
nsfw: true,
},
};
async function handler(ctx) {

View File

@ -18,6 +18,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -16,6 +16,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -20,6 +20,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -13,6 +13,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -13,6 +13,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -16,6 +16,9 @@ export const route: Route = {
maintainers: ['zhenhappy'],
handler,
url: 'ahhhhfs.com/',
features: {
nsfw: true,
},
};
async function handler(ctx) {

View File

@ -14,6 +14,9 @@ export const route: Route = {
maintainers: ['zhenhappy'],
handler,
url: 'ahhhhfs.com/',
features: {
nsfw: true,
},
};
async function handler(ctx) {

View File

@ -19,6 +19,9 @@ export const route: Route = {
maintainers: [],
handler,
url: 'asiantolick.com/',
features: {
nsfw: true,
},
};
async function handler(ctx) {

View File

@ -19,6 +19,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
parameters: {
order: '排序字段,默认按照资源的收录日期来排序,详见下表',

View File

@ -21,6 +21,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -13,6 +13,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
name: 'Category',
maintainers: ['SnowAgar25'],

View File

@ -14,6 +14,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
name: 'Navigation',
maintainers: ['SnowAgar25'],

View File

@ -14,6 +14,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
name: '動画カテゴリー',
maintainers: ['SnowAgar25'],

View File

@ -14,6 +14,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
name: 'AVメーカー',
maintainers: ['SnowAgar25'],

View File

@ -14,6 +14,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
name: 'Search',
maintainers: ['SnowAgar25'],

View File

@ -9,6 +9,9 @@ export const route: Route = {
name: 'Characters',
maintainers: ['flameleaf'],
handler,
features: {
nsfw: true,
},
};
async function handler() {

View File

@ -21,6 +21,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -14,6 +14,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -16,6 +16,9 @@ export const route: Route = {
name: 'User Article',
maintainers: ['TonyRL'],
handler,
features: {
nsfw: true,
},
};
async function handler(ctx) {

View File

@ -21,6 +21,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
name: '漫画更新',
maintainers: ['btdwv', 'marvolo666'],

View File

@ -17,6 +17,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -17,6 +17,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -17,6 +17,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -17,6 +17,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -141,6 +141,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
name: 'Discounted Works',
maintainers: ['cssxsh'],

View File

@ -18,6 +18,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -69,6 +69,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
name: 'Current Release',
maintainers: ['cssxsh'],

View File

@ -6,6 +6,9 @@ export const route: Route = {
name: 'Unknown',
maintainers: [],
handler,
features: {
nsfw: true,
},
};
async function handler(ctx) {

View File

@ -20,6 +20,7 @@ export const route: Route = {
supportBT: true,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
name: 'Favorites',
maintainers: ['yindaheng98', 'syrinka'],

View File

@ -14,6 +14,7 @@ export const route: Route = {
supportBT: true,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
name: 'Search',
maintainers: ['yindaheng98', 'syrinka'],

View File

@ -19,6 +19,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -17,6 +17,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -17,6 +17,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
name: 'Search',
maintainers: ['KTachibanaM', 'AiraNadih'],

View File

@ -19,6 +19,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -14,6 +14,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -86,6 +86,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
name: 'Search',
maintainers: ['nczitzk'],

View File

@ -17,6 +17,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -20,6 +20,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -16,6 +16,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -16,6 +16,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -19,6 +19,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -16,6 +16,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -16,6 +16,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -20,6 +20,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -16,6 +16,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -16,6 +16,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -16,6 +16,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -16,6 +16,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -16,6 +16,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -16,6 +16,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -37,6 +37,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -17,6 +17,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -92,6 +92,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
handler,
};

View File

@ -41,6 +41,9 @@ export const route: Route = {
name: 'Unknown',
maintainers: ['Fatpandac'],
handler,
features: {
nsfw: true,
},
};
async function handler(ctx) {

View File

@ -33,6 +33,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -19,6 +19,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -31,6 +31,9 @@ export const route: Route = {
| | | | |
| ---- | ------ | ------ | ------ |
| 0 | 1 | 2 | 3 |`,
features: {
nsfw: true,
},
};
async function handler(ctx) {

View File

@ -13,6 +13,9 @@ export const route: Route = {
maintainers: ['dddepg'],
handler,
url: 'javdb.com/',
features: {
nsfw: true,
},
};
async function handler(ctx) {

View File

@ -19,6 +19,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -19,6 +19,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -19,6 +19,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -19,6 +19,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -19,6 +19,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -10,6 +10,9 @@ export const route: Route = {
description: `| Last Month | All Time |
| ---------- | -------- |
| 1 | 2 |`,
features: {
nsfw: true,
},
};
async function handler(ctx) {

View File

@ -14,6 +14,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
name: 'Best Reviews',
maintainers: ['nczitzk'],

View File

@ -14,6 +14,9 @@ export const route: Route = {
::: tip
See [Categories](https://www.javlibrary.com/en/genres.php) to view all categories.
:::`,
features: {
nsfw: true,
},
};
async function handler(ctx) {

View File

@ -14,6 +14,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
name: 'Videos by makers',
maintainers: [],

View File

@ -10,6 +10,9 @@ export const route: Route = {
description: `| Last Month | All Time |
| ---------- | -------- |
| 1 | 2 |`,
features: {
nsfw: true,
},
};
async function handler(ctx) {

View File

@ -7,6 +7,9 @@ export const route: Route = {
name: 'Unknown',
maintainers: [],
handler,
features: {
nsfw: true,
},
};
async function handler(ctx) {

View File

@ -10,6 +10,9 @@ export const route: Route = {
description: `| videos with comments (by date) | everything (by date) |
| ------------------------------ | -------------------- |
| 1 | 2 |`,
features: {
nsfw: true,
},
};
async function handler(ctx) {

View File

@ -7,6 +7,9 @@ export const route: Route = {
name: 'Unknown',
maintainers: [],
handler,
features: {
nsfw: true,
},
};
async function handler(ctx) {

View File

@ -10,6 +10,9 @@ export const route: Route = {
description: `| Wanted | Watched | Owned |
| ---------- | ----------- | --------- |
| userwanted | userwatched | userowned |`,
features: {
nsfw: true,
},
};
async function handler(ctx) {

View File

@ -18,6 +18,9 @@ export const route: Route = {
maintainers: ['TonyRL'],
url: 'javtrailers.com/categories',
handler,
features: {
nsfw: true,
},
};
async function handler(ctx) {

View File

@ -17,6 +17,9 @@ export const route: Route = {
name: 'Studios',
maintainers: ['TonyRL'],
handler,
features: {
nsfw: true,
},
};
async function handler(ctx) {

View File

@ -13,6 +13,9 @@ export const route: Route = {
name: '搜索',
maintainers: ['Urabartin'],
handler,
features: {
nsfw: true,
},
};
async function handler(ctx) {

View File

@ -18,6 +18,9 @@ export const route: Route = {
name: '本周热门',
maintainers: ['Urabartin'],
handler,
features: {
nsfw: true,
},
};
async function handler() {

View File

@ -18,6 +18,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -14,6 +14,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -241,6 +241,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -16,6 +16,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -14,6 +14,9 @@ export const route: Route = {
name: 'Unknown',
maintainers: ['nczitzk'],
handler,
features: {
nsfw: true,
},
};
async function handler(ctx) {

View File

@ -13,6 +13,9 @@ export const route: Route = {
maintainers: ['vzz64', 'chrisis58'],
example: '/mangadex/manga/f98660a1-d2e2-461c-960d-7bd13df8b76d/en',
handler,
features: {
nsfw: true,
},
};
async function handler(ctx) {

View File

@ -59,6 +59,7 @@ export const route: Route = {
optional: true,
},
],
nsfw: true,
},
handler,
};

View File

@ -56,6 +56,7 @@ export const route: Route = {
optional: true,
},
],
nsfw: true,
},
handler,
};

View File

@ -84,6 +84,7 @@ It's recommended to use the \`/mangadex/mdlist/:listId?\` route instead for bett
optional: true,
},
],
nsfw: true,
},
handler,
};

View File

@ -55,6 +55,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -26,6 +26,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
radar: [
{

View File

@ -17,6 +17,9 @@ export const route: Route = {
example: '/manyvids/profile/vids/1001213004',
maintainers: ['TonyRL'],
handler,
features: {
nsfw: true,
},
};
const getProfileById = (uid: string) => cache.tryGet(`manyvids:profile:${uid}`, () => ofetch(`https://www.manyvids.com/bff/profile/profiles/${uid}`)) as Promise<UserProfile>;

View File

@ -18,6 +18,9 @@ export const route: Route = {
maintainers: ['TonyRL'],
handler,
url: 'netflav.com/',
features: {
nsfw: true,
},
};
async function handler() {

View File

@ -12,6 +12,7 @@ export const route: Route = {
features: {
antiCrawler: true,
supportBT: true,
nsfw: true,
},
radar: [
{

View File

@ -16,6 +16,9 @@ export const route: Route = {
name: '视频',
maintainers: ['fang63625'],
handler,
features: {
nsfw: true,
},
};
async function handler(ctx) {

View File

@ -15,6 +15,9 @@ export const route: Route = {
name: '最新视频',
maintainers: ['fang63625'],
handler,
features: {
nsfw: true,
},
};
async function handler() {

View File

@ -49,6 +49,7 @@ export const route: Route = {
supportBT: false,
supportPodcast: false,
supportScihub: false,
nsfw: true,
},
name: 'Author Search',
maintainers: ['xueli_sherryli'],

View File

@ -22,6 +22,7 @@ export const route: Route = {
description: 'The value of the session_id cookie after logging in to Patreon, required to access paid posts',
},
],
nsfw: true,
},
radar: [
{

Some files were not shown because too many files have changed in this diff Show More