• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

xxczaki / discord-bot / 23023609301

12 Mar 2026 08:59PM UTC coverage: 95.681% (-0.6%) from 96.242%
23023609301

push

github

xxczaki
optim

993 of 1055 branches covered (94.12%)

Branch coverage included in aggregate %.

44 of 50 new or added lines in 6 files covered. (88.0%)

2 existing lines in 2 files now uncovered.

2131 of 2210 relevant lines covered (96.43%)

13.43 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.77
/src/utils/processTracksWithQueue.ts
1
import { availableParallelism } from 'node:os';
2
import type { EmbedBuilder, VoiceBasedChannel } from 'discord.js';
3
import { type QueueFilters, useMainPlayer } from 'discord-player';
4
import Queue from 'p-queue';
5
import type { ProcessingInteraction } from '../types/ProcessingInteraction';
6
import type { QueueMetadata } from '../types/QueueMetadata';
7
import determineSearchEngine from './determineSearchEngine';
8
import isObject from './isObject';
9
import logger from './logger';
10
import pluralize from './pluralize';
11

12
const MAX_CONCURRENCY = 3;
6✔
13
const PROGRESS_UPDATE_INTERVAL_MS = 2000;
6✔
14

15
type ProcessTrackOptions = {
16
        items: string[];
17
        voiceChannel: VoiceBasedChannel;
18
        interaction: ProcessingInteraction;
19
        embed: EmbedBuilder;
20
        nodeMetadata?: Record<string, unknown>;
21
        onError?: (error: unknown, context: string) => void;
22
};
23

24
export default async function processTracksWithQueue({
25
        items,
26
        voiceChannel,
27
        interaction,
28
        embed,
29
        nodeMetadata = {},
9✔
30
        onError = (error, context) => logger.error(error, context),
15✔
31
}: ProcessTrackOptions) {
32
        const player = useMainPlayer();
9✔
33

34
        let enqueued = 0;
9✔
35
        let processed = 0;
9✔
36
        let lastUpdateTime = 0;
9✔
37

38
        const pluralizeItems = pluralize('item', 'items');
9✔
39

40
        const concurrency = Math.min(
9✔
41
                MAX_CONCURRENCY,
42
                Math.ceil(availableParallelism() / 4),
43
        );
44
        const tracksQueue = new Queue({ concurrency });
9✔
45

46
        const updateProgress = async (force = false) => {
9✔
47
                const now = Date.now();
9✔
48

49
                if (!force && now - lastUpdateTime < PROGRESS_UPDATE_INTERVAL_MS) {
9!
UNCOV
50
                        return;
×
51
                }
52

53
                lastUpdateTime = now;
9✔
54
                const currentProgress = processed;
9✔
55

56
                try {
9✔
57
                        await interaction.editReply({
9✔
58
                                content: null,
59
                                components: [],
60
                                embeds: [
61
                                        embed.setDescription(
62
                                                pluralizeItems`${currentProgress}/${items.length} ${null} processed and added to the queue so far.`,
63
                                        ),
64
                                ],
65
                        });
66
                } catch {}
67
        };
68

69
        const playOptions = {
9✔
70
                fallbackSearchEngine: 'youtubeSearch' as const,
71
                nodeOptions: {
72
                        metadata: {
73
                                interaction,
74
                                ...nodeMetadata,
75
                        } satisfies QueueMetadata,
76
                        defaultFFmpegFilters: ['_normalizer' as keyof QueueFilters],
77
                },
78
                requestedBy: interaction.user,
79
        };
80

81
        const applyQueryMetadata = (
9✔
82
                track: { metadata: unknown; setMetadata: (metadata: unknown) => void },
83
                itemIndex: number,
84
        ) => {
85
                /* v8 ignore start */
86
                if (nodeMetadata.queries) {
87
                        const queries = nodeMetadata.queries as Record<string, string>;
88
                        const queryForTrack = queries[itemIndex.toString()];
89

90
                        if (queryForTrack) {
91
                                track.setMetadata({
92
                                        ...(isObject(track.metadata) ? track.metadata : {}),
93
                                        originalQuery: queryForTrack,
94
                                });
95
                        }
96
                }
97
                /* v8 ignore stop */
98
        };
99

100
        const [firstItem, ...remainingItems] = items;
9✔
101

102
        if (interaction.channel?.isSendable()) {
9!
103
                try {
9✔
104
                        await interaction.channel.sendTyping();
9✔
105
                } catch {}
106
        }
107

108
        try {
9✔
109
                const result = await player.play(voiceChannel, firstItem, {
9✔
110
                        searchEngine: determineSearchEngine(firstItem),
111
                        ...playOptions,
112
                });
113

114
                if (result.track) {
7!
115
                        applyQueryMetadata(result.track, 0);
7✔
116
                }
117

118
                enqueued++;
7✔
119
                processed++;
7✔
120
        } catch (error) {
121
                processed++;
2✔
122
                onError(error, 'Queue processing error');
2✔
123
        }
124

125
        if (remainingItems.length > 0) {
9✔
126
                tracksQueue.on('completed', () => updateProgress());
7✔
127

128
                await tracksQueue.addAll(
7✔
129
                        remainingItems.map((item, index) => async () => {
50✔
130
                                const itemIndex = index + 1;
48✔
131

132
                                try {
48✔
133
                                        const result = await player.play(voiceChannel, item, {
48✔
134
                                                searchEngine: determineSearchEngine(item),
135
                                                ...playOptions,
136
                                        });
137

138
                                        if (result.track) {
32!
139
                                                applyQueryMetadata(result.track, itemIndex);
32✔
140
                                        }
141

142
                                        enqueued++;
32✔
143
                                        processed++;
32✔
144

145
                                        return result;
32✔
146
                                } catch (error) {
147
                                        processed++;
16✔
148
                                        onError(error, 'Queue processing error');
16✔
149

150
                                        return null;
16✔
151
                                }
152
                        }),
153
                );
154
        }
155

156
        await tracksQueue.onIdle();
9✔
157

158
        await updateProgress(true);
9✔
159

160
        return { enqueued };
9✔
161
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc