mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-06-13 01:22:11 +00:00
refactor: Import version, bugs URL and repo URL directly from package.json (#1004)
This commit is contained in:
13
package-lock.json
generated
13
package-lock.json
generated
@@ -30,7 +30,6 @@
|
|||||||
"rimraf": "^6.0.1",
|
"rimraf": "^6.0.1",
|
||||||
"ts-patch": "^3.0.2",
|
"ts-patch": "^3.0.2",
|
||||||
"ts-proto": "^2.2.0",
|
"ts-proto": "^2.2.0",
|
||||||
"ts-transformer-inline-file": "^0.2.0",
|
|
||||||
"typedoc": "^0.26.7",
|
"typedoc": "^0.26.7",
|
||||||
"typedoc-plugin-markdown": "^4.2.7",
|
"typedoc-plugin-markdown": "^4.2.7",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.0.0",
|
||||||
@@ -4919,18 +4918,6 @@
|
|||||||
"@bufbuild/protobuf": "^2.0.0"
|
"@bufbuild/protobuf": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ts-transformer-inline-file": {
|
|
||||||
"version": "0.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ts-transformer-inline-file/-/ts-transformer-inline-file-0.2.0.tgz",
|
|
||||||
"integrity": "sha512-16IBBt0d8MxVdEyxDEFx2+EetgELWs/kUu4FEaL4b1dffu5gB/aw+CwVPW3EzsuO67g8OAF34LcDHsyxQp16Aw==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"typescript": "^4.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/type-check": {
|
"node_modules/type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
"react-native": "./dist/src/platform/react-native.js",
|
"react-native": "./dist/src/platform/react-native.js",
|
||||||
"default": "./dist/src/platform/web.js"
|
"default": "./dist/src/platform/web.js"
|
||||||
},
|
},
|
||||||
|
"./package.json": "./package.json",
|
||||||
"./agnostic": {
|
"./agnostic": {
|
||||||
"types": "./dist/src/platform/lib.d.ts",
|
"types": "./dist/src/platform/lib.d.ts",
|
||||||
"default": "./dist/src/platform/lib.js"
|
"default": "./dist/src/platform/lib.js"
|
||||||
@@ -122,7 +123,6 @@
|
|||||||
"rimraf": "^6.0.1",
|
"rimraf": "^6.0.1",
|
||||||
"ts-patch": "^3.0.2",
|
"ts-patch": "^3.0.2",
|
||||||
"ts-proto": "^2.2.0",
|
"ts-proto": "^2.2.0",
|
||||||
"ts-transformer-inline-file": "^0.2.0",
|
|
||||||
"typedoc": "^0.26.7",
|
"typedoc": "^0.26.7",
|
||||||
"typedoc-plugin-markdown": "^4.2.7",
|
"typedoc-plugin-markdown": "^4.2.7",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.0.0",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
Platform,
|
Platform,
|
||||||
PlayerError
|
PlayerError
|
||||||
} from '../utils/Utils.js';
|
} from '../utils/Utils.js';
|
||||||
|
import packageInfo from '../../package.json' with { type: 'json' };
|
||||||
|
|
||||||
const TAG = 'Player';
|
const TAG = 'Player';
|
||||||
|
|
||||||
@@ -199,7 +200,7 @@ export default class Player {
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const current_library_version = parseInt(Platform.shim.info.version.split('.')[0]);
|
const current_library_version = parseInt(packageInfo.version.split('.', 1)[0]);
|
||||||
const player_data = BinarySerializer.deserialize<SerializablePlayer>(new Uint8Array(buffer));
|
const player_data = BinarySerializer.deserialize<SerializablePlayer>(new Uint8Array(buffer));
|
||||||
|
|
||||||
if (player_data.library_version !== current_library_version) {
|
if (player_data.library_version !== current_library_version) {
|
||||||
@@ -224,7 +225,7 @@ export default class Player {
|
|||||||
if (!cache || !this.sig_sc || !this.nsig_sc)
|
if (!cache || !this.sig_sc || !this.nsig_sc)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const current_library_version = parseInt(Platform.shim.info.version.split('.')[0]);
|
const current_library_version = parseInt(packageInfo.version.split('.', 1)[0]);
|
||||||
|
|
||||||
const buffer = BinarySerializer.serialize({
|
const buffer = BinarySerializer.serialize({
|
||||||
player_id: this.player_id,
|
player_id: this.player_id,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
generateRandomString, getRandomUserAgent,
|
generateRandomString, getRandomUserAgent,
|
||||||
InnertubeError, Platform, SessionError
|
InnertubeError, Platform, SessionError
|
||||||
} from '../utils/Utils.js';
|
} from '../utils/Utils.js';
|
||||||
|
import packageInfo from '../../package.json' with { type: 'json' };
|
||||||
|
|
||||||
import type { DeviceCategory } from '../utils/Utils.js';
|
import type { DeviceCategory } from '../utils/Utils.js';
|
||||||
import type { FetchFunction, ICache } from '../types/index.js';
|
import type { FetchFunction, ICache } from '../types/index.js';
|
||||||
@@ -332,7 +333,7 @@ export default class Session extends EventEmitter {
|
|||||||
try {
|
try {
|
||||||
const session_data = BinarySerializer.deserialize<SerializableSession>(new Uint8Array(buffer));
|
const session_data = BinarySerializer.deserialize<SerializableSession>(new Uint8Array(buffer));
|
||||||
|
|
||||||
if (session_data.library_version !== parseInt(Platform.shim.info.version.split('.')[0])) {
|
if (session_data.library_version !== parseInt(packageInfo.version.split('.', 1)[0])) {
|
||||||
Log.warn(TAG, `Cached session data is from a different library version (${session_data.library_version}). Regenerating session data.`);
|
Log.warn(TAG, `Cached session data is from a different library version (${session_data.library_version}). Regenerating session data.`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -517,7 +518,7 @@ export default class Session extends EventEmitter {
|
|||||||
|
|
||||||
const buffer = BinarySerializer.serialize({
|
const buffer = BinarySerializer.serialize({
|
||||||
...session_data,
|
...session_data,
|
||||||
library_version: parseInt(Platform.shim.info.version)
|
library_version: parseInt(packageInfo.version)
|
||||||
});
|
});
|
||||||
|
|
||||||
await cache.set('innertube_session_data', buffer);
|
await cache.set('innertube_session_data', buffer);
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import * as YTNodes from './nodes.js';
|
import * as YTNodes from './nodes.js';
|
||||||
import { InnertubeError, ParsingError, Platform } from '../utils/Utils.js';
|
import { InnertubeError, ParsingError } from '../utils/Utils.js';
|
||||||
import type { ObservedArray, YTNode, YTNodeConstructor } from './helpers.js';
|
import type { ObservedArray, YTNode, YTNodeConstructor } from './helpers.js';
|
||||||
import { Memo, observe, SuperParsedResult } from './helpers.js';
|
import { Memo, observe, SuperParsedResult } from './helpers.js';
|
||||||
import type { KeyInfo } from './generator.js';
|
import type { KeyInfo } from './generator.js';
|
||||||
import { camelToSnake, generateRuntimeClass, generateTypescriptClass } from './generator.js';
|
import { camelToSnake, generateRuntimeClass, generateTypescriptClass } from './generator.js';
|
||||||
import { Log } from '../utils/index.js';
|
import { Log } from '../utils/index.js';
|
||||||
|
import packageInfo from '../../package.json' with { type: 'json' };
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Continuation,
|
Continuation,
|
||||||
@@ -102,7 +103,7 @@ let ERROR_HANDLER: ParserErrorHandler = ({ classname, ...context }: ParserError)
|
|||||||
Log.warn(TAG,
|
Log.warn(TAG,
|
||||||
new InnertubeError(
|
new InnertubeError(
|
||||||
`Something went wrong at ${classname}!\n` +
|
`Something went wrong at ${classname}!\n` +
|
||||||
`This is a bug, please report it at ${Platform.shim.info.bugs_url}`, {
|
`This is a bug, please report it at ${packageInfo.bugs.url}`, {
|
||||||
stack: context.error.stack,
|
stack: context.error.stack,
|
||||||
classdata: JSON.stringify(context.classdata, null, 2)
|
classdata: JSON.stringify(context.classdata, null, 2)
|
||||||
}
|
}
|
||||||
@@ -122,7 +123,7 @@ let ERROR_HANDLER: ParserErrorHandler = ({ classname, ...context }: ParserError)
|
|||||||
Log.warn(TAG,
|
Log.warn(TAG,
|
||||||
new InnertubeError(
|
new InnertubeError(
|
||||||
`Mutation data required for processing ${classname}, but none found.\n` +
|
`Mutation data required for processing ${classname}, but none found.\n` +
|
||||||
`This is a bug, please report it at ${Platform.shim.info.bugs_url}`
|
`This is a bug, please report it at ${packageInfo.bugs.url}`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
@@ -131,7 +132,7 @@ let ERROR_HANDLER: ParserErrorHandler = ({ classname, ...context }: ParserError)
|
|||||||
new InnertubeError(
|
new InnertubeError(
|
||||||
`Mutation data missing or invalid for ${context.failed} out of ${context.total} MusicMultiSelectMenuItems. ` +
|
`Mutation data missing or invalid for ${context.failed} out of ${context.total} MusicMultiSelectMenuItems. ` +
|
||||||
`The titles of the failed items are: ${context.titles.join(', ')}.\n` +
|
`The titles of the failed items are: ${context.titles.join(', ')}.\n` +
|
||||||
`This is a bug, please report it at ${Platform.shim.info.bugs_url}`
|
`This is a bug, please report it at ${packageInfo.bugs.url}`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
@@ -139,7 +140,7 @@ let ERROR_HANDLER: ParserErrorHandler = ({ classname, ...context }: ParserError)
|
|||||||
Log.warn(TAG,
|
Log.warn(TAG,
|
||||||
new InnertubeError(
|
new InnertubeError(
|
||||||
`${classname} not found!\n` +
|
`${classname} not found!\n` +
|
||||||
`This is a bug, want to help us fix it? Follow the instructions at ${Platform.shim.info.repo_url}/blob/main/docs/updating-the-parser.md or report it at ${Platform.shim.info.bugs_url}!\n` +
|
`This is a bug, want to help us fix it? Follow the instructions at ${packageInfo.homepage.split('#', 1)[0]}/blob/main/docs/updating-the-parser.md or report it at ${packageInfo.bugs.url}!\n` +
|
||||||
`Introspected and JIT generated this class in the meantime:\n${generateTypescriptClass(classname, context.key_info)}`
|
`Introspected and JIT generated this class in the meantime:\n${generateTypescriptClass(classname, context.key_info)}`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
import type { ICache } from '../types/Cache.js';
|
import type { ICache } from '../types/Cache.js';
|
||||||
import { Platform } from '../utils/Utils.js';
|
import { Platform } from '../utils/Utils.js';
|
||||||
import evaluate from './jsruntime/jinter.js';
|
import evaluate from './jsruntime/jinter.js';
|
||||||
import { $INLINE_JSON } from 'ts-transformer-inline-file';
|
|
||||||
import sha1Hash from './polyfills/web-crypto.js';
|
import sha1Hash from './polyfills/web-crypto.js';
|
||||||
|
|
||||||
const { homepage, version, bugs } = $INLINE_JSON('../../package.json');
|
|
||||||
const repo_url = homepage?.split('#')[0];
|
|
||||||
|
|
||||||
class Cache implements ICache {
|
class Cache implements ICache {
|
||||||
#persistent_directory: string;
|
#persistent_directory: string;
|
||||||
#persistent: boolean;
|
#persistent: boolean;
|
||||||
@@ -44,11 +40,6 @@ class Cache implements ICache {
|
|||||||
|
|
||||||
Platform.load({
|
Platform.load({
|
||||||
runtime: 'cf-worker',
|
runtime: 'cf-worker',
|
||||||
info: {
|
|
||||||
version: version,
|
|
||||||
bugs_url: bugs?.url || `${repo_url}/issues`,
|
|
||||||
repo_url
|
|
||||||
},
|
|
||||||
server: true,
|
server: true,
|
||||||
Cache: Cache,
|
Cache: Cache,
|
||||||
sha1Hash,
|
sha1Hash,
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import type { ICache } from '../types/Cache.js';
|
|||||||
import { Platform } from '../utils/Utils.js';
|
import { Platform } from '../utils/Utils.js';
|
||||||
import evaluate from './jsruntime/jinter.js';
|
import evaluate from './jsruntime/jinter.js';
|
||||||
import sha1Hash from './polyfills/web-crypto.js';
|
import sha1Hash from './polyfills/web-crypto.js';
|
||||||
import package_json from '../../package.json' with { type: 'json' };
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -82,11 +81,6 @@ class Cache implements ICache {
|
|||||||
|
|
||||||
Platform.load({
|
Platform.load({
|
||||||
runtime: 'deno',
|
runtime: 'deno',
|
||||||
info: {
|
|
||||||
version: package_json.version,
|
|
||||||
bugs_url: package_json.bugs.url,
|
|
||||||
repo_url: package_json.homepage.split('#')[0]
|
|
||||||
},
|
|
||||||
server: true,
|
server: true,
|
||||||
Cache: Cache,
|
Cache: Cache,
|
||||||
sha1Hash,
|
sha1Hash,
|
||||||
|
|||||||
@@ -18,15 +18,11 @@ import fs from 'fs/promises';
|
|||||||
import CustomEvent from './polyfills/node-custom-event.js';
|
import CustomEvent from './polyfills/node-custom-event.js';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import evaluate from './jsruntime/jinter.js';
|
import evaluate from './jsruntime/jinter.js';
|
||||||
import { $INLINE_JSON } from 'ts-transformer-inline-file';
|
|
||||||
|
|
||||||
const meta_url = import.meta.url;
|
const meta_url = import.meta.url;
|
||||||
const is_cjs = !meta_url;
|
const is_cjs = !meta_url;
|
||||||
const __dirname__ = is_cjs ? __dirname : path.dirname(fileURLToPath(meta_url));
|
const __dirname__ = is_cjs ? __dirname : path.dirname(fileURLToPath(meta_url));
|
||||||
|
|
||||||
const { homepage, version, bugs } = $INLINE_JSON('../../package.json');
|
|
||||||
const repo_url = homepage?.split('#')[0];
|
|
||||||
|
|
||||||
class Cache implements ICache {
|
class Cache implements ICache {
|
||||||
#persistent_directory: string;
|
#persistent_directory: string;
|
||||||
#persistent: boolean;
|
#persistent: boolean;
|
||||||
@@ -100,11 +96,6 @@ class Cache implements ICache {
|
|||||||
|
|
||||||
Platform.load({
|
Platform.load({
|
||||||
runtime: 'node',
|
runtime: 'node',
|
||||||
info: {
|
|
||||||
version: version,
|
|
||||||
bugs_url: bugs?.url || `${repo_url}/issues`,
|
|
||||||
repo_url
|
|
||||||
},
|
|
||||||
server: true,
|
server: true,
|
||||||
Cache: Cache,
|
Cache: Cache,
|
||||||
sha1Hash: async (data: string) => {
|
sha1Hash: async (data: string) => {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import type { ICache } from '../types/Cache.js';
|
import type { ICache } from '../types/Cache.js';
|
||||||
import { Platform } from '../utils/Utils.js';
|
import { Platform } from '../utils/Utils.js';
|
||||||
import sha1Hash from './polyfills/web-crypto.js';
|
import sha1Hash from './polyfills/web-crypto.js';
|
||||||
import package_json from '../../package.json' with { type: 'json' };
|
|
||||||
import evaluate from './jsruntime/jinter.js';
|
import evaluate from './jsruntime/jinter.js';
|
||||||
|
|
||||||
class Cache implements ICache {
|
class Cache implements ICache {
|
||||||
@@ -42,11 +41,6 @@ class Cache implements ICache {
|
|||||||
Platform.load({
|
Platform.load({
|
||||||
runtime: 'react-native',
|
runtime: 'react-native',
|
||||||
server: false,
|
server: false,
|
||||||
info: {
|
|
||||||
version: package_json.version,
|
|
||||||
bugs_url: package_json.bugs.url,
|
|
||||||
repo_url: package_json.homepage.split('#')[0]
|
|
||||||
},
|
|
||||||
Cache: Cache,
|
Cache: Cache,
|
||||||
sha1Hash,
|
sha1Hash,
|
||||||
uuidv4() {
|
uuidv4() {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import type { ICache } from '../types/Cache.js';
|
import type { ICache } from '../types/Cache.js';
|
||||||
import { Platform } from '../utils/Utils.js';
|
import { Platform } from '../utils/Utils.js';
|
||||||
import sha1Hash from './polyfills/web-crypto.js';
|
import sha1Hash from './polyfills/web-crypto.js';
|
||||||
import package_json from '../../package.json' assert { type: 'json' };
|
|
||||||
import evaluate from './jsruntime/jinter.js';
|
import evaluate from './jsruntime/jinter.js';
|
||||||
import * as Log from '../utils/Log.js';
|
import * as Log from '../utils/Log.js';
|
||||||
|
|
||||||
@@ -95,11 +94,6 @@ class Cache implements ICache {
|
|||||||
Platform.load({
|
Platform.load({
|
||||||
runtime: 'browser',
|
runtime: 'browser',
|
||||||
server: false,
|
server: false,
|
||||||
info: {
|
|
||||||
version: package_json.version,
|
|
||||||
bugs_url: package_json.bugs.url,
|
|
||||||
repo_url: package_json.homepage.split('#')[0]
|
|
||||||
},
|
|
||||||
Cache: Cache,
|
Cache: Cache,
|
||||||
sha1Hash,
|
sha1Hash,
|
||||||
uuidv4() {
|
uuidv4() {
|
||||||
|
|||||||
@@ -8,11 +8,6 @@ export type VMPrimative = string | number | boolean | null | undefined;
|
|||||||
|
|
||||||
interface PlatformShim {
|
interface PlatformShim {
|
||||||
runtime: Runtime;
|
runtime: Runtime;
|
||||||
info: {
|
|
||||||
version: string,
|
|
||||||
bugs_url: string,
|
|
||||||
repo_url: string
|
|
||||||
},
|
|
||||||
server: boolean;
|
server: boolean;
|
||||||
Cache: ICacheConstructor;
|
Cache: ICacheConstructor;
|
||||||
sha1Hash(data: string): Promise<string>;
|
sha1Hash(data: string): Promise<string>;
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import type { StoryboardData } from '../parser/classes/PlayerStoryboardSpec.js';
|
import type { StoryboardData } from '../parser/classes/PlayerStoryboardSpec.js';
|
||||||
import PlayerStoryboardSpec from '../parser/classes/PlayerStoryboardSpec.js';
|
import PlayerStoryboardSpec from '../parser/classes/PlayerStoryboardSpec.js';
|
||||||
import { getStringBetweenStrings, InnertubeError, Platform } from './Utils.js';
|
import { getStringBetweenStrings, InnertubeError } from './Utils.js';
|
||||||
import * as Constants from './Constants.js';
|
import * as Constants from './Constants.js';
|
||||||
import * as Log from './Log.js';
|
import * as Log from './Log.js';
|
||||||
|
import packageInfo from '../../package.json' with { type: 'json' };
|
||||||
|
|
||||||
import type Actions from '../core/Actions.js';
|
import type Actions from '../core/Actions.js';
|
||||||
import type Player from '../core/Player.js';
|
import type Player from '../core/Player.js';
|
||||||
@@ -543,7 +544,7 @@ function getColorInfo(format: Format) {
|
|||||||
anonymisedFormat.cipher = 'REDACTED';
|
anonymisedFormat.cipher = 'REDACTED';
|
||||||
|
|
||||||
Log.warn(TAG_, `Unknown matrix coefficients "${color_info.matrix_coefficients}". The DASH manifest is still usable without this.\n`
|
Log.warn(TAG_, `Unknown matrix coefficients "${color_info.matrix_coefficients}". The DASH manifest is still usable without this.\n`
|
||||||
+ `Please report it at ${Platform.shim.info.bugs_url} so we can add support for it.\n`
|
+ `Please report it at ${packageInfo.bugs.url} so we can add support for it.\n`
|
||||||
+ `InnerTube client: ${url.searchParams.get('c')}\nformat:`, anonymisedFormat);
|
+ `InnerTube client: ${url.searchParams.get('c')}\nformat:`, anonymisedFormat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { Memo } from '../parser/helpers.js';
|
|||||||
import { Text } from '../parser/misc.js';
|
import { Text } from '../parser/misc.js';
|
||||||
import * as Log from './Log.js';
|
import * as Log from './Log.js';
|
||||||
import userAgents from './user-agents.js';
|
import userAgents from './user-agents.js';
|
||||||
|
import packageInfo from '../../package.json' with { type: 'json' };
|
||||||
|
|
||||||
const TAG_ = 'Utils';
|
const TAG_ = 'Utils';
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ export class InnertubeError extends Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.date = new Date();
|
this.date = new Date();
|
||||||
this.version = Platform.shim.info.version;
|
this.version = packageInfo.version;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -100,7 +100,6 @@
|
|||||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||||
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
|
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
|
||||||
"plugins": [
|
"plugins": [
|
||||||
{ "transform": "ts-transformer-inline-file/transformer" },
|
|
||||||
{ "transform": "./dev-scripts/enum-optimising-transformer.cjs" }
|
{ "transform": "./dev-scripts/enum-optimising-transformer.cjs" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user