2011-08-15 04:37:22 +02:00
#!/usr/bin/env python3
2012-06-18 02:34:32 +02:00
import sys , os , argparse , subprocess
2011-10-26 03:23:43 +02:00
import livestreamer
2012-06-22 00:07:06 +02:00
from livestreamer . compat import input , stdout , is_win32
2011-08-15 04:37:22 +02:00
2012-06-14 01:53:00 +02:00
exampleusage = """
example usage :
$ livestreamer twitch . tv / onemoregametv
Found streams : 240 p , 360 p , 480 p , 720 p , best , iphonehigh , iphonelow , live
$ livestreamer twitch . tv / onemoregametv 720 p
Stream now playbacks in player ( default is VLC ) .
"""
2012-05-28 01:47:48 +02:00
parser = livestreamer . utils . ArgumentParser ( description = " CLI program that launches streams from various streaming services in a custom video player " ,
2012-06-14 01:53:00 +02:00
fromfile_prefix_chars = " @ " ,
formatter_class = argparse . RawDescriptionHelpFormatter ,
epilog = exampleusage , add_help = False )
2011-08-15 04:37:22 +02:00
parser . add_argument ( " url " , help = " URL to stream " , nargs = " ? " )
2012-05-28 01:47:48 +02:00
parser . add_argument ( " stream " , help = " Stream quality to play, use ' best ' for highest quality available " , nargs = " ? " )
2012-06-14 01:53:00 +02:00
parser . add_argument ( " -h " , " --help " , action = " store_true " , help = " Show this help message and exit " )
2012-06-13 16:06:27 +02:00
parser . add_argument ( " -l " , " --plugins " , action = " store_true " , help = " Print all currently installed plugins " )
2012-06-14 01:53:00 +02:00
playeropt = parser . add_argument_group ( " player options " )
playeropt . add_argument ( " -p " , " --player " , metavar = " player " , help = " Command-line for player, default is ' vlc ' " , default = " vlc " )
playeropt . add_argument ( " -q " , " --quiet-player " , action = " store_true " , help = " Hide all player console output " )
outputopt = parser . add_argument_group ( " file output options " )
outputopt . add_argument ( " -o " , " --output " , metavar = " filename " , help = " Write stream to file instead of playing it " )
outputopt . add_argument ( " -f " , " --force " , action = " store_true " , help = " Always write to file even if it already exists " )
outputopt . add_argument ( " -O " , " --stdout " , action = " store_true " , help = " Write stream to stdout instead of playing it " )
pluginopt = parser . add_argument_group ( " plugin options " )
pluginopt . add_argument ( " -c " , " --cmdline " , action = " store_true " , help = " Print command-line used internally to play stream, this may not be available on all streams " )
pluginopt . add_argument ( " -e " , " --errorlog " , action = " store_true " , help = " Log possible errors from internal command-line to a temporary file, use when debugging " )
pluginopt . add_argument ( " -r " , " --rtmpdump " , metavar = " path " , help = " Specify location of rtmpdump " )
pluginopt . add_argument ( " -j " , " --jtv-cookie " , metavar = " cookie " , help = " Specify JustinTV cookie to allow access to subscription channels " )
2011-08-15 04:37:22 +02:00
2012-03-21 16:31:41 +01:00
RCFILE = os . path . expanduser ( " ~/.livestreamerrc " )
2011-08-15 04:37:22 +02:00
def exit ( msg ) :
2012-05-27 00:30:52 +02:00
sys . exit ( ( " error: {0} " ) . format ( msg ) )
2011-08-15 04:37:22 +02:00
2012-05-25 17:26:11 +02:00
def msg ( msg ) :
2012-05-28 01:17:25 +02:00
sys . stdout . write ( msg + " \n " )
2012-05-25 17:26:11 +02:00
def write_stream ( fd , out , progress ) :
written = 0
while True :
data = fd . read ( 8192 )
if len ( data ) == 0 :
break
try :
out . write ( data )
except IOError :
break
written + = len ( data )
if progress :
2012-05-26 21:44:15 +02:00
sys . stderr . write ( ( " \r Written {0} bytes " ) . format ( written ) )
2012-05-25 17:26:11 +02:00
if progress and written > 0 :
2012-05-26 21:44:15 +02:00
sys . stderr . write ( " \n " )
2012-05-25 17:26:11 +02:00
fd . close ( )
2012-05-26 21:44:15 +02:00
if out != stdout :
out . close ( )
2012-05-27 00:51:00 +02:00
def check_output ( output , force ) :
if os . path . isfile ( output ) and not force :
2012-05-28 01:47:48 +02:00
sys . stderr . write ( ( " File {0} already exists! Overwrite it? [y/N] " ) . format ( output ) )
2012-05-27 00:51:00 +02:00
try :
answer = input ( )
except :
sys . exit ( )
2012-05-26 21:44:15 +02:00
answer = answer . strip ( ) . lower ( )
if answer != " y " :
sys . exit ( )
try :
out = open ( output , " wb " )
except IOError as err :
exit ( ( " Failed to open file {0} - " ) . format ( output , err ) )
return out
2012-05-25 17:26:11 +02:00
2012-05-28 01:11:52 +02:00
def output_stream ( stream , args ) :
progress = False
out = None
try :
2012-06-13 16:06:27 +02:00
fd = stream . open ( )
2012-05-28 01:11:52 +02:00
except livestreamer . StreamError as err :
exit ( ( " Could not open stream - {0} " ) . format ( err ) )
if args . output :
if args . output == " - " :
out = stdout
else :
out = check_output ( args . output , args . force )
progress = True
elif args . stdout :
out = stdout
else :
cmd = args . player + " - "
2012-06-14 01:03:44 +02:00
if args . quiet_player :
pout = open ( os . devnull , " w " )
perr = open ( os . devnull , " w " )
else :
pout = sys . stderr
perr = sys . stdout
2012-06-18 02:34:32 +02:00
player = subprocess . Popen ( cmd , shell = True , stdout = pout , stderr = perr ,
stdin = subprocess . PIPE )
out = player . stdin
2012-05-28 01:11:52 +02:00
if not out :
exit ( " Failed to open a valid stream output " )
2012-06-22 00:07:06 +02:00
if is_win32 :
import msvcrt
msvcrt . setmode ( out . fileno ( ) , os . O_BINARY )
2012-05-28 01:11:52 +02:00
try :
write_stream ( fd , out , progress )
except KeyboardInterrupt :
sys . exit ( )
2011-08-15 04:37:22 +02:00
def handle_url ( args ) :
2012-05-24 15:07:03 +02:00
try :
channel = livestreamer . resolve_url ( args . url )
except livestreamer . NoPluginError :
exit ( ( " No plugin can handle URL: {0} " ) . format ( args . url ) )
2011-08-15 04:37:22 +02:00
2012-04-21 21:57:20 +02:00
try :
streams = channel . get_streams ( )
2012-06-13 16:06:27 +02:00
except livestreamer . StreamError as err :
exit ( str ( err ) )
2012-04-21 21:57:20 +02:00
except livestreamer . PluginError as err :
2012-05-24 15:07:03 +02:00
exit ( str ( err ) )
2011-08-15 04:37:22 +02:00
2012-04-21 20:35:43 +02:00
if len ( streams ) == 0 :
2012-05-24 15:07:03 +02:00
exit ( ( " No streams found on this URL: {0} " ) . format ( args . url ) )
2011-08-15 04:37:22 +02:00
keys = list ( streams . keys ( ) )
keys . sort ( )
validstreams = ( " , " ) . join ( keys )
if args . stream :
if args . stream in streams :
stream = streams [ args . stream ]
2012-05-28 01:11:52 +02:00
if args . cmdline :
if isinstance ( stream , livestreamer . stream . StreamProcess ) :
msg ( stream . cmdline ( ) )
2012-05-26 21:44:15 +02:00
else :
2012-05-28 01:47:48 +02:00
exit ( " Stream does not use a command-line " )
2011-08-15 04:37:22 +02:00
else :
2012-05-28 01:11:52 +02:00
output_stream ( stream , args )
2011-08-15 04:37:22 +02:00
else :
2012-05-28 01:47:48 +02:00
msg ( ( " Invalid stream quality: {0} " ) . format ( args . stream ) )
2012-05-25 17:26:11 +02:00
msg ( ( " Valid streams: {0} " ) . format ( validstreams ) )
2011-08-15 04:37:22 +02:00
else :
2012-05-25 17:26:11 +02:00
msg ( ( " Found streams: {0} " ) . format ( validstreams ) )
2011-08-15 04:37:22 +02:00
def print_plugins ( ) :
2011-10-26 03:23:43 +02:00
pluginlist = list ( livestreamer . get_plugins ( ) . keys ( ) )
2012-05-25 17:26:11 +02:00
msg ( ( " Installed plugins: {0} " ) . format ( " , " . join ( pluginlist ) ) )
2011-08-15 04:37:22 +02:00
def main ( ) :
2011-12-19 15:45:12 +01:00
arglist = sys . argv [ 1 : ]
2012-03-21 16:31:41 +01:00
if os . path . exists ( RCFILE ) :
arglist . insert ( 0 , " @ " + RCFILE )
2011-12-19 15:45:12 +01:00
args = parser . parse_args ( arglist )
2011-08-15 04:37:22 +02:00
2012-06-13 16:06:27 +02:00
livestreamer . options . set ( " errorlog " , args . errorlog )
livestreamer . options . set ( " rtmpdump " , args . rtmpdump )
livestreamer . options . set ( " jtvcookie " , args . jtv_cookie )
2012-01-19 22:50:30 +01:00
2011-08-15 04:37:22 +02:00
if args . url :
handle_url ( args )
elif args . plugins :
print_plugins ( )
else :
parser . print_help ( )