GIT repositories

Index page of all the GIT repositories that are clonable form this server via HTTPS. Übersichtsseite aller GIT-Repositories, die von diesem Server aus über git clone (HTTPS) erreichbar sind.

Services

A bunch of service scripts to convert, analyse and generate data. Ein paar Services zum Konvertieren, Analysieren und Generieren von Daten.

GNU octave web interface

A web interface for GNU Octave, which allows to run scientific calculations from netbooks, tables or smartphones. The interface provides a web form generator for Octave script parameters with pre-validation, automatic script list generation, as well presenting of output text, figures and files in a output HTML page. Ein Webinterface für GNU-Octave, mit dem wissenschaftliche Berechnungen von Netbooks, Tablets oder Smartphones aus durchgeführt werden können. Die Schnittstelle beinhaltet einen Formulargenerator für Octave-Scriptparameter, mit Einheiten und Einfabevalidierung. Textausgabe, Abbildungen und generierte Dateien werden abgefangen und in einer HTML-Seite dem Nutzer als Ergebnis zur Verfügung gestellt.

Youtube Download Klasse

Youtube download class

This class is an easy to use wrapper around the commandline programm youtube-dl, which must be installed on your (*nix) system. See the example how to use it.

Diese Klasse ist um das Kommandozeilenprogramm youtube-dl gestrickt, welches auf dem (*nix) System installiert sein muss. Ein Blick in das nachfolgende Beispiel verrät wie es funktioniert:

Sample source code

Anwendungsbeispiel

<?php
include_once('swlib/swlib.class.php');
 
use sw\YoutubeDownloader;
 
 
// Class configuration. This config is used for all instances.
sw\YoutubeDownloader::config(array(
  'youtube-dl' => '/usr/bin/youtube-dl',
  'default-download-directory' => '/tmp'
));
 
// This is the clip url
$uri = 'https://www.youtube.com/watch?v=BYBw_o_2nG0';
 
// Instantiate
$ytdl = new YoutubeDownloader($uri);
 
// Get the title from youtube
$title = $ytdl->getTitle();
 
// Get Description form youtube
$description = $ytdl->getDescription();
 
// Get all available formats from youttube
$formats = $ytdl->getAvailableFormats();
 
// Your progress callback
$ytdl->setProgressCallback(function($progress) {
  //
  // $progress['progress'] : progress from 0.0 to 1.0
  // $progress['total'] => : total bytes that have to be loaded
  // $progress['speed'] => : download speed in bytes/second
  // $progress['remaining']: time remaining in seconds
  //
  print " -> {$progress['progress']}, {$progress['remaining']}\n";
});
 
print "\n";
print "Title      : $title\n\n";
print "Description: $description\n\n";
print "Formats    :";
 
print sprintf(" %3s | %10s | %10s | %10s\n", 'id', 'type', 'width', 'height');
foreach($formats as $format) {
  print sprintf("      %10s | %10s | %10s | %10s\n", $format['id'],
        $format['type'], $format['width'], $format['height']);
}
print "\n";
 
foreach($formats as $format) {
  if($format['type'] != 'mp4') continue;
  if($format['width'] != 640) continue;
  $ytdl->download($format['id']);
  break;
}

Output

Ausgabe

$ php ytdl.test.php

Title       : Despicable Me - Mini-Movie 'Banana' Preview

Description : No description available.

Formats     :  id |       type |      width |     height
               35 |        flv |        854 |        480
               44 |       webm |        854 |        480
               34 |        flv |        640 |        360
               18 |        mp4 |        640 |        360
               43 |       webm |        640 |        360
                5 |        flv |        400 |        240
               17 |        mp4 |        176 |        144

 -> 0, 16
 -> 0, 20
 -> 0.001, 19
 -> 0.002, 22
 -> 0.004, 20
 -> 0.008, 18
 -> 0.016, 17
 -> 0.031, 17
 -> 0.063, 16
 -> 0.121, 15
 -> 0.179, 14
 -> 0.237, 13
 -> 0.295, 12
 -> 0.353, 11
 -> 0.411, 10
 -> 0.469, 9
 -> 0.527, 8
 -> 0.585, 7
 -> 0.643, 6
 -> 0.701, 5
 -> 0.759, 4
 -> 0.817, 3
 -> 0.875, 2
 -> 0.933, 1
 -> 0.991, 0
 -> 1, 0

Class source code

Klassen-Quelltext

<?php
 
/**
 * Exceptions thrown by class YoutubeDownloader
 *
 * @gpackage de.atwillys.sw.php.swLib
 * @author Stefan Wilhelm
 * @copyright Stefan Wilhelm, 2007-2012
 * @license GPL
 * @version 1.0
 */
 
namespace sw;
 
class YoutubeDownloaderException extends LException {
 
}
<?php
 
/**
 * Wrapper for the youtube-dl command line program. Allows to download streams
 * and meta information.
 *
 * @gpackage de.atwillys.sw.php.swLib
 * @author Stefan Wilhelm
 * @copyright Stefan Wilhelm, 2007-2012
 * @license GPL
 * @version 1.0
 */
 
namespace sw;
 
class YoutubeDownloader {
 
  /**
   * Class configuration
   * @var array
   */
  protected static $config = array(
      'youtube-dl' => '/usr/bin/youtube-dl',
      'default-download-directory' => '/tmp'
  );
 
  /**
   * The output video file path
   * @var string
   */
  protected $outputFile = null;
 
  /**
   * The uri to fetch data or streams from
   * @var string
   */
  protected $pageUri = null;
 
  /**
   * The video title
   * @var string
   */
  protected $title = null;
 
  /**
   * The video description text
   * @var string
   */
  protected $description = null;
 
  /**
   * The used video format
   * @var string
   */
  protected $format = null;
 
  /**
   * Formats the video is available in
   * @var array
   */
  protected $formats = array();
 
  /**
   * The process callback function reference, can be left null
   * @var function
   */
  protected $progressCallback = null;
 
  /**
   * Returns the class configuration. If a configuration array is given, modifies
   * the configuration by key merging.
   * @param array $config
   * @return array
   */
  public static final function config(array $config = array()) {
    if (!empty($config)) {
      self::$config = array_merge(self::$config, $config);
      Tracer::trace_r($config, '$config', 3);
    }
 
    if (empty(self::$config['youtube-dl']) || !FileSystem::isFile(self::$config['youtube-dl']) || !FileSystem::isExecutable(self::$config['youtube-dl'])) {
      $ytdl = trim(exec('which youtube-dl'), "\t\n\r ");
      Tracer::trace("youtube-dl binary search result (which youtube-dl)=$ytdl", 2);
      if (empty($ytdl)) {
        throw new YoutubeDownloaderException("Your configutation is incorrect: can't find the youtube-dl binary: :binary", array(':binary' => self::$config['youtube-dl']));
      } else {
        Tracer::trace("Warning: The configured mimms binary path is wrong (" . self::$config['youtube-dl'] . "), but found binary '$ytdl'");
      }
      self::$config['youtube-dl'] = $ytdl;
    }
    return self::$config;
  }
 
  /**
   * Constructor
   * @param string $uri
   */
  public function __construct($uri = null) {
    if (!empty($uri)) {
      $this->pageUri = $uri;
      try {
        $this->updateProperties();
      } catch (\Exception $e) {
        // Don't interrupt the object construction
        print "$e";
      }
    }
  }
 
  /**
   * Sets the progress callback function reference
   * @param function $callback
   */
  public function setProgressCallback($callback) {
    if (!is_callable($callback)) {
      throw new YoutubeDownloaderException('Your progress callback is not callable.');
    } else {
      $this->progressCallback = $callback;
    }
  }
 
  /**
   * Returns output file path
   * @return string
   */
  public function getOutputFile() {
    return $this->outputFile;
  }
 
  /**
   * Returns the title
   * @return string
   */
  public function getTitle() {
    $this->updateProperties();
    return $this->title;
  }
 
  /**
   * Returns the video description text
   * @return string
   */
  public function getDescription() {
    $this->updateProperties();
    return $this->description;
  }
 
  /**
   * Returns the downloaded format
   * @return string
   */
  public function getFormat() {
    $this->updateProperties();
    return $this->format;
  }
 
  /**
   * Returns the available formats on youtube
   * @return array
   */
  public function getAvailableFormats() {
    $this->updateProperties();
    return $this->formats;
  }
 
  /**
   * Shell process on STDOUT callback, class internal use
   * @param string $text
   */
  public final function onStdOut($text) {
    if (preg_match("/\[download\]\s+([\d\.]+)\%[\s]+of[\s]+([\d\.]+[\s]*[\w]+)[\s]+at[\s]+([\d\.]+[\s]*[\w\/]+)[\s]+ETA[\s]+([\d\:]+)/i", $text, $matches)) {
      if (isset($this->progressCallback)) {
        $progress = array(
            'progress' => floatval($matches[1]) / 100,
            'loaded' => 0,
            'total' => $this->sizeToBytes($matches[2]),
            'speed' => $this->sizeToBytes($matches[3]),
            'remaining' => $this->timeToSeconds($matches[4])
        );
        $progress['loaded'] = intval($progress['progress'] * $progress['total']);
        @call_user_func($this->progressCallback, $progress);
      }
    }
  }
 
  /**
   * Converts string data size formats to SI (e.g. 100K to 100000)
   * @param string $text
   * @return int
   */
  protected function sizeToBytes($text) {
    if (preg_match('/([\d\.]+)[\s]*([\w]*)/i', $text, $matches)) {
      array_shift($matches);
      $n = doubleval(reset($matches));
      $u = strtolower(trim(end($matches)));
      if (strlen($u) > 1)
        $u = substr($u, 0, 1);
      switch ($u) {
        case 't': $n *= 1024;
        case 'g': $n *= 1024;
        case 'm': $n *= 1024;
        case 'k': $n *= 1024;
      }
      return intval($n);
    } else {
      return false;
    }
  }
 
  /**
   * Converts a time duration string (e.g. "01:45:00.0") to seconds
   * @param string $timeString
   * @return int
   */
  protected function timeToSeconds($timeString) {
    $timeString = explode(':', $timeString);
    $t = 0;
    foreach (array_reverse($timeString) as $k => $v) {
      $t += $v * pow(60, $k);
    }
    return $t;
  }
 
  /**
   * Updates the obkect instance variables by the given uri.
   */
  protected function updateProperties() {
    if (is_null($this->title)) {
      if (stripos($this->pageUri, '://www.youtube.com') === false) {
        throw new YoutubeDownloaderException('The given url does contain :page', array(':page', 'www.youtube.com'));
      } else {
        $r = ShellProcess::exec(self::$config['youtube-dl'] . ' --get-title ' . escapeshellarg($this->pageUri));
        if (!empty($r['stderr'])) {
          throw new YoutubeDownloaderException($r['stderr']);
        } else {
          $this->title = trim($r['stdout'], "\n\r\t ");
        }
        $r = ShellProcess::exec(self::$config['youtube-dl'] . ' --get-description ' . escapeshellarg($this->pageUri));
        if (!empty($r['stderr'])) {
          throw new YoutubeDownloaderException($r['stderr']);
        } else {
          $this->description = trim($r['stdout'], "\n\r\t ");
        }
        $r = ShellProcess::exec(self::$config['youtube-dl'] . ' --list-formats ' . escapeshellarg($this->pageUri));
        if (!empty($r['stderr'])) {
          throw new YoutubeDownloaderException($r['stderr']);
        } else {
          $this->formats = array();
          $this->format = null;
          $actual_width = 0;
          foreach (explode("\n", trim($r['stdout'], "\n\r\t ")) as $format) {
            if (preg_match("/^(\d+)[\s\:]+(\w+)\s*\[(\d+)x(\d+)\]/i", $format, $matches)) {
              $format = array(
                  'id' => $matches[1],
                  'type' => $matches[2],
                  'height' => $matches[3],
                  'width' => $matches[4],
              );
              $this->formats[] = $format;
 
              // Use best quality format in mp4 as default.
              if ($format['type'] == 'mp4' && $actual_width < $format['width']) {
                $this->format = $format['id'];
                $actual_width = $format['width'];
              }
            }
          }
          // Default format if no matching mp4
          if (!empty($this->formats) && is_null($this->format)) {
            $this->format = $this->formats[0]['id'];
          }
        }
      }
    }
  }
 
  /**
   * Downloads the video file and returns a reference to itself.
   * @param int $format
   * @return YoutubeDownloader
   */
  public function download($format=null) {
    $this->updateProperties();
    if (!empty($format)) {
      $found = false;
      foreach ($this->formats as $f) {
        if ($f['id'] == $format) {
          $found = true;
          break;
        }
      }
      if (!$found) {
        throw new YoutubeDownloaderException("Did not find this format (:format) in the list of formats", array(':format' => $format));
      } else if (!is_numeric($format)) {
        throw new YoutubeDownloaderException("Video format codes must be numeric, this one is not: :format", array(':format' => $format));
      } else {
        $this->format = $format;
      }
      unset($found);
      unset($f);
    }
    if (!FileSystem::isDirectory(self::$config['default-download-directory'])) {
      throw new YoutubeDownloaderException("Download directory does not exist: :directory", array(':directory' => self::$config['default-download-directory']));
    }
    // Get file name
    $r = ShellProcess::exec(self::$config['youtube-dl'] . ' -f ' . $this->format . ' --get-filename ' . escapeshellarg($this->pageUri));
    if (!empty($r['stderr'])) {
      throw new YoutubeDownloaderException($r['stderr']);
    } else {
      $this->outputFile = trim($r['stdout'], "\n\r\t /");
      if (empty($this->outputFile)) {
        throw new YoutubeDownloaderException('The downloader returned no output file name to use.');
      } else {
        $this->outputFile = self::$config['default-download-directory'] . '/youtube-' . time() . '-' . $this->outputFile;
      }
    }
 
    // Download
    $sh = new ShellProcess();
    $sh->setCommand(self::$config['youtube-dl'] . ' --no-part -f ' . $this->format . ' -o ' . escapeshellarg($this->outputFile) . ' ' . escapeshellarg($this->pageUri));
    $sh->setFetchOutput(true);
    $sh->setCallbacks(array('onstdout' => array($this, 'onStdOut')));
    $sh->run();
 
    if (strlen($sh->getStdErr()) > 0) {
      throw new YoutubeDownloaderException($r['stderr']);
    }
    return $this;
  }
 
}