Quality added
This commit is contained in:
parent
be840ce18b
commit
f304d6bcbd
@ -42,12 +42,15 @@ authorization() //After then you will be asked about type of data you want to cr
|
|||||||
|
|
||||||
### Stream
|
### Stream
|
||||||
|
|
||||||
#### stream(url : `string`, cookie? : `string`)
|
### StreamOptions :
|
||||||
|
|
||||||
|
- quality : `number` :- Sets quality of stream [ 0 = Lowest, 1 = Medium ]. Leave this empty to get highest audio quality.
|
||||||
|
- cookie : `string` :- **[Cookies](https://github.com/play-dl/play-dl/discussions/34) are optional and are required for playing age restricted videos.**
|
||||||
|
|
||||||
|
#### stream(url : `string`, options? : `StreamOptions`)
|
||||||
|
|
||||||
_This is basic to create a stream from a youtube or soundcloud url._
|
_This is basic to create a stream from a youtube or soundcloud url._
|
||||||
|
|
||||||
**[Cookies](https://github.com/play-dl/play-dl/discussions/34) are optional and are required for playing age restricted videos.**
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
let source = await stream("url") // This will create a stream Class.
|
let source = await stream("url") // This will create a stream Class.
|
||||||
|
|
||||||
@ -56,19 +59,17 @@ let resource = createAudioResource(source.stream, {
|
|||||||
}) // This creates resource for playing
|
}) // This creates resource for playing
|
||||||
```
|
```
|
||||||
|
|
||||||
### stream_from_info(info : `infoData`, cookie? : `string`)
|
### stream_from_info(info : `infoData`, options? : `StreamOptions`)
|
||||||
|
|
||||||
_This is basic to create a stream from a info [ from [video_info](https://github.com/play-dl/play-dl#video_infourl--string) function or [soundcloud]() function [**Only SoundCloudTrack class is allowed**] ]._
|
_This is basic to create a stream from a info [ from [video_info](https://github.com/play-dl/play-dl#video_infourl--string) function or [soundcloud]() function [**Only SoundCloudTrack class is allowed**] ]._
|
||||||
|
|
||||||
**[Cookies](https://github.com/play-dl/play-dl/discussions/34) are optional and are required for playing age restricted videos.**
|
|
||||||
|
|
||||||
**Note :** Here, cookies are required only for retrying purposes.
|
**Note :** Here, cookies are required only for retrying purposes.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
let source = await stream_from_info(info) // This will create a stream Class from video_info or SoundCoudTrack Class.
|
let source = await stream_from_info(info) // This will create a stream Class from video_info or SoundCoudTrack Class.
|
||||||
|
|
||||||
/* OR
|
/* OR
|
||||||
let source = await stream_from_info(info, cookie) This will create a stream Class and also give cookies if retrying.
|
let source = await stream_from_info(info, { cookie }) This will create a stream Class and also give cookies if retrying.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let resource = createAudioResource(source.stream, {
|
let resource = createAudioResource(source.stream, {
|
||||||
|
|||||||
@ -17,13 +17,13 @@ client.on('messageCreate', async message => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
let args = message.content.split('play ')[1].split(' ')[0]
|
let args = message.content.split('play ')[1].split(' ')[0]
|
||||||
let stream = await play.stream(args, COOKIE)
|
let stream = await play.stream(args, { cookie : COOKIE })
|
||||||
/*
|
/*
|
||||||
OR if you want to get info about youtube link and then stream it
|
OR if you want to get info about youtube link and then stream it
|
||||||
|
|
||||||
let yt_info = await play.video_info(args, COOKIE)
|
let yt_info = await play.video_info(args, { cookie : COOKIE })
|
||||||
console.log(yt_info.video_details.title)
|
console.log(yt_info.video_details.title)
|
||||||
let stream = await play.stream_from_info(yt_info, COOKIE)
|
let stream = await play.stream_from_info(yt_info, { cookie : COOKIE })
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let resource = createAudioResource(stream.stream, {
|
let resource = createAudioResource(stream.stream, {
|
||||||
|
|||||||
@ -22,7 +22,7 @@ interface SoundCloudTrackDeprecated {
|
|||||||
type: 'track';
|
type: 'track';
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SoundCloudTrackFormat {
|
export interface SoundCloudTrackFormat {
|
||||||
url: string;
|
url: string;
|
||||||
preset: string;
|
preset: string;
|
||||||
duration: number;
|
duration: number;
|
||||||
@ -90,13 +90,13 @@ export class SoundCloudTrack {
|
|||||||
id: this.id,
|
id: this.id,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
url: this.url,
|
url: this.url,
|
||||||
fetched : this.fetched,
|
fetched: this.fetched,
|
||||||
durationInMs: this.durationInMs,
|
durationInMs: this.durationInMs,
|
||||||
durationInSec: this.durationInSec,
|
durationInSec: this.durationInSec,
|
||||||
publisher: this.publisher,
|
publisher: this.publisher,
|
||||||
formats: this.formats,
|
formats: this.formats,
|
||||||
thumbnail: this.thumbnail,
|
thumbnail: this.thumbnail,
|
||||||
user : this.user
|
user: this.user
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -169,13 +169,13 @@ export class SoundCloudPlaylist {
|
|||||||
await Promise.allSettled(work);
|
await Promise.allSettled(work);
|
||||||
}
|
}
|
||||||
|
|
||||||
get total_tracks(){
|
get total_tracks() {
|
||||||
let count = 0
|
let count = 0;
|
||||||
this.tracks.forEach((track) => {
|
this.tracks.forEach((track) => {
|
||||||
if(track instanceof SoundCloudTrack) count++
|
if (track instanceof SoundCloudTrack) count++;
|
||||||
else return
|
else return;
|
||||||
})
|
});
|
||||||
return count
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSON() {
|
toJSON() {
|
||||||
@ -183,19 +183,19 @@ export class SoundCloudPlaylist {
|
|||||||
name: this.name,
|
name: this.name,
|
||||||
id: this.id,
|
id: this.id,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
sub_type : this.sub_type,
|
sub_type: this.sub_type,
|
||||||
url: this.url,
|
url: this.url,
|
||||||
durationInMs: this.durationInMs,
|
durationInMs: this.durationInMs,
|
||||||
durationInSec: this.durationInSec,
|
durationInSec: this.durationInSec,
|
||||||
tracksCount : this.tracksCount,
|
tracksCount: this.tracksCount,
|
||||||
user : this.user,
|
user: this.user,
|
||||||
tracks : this.tracks
|
tracks: this.tracks
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Stream {
|
export class Stream {
|
||||||
stream : PassThrough;
|
stream: PassThrough;
|
||||||
type: StreamType;
|
type: StreamType;
|
||||||
private url: string;
|
private url: string;
|
||||||
private playing_count: number;
|
private playing_count: number;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { StreamType } from '../YouTube/stream';
|
import { StreamType } from '../YouTube/stream';
|
||||||
import { request } from '../YouTube/utils/request';
|
import { request } from '../YouTube/utils/request';
|
||||||
import { SoundCloudPlaylist, SoundCloudTrack, Stream } from './classes';
|
import { SoundCloudPlaylist, SoundCloudTrack, SoundCloudTrackFormat, Stream } from './classes';
|
||||||
|
|
||||||
let soundData: SoundDataOptions;
|
let soundData: SoundDataOptions;
|
||||||
if (fs.existsSync('.data/soundcloud.data')) {
|
if (fs.existsSync('.data/soundcloud.data')) {
|
||||||
@ -33,23 +33,31 @@ export async function soundcloud(url: string): Promise<SoundCloudTrack | SoundCl
|
|||||||
else return new SoundCloudPlaylist(json_data, soundData.client_id);
|
else return new SoundCloudPlaylist(json_data, soundData.client_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function stream(url: string): Promise<Stream> {
|
export async function stream(url: string, quality? : number): Promise<Stream> {
|
||||||
const data = await soundcloud(url);
|
const data = await soundcloud(url);
|
||||||
|
|
||||||
if (data instanceof SoundCloudPlaylist) throw new Error("Streams can't be created from Playlist url");
|
if (data instanceof SoundCloudPlaylist) throw new Error("Streams can't be created from Playlist url");
|
||||||
|
|
||||||
const req_url = data.formats[data.formats.length - 1].url + '?client_id=' + soundData.client_id;
|
const HLSformats = parseHlsFormats(data.formats)
|
||||||
|
if(!quality) quality = HLSformats.length - 1;
|
||||||
|
else if(quality <= 0) quality = 0;
|
||||||
|
else if(quality > HLSformats.length) quality = HLSformats.length - 1;
|
||||||
|
const req_url = HLSformats[quality].url + '?client_id=' + soundData.client_id;
|
||||||
const s_data = JSON.parse(await request(req_url));
|
const s_data = JSON.parse(await request(req_url));
|
||||||
const type = data.formats[data.formats.length - 1].format.mime_type.startsWith('audio/ogg')
|
const type = HLSformats[quality].format.mime_type.startsWith('audio/ogg')
|
||||||
? StreamType.OggOpus
|
? StreamType.OggOpus
|
||||||
: StreamType.Arbitrary;
|
: StreamType.Arbitrary;
|
||||||
return new Stream(s_data.url, type);
|
return new Stream(s_data.url, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function stream_from_info(data: SoundCloudTrack): Promise<Stream> {
|
export async function stream_from_info(data: SoundCloudTrack, quality? :number): Promise<Stream> {
|
||||||
const req_url = data.formats[data.formats.length - 1].url + '?client_id=' + soundData.client_id;
|
const HLSformats = parseHlsFormats(data.formats)
|
||||||
|
if(!quality) quality = HLSformats.length - 1;
|
||||||
|
else if(quality <= 0) quality = 0;
|
||||||
|
else if(quality > HLSformats.length) quality = HLSformats.length - 1;
|
||||||
|
const req_url = HLSformats[quality].url + '?client_id=' + soundData.client_id;
|
||||||
const s_data = JSON.parse(await request(req_url));
|
const s_data = JSON.parse(await request(req_url));
|
||||||
const type = data.formats[data.formats.length - 1].format.mime_type.startsWith('audio/ogg')
|
const type = HLSformats[quality].format.mime_type.startsWith('audio/ogg')
|
||||||
? StreamType.OggOpus
|
? StreamType.OggOpus
|
||||||
: StreamType.Arbitrary;
|
: StreamType.Arbitrary;
|
||||||
return new Stream(s_data.url, type);
|
return new Stream(s_data.url, type);
|
||||||
@ -77,3 +85,11 @@ export async function so_validate(url: string): Promise<false | 'track' | 'playl
|
|||||||
else if (json_data.kind === 'playlist') return 'playlist';
|
else if (json_data.kind === 'playlist') return 'playlist';
|
||||||
else return false;
|
else return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseHlsFormats(data : SoundCloudTrackFormat[]){
|
||||||
|
const result: SoundCloudTrackFormat[] = [];
|
||||||
|
data.forEach((format) => {
|
||||||
|
if(format.format.protocol === 'hls') result.push(format)
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@ -11,7 +11,7 @@ export interface FormatInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class LiveStreaming {
|
export class LiveStreaming {
|
||||||
stream : PassThrough;
|
stream: PassThrough;
|
||||||
type: StreamType;
|
type: StreamType;
|
||||||
private base_url: string;
|
private base_url: string;
|
||||||
private url: string;
|
private url: string;
|
||||||
@ -23,7 +23,7 @@ export class LiveStreaming {
|
|||||||
private segments_urls: string[];
|
private segments_urls: string[];
|
||||||
private request: IncomingMessage | null;
|
private request: IncomingMessage | null;
|
||||||
constructor(dash_url: string, target_interval: number, video_url: string) {
|
constructor(dash_url: string, target_interval: number, video_url: string) {
|
||||||
this.stream = new PassThrough({ highWaterMark: 10 * 1000 * 1000 })
|
this.stream = new PassThrough({ highWaterMark: 10 * 1000 * 1000 });
|
||||||
this.type = StreamType.Arbitrary;
|
this.type = StreamType.Arbitrary;
|
||||||
this.url = dash_url;
|
this.url = dash_url;
|
||||||
this.base_url = '';
|
this.base_url = '';
|
||||||
@ -121,7 +121,7 @@ export class LiveStreaming {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Stream {
|
export class Stream {
|
||||||
stream : PassThrough;
|
stream: PassThrough;
|
||||||
type: StreamType;
|
type: StreamType;
|
||||||
private url: string;
|
private url: string;
|
||||||
private bytes_count: number;
|
private bytes_count: number;
|
||||||
|
|||||||
@ -9,6 +9,11 @@ export enum StreamType {
|
|||||||
Opus = 'opus'
|
Opus = 'opus'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface StreamOptions {
|
||||||
|
quality?: number;
|
||||||
|
cookie?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface InfoData {
|
export interface InfoData {
|
||||||
LiveStreamData: {
|
LiveStreamData: {
|
||||||
isLive: boolean;
|
isLive: boolean;
|
||||||
@ -33,10 +38,9 @@ function parseAudioFormats(formats: any[]) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function stream(url: string, cookie?: string): Promise<Stream | LiveStreaming> {
|
export async function stream(url: string, options: StreamOptions = {}): Promise<Stream | LiveStreaming> {
|
||||||
const info = await video_info(url, cookie);
|
const info = await video_info(url, options.cookie);
|
||||||
const final: any[] = [];
|
const final: any[] = [];
|
||||||
let type: StreamType;
|
|
||||||
if (
|
if (
|
||||||
info.LiveStreamData.isLive === true &&
|
info.LiveStreamData.isLive === true &&
|
||||||
info.LiveStreamData.hlsManifestUrl !== null &&
|
info.LiveStreamData.hlsManifestUrl !== null &&
|
||||||
@ -50,33 +54,26 @@ export async function stream(url: string, cookie?: string): Promise<Stream | Liv
|
|||||||
}
|
}
|
||||||
|
|
||||||
const audioFormat = parseAudioFormats(info.format);
|
const audioFormat = parseAudioFormats(info.format);
|
||||||
const opusFormats = filterFormat(audioFormat, 'opus');
|
if (!options.quality) options.quality = audioFormat.length - 1;
|
||||||
|
else if (options.quality <= 0) options.quality = 0;
|
||||||
if (opusFormats.length === 0) {
|
else if (options.quality > audioFormat.length) options.quality = audioFormat.length - 1;
|
||||||
type = StreamType.Arbitrary;
|
final.push(audioFormat[options.quality]);
|
||||||
if (audioFormat.length === 0) {
|
let type: StreamType =
|
||||||
final.push(info.format[info.format.length - 1]);
|
audioFormat[options.quality].codec === 'opus' && audioFormat[options.quality].container === 'webm'
|
||||||
} else {
|
? StreamType.WebmOpus
|
||||||
final.push(audioFormat[audioFormat.length - 1]);
|
: StreamType.Arbitrary;
|
||||||
}
|
|
||||||
} else {
|
|
||||||
type = StreamType.WebmOpus;
|
|
||||||
final.push(opusFormats[opusFormats.length - 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Stream(
|
return new Stream(
|
||||||
final[0].url,
|
final[0].url,
|
||||||
type,
|
type,
|
||||||
info.video_details.durationInSec,
|
info.video_details.durationInSec,
|
||||||
Number(final[0].contentLength),
|
Number(final[0].contentLength),
|
||||||
info.video_details.url,
|
info.video_details.url,
|
||||||
cookie as string
|
options.cookie as string
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function stream_from_info(info: InfoData, cookie?: string): Promise<Stream | LiveStreaming> {
|
export async function stream_from_info(info: InfoData, options: StreamOptions = {}): Promise<Stream | LiveStreaming> {
|
||||||
const final: any[] = [];
|
const final: any[] = [];
|
||||||
let type: StreamType;
|
|
||||||
if (
|
if (
|
||||||
info.LiveStreamData.isLive === true &&
|
info.LiveStreamData.isLive === true &&
|
||||||
info.LiveStreamData.hlsManifestUrl !== null &&
|
info.LiveStreamData.hlsManifestUrl !== null &&
|
||||||
@ -90,34 +87,20 @@ export async function stream_from_info(info: InfoData, cookie?: string): Promise
|
|||||||
}
|
}
|
||||||
|
|
||||||
const audioFormat = parseAudioFormats(info.format);
|
const audioFormat = parseAudioFormats(info.format);
|
||||||
const opusFormats = filterFormat(audioFormat, 'opus');
|
if (!options.quality) options.quality = audioFormat.length - 1;
|
||||||
|
else if (options.quality <= 0) options.quality = 0;
|
||||||
if (opusFormats.length === 0) {
|
else if (options.quality > audioFormat.length) options.quality = audioFormat.length - 1;
|
||||||
type = StreamType.Arbitrary;
|
final.push(audioFormat[options.quality]);
|
||||||
if (audioFormat.length === 0) {
|
let type: StreamType =
|
||||||
final.push(info.format[info.format.length - 1]);
|
audioFormat[options.quality].codec === 'opus' && audioFormat[options.quality].container === 'webm'
|
||||||
} else {
|
? StreamType.WebmOpus
|
||||||
final.push(audioFormat[audioFormat.length - 1]);
|
: StreamType.Arbitrary;
|
||||||
}
|
|
||||||
} else {
|
|
||||||
type = StreamType.WebmOpus;
|
|
||||||
final.push(opusFormats[opusFormats.length - 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Stream(
|
return new Stream(
|
||||||
final[0].url,
|
final[0].url,
|
||||||
type,
|
type,
|
||||||
info.video_details.durationInSec,
|
info.video_details.durationInSec,
|
||||||
Number(final[0].contentLength),
|
Number(final[0].contentLength),
|
||||||
info.video_details.url,
|
info.video_details.url,
|
||||||
cookie as string
|
options.cookie as string
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterFormat(formats: any[], codec: string) {
|
|
||||||
const result: any[] = [];
|
|
||||||
formats.forEach((format) => {
|
|
||||||
if (format.codec === codec) result.push(format);
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -66,10 +66,13 @@ export async function video_basic_info(url: string, cookie?: string) {
|
|||||||
initial_response.contents.twoColumnWatchNextResults.results.results.contents[1]?.videoSecondaryInfoRenderer
|
initial_response.contents.twoColumnWatchNextResults.results.results.contents[1]?.videoSecondaryInfoRenderer
|
||||||
?.owner?.videoOwnerRenderer?.badges[0];
|
?.owner?.videoOwnerRenderer?.badges[0];
|
||||||
const html5player = `https://www.youtube.com${body.split('"jsUrl":"')[1].split('"')[0]}`;
|
const html5player = `https://www.youtube.com${body.split('"jsUrl":"')[1].split('"')[0]}`;
|
||||||
const related: any[] = []
|
const related: any[] = [];
|
||||||
initial_response.contents.twoColumnWatchNextResults.secondaryResults.secondaryResults.results.forEach((res: any) => {
|
initial_response.contents.twoColumnWatchNextResults.secondaryResults.secondaryResults.results.forEach(
|
||||||
if(res.compactVideoRenderer) related.push(`https://www.youtube.com/watch?v=${res.compactVideoRenderer.videoId}`)
|
(res: any) => {
|
||||||
})
|
if (res.compactVideoRenderer)
|
||||||
|
related.push(`https://www.youtube.com/watch?v=${res.compactVideoRenderer.videoId}`);
|
||||||
|
}
|
||||||
|
);
|
||||||
const format = [];
|
const format = [];
|
||||||
const vid = player_response.videoDetails;
|
const vid = player_response.videoDetails;
|
||||||
const microformat = player_response.microformat.playerMicroformatRenderer;
|
const microformat = player_response.microformat.playerMicroformatRenderer;
|
||||||
@ -106,7 +109,7 @@ export async function video_basic_info(url: string, cookie?: string) {
|
|||||||
html5player,
|
html5player,
|
||||||
format,
|
format,
|
||||||
video_details,
|
video_details,
|
||||||
related_videos : related
|
related_videos: related
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,21 +7,22 @@ import fs from 'fs';
|
|||||||
import { sp_validate, yt_validate, so_validate } from '.';
|
import { sp_validate, yt_validate, so_validate } from '.';
|
||||||
import { SpotifyAuthorize } from './Spotify';
|
import { SpotifyAuthorize } from './Spotify';
|
||||||
import { check_id, stream as so_stream, stream_from_info as so_stream_info } from './SoundCloud';
|
import { check_id, stream as so_stream, stream_from_info as so_stream_info } from './SoundCloud';
|
||||||
import { InfoData, stream as yt_stream, stream_from_info as yt_stream_info } from './YouTube/stream';
|
import { InfoData, stream as yt_stream, StreamOptions, stream_from_info as yt_stream_info } from './YouTube/stream';
|
||||||
import { SoundCloudTrack, Stream as SoStream } from './SoundCloud/classes';
|
import { SoundCloudTrack, Stream as SoStream } from './SoundCloud/classes';
|
||||||
import { LiveStreaming, Stream as YTStream } from './YouTube/classes/LiveStream';
|
import { LiveStreaming, Stream as YTStream } from './YouTube/classes/LiveStream';
|
||||||
|
|
||||||
export async function stream(url: string, cookie?: string): Promise<YTStream | LiveStreaming | SoStream> {
|
export async function stream(url: string, options: StreamOptions = {}): Promise<YTStream | LiveStreaming | SoStream> {
|
||||||
if (url.indexOf('soundcloud') !== -1) return await so_stream(url);
|
if(url.length === 0) throw new Error('Stream URL has a length of 0. Check your url again.')
|
||||||
else return await yt_stream(url, cookie);
|
if (url.indexOf('soundcloud') !== -1) return await so_stream(url, options.quality);
|
||||||
|
else return await yt_stream(url, { cookie : options.cookie });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function stream_from_info(
|
export async function stream_from_info(
|
||||||
info: InfoData | SoundCloudTrack,
|
info: InfoData | SoundCloudTrack,
|
||||||
cookie?: string
|
options: StreamOptions = {}
|
||||||
): Promise<YTStream | LiveStreaming | SoStream> {
|
): Promise<YTStream | LiveStreaming | SoStream> {
|
||||||
if (info instanceof SoundCloudTrack) return await so_stream_info(info);
|
if (info instanceof SoundCloudTrack) return await so_stream_info(info);
|
||||||
else return await yt_stream_info(info, cookie);
|
else return await yt_stream_info(info, { cookie : options.cookie });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function validate(url: string): Promise<string | boolean> {
|
export async function validate(url: string): Promise<string | boolean> {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user