Skip to content

Commit

Permalink
Add an option for YouTube video caching (Closes #61)
Browse files Browse the repository at this point in the history
  • Loading branch information
ysdragon committed Oct 22, 2024
1 parent 66f8f31 commit 701a913
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 28 deletions.
5 changes: 4 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ PREFIX = "$" # The prefix used to trigger your self-bot commands
GUILD_ID = "" # The ID of the Discord server your self-bot will be running on
COMMAND_CHANNEL_ID = "" # The ID of the Discord channel where your self-bot will respond to commands
VIDEO_CHANNEL_ID = "" # The ID of the Discord voice/video channel where your self-bot will stream videos

# General options
VIDEOS_FOLDER = "./videos" # The local path where you store video files
PREVIEW_CACHE = "./tmp/preview-cache" # The local path where your self-bot will cache video preview thumbnails
VIDEO_CACHE = "./tmp/video-cache" # The local path where your self-bot will cache youtube videos
YT_VIDEO_CACHE: "false" # Whether to enable youtube video caching, set to "true" to enable, "false" to disable
YT_VIDEO_CACHE_DIR = "./tmp/video-cache" # The local path where your self-bot will cache youtube videos

# Stream options
RESPECT_VIDEO_PARAMS = "false" # This option is used to respect video parameters such as width, height, fps, bitrate, and max bitrate.
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,12 @@ PREFIX = "$" # The prefix used to trigger your self-bot commands
GUILD_ID = "" # The ID of the Discord server your self-bot will be running on
COMMAND_CHANNEL_ID = "" # The ID of the Discord channel where your self-bot will respond to commands
VIDEO_CHANNEL_ID = "" # The ID of the Discord voice/video channel where your self-bot will stream videos

# General options
VIDEOS_FOLDER = "./videos" # The local path where you store video files
PREVIEW_CACHE = "./tmp/preview-cache" # The local path where your self-bot will cache video preview thumbnails
VIDEO_CACHE = "./tmp/video-cache" # The local path where your self-bot will cache youtube videos
YT_VIDEO_CACHE: "false" # Whether to enable youtube video caching, set to "true" to enable, "false" to disable
YT_VIDEO_CACHE_DIR = "./tmp/video-cache" # The local path where your self-bot will cache youtube videos

# Stream options
RESPECT_VIDEO_PARAMS = "false" # This option is used to respect video parameters such as width, height, fps, bitrate, and max bitrate.
Expand Down
5 changes: 4 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ services:
GUILD_ID: "" # The ID of the Discord server your self-bot will be running on
COMMAND_CHANNEL_ID: "" # The ID of the Discord channel where your self-bot will respond to commands
VIDEO_CHANNEL_ID: "" # The ID of the Discord voice/video channel where your self-bot will stream videos

# General options
VIDEOS_FOLDER: "./videos" # The local path where you store video files
PREVIEW_CACHE: "./tmp/preview-cache" # The local path where your self-bot will cache video preview thumbnails
VIDEO_CACHE: "./tmp/video-cache" # The local path where your self-bot will cache youtube videos
YT_VIDEO_CACHE: "false" # Whether to enable youtube video caching, set to "true" to enable, "false" to disable
YT_VIDEO_CACHE_DIR: "./tmp/video-cache" # The local path where your self-bot will cache youtube videos

# Stream options
RESPECT_VIDEO_PARAMS: "false" # This option is used to respect video parameters such as width, height, fps, bitrate, and max bitrate.
Expand Down
5 changes: 4 additions & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ export default {
guildId: process.env.GUILD_ID ? process.env.GUILD_ID : '',
cmdChannelId: process.env.COMMAND_CHANNEL_ID ? process.env.COMMAND_CHANNEL_ID : '',
videoChannelId: process.env.VIDEO_CHANNEL_ID ? process.env.VIDEO_CHANNEL_ID : '',

// General options
previewCache: process.env.PREVIEW_CACHE ? process.env.PREVIEW_CACHE : './tmp/preview-cache',
videoCache: process.env.VIDEO_CACHE ? process.env.VIDEO_CACHE : './tmp/video-cache',
ytVideoCache: process.env.YT_VIDEO_CACHE ? parseBoolean(process.env.YT_VIDEO_CACHE) : false,
ytVideoCacheDir: process.env.YT_VIDEO_CACHE_DIR ? process.env.YT_VIDEO_CACHE_DIR : './tmp/video-cache',

// Stream options
respect_video_params: process.env.RESPECT_VIDEO_PARAMS ? parseBoolean(process.env.RESPECT_VIDEO_PARAMS) : false,
Expand Down
43 changes: 32 additions & 11 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,22 +44,22 @@ if (!fs.existsSync(config.videosFolder)) {
fs.mkdirSync(config.videosFolder);
}

// Create previewCache/videoCache parent dir if it doesn't exist
if (!fs.existsSync(path.dirname(config.videoCache))) {
fs.mkdirSync(path.dirname(config.videoCache), { recursive: true });
// Create previewCache/ytVideoCacheDir parent dir if it doesn't exist
if (!fs.existsSync(path.dirname(config.ytVideoCacheDir))) {
fs.mkdirSync(path.dirname(config.ytVideoCacheDir), { recursive: true });
}

// Create the previewCache dir if it doesn't exist
if (!fs.existsSync(config.previewCache)) {
fs.mkdirSync(config.previewCache);
}

// Create the videoCache dir if it doesn't exist
if (!fs.existsSync(config.videoCache)) {
fs.mkdirSync(config.videoCache);
// Create the ytVideoCacheDir dir if it doesn't exist
if (!fs.existsSync(config.ytVideoCacheDir)) {
fs.mkdirSync(config.ytVideoCacheDir);
}

const tmpVideo = `${config.videoCache}/temp_vid.mp4`;
const tmpVideo = `${config.ytVideoCacheDir}/temp_vid.mp4`;

const videoFiles = fs.readdirSync(config.videosFolder);
let videos = videoFiles.map(file => {
Expand Down Expand Up @@ -498,7 +498,7 @@ async function cleanupStreamStatus() {
};
}

async function ytPlay(ytVideo: any): Promise<string | null> {
async function ytVideoCache(ytVideo: any): Promise<string | null> {
// Filter formats to get the best video format without audio
const videoFormats = ytVideo.formats.filter(
(format: { hasVideo: boolean; hasAudio: boolean }) =>
Expand Down Expand Up @@ -568,8 +568,17 @@ async function getVideoUrl(videoUrl: string): Promise<string | null> {

return highestTsFormat ? highestTsFormat.url : null;
} else {
// If it's not a livestream, play the video
return await ytPlay(video);
// Check if youtube video caching is enabled
if (config.ytVideoCache) {
return await ytVideoCache(video);
} else {
const videoFormats = video.formats
.filter((format: {
hasVideo: any; hasAudio: any;
}) => format.hasVideo && format.hasAudio);

return videoFormats[0].url ? videoFormats[0].url : null;
}
}
} catch (error) {
console.error("Error occurred while getting video URL:", error);
Expand All @@ -590,7 +599,19 @@ async function ytPlayTitle(title: string): Promise<string | null> {
// Ensure videoId is valid before proceeding
if (videoId) {
const ytVideoInfo = await ytdl.getInfo(videoId);
return await ytPlay(ytVideoInfo);

// Check if youtube video caching is enabled
if (config.ytVideoCache) {
return await ytVideoCache(video);
} else {
const videoFormats = ytVideoInfo.formats
.filter((format: {
hasVideo: any; hasAudio: any;
}) => format.hasVideo && format.hasAudio);

return videoFormats[0].url ? videoFormats[0].url : null;
}

}
}

Expand Down
21 changes: 8 additions & 13 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */

"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"sourceMap": true,
Expand All @@ -11,20 +10,16 @@
"noEmitHelpers": true,
"outDir": "dist",
"rootDir": "src",

/* Language and Environment */
"target": "es2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */

"target": "es2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */

"module": "commonjs", /* Specify what module code is generated. */
/* Emit */
"importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */

"importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
"strict": true, /* Enable all strict type-checking options. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}
}

0 comments on commit 701a913

Please sign in to comment.