2022-02-28 15:24:06 +00:00
//--- Modules/Imports ---//
2022-02-25 18:54:15 +00:00
import { Http } from '@capacitor-community/http' ;
2022-03-16 09:07:40 +00:00
import Innertube from './innertube'
2022-03-20 12:20:13 +00:00
import constants from './constants' ;
import useRender from './renderers' ;
2022-02-25 18:54:15 +00:00
2022-02-28 15:24:06 +00:00
//--- Logger Function ---//
2022-03-16 09:07:40 +00:00
function logger ( func , data , isError = false ) {
searchModule . logs . unshift ( {
name : func ,
time : Date . now ( ) ,
data : data ,
error : isError
} )
2022-02-28 15:24:06 +00:00
}
2022-03-13 23:21:41 +00:00
//--- Youtube Base Parser ---//
function youtubeParse ( html , callback ) {
2022-03-16 09:07:40 +00:00
//--- Replace Encoded Characters ---///
html = html . replace ( /\\x([0-9A-F]{2})/ig , ( ... items ) => { return String . fromCharCode ( parseInt ( items [ 1 ] , 16 ) ) ; } ) ;
//--- Properly Format JSON ---//
html = html . replaceAll ( "\\\\\"" , "" ) ;
//--- Parse JSON ---//
html = JSON . parse ( html ) ;
//--- Get Results ---// ( Thanks To appit-online On Github ) -> https://github.com/appit-online/youtube-search/blob/master/src/lib/search.ts
let results ;
if ( html && html . contents && html . contents . sectionListRenderer && html . contents . sectionListRenderer . contents &&
html . contents . sectionListRenderer . contents . length > 0 &&
html . contents . sectionListRenderer . contents [ 0 ] . itemSectionRenderer &&
html . contents . sectionListRenderer . contents [ 0 ] . itemSectionRenderer . contents . length > 0 ) {
results = html . contents . sectionListRenderer . contents [ 0 ] . itemSectionRenderer . contents ;
2022-03-19 05:35:28 +00:00
logger ( constants . LOGGER _NAMES . search , results ) ;
2022-03-16 09:07:40 +00:00
callback ( results ) ;
} else {
try {
results = JSON . parse ( html . split ( '{"itemSectionRenderer":{"contents":' ) [ html . split ( '{"itemSectionRenderer":{"contents":' ) . length - 1 ] . split ( ',"continuations":[{' ) [ 0 ] ) ;
2022-03-19 05:35:28 +00:00
logger ( constants . LOGGER _NAMES . search , results ) ;
2022-03-16 09:07:40 +00:00
callback ( results ) ;
} catch ( e ) { }
try {
results = JSON . parse ( html . split ( '{"itemSectionRenderer":' ) [ html . split ( '{"itemSectionRenderer":' ) . length - 1 ] . split ( '},{"continuationItemRenderer":{' ) [ 0 ] ) . contents ;
2022-03-19 05:35:28 +00:00
logger ( constants . LOGGER _NAMES . search , results ) ;
2022-03-16 09:07:40 +00:00
callback ( results ) ;
} catch ( e ) { }
}
2022-03-13 23:21:41 +00:00
}
2022-03-04 02:07:53 +00:00
//--- Search Main Function ---//
function youtubeSearch ( text , callback ) {
2022-03-16 09:07:40 +00:00
Http . request ( {
method : 'GET' ,
2022-03-17 05:57:28 +00:00
url : ` ${ constants . URLS . YT _URL } /results ` ,
2022-03-16 09:07:40 +00:00
params : { q : text , hl : "en" }
} )
. then ( ( res ) => {
//--- Get HTML Only ---//
let html = res . data ;
//--- Isolate The Script Containing Video Information ---//
html = html . split ( "var ytInitialData = '" ) [ 1 ] . split ( "';</script>" ) [ 0 ] ;
youtubeParse ( html , ( data ) => {
callback ( data ) ;
} )
} )
. catch ( ( err ) => {
2022-03-19 05:35:28 +00:00
logger ( constants . LOGGER _NAMES . search , err , true ) ;
2022-03-16 09:07:40 +00:00
callback ( err ) ;
} ) ;
2022-03-04 02:07:53 +00:00
}
2022-03-16 09:07:40 +00:00
const searchModule = {
logs : new Array ( ) ,
//--- Get YouTube's Search Auto Complete ---//
autoComplete ( text , callback ) {
Http . request ( {
method : 'GET' ,
2022-03-17 05:57:28 +00:00
url : ` ${ constants . URLS . YT _SUGGESTIONS } /search ` ,
2022-03-16 09:07:40 +00:00
params : { client : 'youtube' , q : text }
} )
. then ( ( res ) => {
2022-03-19 05:35:28 +00:00
logger ( constants . LOGGER _NAMES . autoComplete , res ) ;
2022-03-16 09:07:40 +00:00
callback ( res . data ) ;
} )
. catch ( ( err ) => {
2022-03-19 05:35:28 +00:00
logger ( constants . LOGGER _NAMES . autoComplete , err , true ) ;
2022-03-16 09:07:40 +00:00
callback ( err ) ;
} ) ;
} ,
search ( text , callback ) {
let results = new Array ( ) ;
youtubeSearch ( text , ( videos ) => {
for ( const i in videos ) {
const video = videos [ i ] ;
if ( video . compactVideoRenderer ) {
//--- If Entry Is A Video ---//
results . push ( {
id : video . compactVideoRenderer . videoId ,
title : video . compactVideoRenderer . title . runs [ 0 ] . text ,
runtime : video . compactVideoRenderer . lengthText . runs [ 0 ] . text ,
uploaded : video . compactVideoRenderer . publishedTimeText . runs [ 0 ] . text ,
views : video . compactVideoRenderer . viewCountText . runs [ 0 ] . text ,
thumbnails : video . compactVideoRenderer . thumbnail . thumbnails
} )
} else {
//--- If Entry Is Not A Video ---//
2022-03-19 05:35:28 +00:00
//logger(constants.LOGGER_NAMES.search, { type: "Error Caught Successfully", error: video }, true);
2022-03-16 09:07:40 +00:00
}
}
} )
callback ( results ) ;
} ,
2022-03-19 06:17:18 +00:00
getRemainingVideoInfo ( id , callback ) {
String . prototype . decodeEscapeSequence = function ( ) {
return this . replace ( /\\x([0-9A-Fa-f]{2})/g , function ( ) {
return String . fromCharCode ( parseInt ( arguments [ 1 ] , 16 ) ) ;
} ) ;
} ;
Http . request ( {
method : 'GET' ,
url : ` ${ constants . URLS . YT _URL } /watch ` ,
params : { v : id }
} )
. then ( ( res ) => {
let dataUpdated = res . data . decodeEscapeSequence ( )
let likes = dataUpdated . split ( ` "defaultIcon":{"iconType":"LIKE"},"defaultText":{"runs":[{"text":" ` ) [ 1 ] . split ( ` "}],"accessibility": ` ) [ 0 ]
let uploadDate = dataUpdated . split ( ` "uploadDate":" ` ) [ 1 ] . split ( ` }},"trackingParams":" ` ) [ 0 ] . slice ( 0 , - 2 ) ;
let data = {
"likes" : likes ,
"uploadDate" : uploadDate
}
logger ( "vidData" , data )
callback ( data )
} )
. catch ( ( err ) => {
logger ( "codeRun" , err , true ) ;
callback ( err ) ;
} ) ;
} ,
getReturnYoutubeDislike ( id , callback ) {
Http . request ( {
method : 'GET' ,
url : ` https://returnyoutubedislikeapi.com/votes ` ,
params : { videoId : id }
} )
. then ( ( res ) => {
logger ( "rydData" , res . data )
callback ( res . data )
} )
. catch ( ( err ) => {
logger ( "codeRun" , err , true ) ;
callback ( err ) ;
} ) ;
2022-03-16 09:07:40 +00:00
}
2022-02-25 18:54:15 +00:00
2022-03-16 09:07:40 +00:00
}
2022-03-13 23:45:04 +00:00
2022-03-20 09:02:52 +00:00
//--- Recommendations ---//
2022-03-17 13:17:32 +00:00
2022-03-18 06:15:19 +00:00
let InnertubeAPI ;
2022-03-20 09:02:52 +00:00
// Loads Innertube object. This will be the object used in all future Innertube API calls. getAPI Code provided by Lightfire228 (https://github.com/Lightfire228)
2022-03-17 13:17:32 +00:00
// These are just a way for the backend Javascript to communicate with the front end Vue scripts. Essentially a wrapper inside a wrapper
2022-03-20 09:50:13 +00:00
const innertubeModule = {
2022-03-18 06:15:19 +00:00
async getAPI ( ) {
2022-03-18 11:50:44 +00:00
if ( ! InnertubeAPI ) {
2022-03-19 05:35:28 +00:00
InnertubeAPI = await Innertube . createAsync ( ( message , isError ) => { logger ( constants . LOGGER _NAMES . innertube , message , isError ) ; } )
2022-03-18 11:50:44 +00:00
}
2022-03-18 06:15:19 +00:00
return InnertubeAPI ;
} ,
2022-03-17 13:17:32 +00:00
async getVid ( id ) {
2022-03-20 07:24:38 +00:00
try {
return await InnertubeAPI . VidInfoAsync ( id )
} catch ( error ) {
logger ( constants . LOGGER _NAMES . watch , error , true )
}
2022-03-17 13:17:32 +00:00
} ,
2022-03-19 13:15:40 +00:00
// It just works™
2022-03-20 12:20:13 +00:00
// Front page recommendation
2022-03-17 05:57:28 +00:00
async recommend ( ) {
2022-03-19 13:15:40 +00:00
const response = await InnertubeAPI . getRecommendationsAsync ( ) ;
if ( ! response . success ) throw new Error ( "An error occurred and innertube failed to respond" )
const contents = response . data . contents . singleColumnBrowseResultsRenderer . tabs [ 0 ] . tabRenderer . content . sectionListRenderer . contents
2022-03-20 12:20:13 +00:00
const final = contents . map ( ( shelves ) => {
2022-03-19 13:15:40 +00:00
const video = shelves . shelfRenderer ? . content ? . horizontalListRenderer ? . items
if ( video ) return video . map ( ( item ) => {
2022-03-20 12:20:13 +00:00
if ( item ) {
const renderedItem = useRender ( item [ Object . keys ( item ) [ 0 ] ] , Object . keys ( item ) [ 0 ] )
console . log ( renderedItem )
return renderedItem
} else { return undefined }
2022-03-19 13:15:40 +00:00
} )
} )
2022-03-20 12:20:13 +00:00
console . log ( final )
return final
} ,
2022-02-25 18:39:17 +00:00
2022-03-20 12:20:13 +00:00
// This is the recommendations that exist under videos
2022-03-21 00:27:41 +00:00
viewRecommends ( recommendList ) {
if ( recommendList ) return recommendList . map ( ( item ) => {
if ( item ) {
return useRender ( item [ Object . keys ( item ) [ 0 ] ] , Object . keys ( item ) [ 0 ] )
} else { return undefined }
} )
2022-03-21 13:21:18 +00:00
} ,
2022-03-20 09:50:13 +00:00
}
2022-02-28 15:24:06 +00:00
//--- Start ---//
2022-02-25 18:39:17 +00:00
export default ( { app } , inject ) => {
2022-03-20 12:20:13 +00:00
inject ( 'youtube' , { ... searchModule , ... innertubeModule } )
2022-03-18 06:15:19 +00:00
inject ( "logger" , logger )
2022-02-25 18:39:17 +00:00
}
2022-03-19 18:26:20 +00:00
logger ( constants . LOGGER _NAMES . init , "Program Started" ) ;