Merge branch 'main' into dependabot/npm_and_yarn/array-move-4.0.0
This commit is contained in:
@@ -1,7 +0,0 @@
|
||||
contact_links:
|
||||
- name: ❔ Questions / Help
|
||||
url: https://github.com/eritislami/evobot/discussions
|
||||
about: Please use GitHub Discussions for help and questions, do not start an issue.
|
||||
- name: 📃 Discord.js Guide
|
||||
url: https://discordjs.guide/
|
||||
about: The official guide for discord.js
|
||||
+5
-5
@@ -1,7 +1,7 @@
|
||||
ARG NODE_VERSION=18.18.2-slim
|
||||
FROM node:${NODE_VERSION} as base
|
||||
|
||||
ENV USER=evobot
|
||||
ENV USER=Apollo
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends python3 build-essential && \
|
||||
@@ -9,10 +9,10 @@ RUN apt-get update && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN groupadd -r ${USER} && \
|
||||
useradd --create-home --home /home/evobot -r -g ${USER} ${USER}
|
||||
useradd --create-home --home /home/apollo -r -g ${USER} ${USER}
|
||||
|
||||
USER ${USER}
|
||||
WORKDIR /home/evobot
|
||||
WORKDIR /home/apollo
|
||||
|
||||
FROM base as build
|
||||
|
||||
@@ -26,7 +26,7 @@ RUN rm -rf node_modules && \
|
||||
FROM node:${NODE_VERSION} as prod
|
||||
|
||||
COPY --chown=${USER}:${USER} package*.json ./
|
||||
COPY --from=build --chown=${USER}:${USER} /home/evobot/node_modules ./node_modules
|
||||
COPY --from=build --chown=${USER}:${USER} /home/evobot/dist ./dist
|
||||
COPY --from=build --chown=${USER}:${USER} /home/apollo/node_modules ./node_modules
|
||||
COPY --from=build --chown=${USER}:${USER} /home/apollo/dist ./dist
|
||||
|
||||
CMD [ "node", "./dist/index.js" ]
|
||||
@@ -1,15 +1,5 @@
|
||||

|
||||

|
||||
[](http://commitizen.github.io/cz-cli/)
|
||||
|
||||

|
||||
|
||||
# 🤖 EvoBot (Discord Music Bot)
|
||||
|
||||
> EvoBot is a Discord Music Bot built with TypeScript, discord.js & uses Command Handler from [discordjs.guide](https://discordjs.guide)
|
||||
|
||||
## Requirements
|
||||
|
||||
<h1> Hello </h1>
|
||||
1. Discord Bot Token **[Guide](https://discordjs.guide/preparations/setting-up-a-bot-application.html#creating-your-bot)**
|
||||
1.1. Enable 'Message Content Intent' in Discord Developer Portal
|
||||
2. Node.js 16.11.0 or newer
|
||||
@@ -17,8 +7,6 @@
|
||||
## 🚀 Getting Started
|
||||
|
||||
```sh
|
||||
git clone https://github.com/eritislami/evobot.git
|
||||
cd evobot
|
||||
npm install
|
||||
```
|
||||
|
||||
@@ -43,10 +31,9 @@ Copy or Rename `config.json.example` to `config.json` and fill out the values:
|
||||
|
||||
## 🐬 Docker Configuration
|
||||
|
||||
For those who would prefer to use our [Docker container](https://hub.docker.com/repository/docker/eritislami/evobot), you may provide values from `config.json` as environment variables.
|
||||
|
||||
```shell
|
||||
docker run -e "TOKEN=<discord-token>" eritislami/evobot
|
||||
docker run -e "TOKEN=<discord-token>" f04c/apollo
|
||||
```
|
||||
|
||||
## 📝 Features & Commands
|
||||
@@ -125,13 +112,3 @@ Currently available locales are:
|
||||
- Vietnamese (vi)
|
||||
- Check [Contributing](#-contributing) if you wish to help add more languages!
|
||||
- For languages please use [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) two letter format
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
1. [Fork the repository](https://github.com/eritislami/evobot/fork)
|
||||
2. Clone your fork: `git clone https://github.com/your-username/evobot.git`
|
||||
3. Create your feature branch: `git checkout -b my-new-feature`
|
||||
4. Stage changes `git add .`
|
||||
5. Commit your changes: `cz` OR `npm run commit` do not use `git commit`
|
||||
6. Push to the branch: `git push origin my-new-feature`
|
||||
7. Submit a pull request
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
{
|
||||
"name": "EvoBot",
|
||||
"description": "🤖 Discord Music Bot built with discord.js",
|
||||
"repository": "https://github.com/eritislami/evobot",
|
||||
"logo": "https://i.imgur.com/JFxgbWH.png",
|
||||
"name": "Apollo",
|
||||
"description": "Discord Music Bot built with discord.js",
|
||||
"keywords": ["discord", "discordjs", "typescript", "music", "bot"]
|
||||
}
|
||||
|
||||
+3
-1
@@ -21,7 +21,9 @@ export default {
|
||||
});
|
||||
|
||||
helpEmbed.setTimestamp();
|
||||
|
||||
helpEmbed.setFooter({
|
||||
text: "Made with ❤️ by F04C"
|
||||
});
|
||||
return interaction.reply({ embeds: [helpEmbed] }).catch(console.error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,4 +5,5 @@ export interface Config {
|
||||
STAY_TIME: number;
|
||||
DEFAULT_VOLUME: number;
|
||||
LOCALE: string;
|
||||
USE_INVIDIOUS_PROXY: boolean;
|
||||
}
|
||||
|
||||
Generated
+31
-18
@@ -1,15 +1,17 @@
|
||||
{
|
||||
"name": "evobot",
|
||||
"name": "apollo",
|
||||
"version": "2.9.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "evobot",
|
||||
"name": "apollo",
|
||||
"version": "2.9.0",
|
||||
"dependencies": {
|
||||
"@discordjs/voice": "^0.17.0",
|
||||
"array-move": "^4.0.0",
|
||||
|
||||
"@discordjs/voice": "^0.18.0",
|
||||
"array-move": "^3.0.1",
|
||||
|
||||
"discord.js": "^14.15.3",
|
||||
"dotenv": "^16.4.5",
|
||||
"ffmpeg-static": "^4.4.1",
|
||||
@@ -476,23 +478,28 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@discordjs/voice": {
|
||||
"version": "0.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@discordjs/voice/-/voice-0.17.0.tgz",
|
||||
"integrity": "sha512-hArn9FF5ZYi1IkxdJEVnJi+OxlwLV0NJYWpKXsmNOojtGtAZHxmsELA+MZlu2KW1F/K1/nt7lFOfcMXNYweq9w==",
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@discordjs/voice/-/voice-0.18.0.tgz",
|
||||
"integrity": "sha512-BvX6+VJE5/vhD9azV9vrZEt9hL1G+GlOdsQaVl5iv9n87fkXjf3cSwllhR3GdaUC8m6dqT8umXIWtn3yCu4afg==",
|
||||
"dependencies": {
|
||||
"@types/ws": "^8.5.10",
|
||||
"discord-api-types": "0.37.83",
|
||||
"@types/ws": "^8.5.12",
|
||||
"discord-api-types": "^0.37.103",
|
||||
"prism-media": "^1.3.5",
|
||||
"tslib": "^2.6.2",
|
||||
"ws": "^8.16.0"
|
||||
"tslib": "^2.6.3",
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.11.0"
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/discordjs/discord.js?sponsor"
|
||||
}
|
||||
},
|
||||
"node_modules/@discordjs/voice/node_modules/discord-api-types": {
|
||||
"version": "0.37.107",
|
||||
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.107.tgz",
|
||||
"integrity": "sha512-XOxmxnhtYIRH55kLTrc/JS3nJV1l3wfBtTptFiRGdGDOe2qdCT4DltpxSgskasfDrKfw71Z5quG4tYqTxyPJ7g=="
|
||||
},
|
||||
"node_modules/@discordjs/voice/node_modules/opusscript": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/opusscript/-/opusscript-0.0.8.tgz",
|
||||
@@ -525,6 +532,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@discordjs/voice/node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
||||
},
|
||||
"node_modules/@discordjs/ws": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.1.1.tgz",
|
||||
@@ -691,9 +703,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "8.5.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz",
|
||||
"integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==",
|
||||
"version": "8.5.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz",
|
||||
"integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
@@ -2618,6 +2630,7 @@
|
||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz",
|
||||
"integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chokidar": "^3.5.2",
|
||||
"debug": "^3.2.7",
|
||||
@@ -3606,9 +3619,9 @@
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.17.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz",
|
||||
"integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==",
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
||||
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
|
||||
+2
-4
@@ -1,13 +1,10 @@
|
||||
{
|
||||
"name": "evobot",
|
||||
"name": "apollo",
|
||||
"version": "2.9.0",
|
||||
"description": "Discord music bot built with discord.js",
|
||||
"main": "index.ts",
|
||||
"author": "Erit Islami <eritislami@gmail.com>",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/eritislami/evobot",
|
||||
"repository": "github:eritislami/evobot",
|
||||
"bugs": "https://github.com/eritislami/evobot/issues",
|
||||
"engines": {
|
||||
"node": ">=16.11.0"
|
||||
},
|
||||
@@ -22,6 +19,7 @@
|
||||
"dependencies": {
|
||||
"@discordjs/voice": "^0.17.0",
|
||||
"array-move": "^4.0.0",
|
||||
|
||||
"discord.js": "^14.15.3",
|
||||
"dotenv": "^16.4.5",
|
||||
"ffmpeg-static": "^4.4.1",
|
||||
|
||||
+184
-18
@@ -1,25 +1,160 @@
|
||||
import { AudioResource, createAudioResource, StreamType } from "@discordjs/voice";
|
||||
import youtube from "youtube-sr";
|
||||
import { i18n } from "../utils/i18n";
|
||||
import { videoPattern, isURL } from "../utils/patterns";
|
||||
import { isURL, videoPattern } from "../utils/patterns";
|
||||
|
||||
const { stream, video_basic_info } = require("play-dl");
|
||||
import { Readable } from "stream";
|
||||
import { extractID } from "../utils/extractor";
|
||||
import { config } from "../utils/config";
|
||||
|
||||
const INVIDIOUS_BASE_URL = "https://inv.nadeko.net";
|
||||
|
||||
type InvidiousResponse = {
|
||||
type: string;
|
||||
title: string;
|
||||
videoId: string;
|
||||
videoThumbnails: Array<{
|
||||
quality: string;
|
||||
url: string;
|
||||
width: number;
|
||||
height: number;
|
||||
}>;
|
||||
storyboards: Array<{
|
||||
url: string;
|
||||
templateUrl: string;
|
||||
width: number;
|
||||
height: number;
|
||||
count: number;
|
||||
interval: number;
|
||||
storyboardWidth: number;
|
||||
storyboardHeight: number;
|
||||
storyboardCount: number;
|
||||
}>;
|
||||
description: string;
|
||||
descriptionHtml: string;
|
||||
published: number;
|
||||
publishedText: string;
|
||||
keywords: Array<string>;
|
||||
viewCount: number;
|
||||
likeCount: number;
|
||||
dislikeCount: number;
|
||||
paid: boolean;
|
||||
premium: boolean;
|
||||
isFamilyFriendly: boolean;
|
||||
allowedRegions: Array<string>;
|
||||
genre: string;
|
||||
genreUrl: any;
|
||||
author: string;
|
||||
authorId: string;
|
||||
authorUrl: string;
|
||||
authorVerified: boolean;
|
||||
authorThumbnails: Array<{
|
||||
url: string;
|
||||
width: number;
|
||||
height: number;
|
||||
}>;
|
||||
subCountText: string;
|
||||
lengthSeconds: number;
|
||||
allowRatings: boolean;
|
||||
rating: number;
|
||||
isListed: boolean;
|
||||
liveNow: boolean;
|
||||
isPostLiveDvr: boolean;
|
||||
isUpcoming: boolean;
|
||||
dashUrl: string;
|
||||
adaptiveFormats: Array<{
|
||||
init: string;
|
||||
index: string;
|
||||
bitrate: string;
|
||||
url: string;
|
||||
itag: string;
|
||||
type: string;
|
||||
clen: string;
|
||||
lmt: string;
|
||||
projectionType: string;
|
||||
container: string;
|
||||
encoding: string;
|
||||
audioQuality?: string;
|
||||
audioSampleRate?: number;
|
||||
audioChannels?: number;
|
||||
fps?: number;
|
||||
size?: string;
|
||||
resolution?: string;
|
||||
qualityLabel?: string;
|
||||
colorInfo?: {
|
||||
primaries: string;
|
||||
transferCharacteristics: string;
|
||||
matrixCoefficients: string;
|
||||
};
|
||||
}>;
|
||||
formatStreams: Array<{
|
||||
url: string;
|
||||
itag: string;
|
||||
type: string;
|
||||
quality: string;
|
||||
bitrate: string;
|
||||
fps: number;
|
||||
size: string;
|
||||
resolution: string;
|
||||
qualityLabel: string;
|
||||
container: string;
|
||||
encoding: string;
|
||||
}>;
|
||||
captions: Array<{
|
||||
label: string;
|
||||
language_code: string;
|
||||
url: string;
|
||||
}>;
|
||||
recommendedVideos: Array<{
|
||||
videoId: string;
|
||||
title: string;
|
||||
videoThumbnails: Array<{
|
||||
quality: string;
|
||||
url: string;
|
||||
width: number;
|
||||
height: number;
|
||||
}>;
|
||||
author: string;
|
||||
authorUrl: string;
|
||||
authorId: string;
|
||||
authorVerified: boolean;
|
||||
lengthSeconds: number;
|
||||
viewCountText: string;
|
||||
viewCount: number;
|
||||
}>;
|
||||
};
|
||||
|
||||
const getBasicVideoInfo = async (url: string) => {
|
||||
const videoId = extractID(url);
|
||||
|
||||
if (!videoId) {
|
||||
throw new Error("Invalid YouTube URL");
|
||||
}
|
||||
|
||||
const response = await fetch(`https://inv.nadeko.net/api/v1/videos/${videoId}`);
|
||||
const data = (await response.json()) as InvidiousResponse;
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
export interface SongData {
|
||||
url: string;
|
||||
title: string;
|
||||
duration: number;
|
||||
adaptiveFormats?: InvidiousResponse["adaptiveFormats"];
|
||||
}
|
||||
|
||||
export class Song {
|
||||
public readonly url: string;
|
||||
public readonly title: string;
|
||||
public readonly duration: number;
|
||||
public readonly adaptiveFormats?: InvidiousResponse["adaptiveFormats"];
|
||||
|
||||
public constructor({ url, title, duration }: SongData) {
|
||||
public constructor({ url, title, duration, adaptiveFormats = [] }: SongData) {
|
||||
this.url = url;
|
||||
this.title = title;
|
||||
this.duration = duration;
|
||||
this.adaptiveFormats = adaptiveFormats;
|
||||
}
|
||||
|
||||
public static async from(url: string = "", search: string = "") {
|
||||
@@ -28,12 +163,13 @@ export class Song {
|
||||
let songInfo;
|
||||
|
||||
if (isYoutubeUrl) {
|
||||
songInfo = await video_basic_info(url);
|
||||
songInfo = await getBasicVideoInfo(url);
|
||||
|
||||
return new this({
|
||||
url: songInfo.video_details.url,
|
||||
title: songInfo.video_details.title,
|
||||
duration: parseInt(songInfo.video_details.durationInSec)
|
||||
url,
|
||||
title: songInfo.title,
|
||||
duration: songInfo.lengthSeconds,
|
||||
adaptiveFormats: songInfo.adaptiveFormats
|
||||
});
|
||||
} else {
|
||||
const result = await youtube.searchOne(search);
|
||||
@@ -50,31 +186,61 @@ export class Song {
|
||||
throw err;
|
||||
}
|
||||
|
||||
songInfo = await video_basic_info(`https://youtube.com/watch?v=${result.id}`);
|
||||
songInfo = await getBasicVideoInfo(`https://youtube.com/watch?v=${result.id}`);
|
||||
|
||||
return new this({
|
||||
url: songInfo.video_details.url,
|
||||
title: songInfo.video_details.title,
|
||||
duration: parseInt(songInfo.video_details.durationInSec)
|
||||
url: `https://youtube.com/watch?v=${result.id}`,
|
||||
title: songInfo.title,
|
||||
duration: songInfo.lengthSeconds,
|
||||
adaptiveFormats: songInfo.adaptiveFormats
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async makeResource(): Promise<AudioResource<Song> | void> {
|
||||
let playStream;
|
||||
const format = this.adaptiveFormats
|
||||
?.sort((a, b) => {
|
||||
return parseInt(b.bitrate!) - parseInt(a.bitrate!);
|
||||
})
|
||||
.find((format) => format.type.startsWith("audio/"));
|
||||
|
||||
const source = this.url.includes("youtube") ? "youtube" : "soundcloud";
|
||||
if (!format) return;
|
||||
|
||||
if (source === "youtube") {
|
||||
playStream = await stream(this.url);
|
||||
let formatUrl = format.url;
|
||||
|
||||
if (config.USE_INVIDIOUS_PROXY) {
|
||||
const invidiousUrl = new URL(INVIDIOUS_BASE_URL);
|
||||
const formatUrlObject = new URL(format.url);
|
||||
|
||||
formatUrlObject.hostname = invidiousUrl.hostname;
|
||||
|
||||
formatUrl = formatUrlObject.toString();
|
||||
}
|
||||
|
||||
if (!stream) return;
|
||||
const response = await fetch(formatUrl);
|
||||
|
||||
return createAudioResource(playStream.stream, { metadata: this, inputType: playStream.type, inlineVolume: true });
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
|
||||
const stream = new Readable({
|
||||
read() {
|
||||
this.push(Buffer.from(arrayBuffer));
|
||||
this.push(null);
|
||||
}
|
||||
});
|
||||
|
||||
const codec = format.encoding;
|
||||
|
||||
const type: StreamType =
|
||||
codec === "opus" && format.container === "webm" ? StreamType.WebmOpus : StreamType.Arbitrary;
|
||||
|
||||
return createAudioResource(stream, {
|
||||
metadata: this,
|
||||
inputType: type,
|
||||
inlineVolume: true
|
||||
});
|
||||
}
|
||||
|
||||
public startMessage() {
|
||||
return i18n.__mf("play.startedPlaying", { title: this.title, url: this.url });
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -12,7 +12,8 @@ try {
|
||||
PRUNING: process.env.PRUNING === "true" ? true : false,
|
||||
STAY_TIME: parseInt(process.env.STAY_TIME!) || 30,
|
||||
DEFAULT_VOLUME: parseInt(process.env.DEFAULT_VOLUME!) || 100,
|
||||
LOCALE: process.env.LOCALE || "en"
|
||||
LOCALE: process.env.LOCALE || "en",
|
||||
USE_INVIDIOUS_PROXY: process.env.USE_INVIDIOUS_PROXY === "true" ? true : false
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { videoIdPattern, videoPattern } from "../utils/patterns";
|
||||
|
||||
export const extractID = (url: string) => {
|
||||
const url_ = url.trim();
|
||||
if (url_.startsWith("https")) {
|
||||
if (url_.indexOf("list=") === -1) {
|
||||
const video_id = extractVideoId(url_);
|
||||
if (!video_id) throw new Error("This is not a YouTube url or videoId or PlaylistID");
|
||||
return video_id;
|
||||
} else {
|
||||
return url_.split("list=")[1].split("&")[0];
|
||||
}
|
||||
} else return url_;
|
||||
};
|
||||
|
||||
function extractVideoId(urlOrId: string): string | false {
|
||||
if (urlOrId.startsWith("https://") && urlOrId.match(videoPattern)) {
|
||||
let id: string;
|
||||
if (urlOrId.includes("youtu.be/")) {
|
||||
id = urlOrId.split("youtu.be/")[1].split(/(\?|\/|&)/)[0];
|
||||
} else if (urlOrId.includes("youtube.com/embed/")) {
|
||||
id = urlOrId.split("youtube.com/embed/")[1].split(/(\?|\/|&)/)[0];
|
||||
} else if (urlOrId.includes("youtube.com/shorts/")) {
|
||||
id = urlOrId.split("youtube.com/shorts/")[1].split(/(\?|\/|&)/)[0];
|
||||
} else if (urlOrId.includes("youtube.com/live/")) {
|
||||
id = urlOrId.split("youtube.com/live/")[1].split(/(\?|\/|&)/)[0];
|
||||
} else {
|
||||
id = (urlOrId.split("watch?v=")[1] ?? urlOrId.split("&v=")[1]).split(/(\?|\/|&)/)[0];
|
||||
}
|
||||
|
||||
if (id.match(videoIdPattern)) return id;
|
||||
} else if (urlOrId.match(videoIdPattern)) {
|
||||
return urlOrId;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -4,3 +4,4 @@ export const scRegex = /^https?:\/\/(soundcloud\.com)\/(.*)$/;
|
||||
export const mobileScRegex = /^https?:\/\/(soundcloud\.app\.goo\.gl)\/(.*)$/;
|
||||
export const isURL =
|
||||
/^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/;
|
||||
export const videoIdPattern = /^[a-zA-Z\d_-]{11,12}$/;
|
||||
Reference in New Issue
Block a user