/usr/share/perl5/FlashVideo/HLSDownloader.pm is in get-flash-videos 1.25.98-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | # Part of get-flash-videos. See get_flash_videos for copyright.
package FlashVideo::HLSDownloader;
use strict;
use warnings;
use base 'FlashVideo::Downloader';
use FlashVideo::Utils;
use FlashVideo::JSON;
use Term::ProgressBar;
my $bitrate_index = {
high => 0,
medium => 1,
low => 2
};
sub cleanup_audio {
my ($in_file, $out_file) = @_;
my @args = {};
# Look for executable (ffmpeg or avconv)
if (!is_program_on_path("ffmpeg")) {
if (!is_program_on_path("avconv")) {
die "Could not find ffmpeg nor avconv executable!";
} else {
@args = (
"avconv",
"-i", $in_file,
"-bsf:a", "aac_adtstoasc",
"-c", "copy",
"-f", "mp4",
$out_file
);
}
} else {
@args = (
"ffmpeg",
"-i", $in_file,
"-absf", "aac_adtstoasc",
"-c", "copy",
"-f", "mp4",
$out_file
);
}
# Execute command
if (system(@args) != 0) {
die "Calling @args failed: $?";
}
return 1;
}
sub read_hls_playlist {
my($browser, $url) = @_;
$browser->get($url);
if (!$browser->success) {
die "Couldn't download m3u file, $url: " . $browser->response->status_line;
}
my @lines = split(/\r?\n/, $browser->content);
my %urltable = ();
my $i = 0;
# Fill the url table
foreach my $line (@lines) {
if ($line =~ /EXT-X-STREAM-INF/ && $line =~ /BANDWIDTH/) {
$line =~ /BANDWIDTH=([0-9]*)/;
$urltable{int($1)} = $lines[$i + 1];
}
$i++;
}
return %urltable;
}
sub download {
my ($self, $args, $file, $browser) = @_;
my $hls_url = $args->{args}->{hls_url};
my $prefs = $args->{args}->{prefs};
$browser->get($hls_url);
my %urls = read_hls_playlist($browser, $hls_url);
# Sort the urls and select the suitable one based upon quality preference
my $quality = $bitrate_index->{$prefs->{quality}};
my $min = $quality < scalar(keys(%urls)) ? $quality : scalar(keys(%urls));
my $key = (sort {int($b) <=> int($a)} keys %urls)[$min];
my ($hls_base, $trail) = ($hls_url =~ m/(.*\/)(.*)\.m3u8/);
my $filename_mp4 = $args->{flv};
my $filename_ts = $args->{flv} . ".ts";
my $filename_ts_segment = $args->{flv} . ".tsx";
my $video_url = $urls{$key} =~ m/http(s?):\/\// ? $urls{$key} : $hls_base.$urls{$key};
$browser->get($video_url);
if (! $browser->success) {
die "Unable to read segments" . $browser->response->status_line;
}
my @lines = split(/\r?\n/, $browser->content);
my @segments = ();
# Fill the url table
foreach my $line (@lines) {
if ($line !~ /#/) {
push @segments, $line;
}
}
my $i = 1;
my $num_segs = @segments;
info "Downloading segments";
my $progress_bar = Term::ProgressBar->new($num_segs);
open(my $fh_app, '>', $filename_ts) or die "Could not open file $filename_ts";
binmode($fh_app);
my $buffer;
foreach my $url (@segments) {
# Download and save each segment in a re-used segment file.
# Otherwise, the process memory expands monotonically. Large downloads would use up
# all memory and kill the process.
$browser->get($url, ":content_file" => $filename_ts_segment);
# Open the segment and append it to the TS file.
open(SEG, '<', $filename_ts_segment) or die "Could not open file $filename_ts_segment";
binmode(SEG);
while (read(SEG, $buffer, 16384)) {
print $fh_app $buffer;
}
close(SEG);
$progress_bar->update($i);
$i++;
}
# Remove the segment file as it is no longer needed.
unlink $filename_ts_segment;
close($fh_app);
cleanup_audio($filename_ts, $filename_mp4);
$self->{printable_filename} = $filename_mp4;
unlink $filename_ts;
return -s $filename_mp4;
}
1;
|