From 6d9997dd4ff61a721d074bf3ca26661665c2fd34 Mon Sep 17 00:00:00 2001
From: u-sayuri <54628820+u-sayuri@users.noreply.github.com>
Date: Wed, 30 Jun 2021 23:13:07 +0700
Subject: [PATCH 1/4] Proposal changes based on #477

This fixes the problem where the first track of parsed youtube playlist gets played twice;
Changed code order so initial `trackStart` event can now see the queue;
Added a new parameter to `Player#play` and `Player_searchTracks`: `startFromIndex (number)`:
This parameter lets the user priorities the track with corresponding index (array index, starts from 0), `0` by default.
If one is specified and there is no queue, the specified track will be played first;
Otherwise if it's already playing then the specified track will be put on the first index of the added playlist.
---
 src/Player.ts | 46 ++++++++++++++++++++++++++++------------------
 1 file changed, 28 insertions(+), 18 deletions(-)

diff --git a/src/Player.ts b/src/Player.ts
index 61e3de4..607e097 100644
--- a/src/Player.ts
+++ b/src/Player.ts
@@ -144,7 +144,7 @@ export class Player extends EventEmitter {
      * @returns {Promise<Track>}
      * @private
      */
-    private _searchTracks(message: Message, query: string, firstResult?: boolean): Promise<Track> {
+    private _searchTracks(message: Message, query: string, firstResult?: boolean, startFromIndex?: number = 0): Promise<Track> {
         return new Promise(async (resolve) => {
             let tracks: Track[] = [];
             const queryType = Util.getQueryType(query);
@@ -302,17 +302,21 @@ export class Player extends EventEmitter {
                     };
 
                     this.emit(PlayerEvents.PLAYLIST_PARSE_END, pl, message);
+                    if (startFromIndex < 0) startFromIndex = 0;
+                    if (startFromIndex > tracks.length - 1) startFromIndex = tracks.length - 1;
 
                     if (this.isPlaying(message)) {
+                        const prioritisedTrack = tracks.splice(startFromIndex, 1);
+                        tracks.unshift(prioritisedTrack);
                         const queue = this._addTracksToQueue(message, tracks);
-                        this.emit(PlayerEvents.PLAYLIST_ADD, message, queue, pl);
+                        this.emit(PlayerEvents.PLAYLIST_ADD, message, queue, playlist);
                     } else {
-                        const track = tracks[0];
-                        const queue = (await this._createQueue(message, track).catch((e) => void this.emit(PlayerEvents.ERROR, e, message))) as Queue;
+                        const track = tracks[startFromIndex];                       
+                        const queue = (await this._createQueue(message, track).catch((e) =&gt; void this.emit(PlayerEvents.ERROR, e, message))) as Queue;                        
                         this.emit(PlayerEvents.PLAYLIST_ADD, message, queue, pl);
+                        tracks.splice(startFromIndex, 1);                        
+                        this._addTracksToQueue(message, tracks);                        
                         this.emit(PlayerEvents.TRACK_START, message, queue.tracks[0], queue);
-                        tracks.shift();
-                        this._addTracksToQueue(message, tracks);
                     }
 
                     return;
@@ -357,17 +361,21 @@ export class Player extends EventEmitter {
                     // @ts-ignore
                     // tslint:disable-next-line:no-shadowed-variable
                     const tracks = playlist.videos as Track[];
+                    if (startFromIndex < 0) startFromIndex = 0;
+                    if (startFromIndex > tracks.length - 1) startFromIndex = tracks.length - 1;
 
                     if (this.isPlaying(message)) {
+                        const prioritisedTrack = tracks.splice(startFromIndex, 1);
+                        tracks.unshift(prioritisedTrack);
                         const queue = this._addTracksToQueue(message, tracks);
                         this.emit(PlayerEvents.PLAYLIST_ADD, message, queue, playlist);
                     } else {
-                        const track = tracks[0];
-                        const queue = (await this._createQueue(message, track).catch((e) => void this.emit(PlayerEvents.ERROR, e, message))) as Queue;
-                        this.emit(PlayerEvents.PLAYLIST_ADD, message, queue, playlist);
+                        const track = tracks[startFromIndex];                       
+                        const queue = (await this._createQueue(message, track).catch((e) =&gt; void this.emit(PlayerEvents.ERROR, e, message))) as Queue;                        
+                        this.emit(PlayerEvents.PLAYLIST_ADD, message, queue, pl);
+                        tracks.splice(startFromIndex, 1);                        
+                        this._addTracksToQueue(message, tracks);                        
                         this.emit(PlayerEvents.TRACK_START, message, queue.tracks[0], queue);
-                        tracks[0];
-                        this._addTracksToQueue(message, tracks);
                     }
 
                     return;
@@ -410,17 +418,20 @@ export class Player extends EventEmitter {
                     res.duration = res.tracks.reduce((a, c) => a + c.durationMS, 0);
 
                     this.emit(PlayerEvents.PLAYLIST_PARSE_END, res, message);
+                    if (startFromIndex < 0) res.startFromIndex = 0;
+                    if (startFromIndex > tracks.length - 1) startFromIndex = res.tracks.length - 1;
 
                     if (this.isPlaying(message)) {
-                        const queue = this._addTracksToQueue(message, res.tracks);
+                        const prioritisedTrack = res.tracks.splice(startFromIndex, 1);
+                        res.tracks.unshift(prioritisedTrack);
                         this.emit(PlayerEvents.PLAYLIST_ADD, message, queue, res);
                     } else {
-                        const track = res.tracks[0];
+                        const track = res.tracks[startFromIndex ? startFromIndex - 1 : 0];        
                         const queue = (await this._createQueue(message, track).catch((e) => void this.emit(PlayerEvents.ERROR, e, message))) as Queue;
                         this.emit(PlayerEvents.PLAYLIST_ADD, message, queue, res);
-                        this.emit(PlayerEvents.TRACK_START, message, queue.tracks[0], queue);
-                        res.tracks.shift();
+                        res.tracks.splice(startFromIndex ? startFromIndex - 1 : 0, 1);   
                         this._addTracksToQueue(message, res.tracks);
+                        this.emit(PlayerEvents.TRACK_START, message, queue.tracks[0], queue);
                     }
 
                     return;
@@ -459,7 +470,6 @@ export class Player extends EventEmitter {
                     this.emit(PlayerEvents.SEARCH_INVALID_RESPONSE, message, query, tracks, content, collector);
                 }
             });
-
             collector.on('end', (_, reason) => {
                 if (reason === 'time') {
                     this.emit(PlayerEvents.SEARCH_CANCEL, message, query, tracks);
@@ -476,7 +486,7 @@ export class Player extends EventEmitter {
      * @example await player.play(message, "never gonna give you up", true)
      * @returns {Promise<void>}
      */
-    async play(message: Message, query: string | Track, firstResult?: boolean): Promise<void> {
+    async play(message: Message, query: string | Track, firstResult?: boolean, startFromIndex?: number = 0): Promise<void> {
         if (!message) throw new PlayerError('Play function needs message');
         if (!query) throw new PlayerError('Play function needs search query as a string or Player.Track object');
 
@@ -533,7 +543,7 @@ export class Player extends EventEmitter {
                     }
                 }
 
-                if (!track) track = await this._searchTracks(message, query, firstResult);
+                if (!track) track = await this._searchTracks(message, query, firstResult, startFromIndex);
             }
         }
 

From 972d2641bd6d3402df0366ac502274d5856654b5 Mon Sep 17 00:00:00 2001
From: u-sayuri <54628820+u-sayuri@users.noreply.github.com>
Date: Wed, 30 Jun 2021 23:18:11 +0700
Subject: [PATCH 2/4] Update Player.ts

Fix typo
---
 src/Player.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Player.ts b/src/Player.ts
index 607e097..0740c3d 100644
--- a/src/Player.ts
+++ b/src/Player.ts
@@ -309,7 +309,7 @@ export class Player extends EventEmitter {
                         const prioritisedTrack = tracks.splice(startFromIndex, 1);
                         tracks.unshift(prioritisedTrack);
                         const queue = this._addTracksToQueue(message, tracks);
-                        this.emit(PlayerEvents.PLAYLIST_ADD, message, queue, playlist);
+                        this.emit(PlayerEvents.PLAYLIST_ADD, message, queue, pl);
                     } else {
                         const track = tracks[startFromIndex];                       
                         const queue = (await this._createQueue(message, track).catch((e) =&gt; void this.emit(PlayerEvents.ERROR, e, message))) as Queue;                        

From 9fb493b3dfaabfc2fc2d3d5adb9e8c31b907fed7 Mon Sep 17 00:00:00 2001
From: u-sayuri <54628820+u-sayuri@users.noreply.github.com>
Date: Wed, 30 Jun 2021 23:21:19 +0700
Subject: [PATCH 3/4] Update Player.ts

Add JSDoc descriptions
---
 src/Player.ts | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/Player.ts b/src/Player.ts
index 0740c3d..4997fcb 100644
--- a/src/Player.ts
+++ b/src/Player.ts
@@ -141,6 +141,7 @@ export class Player extends EventEmitter {
      * @param {DiscordMessage} message The message
      * @param {string} query The query
      * @param {boolean} [firstResult=false] If it should return the first result
+     * @param {number} [startFromIndex=0] Prioritise playing the track with following index (Only works with playlist)
      * @returns {Promise<Track>}
      * @private
      */
@@ -483,6 +484,7 @@ export class Player extends EventEmitter {
      * @param {DiscordMessage} message The discord.js message object
      * @param {string|Track} query Search query, can be `Player.Track` instance
      * @param {Boolean} [firstResult=false] If it should play the first result
+     * @param {number} [startFromIndex=0] Prioritise playing the track with following index (Only works with playlist)
      * @example await player.play(message, "never gonna give you up", true)
      * @returns {Promise<void>}
      */

From 4639eb780bf71d3f2f0cb8191fe739f16207c8d7 Mon Sep 17 00:00:00 2001
From: u-sayuri <54628820+u-sayuri@users.noreply.github.com>
Date: Fri, 2 Jul 2021 12:30:02 +0700
Subject: [PATCH 4/4] Update Player.ts

Resolve to match requirements
Fix some false implementations of the code so `trackStart` event displays the correct track
---
 src/Player.ts | 30 +++++++++++++++---------------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/src/Player.ts b/src/Player.ts
index 4997fcb..4b638f7 100644
--- a/src/Player.ts
+++ b/src/Player.ts
@@ -145,7 +145,7 @@ export class Player extends EventEmitter {
      * @returns {Promise<Track>}
      * @private
      */
-    private _searchTracks(message: Message, query: string, firstResult?: boolean, startFromIndex?: number = 0): Promise<Track> {
+    private _searchTracks(message: Message, query: string, firstResult?: boolean, startFromIndex = 0): Promise<Track> {
         return new Promise(async (resolve) => {
             let tracks: Track[] = [];
             const queryType = Util.getQueryType(query);
@@ -312,12 +312,12 @@ export class Player extends EventEmitter {
                         const queue = this._addTracksToQueue(message, tracks);
                         this.emit(PlayerEvents.PLAYLIST_ADD, message, queue, pl);
                     } else {
-                        const track = tracks[startFromIndex];                       
-                        const queue = (await this._createQueue(message, track).catch((e) =&gt; void this.emit(PlayerEvents.ERROR, e, message))) as Queue;                        
+                        const track = tracks[startFromIndex];
+                        const queue = (await this._createQueue(message, track).catch((e) => void this.emit(PlayerEvents.ERROR, e, message))) as Queue;
                         this.emit(PlayerEvents.PLAYLIST_ADD, message, queue, pl);
-                        tracks.splice(startFromIndex, 1);                        
-                        this._addTracksToQueue(message, tracks);                        
-                        this.emit(PlayerEvents.TRACK_START, message, queue.tracks[0], queue);
+                        tracks.splice(startFromIndex, 1);
+                        this._addTracksToQueue(message, tracks);
+                        this.emit(PlayerEvents.TRACK_START, message, track, queue);
                     }
 
                     return;
@@ -371,12 +371,12 @@ export class Player extends EventEmitter {
                         const queue = this._addTracksToQueue(message, tracks);
                         this.emit(PlayerEvents.PLAYLIST_ADD, message, queue, playlist);
                     } else {
-                        const track = tracks[startFromIndex];                       
-                        const queue = (await this._createQueue(message, track).catch((e) =&gt; void this.emit(PlayerEvents.ERROR, e, message))) as Queue;                        
+                        const track = tracks[startFromIndex];
+                        const queue = (await this._createQueue(message, track).catch((e) => void this.emit(PlayerEvents.ERROR, e, message))) as Queue;
                         this.emit(PlayerEvents.PLAYLIST_ADD, message, queue, pl);
-                        tracks.splice(startFromIndex, 1);                        
-                        this._addTracksToQueue(message, tracks);                        
-                        this.emit(PlayerEvents.TRACK_START, message, queue.tracks[0], queue);
+                        tracks.splice(startFromIndex, 1);
+                        this._addTracksToQueue(message, tracks);
+                        this.emit(PlayerEvents.TRACK_START, message, track, queue);
                     }
 
                     return;
@@ -427,12 +427,12 @@ export class Player extends EventEmitter {
                         res.tracks.unshift(prioritisedTrack);
                         this.emit(PlayerEvents.PLAYLIST_ADD, message, queue, res);
                     } else {
-                        const track = res.tracks[startFromIndex ? startFromIndex - 1 : 0];        
+                        const track = res.tracks[startFromIndex];
                         const queue = (await this._createQueue(message, track).catch((e) => void this.emit(PlayerEvents.ERROR, e, message))) as Queue;
                         this.emit(PlayerEvents.PLAYLIST_ADD, message, queue, res);
-                        res.tracks.splice(startFromIndex ? startFromIndex - 1 : 0, 1);   
+                        res.tracks.splice(startFromIndex, 1);
                         this._addTracksToQueue(message, res.tracks);
-                        this.emit(PlayerEvents.TRACK_START, message, queue.tracks[0], queue);
+                        this.emit(PlayerEvents.TRACK_START, message, track, queue);
                     }
 
                     return;
@@ -488,7 +488,7 @@ export class Player extends EventEmitter {
      * @example await player.play(message, "never gonna give you up", true)
      * @returns {Promise<void>}
      */
-    async play(message: Message, query: string | Track, firstResult?: boolean, startFromIndex?: number = 0): Promise<void> {
+    async play(message: Message, query: string | Track, firstResult?: boolean, startFromIndex = 0): Promise<void> {
         if (!message) throw new PlayerError('Play function needs message');
         if (!query) throw new PlayerError('Play function needs search query as a string or Player.Track object');