twitch.pl 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4. use LWP::UserAgent;
  5. # App config
  6. my $HTTP_TIMEOUT = 10;
  7. my $HTTP_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/7046A194A';
  8. my $HTTP_VERIFY = 0;
  9. # Globals
  10. my $LWP;
  11. my $QUALITY_URL;
  12. my $STREAM_URL;
  13. # Command-line parameters
  14. if (scalar(@ARGV) < 1) {
  15. die('Usage: ' . $0 . "M3U8_URL\n");
  16. }
  17. $QUALITY_URL = $ARGV[0];
  18. # Environmental parameters
  19. my $DEBUG = 0;
  20. if (defined($ENV{'DEBUG'}) && $ENV{'DEBUG'} =~ /(\d+)/) {
  21. $DEBUG = $1;
  22. }
  23. # Prototypes
  24. sub fetch($$);
  25. # Fetch and parse the quality playlist
  26. {
  27. my $playlist;
  28. if (fetch($QUALITY_URL, \$playlist) == 200) {
  29. if ($DEBUG > 1) {
  30. print STDERR "Quality playist:\n" . $playlist . "\n";
  31. }
  32. my $baseURL = $QUALITY_URL;
  33. $baseURL =~ s/\?[^\?]*$//;
  34. $baseURL =~ s/\/[^\/]*$//;
  35. if ($DEBUG > 2) {
  36. print STDERR 'Quality playlist base URL: ' . $baseURL . "\n";
  37. }
  38. my $lastBandwidth = 0;
  39. my $savedBandwidth = 0;
  40. foreach my $segment (split(/^/, $playlist)) {
  41. if ($segment =~ /^\s*$/) {
  42. next;
  43. }
  44. $segment =~ s/^\s+//;
  45. $segment =~ s/\s+$//;
  46. # Extract bandwidth data
  47. if ($segment =~ /^#/) {
  48. if ($segment =~ /^#EXT-X-STREAM-INF:.*\bBANDWIDTH=(\d+)\b/) {
  49. $lastBandwidth = $1;
  50. }
  51. next;
  52. }
  53. # Keep the best stream
  54. if ($DEBUG > 1) {
  55. print STDERR 'Considering stream URL (' . $lastBandwidth . '): ' . $segment . "\n";
  56. }
  57. if ($lastBandwidth > $savedBandwidth) {
  58. $savedBandwidth = $lastBandwidth;
  59. $STREAM_URL = $segment;
  60. }
  61. }
  62. }
  63. }
  64. if (!defined($STREAM_URL)) {
  65. die("Unable to find stream URL\n");
  66. }
  67. if ($DEBUG) {
  68. print STDERR 'Keeping stream URL: ' . $STREAM_URL . "\n";
  69. }
  70. # Fetch and parse the stream playlist
  71. my @stream_segments = ();
  72. {
  73. my $playlist;
  74. if (fetch($STREAM_URL, \$playlist) == 200) {
  75. if ($DEBUG > 1) {
  76. print STDERR "Stream playlist:\n" . $playlist . "\n";
  77. }
  78. my $baseURL = $STREAM_URL;
  79. $baseURL =~ s/\?[^\?]*$//;
  80. $baseURL =~ s/\/[^\/]*$//;
  81. if ($DEBUG > 2) {
  82. print STDERR 'Playlist base URL: ' . $baseURL . "\n";
  83. }
  84. foreach my $segment (split(/^/, $playlist)) {
  85. if ($segment =~ /^#/ || $segment =~ /^\s*$/) {
  86. next;
  87. }
  88. $segment =~ s/^\s+//;
  89. $segment =~ s/\s+$//;
  90. push(@stream_segments, $baseURL . '/' . $segment);
  91. }
  92. }
  93. }
  94. # DEV-ONLY DEBUG
  95. foreach my $segment (@stream_segments) {
  96. print STDERR $segment . "\n";
  97. }
  98. # Cleanup
  99. exit(0);
  100. # GET the requested URL, accepting encoded data and extracting the HTTP status code
  101. sub fetch($$) {
  102. my ($url, $data) = @_;
  103. # Init the global LWP as needed
  104. if (!defined($LWP)) {
  105. $LWP = new LWP::UserAgent;
  106. $LWP->agent($HTTP_UA);
  107. $LWP->timeout($HTTP_TIMEOUT);
  108. $LWP->ssl_opts({ 'verify_hostname' => $HTTP_VERIFY });
  109. }
  110. # Make a request
  111. my $request = HTTP::Request->new('GET' => $url);
  112. $request->header('Accept-Encoding' => HTTP::Message::decodable);
  113. my $response = $LWP->request($request);
  114. # Do something useful with the response
  115. my $code = 400;
  116. if ($response->status_line =~ /^(\d+)\s/) {
  117. $code = $1;
  118. }
  119. ${$data} = $response->decoded_content();
  120. return $code;
  121. }