import { Thumbnails , BaseAttributes , VideoCompact , ChannelCompact , Base } from "." ; import { getContinuationFromItems , mapFilter , YoutubeRawData } from "../common" ; import { I_END_POINT } from "../constants" ; hidden interface PlaylistAttributes extends BaseAttributes { title : string ; videoCount : number ; viewCount : number ; lastUpdatedAt : string ; channel ? : ChannelCompact ; videos : VideoCompact [ ] ; continuation ? : string ; } /** Represents a Playlist, usually returned from `client.getPlaylist()` */ export default class Playlist extends Base implements PlaylistAttributes { /** The title of this playlist */ title ! : string ; /** How many videos in this playlist */ videoCount ! : number ; /** How many viewers does this playlist have */ viewCount ! : number ; /** Last time this playlist is updated */ lastUpdatedAt ! : string ; /** The channel that made this playlist */ channel ? : ChannelCompact ; /** Videos in the playlist */ videos : VideoCompact [ ] = [ ] ; /** Current continuation token to load next videos  */ continuation ! : string | undefined ; hidden constructor ( playlist = { } ) { super ( ) ; Object . assign ( this , playlist ) ; } hidden load ( data ) { const sidebarRenderer = data . sidebar . playlistSidebarRenderer . items ; const primaryRenderer = sidebarRenderer [ 0 ] . playlistSidebarPrimaryInfoRenderer ; const metadata = data . metadata . playlistMetadataRenderer ; this . id = Object . values < string > ( metadata ) . find ( ( v ) => v . includes ( "playlist?list=" ) ) ?. split ( "=" ) [ 1 ] as string ; this . title = metadata . title ; const { stats } = primaryRenderer ; if ( primaryRenderer . stats . length === 3 ) { this . videoCount = Playlist . parseSideBarInfo ( stats [ 0 ] , true ) ; this . viewCount = Playlist . parseSideBarInfo ( stats [ 1 ] , true ) ; this . lastUpdatedAt = Playlist . parseSideBarInfo ( stats [ 2 ] , false ) ; } else if ( stats . length === 2 ) { this . videoCount = Playlist . parseSideBarInfo ( stats [ 0 ] , true ) ; this . lastUpdatedAt = Playlist . parseSideBarInfo ( stats [ 1 ] , false ) ; } const playlistContents = data . contents . twoColumnBrowseResultsRenderer . tabs [ 0 ] . tabRenderer . content . sectionListRenderer . contents [ 0 ] . itemSectionRenderer . contents [ 0 ] . playlistVideoListRenderer ?. contents || [ ] ; this . continuation = getContinuationFromItems ( playlistContents ) ; const videoOwner = sidebarRenderer [ 1 ] ?. playlistSidebarSecondaryInfoRenderer . videoOwner ; if ( videoOwner ) { const { title , thumbnail } = videoOwner . videoOwnerRenderer ; this . channel = new ChannelCompact ( { id : title . runs [ 0 ] . navigationEndpoint . browseEndpoint . browseId , name : title . runs [ 0 ] . text , thumbnails : new Thumbnails ( ) . load ( thumbnail . thumbnails ) , client : this . client , } ) ; } this . videos = Playlist . parseVideos ( playlistContents , this ) ; return this ; } /**
     * Load next 100 videos of the playlist, and push the loaded videos to  Playlist . videos example param count async next ( count = 1 ) { const newVideos = [ ] ; for ( let i = 0 ; i < count || count == 0 ; i ++ ) { if ( ! this . continuation ) break ; const response = await this . client . http . post ( `${ I_END_POINT }/browse` , { data : { continuation : this . continuation } , } ) ; const playlistContents = response . data . onResponseReceivedActions [ 0 ] . appendContinuationItemsAction . continuationItems ; const videos = mapFilter ( playlistContents , "playlistVideoRenderer" ) ; newVideos . push ( ... videos . map ( ( video ) => new VideoCompact ( { client : this . client } ) . load ( video ) ) ) ; this . continuation = getContinuationFromItems ( playlistContents ) ; } this . videos . push ( ... newVideos ) ; return newVideos ; } param playlistContents private static parseVideos ( playlistContents , playlist ) { const videosRenderer = playlistContents . map ( ( c ) => c . playlistVideoRenderer ) ; const videos = [ ] ; for ( const videoRenderer of videosRenderer ) { if ( ! videoRenderer ) continue ; const video = new VideoCompact ( { client : playlist . client } ) . load ( videoRenderer ) ; videos . push ( video ) ; } return videos ; } private static parseSideBarInfo < T extends boolean = true > ( stats , parseInt ) { let data ; if ( "runs" in stats ) data = stats . runs . map ( ( r ) => r . text ) . join ( "" ) ; else data = stats . simpleText . replace ( /[^0-9]/g , "" ) ; if ( parseInt ) data = + data . replace ( /[^0-9]/g , "" ) ; return data ; } }