@ -39,6 +39,8 @@ export class MainComponent implements OnInit {
audioFolderPath ;
videoFolderPath ;
cachedAvailableFormats = { } ;
// youtube api
youtubeSearchEnabled = false ;
youtubeAPIKey = null ;
@ -52,7 +54,104 @@ export class MainComponent implements OnInit {
urlForm = new FormControl ( '' , [ Validators . required ] ) ;
qualityOptions = {
'video' : [
{
'resolution' : null ,
'value' : '' ,
'label' : 'Max'
} ,
{
'resolution' : '3840x2160' ,
'value' : '2160' ,
'label' : '2160p (4K)'
} ,
{
'resolution' : '2560x1440' ,
'value' : '1440' ,
'label' : '1440p'
} ,
{
'resolution' : '1920x1080' ,
'value' : '1080' ,
'label' : '1080p'
} ,
{
'resolution' : '1280x720' ,
'value' : '720' ,
'label' : '720p'
} ,
{
'resolution' : '720x480' ,
'value' : '480' ,
'label' : '480p'
} ,
{
'resolution' : '480x360' ,
'value' : '360' ,
'label' : '360p'
} ,
{
'resolution' : '360x240' ,
'value' : '240' ,
'label' : '240p'
} ,
{
'resolution' : '256x144' ,
'value' : '144' ,
'label' : '144p'
}
] ,
'audio' : [
{
'kbitrate' : null ,
'value' : '' ,
'label' : 'Max'
} ,
{
'kbitrate' : '256' ,
'value' : '256K' ,
'label' : '256 Kbps'
} ,
{
'kbitrate' : '160' ,
'value' : '160K' ,
'label' : '160 Kbps'
} ,
{
'kbitrate' : '128' ,
'value' : '128K' ,
'label' : '128 Kbps'
} ,
{
'kbitrate' : '96' ,
'value' : '96K' ,
'label' : '96 Kbps'
} ,
{
'kbitrate' : '70' ,
'value' : '70K' ,
'label' : '70 Kbps'
} ,
{
'kbitrate' : '50' ,
'value' : '50K' ,
'label' : '50 Kbps'
} ,
{
'kbitrate' : '32' ,
'value' : '32K' ,
'label' : '32 Kbps'
}
]
}
selectedQuality = '' ;
formats_loading = false ;
@ViewChild ( 'urlinput' , { read : ElementRef , static : false } ) urlInput : ElementRef ;
last_valid_url = '' ;
last_url_check = 0 ;
constructor ( private postsService : PostsService , private youtubeSearch : YoutubeSearchService , public snackBar : MatSnackBar ,
private router : Router ) {
@ -67,7 +166,8 @@ export class MainComponent implements OnInit {
this . baseStreamPath = result [ 'YoutubeDLMaterial' ] [ 'Downloader' ] [ 'path-base' ] ;
this . audioFolderPath = result [ 'YoutubeDLMaterial' ] [ 'Downloader' ] [ 'path-audio' ] ;
this . videoFolderPath = result [ 'YoutubeDLMaterial' ] [ 'Downloader' ] [ 'path-video' ] ;
this . youtubeSearchEnabled = result [ 'YoutubeDLMaterial' ] [ 'API' ] && result [ 'YoutubeDLMaterial' ] [ 'API' ] [ 'use_youtube_API' ] ;
this . youtubeSearchEnabled = result [ 'YoutubeDLMaterial' ] [ 'API' ] && result [ 'YoutubeDLMaterial' ] [ 'API' ] [ 'use_youtube_API' ] &&
result [ 'YoutubeDLMaterial' ] [ 'API' ] [ 'youtube_API_key' ] ;
this . youtubeAPIKey = this . youtubeSearchEnabled ? result [ 'YoutubeDLMaterial' ] [ 'API' ] [ 'youtube_API_key' ] : null ;
this . postsService . path = backendUrl ;
@ -150,17 +250,18 @@ export class MainComponent implements OnInit {
if ( forceView === false && this . downloadOnlyMode && ! this . iOS ) {
if ( is_playlist ) {
for ( let i = 0 ; i < name . length ; i ++ ) {
this . downloadAudioFile ( name [ i ] ) ;
this . downloadAudioFile ( decodeURI ( name [ i ] ) ) ;
}
} else {
this . downloadAudioFile ( name ) ;
this . downloadAudioFile ( decodeURI ( name ) ) ;
}
} else {
if ( is_playlist ) {
this . router . navigate ( [ '/player' , { fileNames : name.join ( '|nvr|' ) , type : 'audio' } ] ) ;
// window.location.href = this.baseStreamPath + this.audioFolderPath + name[0] + '.mp3';
} else {
window . location . href = this . baseStreamPath + this . audioFolderPath + name + '.mp3' ;
this . router . navigate ( [ '/player' , { fileNames : name , type : 'audio' } ] ) ;
// window.location.href = this.baseStreamPath + this.audioFolderPath + name + '.mp3';
}
}
@ -177,10 +278,10 @@ export class MainComponent implements OnInit {
if ( forceView === false && this . downloadOnlyMode ) {
if ( is_playlist ) {
for ( let i = 0 ; i < name . length ; i ++ ) {
this . downloadVideoFile ( name [ i ] ) ;
this . downloadVideoFile ( decodeURI ( name [ i ] ) ) ;
}
} else {
this . downloadVideoFile ( name ) ;
this . downloadVideoFile ( decodeURI ( name ) ) ;
}
} else {
if ( is_playlist ) {
@ -206,7 +307,17 @@ export class MainComponent implements OnInit {
if ( this . audioOnly ) {
this . downloadingfile = true ;
this . postsService . makeMP3 ( this . url ) . subscribe ( posts = > {
let customQualityConfiguration = null ;
if ( this . selectedQuality !== '' ) {
const cachedFormatsExists = this . cachedAvailableFormats [ this . url ] ;
if ( cachedFormatsExists ) {
const audio_formats = this . cachedAvailableFormats [ this . url ] [ 'audio' ] ;
customQualityConfiguration = audio_formats [ this . selectedQuality ] [ 'format_id' ] ;
}
}
this . postsService . makeMP3 ( this . url , ( this . selectedQuality === '' ? null : this . selectedQuality ) ,
customQualityConfiguration ) . subscribe ( posts = > {
const is_playlist = ! ! ( posts [ 'file_names' ] ) ;
this . path = is_playlist ? posts [ 'file_names' ] : posts [ 'audiopathEncoded' ] ;
if ( this . path !== '-1' ) {
@ -217,8 +328,20 @@ export class MainComponent implements OnInit {
this . openSnackBar ( 'Download failed!' , 'OK.' ) ;
} ) ;
} else {
let customQualityConfiguration = null ;
const cachedFormatsExists = this . cachedAvailableFormats [ this . url ] ;
if ( cachedFormatsExists ) {
const video_formats = this . cachedAvailableFormats [ this . url ] [ 'video' ] ;
if ( video_formats [ 'best_audio_format' ] && this . selectedQuality !== '' / * &&
video_formats [ this . selectedQuality ] [ 'acodec' ] === 'none' * / ) {
console . log ( this . selectedQuality ) ;
customQualityConfiguration = video_formats [ this . selectedQuality ] [ 'format_id' ] + '+' + video_formats [ 'best_audio_format' ] ;
}
}
this . downloadingfile = true ;
this . postsService . makeMP4 ( this . url ) . subscribe ( posts = > {
this . postsService . makeMP4 ( this . url , ( this . selectedQuality === '' ? null : this . selectedQuality ) ,
customQualityConfiguration ) . subscribe ( posts = > {
const is_playlist = ! ! ( posts [ 'file_names' ] ) ;
this . path = is_playlist ? posts [ 'file_names' ] : posts [ 'videopathEncoded' ] ;
if ( this . path !== '-1' ) {
@ -239,10 +362,13 @@ export class MainComponent implements OnInit {
const blob : Blob = res ;
saveAs ( blob , name + '.mp3' ) ;
if ( ! this . fileManagerEnabled ) {
// tell server to delete the file once downloaded
this . postsService . deleteFile ( name , true ) . subscribe ( delRes = > {
// reload mp3s
this . getMp3s ( ) ;
} ) ;
}
} ) ;
}
@ -251,10 +377,13 @@ export class MainComponent implements OnInit {
const blob : Blob = res ;
saveAs ( blob , name + '.mp4' ) ;
if ( ! this . fileManagerEnabled ) {
// tell server to delete the file once downloaded
this . postsService . deleteFile ( name , false ) . subscribe ( delRes = > {
// reload mp4s
this . getMp4s ( ) ;
} ) ;
}
} ) ;
}
@ -291,7 +420,22 @@ export class MainComponent implements OnInit {
// tslint:disable-next-line: max-line-length
const strRegex = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)/ ;
const re = new RegExp ( strRegex ) ;
return re . test ( str ) ;
const valid = re . test ( str ) ;
if ( ! valid ) { return false ; }
// tslint:disable-next-line: max-line-length
const youtubeStrRegex = /(?:http(?:s)?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:(?:watch)?\?(?:.*&)?v(?:i)?=|(?:embed|v|vi|user)\/))([^\?&\"'<> #]+)/ ;
const reYT = new RegExp ( youtubeStrRegex ) ;
const ytValid = reYT . test ( str ) ;
if ( valid && ytValid && Date . now ( ) - this . last_url_check > 1000 ) {
if ( str !== this . last_valid_url ) {
// get info
this . getURLInfo ( str ) ;
}
this . last_valid_url = str ;
}
return valid ;
}
// snackbar helper
@ -301,6 +445,22 @@ export class MainComponent implements OnInit {
} ) ;
}
getURLInfo ( url ) {
console . log ( this . cachedAvailableFormats [ url ] ) ;
if ( ! ( this . cachedAvailableFormats [ url ] ) ) {
this . formats_loading = true ;
console . log ( 'has no cached formats available' ) ;
this . postsService . getFileInfo ( [ url ] , 'irrelevant' , true ) . subscribe ( res = > {
if ( url === this . url ) { this . formats_loading = false ; }
const infos = res [ 'result' ] ;
const parsed_infos = this . getAudioAndVideoFormats ( infos . formats ) ;
console . log ( 'got formats for ' + url ) ;
const available_formats = { audio : parsed_infos [ 0 ] , video : parsed_infos [ 1 ] } ;
this . cachedAvailableFormats [ url ] = available_formats ;
} ) ;
}
}
attachToInput() {
Observable . fromEvent ( this . urlInput . nativeElement , 'keyup' )
. map ( ( e : any ) = > e . target . value ) // extract the value of input
@ -334,5 +494,71 @@ export class MainComponent implements OnInit {
onResize ( event ) {
this . files_cols = ( event . target . innerWidth <= 450 ) ? 2 : 4 ;
}
videoModeChanged ( new_val ) {
this . selectedQuality = '' ;
}
getAudioAndVideoFormats ( formats ) : any [ ] {
const audio_formats = { } ;
const video_formats = { } ;
for ( let i = 0 ; i < formats . length ; i ++ ) {
const format_obj = { type : null } ;
const format = formats [ i ] ;
const format_type = ( format . vcodec === 'none' ) ? 'audio' : 'video' ;
format_obj . type = format_type ;
// console.log(format);
if ( format_obj . type === 'audio' && format . abr ) {
const key = format . abr . toString ( ) + 'K' ;
format_obj [ 'bitrate' ] = format . abr ;
format_obj [ 'format_id' ] = format . format_id ;
format_obj [ 'ext' ] = format . ext ;
// don't overwrite if not m4a
if ( audio_formats [ key ] ) {
if ( format . ext === 'm4a' ) {
audio_formats [ key ] = format_obj ;
}
} else {
audio_formats [ key ] = format_obj ;
}
} else if ( format_obj . type === 'video' ) {
// check if video format is mp4
const key = format . height . toString ( ) ;
if ( format . ext === 'mp4' ) {
format_obj [ 'height' ] = format . height ;
format_obj [ 'acodec' ] = format . acodec ;
format_obj [ 'format_id' ] = format . format_id ;
// no acodec means no overwrite
if ( ! ( video_formats [ key ] ) || format_obj [ 'acodec' ] !== 'none' ) {
video_formats [ key ] = format_obj ;
}
}
}
}
video_formats [ 'best_audio_format' ] = this . getBestAudioFormatForMp4 ( audio_formats ) ;
return [ audio_formats , video_formats ]
}
getBestAudioFormatForMp4 ( audio_formats ) {
let best_audio_format_for_mp4 = null ;
let best_audio_format_bitrate = 0 ;
const available_audio_format_keys = Object . keys ( audio_formats ) ;
for ( let i = 0 ; i < available_audio_format_keys . length ; i ++ ) {
const audio_format_key = available_audio_format_keys [ i ] ;
const audio_format = audio_formats [ audio_format_key ] ;
const is_m4a = audio_format . ext === 'm4a' ;
if ( is_m4a && audio_format . bitrate > best_audio_format_bitrate ) {
best_audio_format_for_mp4 = audio_format . format_id ;
best_audio_format_bitrate = audio_format . bitrate ;
}
}
return best_audio_format_for_mp4 ;
}
}