#! /usr/bin/perl -w #-------------------- # server.pl # Stream2Dream Linux server. This is the replacement for the .Net version of windows. # It needs perl, samba and vlc. # Current version: 2.5 (23-10-2011) by Teslina # @ http://www.teslina.com/dreambox/stream2dream-for-linux/ # Based on the original Version by TheYOSH # @ http://theyosh.nl/projects/stream2dream-linux/stream2dream-linux - dreambox@theyosh.nl # # Change log: # =========== # # Changes by Teslina (http://www.teslina.com/dreambox/stream2dream-for-linux/): # 23-10-2011: # Updated the server to version 2.5 # Added more debugging Options # Speeding up socket response to prevent timeouts from dream # # 2008: # Final Version by TheYOSH (http://theyosh.nl/projects/stream2dream-linux/stream2dream-linux - dreambox@theyosh.nl) #-------------------- use strict; use IO::Socket; use Switch; use Date::Format; use LWP::Simple; use threads; use threads::shared; # flush after every write $| = 1; # Settings #Normal port number (should not be changed) my $port = 9916; # The IP of the streaming server (Linux box) my $server = "192.168.1.165"; # The name of the samba share (Not the location) my $share = "movies"; # Change the outputshare to a different place my $outputshare = $share; # The location on the disk my $location = "/home/mediacenter/movies"; # Output location my $outputlocation = $location; # Alternative video codec (If VLC has problems coding video.) Set to 1 to activate my $alternativecode = 1; # Location of VLC my $vlc = "/usr/bin/vlc"; # Default vlc gui, set to dummy to overrule the Dreambox setting my $vlcgui = "telnet"; # Debug modus, set to 0 to turn debug of my $debug = 0; # System variables, should not be changed! my $transform = ""; #my $httpHeader = 0; my @pids = (); my $streamfile = ""; my $progressfile = ""; my $progressthread; my ($progressloop):shared; # Start server, open network connection for listening my $sock = new IO::Socket::INET( LocalHost => $server, LocalPort => $port, Proto => 'tcp', Listen => SOMAXCONN, Reuse => 1); $sock or die debugMsg("TCP-Server","no socket: $!",0); my($new_sock, $c_addr, $buf); debugMsg("TCP-Server","Starting listener...",0); debugMsg("Info","The 'LT Stream-Player' can connect to us - $server - now :-)",0); # Loop unlimited times for server requests while(1){ $new_sock = $sock->accept(); # get the host and port number of newly connected client. $c_addr = $new_sock->peerhost(); my $client_port = $new_sock->peerport(); debugMsg("TCP-Server","Client: $c_addr logged in",0); while (defined ($buf = <$new_sock>)){ $buf = trim($buf); my $cmdnr = substr($buf,0,1); my $output = ""; my $vlccmd = ""; my $mytime = time2str("%d.%m.%Y %H:%M:%S",time); debugMsg("Dreambox",$buf,1); if($buf eq "GET / HTTP/1.1"){ print $new_sock "HTTP/1.0 200 OK\n"; print $new_sock "Server: LT-Stream2Dream\n"; print $new_sock "Date: $mytime\n"; print $new_sock "Content-Type: text/html\n"; print $new_sock "Accept-Ranges: bytes\n"; print $new_sock "Content-Length: 2\n\n"; print $new_sock "ok###"; $output = " HTTP/1.0 200 OK Server: LT-Stream2Dream Date: $mytime Content-Type: text/html Accept-Ranges: bytes Content-Length: 2 ok###"; # wenn die Dream den HTTP Reply erhaelt, kreiert sie die DIRS # /tmp/mp und /tmp/mpstream # reply muss aber in ca. 2 sekunden kommen, sonst timeout und error # daher muss socket schneller antworten! debugMsg("Debug ## ","Sending HTTP Response.",1); debugMsg("Sending Command ## ",$output,1); next; } switch ($cmdnr) { case 0 { # Set the transcode settings $streamfile = "\"" . $outputlocation . "/MPStream.ts\""; $progressfile = $outputlocation . "/progress.txt"; my $start = index($buf,"\$-I")+3; if ($vlcgui ne "dummy") { $vlcgui = substr($buf,$start,index($buf," \$\$ ")-$start); } $transform = substr($buf,index($buf,":sout")); $transform =~ s/dst=\$/dst=$streamfile/; if ($alternativecode > 0) { $transform =~ s/mp2v/mpgv/; } # Dreambox wants mp2v codec, but codec has been changed to mpgv in newer linux versions) $output = "ok\$2.5\$mount -t cifs //\$/$share /tmp/mp -o ro,nolock,rsize=\$r,wsize=\$w,user=\$u,password=\$p; mount -t cifs //\$/$outputshare /tmp/mpstream -o ro,nolock,rsize=\$r,wsize=\$w,user=\$u,password=\$p###"; debugMsg("Debug ## ","Set the transcode settings (code 0)",1); debugMsg("Sending Command ## ",$output,1); } case 1 { # Play the selected movie my $lMovieSize = 0; debugMsg("Debug ## ","Play the selected movie (code 1)",1); $vlccmd = $vlc . " -I $vlcgui "; my $file = substr($buf,1); debugMsg("Debug ## ","Filename: $file",1); if ( index($file,"youtube") != -1 ) { # YouTube $file = getYouTubeVideoUrl($file); $vlccmd .= "\"" . $file . "\" "; } elsif ( substr($file,0,7) eq "*http://" ) { $vlccmd .= "\"" . substr($file,1) . "\" "; } elsif ( index($file,"play.dvd.avi") != -1) { # DVD Movie $file = getDVDUrl($location . "/" . $file); $vlccmd .= "\"" . $file . "\" "; } else { # Default operation $lMovieSize = -s $location . "/" . $file; $vlccmd .= "\"file://" . $location . "/" . $file . "\" "; } $vlccmd .= "\"" . $transform . "\" "; #$vlccmd = $vlccmd . "> /dev/null 2>/dev/null "; $vlccmd = $vlccmd . "> /tmp/vlc.log "; debugMsg("vlc","CMD: $vlccmd",1); $vlccmd = $vlccmd . "&"; $output = "ok###"; system($vlccmd); my $getVLCpidCMD = "ps fax -o pid -o cmd | grep vlc | grep \"" . $file . "\" | grep -v grep"; debugMsg("vlc","Movie: $file",0); debugMsg("Sending Command ## ",$output,1); my $data = readpipe $getVLCpidCMD; my $pid; foreach $pid (split(/\n/,$data)) { my @piddata = split(/ /,trim($pid)); push(@pids,$piddata[0]); } # Start the progress thread.... $progressloop = 1; $progressthread = threads->new(\&updateProgress, $lMovieSize ); } case 2 { # Exit current movie... $progressloop = 0; my $pid; foreach $pid (@pids) { system("kill " . $pid . " &"); } @pids = (); system("rm -f $outputlocation/MPStream.ts"); system("rm -f $progressfile"); $output = "ok###"; debugMsg("Debug ## ","Exit current Movie (code 2)",1); debugMsg("Sending Command ## ",$output,1); } case 9 { debugMsg("Debug ## ","Logout (code 9)",1); debugMsg("TCP-Server","Client: $c_addr logged out",0); $output = "ok###"; system("rm -f $outputlocation/MPStream.ts"); debugMsg("Sending Command ## ",$output,1); } } print $new_sock $output; } } sub updateProgress() { my $pTargetSize = $_[0]; my $percentage = 0; while ($progressloop) { my $lStreamFileSize = -s substr($streamfile,1,-1); my $progressstring = round($lStreamFileSize/1024/1024); if ($pTargetSize != 0) { $percentage = round($lStreamFileSize/$pTargetSize*100); $progressstring = $progressstring . "-" . $percentage; } open (PROGRESSFILE, ">$progressfile"); print PROGRESSFILE $progressstring; close (PROGRESSFILE); if ($percentage == 100) { # File is converted.... stop running.... $progressloop = 0; } sleep (1); } } sub getYouTubeVideoUrl { my $lWatchLocation = $_[0]; my $YouTubeContent = get $lWatchLocation; my $lStart = index($YouTubeContent,", \"t\": \""); $lStart += 8; my $lStop = index($YouTubeContent,"\", \"hl\":"); my $cookieID = substr($YouTubeContent,$lStart,$lStop-$lStart); $lStart = index($lWatchLocation,"watch?v="); $lStart += 8; my $videourl = "http://youtube.com/get_video?video_id=" . substr($lWatchLocation,$lStart) . "&t=" . $cookieID; return $videourl; } sub getDVDUrl { my @lFolders = split(/\//,$_[0]); pop(@lFolders); # Remove the play.vob file my $lReturnValue = "dvdsimple://"; my $lFolder; foreach $lFolder (@lFolders) { $lReturnValue .= "/" . $lFolder; } return $lReturnValue . "/VIDEO_TS"; } sub debugMsg { my ($pSystem,$pMessage,$pDebug) = ($_[0], $_[1], $_[2]); if(($pDebug == 1 && $debug) || !$pDebug){ print time2str("%d-%m-%Y %H:%M:%S",time) . " [$pSystem] $pMessage\n"; } } sub round { my($number) = shift; return int($number + .5 * ($number <=> 0)); } sub trim { my $string = $_[0]; $string =~ s/^\s+//; $string =~ s/\s+$//; return $string; }