diff options
author | Thomas Kluyver <takowl@gmail.com> | 2013-12-03 12:37:53 -0800 |
---|---|---|
committer | Thomas Kluyver <takowl@gmail.com> | 2013-12-03 12:37:53 -0800 |
commit | c1ee1ae113f701a2952228c0cf1c7331e6ff19ed (patch) | |
tree | 24eb44e3d08fba199265a84b5843beb32272417c | |
parent | 5caa22112a11f2cabdacd8302536580012a2bf98 (diff) | |
parent | 0c1bcae869c7fd9607f187c828de5eb2a2d7bbe5 (diff) | |
download | pexpect-c1ee1ae113f701a2952228c0cf1c7331e6ff19ed.tar.gz |
Merge pull request #26 from takluyver/examples-cleanup-1
Remove unhelpful examples
-rw-r--r-- | doc/examples.rst | 16 | ||||
-rwxr-xr-x | examples/bd_client.py | 61 | ||||
-rwxr-xr-x | examples/bd_serv.py | 338 | ||||
-rwxr-xr-x | examples/fix_cvs_files.py | 115 | ||||
-rwxr-xr-x | examples/rippy.py | 999 | ||||
-rwxr-xr-x | examples/ssh_session.py | 120 | ||||
-rwxr-xr-x | examples/sshls.py | 84 |
7 files changed, 0 insertions, 1733 deletions
diff --git a/doc/examples.rst b/doc/examples.rst index cb89904..6338b5c 100644 --- a/doc/examples.rst +++ b/doc/examples.rst @@ -29,12 +29,6 @@ Examples. This will start a subshell and log all input and output to a file. This demonstrates the :meth:`~pexpect.spawn.interact` method of Pexpect. -`fix_cvs_files.py <https://github.com/pexpect/pexpect/blob/master/examples/fix_cvs_files.py>`_ - This is for cleaning up binary files improperly added to CVS. This - script scans the given path to find binary files; checks with CVS to - see if the sticky options are set to -kb; finally if sticky options - are not -kb then uses 'cvs admin' to set the -kb option. - `ftp.py <https://github.com/pexpect/pexpect/blob/master/examples/ftp.py>`_ This demonstrates an FTP "bookmark". This connects to an ftp site; does a few ftp tasks; and then gives the user interactive control over @@ -57,16 +51,6 @@ Examples. backwards. It then gives the user iteractive control of Python. It's pretty useless! -`rippy.py <https://github.com/pexpect/pexpect/blob/master/examples/rippy.py>`_ - This is a wizard for mencoder. It greatly simplifies the process of - ripping a DVD to Divx (mpeg4) format. It can transcode from any video - file to another. It has options for resampling the audio stream; - removing interlace artifacts, fitting to a target file size, etc. - There are lots of options, but the process is simple and easy to use. - -`sshls.py <https://github.com/pexpect/pexpect/blob/master/examples/sshls.py>`_ - This lists a directory on a remote machine. - `ssh_tunnel.py <https://github.com/pexpect/pexpect/blob/master/examples/ssh_tunnel.py>`_ This starts an SSH tunnel to a remote machine. It monitors the connection and restarts the tunnel if it goes down. diff --git a/examples/bd_client.py b/examples/bd_client.py deleted file mode 100755 index 8be01f7..0000000 --- a/examples/bd_client.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python - -'''This is a very simple client for the backdoor daemon. This is intended more -for testing rather than normal use. See bd_serv.py - -PEXPECT LICENSE - - This license is approved by the OSI and FSF as GPL-compatible. - http://opensource.org/licenses/isc-license.txt - - Copyright (c) 2012, Noah Spurrier <noah@noah.org> - PERMISSION TO USE, COPY, MODIFY, AND/OR DISTRIBUTE THIS SOFTWARE FOR ANY - PURPOSE WITH OR WITHOUT FEE IS HEREBY GRANTED, PROVIDED THAT THE ABOVE - COPYRIGHT NOTICE AND THIS PERMISSION NOTICE APPEAR IN ALL COPIES. - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -''' - -from __future__ import print_function - -from __future__ import absolute_import - -import socket -import sys, time, select - -def recv_wrapper(s): - r,w,e = select.select([s.fileno()],[],[], 2) - if not r: - return '' - #cols = int(s.recv(4)) - #rows = int(s.recv(4)) - cols = 80 - rows = 24 - packet_size = cols * rows * 2 # double it for good measure - return s.recv(packet_size) - -#HOST = '' #'localhost' # The remote host -#PORT = 1664 # The same port as used by the server -s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) -s.connect(sys.argv[1])#(HOST, PORT)) -time.sleep(1) -#s.setblocking(0) -#s.send('COMMAND' + '\x01' + sys.argv[1]) -s.send(':sendline ' + sys.argv[2]) -print(recv_wrapper(s)) -s.close() -sys.exit() -#while True: -# data = recv_wrapper(s) -# if data == '': -# break -# sys.stdout.write (data) -# sys.stdout.flush() -#s.close() - diff --git a/examples/bd_serv.py b/examples/bd_serv.py deleted file mode 100755 index d4bb4ea..0000000 --- a/examples/bd_serv.py +++ /dev/null @@ -1,338 +0,0 @@ -#!/usr/bin/env python - -'''Back door shell server - -This exposes an shell terminal on a socket. - - --hostname : sets the remote host name to open an ssh connection to. - --username : sets the user name to login with - --password : (optional) sets the password to login with - --port : set the local port for the server to listen on - --watch : show the virtual screen after each client request - -PEXPECT LICENSE - - This license is approved by the OSI and FSF as GPL-compatible. - http://opensource.org/licenses/isc-license.txt - - Copyright (c) 2012, Noah Spurrier <noah@noah.org> - PERMISSION TO USE, COPY, MODIFY, AND/OR DISTRIBUTE THIS SOFTWARE FOR ANY - PURPOSE WITH OR WITHOUT FEE IS HEREBY GRANTED, PROVIDED THAT THE ABOVE - COPYRIGHT NOTICE AND THIS PERMISSION NOTICE APPEAR IN ALL COPIES. - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -''' - -from __future__ import print_function - -from __future__ import absolute_import - -# Having the password on the command line is not a good idea, but -# then this entire project is probably not the most security concious thing -# I've ever built. This should be considered an experimental tool -- at best. -import pxssh, pexpect, ANSI -import time, sys, os, getopt, getpass, traceback, threading, socket - -def exit_with_usage(exit_code=1): - - print(globals()['__doc__']) - os._exit(exit_code) - -class roller (threading.Thread): - - '''This runs a function in a loop in a thread.''' - - def __init__(self, interval, function, args=[], kwargs={}): - - '''The interval parameter defines time between each call to the function. - ''' - - threading.Thread.__init__(self) - self.interval = interval - self.function = function - self.args = args - self.kwargs = kwargs - self.finished = threading.Event() - - def cancel(self): - - '''Stop the roller.''' - - self.finished.set() - - def run(self): - - while not self.finished.isSet(): - # self.finished.wait(self.interval) - self.function(*self.args, **self.kwargs) - -def endless_poll (child, prompt, screen, refresh_timeout=0.1): - - '''This keeps the screen updated with the output of the child. This runs in - a separate thread. See roller(). ''' - - #child.logfile_read = screen - try: - s = child.read_nonblocking(4000, 0.1) - screen.write(s) - except: - pass - #while True: - # #child.prompt (timeout=refresh_timeout) - # try: - # #child.read_nonblocking(1,timeout=refresh_timeout) - # child.read_nonblocking(4000, 0.1) - # except: - # pass - -def daemonize (stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): - - '''This forks the current process into a daemon. Almost none of this is - necessary (or advisable) if your daemon is being started by inetd. In that - case, stdin, stdout and stderr are all set up for you to refer to the - network connection, and the fork()s and session manipulation should not be - done (to avoid confusing inetd). Only the chdir() and umask() steps remain - as useful. - - References: - UNIX Programming FAQ - 1.7 How do I get my program to act like a daemon? - http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16 - - Advanced Programming in the Unix Environment - W. Richard Stevens, 1992, Addison-Wesley, ISBN 0-201-56317-7. - - The stdin, stdout, and stderr arguments are file names that will be opened - and be used to replace the standard file descriptors in sys.stdin, - sys.stdout, and sys.stderr. These arguments are optional and default to - /dev/null. Note that stderr is opened unbuffered, so if it shares a file - with stdout then interleaved output may not appear in the order that you - expect. ''' - - # Do first fork. - try: - pid = os.fork() - if pid > 0: - sys.exit(0) # Exit first parent. - except OSError as e: - sys.stderr.write ("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror) ) - sys.exit(1) - - # Decouple from parent environment. - os.chdir("/") - os.umask(0) - os.setsid() - - # Do second fork. - try: - pid = os.fork() - if pid > 0: - sys.exit(0) # Exit second parent. - except OSError as e: - sys.stderr.write ("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror) ) - sys.exit(1) - - # Now I am a daemon! - - # Redirect standard file descriptors. - si = open(stdin, 'r') - so = open(stdout, 'a+') - se = open(stderr, 'a+', 0) - os.dup2(si.fileno(), sys.stdin.fileno()) - os.dup2(so.fileno(), sys.stdout.fileno()) - os.dup2(se.fileno(), sys.stderr.fileno()) - - # I now return as the daemon - return 0 - -def add_cursor_blink (response, row, col): - - i = (row-1) * 80 + col - return response[:i]+'<img src="http://www.noah.org/cursor.gif">'+response[i:] - -def main (): - - try: - optlist, args = getopt.getopt(sys.argv[1:], 'h?d', ['help','h','?', 'hostname=', 'username=', 'password=', 'port=', 'watch']) - except Exception as e: - print(str(e)) - exit_with_usage() - - command_line_options = dict(optlist) - options = dict(optlist) - # There are a million ways to cry for help. These are but a few of them. - if [elem for elem in command_line_options if elem in ['-h','--h','-?','--?','--help']]: - exit_with_usage(0) - - hostname = "127.0.0.1" - port = 1664 - username = os.getenv('USER') - password = "" - daemon_mode = False - if '-d' in options: - daemon_mode = True - if '--watch' in options: - watch_mode = True - else: - watch_mode = False - if '--hostname' in options: - hostname = options['--hostname'] - if '--port' in options: - port = int(options['--port']) - if '--username' in options: - username = options['--username'] - print("Login for %s@%s:%s" % (username, hostname, port)) - if '--password' in options: - password = options['--password'] - else: - password = getpass.getpass('password: ') - - if daemon_mode: - print("daemonizing server") - daemonize() - #daemonize('/dev/null','/tmp/daemon.log','/tmp/daemon.log') - - sys.stdout.write ('server started with pid %d\n' % os.getpid() ) - - virtual_screen = ANSI.ANSI (24,80) - child = pxssh.pxssh() - child.login (hostname, username, password) - print('created shell. command line prompt is', child.PROMPT) - #child.sendline ('stty -echo') - #child.setecho(False) - virtual_screen.write (child.before) - virtual_screen.write (child.after) - - if os.path.exists("/tmp/mysock"): os.remove("/tmp/mysock") - s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - localhost = '127.0.0.1' - s.bind('/tmp/mysock') - os.chmod('/tmp/mysock',0o777) - print('Listen') - s.listen(1) - print('Accept') - #s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - #localhost = '127.0.0.1' - #s.bind((localhost, port)) - #print 'Listen' - #s.listen(1) - - r = roller (0.01, endless_poll, (child, child.PROMPT, virtual_screen)) - r.start() - print("screen poll updater started in background thread") - sys.stdout.flush() - - try: - while True: - conn, addr = s.accept() - print('Connected by', addr) - data = conn.recv(1024) - if data[0]!=':': - cmd = ':sendline' - arg = data.strip() - else: - request = data.split(' ', 1) - if len(request)>1: - cmd = request[0].strip() - arg = request[1].strip() - else: - cmd = request[0].strip() - if cmd == ':exit': - r.cancel() - break - elif cmd == ':sendline': - child.sendline (arg) - #child.prompt(timeout=2) - time.sleep(0.2) - shell_window = str(virtual_screen) - elif cmd == ':send' or cmd==':xsend': - if cmd==':xsend': - arg = arg.decode("hex") - child.send (arg) - time.sleep(0.2) - shell_window = str(virtual_screen) - elif cmd == ':cursor': - shell_window = '%x%x' % (virtual_screen.cur_r, virtual_screen.cur_c) - elif cmd == ':refresh': - shell_window = str(virtual_screen) - - response = [] - response.append (shell_window) - #response = add_cursor_blink (response, row, col) - sent = conn.send('\n'.join(response)) - if watch_mode: print('\n'.join(response)) - if sent < len (response): - print("Sent is too short. Some data was cut off.") - conn.close() - finally: - r.cancel() - print("cleaning up socket") - s.close() - if os.path.exists("/tmp/mysock"): os.remove("/tmp/mysock") - print("done!") - -def pretty_box (rows, cols, s): - - '''This puts an ASCII text box around the given string, s. - ''' - - top_bot = '+' + '-'*cols + '+\n' - return top_bot + '\n'.join(['|'+line+'|' for line in s.split('\n')]) + '\n' + top_bot - -def error_response (msg): - - response = [] - response.append ('''All commands start with : -:{REQUEST} {ARGUMENT} -{REQUEST} may be one of the following: - :sendline: Run the ARGUMENT followed by a line feed. - :send : send the characters in the ARGUMENT without a line feed. - :refresh : Use to catch up the screen with the shell if state gets out of sync. -Example: - :sendline ls -l -You may also leave off :command and it will be assumed. -Example: - ls -l -is equivalent to: - :sendline ls -l -''') - response.append (msg) - return '\n'.join(response) - -def parse_host_connect_string (hcs): - - '''This parses a host connection string in the form - username:password@hostname:port. All fields are options expcet hostname. A - dictionary is returned with all four keys. Keys that were not included are - set to empty strings ''. Note that if your password has the '@' character - then you must backslash escape it. ''' - - if '@' in hcs: - p = re.compile (r'(?P<username>[^@:]*)(:?)(?P<password>.*)(?!\\)@(?P<hostname>[^:]*):?(?P<port>[0-9]*)') - else: - p = re.compile (r'(?P<username>)(?P<password>)(?P<hostname>[^:]*):?(?P<port>[0-9]*)') - m = p.search (hcs) - d = m.groupdict() - d['password'] = d['password'].replace('\\@','@') - return d - -if __name__ == "__main__": - - try: - start_time = time.time() - print(time.asctime()) - main() - print(time.asctime()) - print("TOTAL TIME IN MINUTES:", end=' ') - print((time.time() - start_time) / 60.0) - except Exception as e: - print(str(e)) - tb_dump = traceback.format_exc() - print(str(tb_dump)) - diff --git a/examples/fix_cvs_files.py b/examples/fix_cvs_files.py deleted file mode 100755 index 23ba89b..0000000 --- a/examples/fix_cvs_files.py +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/env python - -'''This is for cleaning up binary files improperly added to CVS. This script -scans the given path to find binary files; checks with CVS to see if the sticky -options are set to -kb; finally if sticky options are not -kb then uses 'cvs -admin' to set the -kb option. - -This script ignores CVS directories, symbolic links, and files not known under -CVS control (cvs status is 'Unknown'). - -Run this on a CHECKED OUT module sandbox, not on the repository itself. After -if fixes the sticky options on any files you should manually do a 'cvs commit' -to accept the changes. Then be sure to have all users do a 'cvs up -A' to -update the Sticky Option status. - -Noah Spurrier -20030426 - -PEXPECT LICENSE - - This license is approved by the OSI and FSF as GPL-compatible. - http://opensource.org/licenses/isc-license.txt - - Copyright (c) 2012, Noah Spurrier <noah@noah.org> - PERMISSION TO USE, COPY, MODIFY, AND/OR DISTRIBUTE THIS SOFTWARE FOR ANY - PURPOSE WITH OR WITHOUT FEE IS HEREBY GRANTED, PROVIDED THAT THE ABOVE - COPYRIGHT NOTICE AND THIS PERMISSION NOTICE APPEAR IN ALL COPIES. - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -''' - -from __future__ import absolute_import -from __future__ import print_function - -import os, sys, time -import pexpect - -VERBOSE = 1 - -def is_binary (filename): - - '''Assume that any file with a character where the 8th bit is set is - binary. ''' - - fin = open(filename, 'rb') - wholething = fin.read() - fin.close() - for c in wholething: - if ord(c) & 0x80: - return 1 - return 0 - -def is_kb_sticky (filename): - - '''This checks if 'cvs status' reports '-kb' for Sticky options. If the - Sticky Option status is '-ks' then this returns 1. If the status is - 'Unknown' then it returns 1. Otherwise 0 is returned. ''' - - try: - s = pexpect.spawn ('cvs status %s' % filename) - i = s.expect (['Sticky Options:\s*(.*)\r\n', 'Status: Unknown']) - if i==1 and VERBOSE: - print('File not part of CVS repository:', filename) - return 1 # Pretend it's OK. - if s.match.group(1) == '-kb': - return 1 - s = None - except: - print('Something went wrong trying to run external cvs command.') - print(' cvs status %s' % filename) - print('The cvs command returned:') - print(s.before) - return 0 - -def cvs_admin_kb (filename): - - '''This uses 'cvs admin' to set the '-kb' sticky option. ''' - - s = pexpect.run ('cvs admin -kb %s' % filename) - # There is a timing issue. If I run 'cvs admin' too quickly - # cvs sometimes has trouble obtaining the directory lock. - time.sleep(1) - -def walk_and_clean_cvs_binaries (arg, dirname, names): - - '''This contains the logic for processing files. This is the os.path.walk - callback. This skips dirnames that end in CVS. ''' - - if len(dirname)>3 and dirname[-3:]=='CVS': - return - for n in names: - fullpath = os.path.join (dirname, n) - if os.path.isdir(fullpath) or os.path.islink(fullpath): - continue - if is_binary(fullpath): - if not is_kb_sticky (fullpath): - if VERBOSE: print(fullpath) - cvs_admin_kb (fullpath) - -def main (): - - if len(sys.argv) == 1: - root = '.' - else: - root = sys.argv[1] - os.path.walk (root, walk_and_clean_cvs_binaries, None) - -if __name__ == '__main__': - main () diff --git a/examples/rippy.py b/examples/rippy.py deleted file mode 100755 index 4e8ed3d..0000000 --- a/examples/rippy.py +++ /dev/null @@ -1,999 +0,0 @@ -#!/usr/bin/env python - -'''Rippy! - -This is old and probably does not work anymore. -This script helps to convert video from one format to another. -This is useful for ripping DVD to mpeg4 video (XviD, DivX). - -Features: - * automatic crop detection - * mp3 audio compression with resampling options - * automatic bitrate calculation based on desired target size - * optional interlace removal, b/w video optimization, video scaling - -Run the script with no arguments to start with interactive prompts: - rippy.py -Run the script with the filename of a config to start automatic mode: - rippy.py rippy.conf - -After Rippy is finished it saves the current configuation in a file called -'rippy.conf' in the local directoy. This can be used to rerun process using the -exact same settings by passing the filename of the conf file as an argument to -Rippy. Rippy will read the options from the file instead of asking you for -options interactively. So if you run rippy with 'dry_run=1' then you can run -the process again later using the 'rippy.conf' file. Don't forget to edit -'rippy.conf' to set 'dry_run=0'! - -If you run rippy with 'dry_run' and 'verbose' true then the output generated is -valid command line commands. you could (in theory) cut-and-paste the commands -to a shell prompt. You will need to tweak some values such as crop area and bit -rate because these cannot be calculated in a dry run. This is useful if you -want to get an idea of what Rippy plans to do. - -For all the trouble that Rippy goes through to calculate the best bitrate for a -desired target video size it sometimes fails to get it right. Sometimes the -final video size will differ more than you wanted from the desired size, but if -you are really motivated and have a lot of time on your hands then you can run -Rippy again with a manually calculated bitrate. After all compression is done -the first time Rippy will recalculate the bitrate to give you the nearly exact -bitrate that would have worked. You can then edit the 'rippy.conf' file; set -the video_bitrate with this revised bitrate; and then run Rippy all over again. -There is nothing like 4-pass video compression to get it right! Actually, this -could be done in three passes since I don't need to do the second pass -compression before I calculate the revised bitrate. I'm also considering an -enhancement where Rippy would compress ten spread out chunks, 1-minute in -length to estimate the bitrate. - -PEXPECT LICENSE - - This license is approved by the OSI and FSF as GPL-compatible. - http://opensource.org/licenses/isc-license.txt - - Copyright (c) 2012, Noah Spurrier <noah@noah.org> - PERMISSION TO USE, COPY, MODIFY, AND/OR DISTRIBUTE THIS SOFTWARE FOR ANY - PURPOSE WITH OR WITHOUT FEE IS HEREBY GRANTED, PROVIDED THAT THE ABOVE - COPYRIGHT NOTICE AND THIS PERMISSION NOTICE APPEAR IN ALL COPIES. - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -''' - -from __future__ import print_function - -from __future__ import absolute_import - -import sys, os, re, math, stat, getopt, traceback, types, time -import pexpect - - -try: - raw_input -except NameError: - raw_input = input - - -__version__ = '1.2' -__revision__ = '$Revision: 11 $' -__all__ = ['main', __version__, __revision__] - -GLOBAL_LOGFILE_NAME = "rippy_%d.log" % os.getpid() -GLOBAL_LOGFILE = open (GLOBAL_LOGFILE_NAME, "wb") - -############################################################################### -# This giant section defines the prompts and defaults used in interactive mode. -############################################################################### -# Python dictionaries are unordered, so -# I have this list that maintains the order of the keys. -prompts_key_order = ( -'verbose_flag', -'dry_run_flag', -'video_source_filename', -'video_chapter', -'video_final_filename', -'video_length', -'video_aspect_ratio', -'video_scale', -'video_encode_passes', -'video_codec', -'video_fourcc_override', -'video_bitrate', -'video_bitrate_overhead', -'video_target_size', -'video_deinterlace_flag', -'video_crop_area', -'video_gray_flag', -'subtitle_id', -'audio_id', -'audio_codec', -'audio_raw_filename', -'audio_volume_boost', -'audio_sample_rate', -'audio_bitrate', -#'audio_lowpass_filter', -'delete_tmp_files_flag' -) -# -# The 'prompts' dictionary holds all the messages shown to the user in -# interactive mode. The 'prompts' dictionary schema is defined as follows: -# prompt_key : ( default value, prompt string, help string, level of difficulty (0,1,2) ) -# -prompts = { -'video_source_filename':("dvd://1", 'video source filename?', '''This is the filename of the video that you want to convert from. -It can be any file that mencoder supports. -You can also choose a DVD device using the dvd://1 syntax. -Title 1 is usually the main title on a DVD.''',0), -'video_chapter':("none",'video chapter?','''This is the chapter number. Usually disks such as TV series seasons will be divided into chapters. Maybe be set to none.''',0), -'video_final_filename':("video_final.avi", "video final filename?", '''This is the name of the final video.''',0), -'audio_raw_filename':("audiodump.wav", "audio raw filename?", '''This is the audio raw PCM filename. This is prior to compression. -Note that mplayer automatically names this audiodump.wav, so don't change this.''',1000), -#'audio_compressed_filename':("audiodump.mp3","Audio compressed filename?", '''This is the name of the compressed audio that will be mixed -#into the final video. Normally you don't need to change this.''',2), -'video_length':("none","video length in seconds?",'''This sets the length of the video in seconds. This is used to estimate the -bitrate for a target video file size. Set to 'calc' to have Rippy calculate -the length. Set to 'none' if you don't want rippy to estimate the bitrate -- -you will have to manually specify bitrate.''',1), -'video_aspect_ratio':("calc","aspect ratio?",'''This sets the aspect ratio of the video. Most DVDs are 16/9 or 4/3.''',1), -'video_scale':("none","video scale?",'''This scales the video to the given output size. The default is to do no scaling. -You may type in a resolution such as 320x240 or you may use presets. - qntsc: 352x240 (NTSC quarter screen) - qpal: 352x288 (PAL quarter screen) - ntsc: 720x480 (standard NTSC) - pal: 720x576 (standard PAL) - sntsc: 640x480 (square pixel NTSC) - spal: 768x576 (square pixel PAL)''',1), -'video_codec':("mpeg4","video codec?",'''This is the video compression to use. This is passed directly to mencoder, so -any format that it recognizes should work. For XviD or DivX use mpeg4. -Almost all MS Windows systems support wmv2 out of the box. -Some common codecs include: -mjpeg, h263, h263p, h264, mpeg4, msmpeg4, wmv1, wmv2, mpeg1video, mpeg2video, huffyuv, ffv1. -''',2), -'audio_codec':("mp3","audio codec?",'''This is the audio compression to use. This is passed directly to mencoder, so -any format that it recognizes will work. -Some common codecs include: -mp3, mp2, aac, pcm -See mencoder manual for details.''',2), -'video_fourcc_override':("XVID","force fourcc code?",'''This forces the fourcc codec to the given value. XVID is safest for Windows. -The following are common fourcc values: - FMP4 - This is the mencoder default. This is the "real" value. - XVID - used by Xvid (safest) - DX50 - - MP4S - Microsoft''',2), -'video_encode_passes':("1","number of encode passes?",'''This sets how many passes to use to encode the video. You can choose 1 or 2. -Using two pases takes twice as long as one pass, but produces a better -quality video. I found that the improvement is not that impressive.''',1), -'verbose_flag':("Y","verbose output?",'''This sets verbose output. If true then all commands and arguments are printed -before they are run. This is useful to see exactly how commands are run.''',1), -'dry_run_flag':("N","dry run?",'''This sets 'dry run' mode. If true then commands are not run. This is useful -if you want to see what would the script would do.''',1), -'video_bitrate':("calc","video bitrate?",'''This sets the video bitrate. This overrides video_target_size. -Set to 'calc' to automatically estimate the bitrate based on the -video final target size. If you set video_length to 'none' then -you will have to specify this video_bitrate.''',1), -'video_target_size':("737280000","video final target size?",'''This sets the target video size that you want to end up with. -This is over-ridden by video_bitrate. In other words, if you specify -video_bitrate then video_target_size is ignored. -Due to the unpredictable nature of VBR compression the final video size -may not exactly match. The following are common CDR sizes: - 180MB CDR (21 minutes) holds 193536000 bytes - 550MB CDR (63 minutes) holds 580608000 bytes - 650MB CDR (74 minutes) holds 681984000 bytes - 700MB CDR (80 minutes) holds 737280000 bytes''',0), -'video_bitrate_overhead':("1.0","bitrate overhead factor?",'''Adjust this value if you want to leave more room for -other files such as subtitle files. -If you specify video_bitrate then this value is ignored.''',2), -'video_crop_area':("detect","crop area?",'''This sets the crop area to remove black bars from the top or sides of the video. -This helps save space. Set to 'detect' to automatically detect the crop area. -Set to 'none' to not crop the video. Normally you don't need to change this.''',1), -'video_deinterlace_flag':("N","is the video interlaced?",'''This sets the deinterlace flag. If set then mencoder will be instructed -to filter out interlace artifacts (using '-vf pp=md').''',1), -'video_gray_flag':("N","is the video black and white (gray)?",'''This improves output for black and white video.''',1), -'subtitle_id':("None","Subtitle ID stream?",'''This selects the subtitle stream to extract from the source video. -Normally, 0 is the English subtitle stream for a DVD. -Subtitles IDs with higher numbers may be other languages.''',1), -'audio_id':("128","audio ID stream?",'''This selects the audio stream to extract from the source video. -If your source is a VOB file (DVD) then stream IDs start at 128. -Normally, 128 is the main audio track for a DVD. -Tracks with higher numbers may be other language dubs or audio commentary.''',1), -'audio_sample_rate':("32000","audio sample rate (Hz) 48000, 44100, 32000, 24000, 12000",'''This sets the rate at which the compressed audio will be resampled. -DVD audio is 48 kHz whereas music CDs use 44.1 kHz. The higher the sample rate -the more space the audio track will take. That will leave less space for video. -32 kHz is a good trade-off if you are trying to fit a video onto a CD.''',1), -'audio_bitrate':("96","audio bitrate (kbit/s) 192, 128, 96, 64?",'''This sets the bitrate for MP3 audio compression. -The higher the bitrate the more space the audio track will take. -That will leave less space for video. Most people find music to be acceptable -at 128 kBitS. 96 kBitS is a good trade-off if you are trying to fit a video onto a CD.''',1), -'audio_volume_boost':("none","volume dB boost?",'''Many DVDs have very low audio volume. This sets an audio volume boost in Decibels. -Values of 6 to 10 usually adjust quiet DVDs to a comfortable level.''',1), -#'audio_lowpass_filter':("16","audio lowpass filter (kHz)?",'''This sets the low-pass filter for the audio. -#Normally this should be half of the audio sample rate. -#This improves audio compression and quality. -#Normally you don't need to change this.''',1), -'delete_tmp_files_flag':("N","delete temporary files when finished?",'''If Y then %s, audio_raw_filename, and 'divx2pass.log' will be deleted at the end.'''%GLOBAL_LOGFILE_NAME,1) -} - -############################################################################## -# This is the important convert control function -############################################################################## -def convert (options): - '''This is the heart of it all -- this performs an end-to-end conversion of - a video from one format to another. It requires a dictionary of options. - The conversion process will also add some keys to the dictionary - such as length of the video and crop area. The dictionary is returned. - This options dictionary could be used again to repeat the convert process - (it is also saved to rippy.conf as text). - ''' - if options['subtitle_id'] is not None: - print("# extract subtitles") - apply_smart (extract_subtitles, options) - else: - print("# do not extract subtitles.") - - # Optimization - # I really only need to calculate the exact video length if the user - # selected 'calc' for video_bitrate - # or - # selected 'detect' for video_crop_area. - if options['video_bitrate']=='calc' or options['video_crop_area']=='detect': - # As strange as it seems, the only reliable way to calculate the length - # of a video (in seconds) is to extract the raw, uncompressed PCM audio stream - # and then calculate the length of that. This is because MP4 video is VBR, so - # you cannot get exact time based on compressed size. - if options['video_length']=='calc': - print("# extract PCM raw audio to %s" % (options['audio_raw_filename'])) - apply_smart (extract_audio, options) - options['video_length'] = apply_smart (get_length, options) - print("# Length of raw audio file : %d seconds (%0.2f minutes)" % (options['video_length'], float(options['video_length'])/60.0)) - if options['video_bitrate']=='calc': - options['video_bitrate'] = options['video_bitrate_overhead'] * apply_smart (calc_video_bitrate, options) - print("# video bitrate : " + str(options['video_bitrate'])) - if options['video_crop_area']=='detect': - options['video_crop_area'] = apply_smart (crop_detect, options) - print("# crop area : " + str(options['video_crop_area'])) - print("# compression estimate") - print(apply_smart (compression_estimate, options)) - - print("# compress video") - apply_smart (compress_video, options) - 'audio_volume_boost', - - print("# delete temporary files:", end=' ') - if options['delete_tmp_files_flag']: - print("yes") - apply_smart (delete_tmp_files, options) - else: - print("no") - - # Finish by saving options to rippy.conf and - # calclating if final_size is less than target_size. - o = ["# options used to create video\n"] - video_actual_size = get_filesize (options['video_final_filename']) - if options['video_target_size'] != 'none': - revised_bitrate = calculate_revised_bitrate (options['video_bitrate'], options['video_target_size'], video_actual_size) - o.append("# revised video_bitrate : %d\n" % revised_bitrate) - for k,v in options.items(): - o.append (" %30s : %s\n" % (k, v)) - print('# '.join(o)) - fout = open("rippy.conf","wb").write(''.join(o)) - print("# final actual video size = %d" % video_actual_size) - if options['video_target_size'] != 'none': - if video_actual_size > options['video_target_size']: - print("# FINAL VIDEO SIZE IS GREATER THAN DESIRED TARGET") - print("# final video size is %d bytes over target size" % (video_actual_size - options['video_target_size'])) - else: - print("# final video size is %d bytes under target size" % (options['video_target_size'] - video_actual_size)) - print("# If you want to run the entire compression process all over again") - print("# to get closer to the target video size then trying using a revised") - print("# video_bitrate of %d" % revised_bitrate) - - return options - -############################################################################## - -def exit_with_usage(exit_code=1): - print(globals()['__doc__']) - print('version:', globals()['__version__']) - sys.stdout.flush() - os._exit(exit_code) - -def check_missing_requirements (): - '''This list of missing requirements (mencoder, mplayer, lame, and mkvmerge). - Returns None if all requirements are in the execution path. - ''' - missing = [] - if pexpect.which("mencoder") is None: - missing.append("mencoder") - if pexpect.which("mplayer") is None: - missing.append("mplayer") - cmd = "mencoder -oac help" - (command_output, exitstatus) = run(cmd) - ar = re.findall("(mp3lame)", command_output) - if len(ar)==0: - missing.append("Mencoder was not compiled with mp3lame support.") - - #if pexpect.which("lame") is None: - # missing.append("lame") - #if pexpect.which("mkvmerge") is None: - # missing.append("mkvmerge") - if len(missing)==0: - return None - return missing - -def input_option (message, default_value="", help=None, level=0, max_level=0): - '''This is a fancy raw_input function. - If the user enters '?' then the contents of help is printed. - - The 'level' and 'max_level' are used to adjust which advanced options - are printed. 'max_level' is the level of options that the user wants - to see. 'level' is the level of difficulty for this particular option. - If this level is <= the max_level the user wants then the - message is printed and user input is allowed; otherwise, the - default value is returned automatically without user input. - ''' - if default_value != '': - message = "%s [%s] " % (message, default_value) - if level > max_level: - return default_value - while 1: - user_input = raw_input (message) - if user_input=='?': - print(help) - elif user_input=='': - return default_value - else: - break - return user_input - -def progress_callback (d=None): - '''This callback simply prints a dot to show activity. - This is used when running external commands with pexpect.run. - ''' - sys.stdout.write (".") - sys.stdout.flush() - -def run(cmd): - global GLOBAL_LOGFILE - print(cmd, file=GLOBAL_LOGFILE) - (command_output, exitstatus) = pexpect.run(cmd, events={pexpect.TIMEOUT:progress_callback}, timeout=5, withexitstatus=True, logfile=GLOBAL_LOGFILE) - if exitstatus != 0: - print("RUN FAILED. RETURNED EXIT STATUS:", exitstatus) - print("RUN FAILED. RETURNED EXIT STATUS:", exitstatus, file=GLOBAL_LOGFILE) - return (command_output, exitstatus) - -def apply_smart (func, args): - '''This is similar to func(**args), but this won't complain about - extra keys in 'args'. This ignores keys in 'args' that are - not required by 'func'. This passes None to arguments that are - not defined in 'args'. That's fine for arguments with a default valeue, but - that's a bug for required arguments. I should probably raise a TypeError. - The func parameter can be a function reference or a string. - If it is a string then it is converted to a function reference. - ''' - if type(func) is type(''): - if func in globals(): - func = globals()[func] - else: - raise NameError("name '%s' is not defined" % func) - if hasattr(func, '__func__'): # Handle case when func is a class method. - func = func.__func__ - argcount = func.__code__.co_argcount - required_args = dict([(k,args.get(k)) for k in func.__code__.co_varnames[:argcount]]) - return func(**required_args) - -def count_unique (items): - '''This takes a list and returns a sorted list of tuples with a count of each unique item in the list. - Example 1: - count_unique(['a','b','c','a','c','c','a','c','c']) - returns: - [(5,'c'), (3,'a'), (1,'b')] - Example 2 -- get the most frequent item in a list: - count_unique(['a','b','c','a','c','c','a','c','c'])[0][1] - returns: - 'c' - ''' - stats = {} - for i in items: - if i in stats: - stats[i] = stats[i] + 1 - else: - stats[i] = 1 - stats = [(v, k) for k, v in stats.items()] - stats.sort() - stats.reverse() - return stats - -def calculate_revised_bitrate (video_bitrate, video_target_size, video_actual_size): - '''This calculates a revised video bitrate given the video_bitrate used, - the actual size that resulted, and the video_target_size. - This can be used if you want to compress the video all over again in an - attempt to get closer to the video_target_size. - ''' - return int(math.floor(video_bitrate * (float(video_target_size) / float(video_actual_size)))) - -def get_aspect_ratio (video_source_filename): - '''This returns the aspect ratio of the original video. - This is usualy 1.78:1(16/9) or 1.33:1(4/3). - This function is very lenient. It basically guesses 16/9 whenever - it cannot figure out the aspect ratio. - ''' - cmd = "mplayer '%s' -vo png -ao null -frames 1" % video_source_filename - (command_output, exitstatus) = run(cmd) - ar = re.findall("Movie-Aspect is ([0-9]+\.?[0-9]*:[0-9]+\.?[0-9]*)", command_output) - if len(ar)==0: - return '16/9' - if ar[0] == '1.78:1': - return '16/9' - if ar[0] == '1.33:1': - return '4/3' - return '16/9' - #idh = re.findall("ID_VIDEO_HEIGHT=([0-9]+)", command_output) - #if len(idw)==0 or len(idh)==0: - # print 'WARNING!' - # print 'Could not get aspect ration. Assuming 1.78:1 (16/9).' - # return 1.78 - #return float(idw[0])/float(idh[0]) -#ID_VIDEO_WIDTH=720 -#ID_VIDEO_HEIGHT=480 -#Movie-Aspect is 1.78:1 - prescaling to correct movie aspect. - - -def get_aid_list (video_source_filename): - '''This returns a list of audio ids in the source video file. - TODO: Also extract ID_AID_nnn_LANG to associate language. Not all DVDs include this. - ''' - cmd = "mplayer '%s' -vo null -ao null -frames 0 -identify" % video_source_filename - (command_output, exitstatus) = run(cmd) - idl = re.findall("ID_AUDIO_ID=([0-9]+)", command_output) - idl.sort() - return idl - -def get_sid_list (video_source_filename): - '''This returns a list of subtitle ids in the source video file. - TODO: Also extract ID_SID_nnn_LANG to associate language. Not all DVDs include this. - ''' - cmd = "mplayer '%s' -vo null -ao null -frames 0 -identify" % video_source_filename - (command_output, exitstatus) = run(cmd) - idl = re.findall("ID_SUBTITLE_ID=([0-9]+)", command_output) - idl.sort() - return idl - -def extract_audio (video_source_filename, audio_id=128, verbose_flag=0, dry_run_flag=0): - '''This extracts the given audio_id track as raw uncompressed PCM from the given source video. - Note that mplayer always saves this to audiodump.wav. - At this time there is no way to set the output audio name. - ''' - #cmd = "mplayer %(video_source_filename)s -vc null -vo null -aid %(audio_id)s -ao pcm:fast -noframedrop" % locals() - cmd = "mplayer -quiet '%(video_source_filename)s' -vc dummy -vo null -aid %(audio_id)s -ao pcm:fast -noframedrop" % locals() - if verbose_flag: print(cmd) - if not dry_run_flag: - run(cmd) - print() - -def extract_subtitles (video_source_filename, subtitle_id=0, verbose_flag=0, dry_run_flag=0): - '''This extracts the given subtitle_id track as VOBSUB format from the given source video. - ''' - cmd = "mencoder -quiet '%(video_source_filename)s' -o /dev/null -nosound -ovc copy -vobsubout subtitles -vobsuboutindex 0 -sid %(subtitle_id)s" % locals() - if verbose_flag: print(cmd) - if not dry_run_flag: - run(cmd) - print() - -def get_length (audio_raw_filename): - '''This attempts to get the length of the media file (length is time in seconds). - This should not be confused with size (in bytes) of the file data. - This is best used on a raw PCM AUDIO file because mplayer cannot get an accurate - time for many compressed video and audio formats -- notably MPEG4 and MP3. - Weird... - This returns -1 if it cannot get the length of the given file. - ''' - cmd = "mplayer %s -vo null -ao null -frames 0 -identify" % audio_raw_filename - (command_output, exitstatus) = run(cmd) - idl = re.findall("ID_LENGTH=([0-9.]*)", command_output) - idl.sort() - if len(idl) != 1: - print("ERROR: cannot get length of raw audio file.") - print("command_output of mplayer identify:") - print(command_output) - print("parsed command_output:") - print(str(idl)) - return -1 - return float(idl[0]) - -def get_filesize (filename): - '''This returns the number of bytes a file takes on storage.''' - return os.stat(filename)[stat.ST_SIZE] - -def calc_video_bitrate (video_target_size, audio_bitrate, video_length, extra_space=0, dry_run_flag=0): - '''This gives an estimate of the video bitrate necessary to - fit the final target size. This will take into account room to - fit the audio and extra space if given (for container overhead or whatnot). - video_target_size is in bytes, - audio_bitrate is bits per second (96, 128, 256, etc.) ASSUMING CBR, - video_length is in seconds, - extra_space is in bytes. - a 180MB CDR (21 minutes) holds 193536000 bytes. - a 550MB CDR (63 minutes) holds 580608000 bytes. - a 650MB CDR (74 minutes) holds 681984000 bytes. - a 700MB CDR (80 minutes) holds 737280000 bytes. - ''' - if dry_run_flag: - return -1 - if extra_space is None: extra_space = 0 - #audio_size = os.stat(audio_compressed_filename)[stat.ST_SIZE] - audio_size = (audio_bitrate * video_length * 1000) / 8.0 - video_target_size = video_target_size - audio_size - extra_space - return (int)(calc_video_kbitrate (video_target_size, video_length)) - -def calc_video_kbitrate (target_size, length_secs): - '''Given a target byte size free for video data, this returns the bitrate in kBit/S. - For mencoder vbitrate 1 kBit = 1000 Bits -- not 1024 bits. - target_size = bitrate * 1000 * length_secs / 8 - target_size = bitrate * 125 * length_secs - bitrate = target_size/(125*length_secs) - ''' - return int(target_size / (125.0 * length_secs)) - -def crop_detect (video_source_filename, video_length, dry_run_flag=0): - '''This attempts to figure out the best crop for the given video file. - Basically it runs crop detect for 10 seconds on five different places in the video. - It picks the crop area that was most often detected. - ''' - skip = int(video_length/9) # offset to skip (-ss option in mencoder) - sample_length = 10 - cmd1 = "mencoder '%s' -quiet -ss %d -endpos %d -o /dev/null -nosound -ovc lavc -vf cropdetect" % (video_source_filename, skip, sample_length) - cmd2 = "mencoder '%s' -quiet -ss %d -endpos %d -o /dev/null -nosound -ovc lavc -vf cropdetect" % (video_source_filename, 2*skip, sample_length) - cmd3 = "mencoder '%s' -quiet -ss %d -endpos %d -o /dev/null -nosound -ovc lavc -vf cropdetect" % (video_source_filename, 4*skip, sample_length) - cmd4 = "mencoder '%s' -quiet -ss %d -endpos %d -o /dev/null -nosound -ovc lavc -vf cropdetect" % (video_source_filename, 6*skip, sample_length) - cmd5 = "mencoder '%s' -quiet -ss %d -endpos %d -o /dev/null -nosound -ovc lavc -vf cropdetect" % (video_source_filename, 8*skip, sample_length) - if dry_run_flag: - return "0:0:0:0" - (command_output1, exitstatus1) = run(cmd1) - (command_output2, exitstatus2) = run(cmd2) - (command_output3, exitstatus3) = run(cmd3) - (command_output4, exitstatus4) = run(cmd4) - (command_output5, exitstatus5) = run(cmd5) - idl = re.findall("-vf crop=([0-9]+:[0-9]+:[0-9]+:[0-9]+)", command_output1) - idl = idl + re.findall("-vf crop=([0-9]+:[0-9]+:[0-9]+:[0-9]+)", command_output2) - idl = idl + re.findall("-vf crop=([0-9]+:[0-9]+:[0-9]+:[0-9]+)", command_output3) - idl = idl + re.findall("-vf crop=([0-9]+:[0-9]+:[0-9]+:[0-9]+)", command_output4) - idl = idl + re.findall("-vf crop=([0-9]+:[0-9]+:[0-9]+:[0-9]+)", command_output5) - items_count = count_unique(idl) - return items_count[0][1] - - -def build_compression_command (video_source_filename, video_final_filename, video_target_size, audio_id=128, video_bitrate=1000, video_codec='mpeg4', audio_codec='mp3', video_fourcc_override='FMP4', video_gray_flag=0, video_crop_area=None, video_aspect_ratio='16/9', video_scale=None, video_encode_passes=2, video_deinterlace_flag=0, audio_volume_boost=None, audio_sample_rate=None, audio_bitrate=None, seek_skip=None, seek_length=None, video_chapter=None): -#Notes:For DVD, VCD, and SVCD use acodec=mp2 and vcodec=mpeg2video: -#mencoder movie.avi -o movie.VOB -ovc lavc -oac lavc -lavcopts acodec=mp2:abitrate=224:vcodec=mpeg2video:vbitrate=2000 - - # - # build video filter (-vf) argument - # - video_filter = '' - if video_crop_area and video_crop_area.lower()!='none': - video_filter = video_filter + 'crop=%s' % video_crop_area - if video_deinterlace_flag: - if video_filter != '': - video_filter = video_filter + ',' - video_filter = video_filter + 'pp=md' - if video_scale and video_scale.lower()!='none': - if video_filter != '': - video_filter = video_filter + ',' - video_filter = video_filter + 'scale=%s' % video_scale - # optional video rotation -- were you holding your camera sideways? - #if video_filter != '': - # video_filter = video_filter + ',' - #video_filter = video_filter + 'rotate=2' - if video_filter != '': - video_filter = '-vf ' + video_filter - - # - # build chapter argument - # - if video_chapter is not None: - chapter = '-chapter %d-%d' %(video_chapter,video_chapter) - else: - chapter = '' -# chapter = '-chapter 2-2' - - # - # build audio_filter argument - # - audio_filter = '' - if audio_sample_rate: - if audio_filter != '': - audio_filter = audio_filter + ',' - audio_filter = audio_filter + 'lavcresample=%s' % audio_sample_rate - if audio_volume_boost is not None: - if audio_filter != '': - audio_filter = audio_filter + ',' - audio_filter = audio_filter + 'volume=%0.1f:1'%audio_volume_boost - if audio_filter != '': - audio_filter = '-af ' + audio_filter - # - #if audio_sample_rate: - # audio_filter = ('-srate %d ' % audio_sample_rate) + audio_filter - - # - # build lavcopts argument - # - #lavcopts = '-lavcopts vcodec=%s:vbitrate=%d:mbd=2:aspect=%s:acodec=%s:abitrate=%d:vpass=1' % (video_codec,video_bitrate,audio_codec,audio_bitrate) - lavcopts = '-lavcopts vcodec=%(video_codec)s:vbitrate=%(video_bitrate)d:mbd=2:aspect=%(video_aspect_ratio)s:acodec=%(audio_codec)s:abitrate=%(audio_bitrate)d:vpass=1' % (locals()) - if video_gray_flag: - lavcopts = lavcopts + ':gray' - - seek_filter = '' - if seek_skip is not None: - seek_filter = '-ss %s' % (str(seek_skip)) - if seek_length is not None: - seek_filter = seek_filter + ' -endpos %s' % (str(seek_length)) - -# cmd = "mencoder -quiet -info comment='Arkivist' '%(video_source_filename)s' %(seek_filter)s %(chapter)s -aid %(audio_id)s -o '%(video_final_filename)s' -ffourcc %(video_fourcc_override)s -ovc lavc -oac lavc %(lavcopts)s %(video_filter)s %(audio_filter)s" % locals() - cmd = "mencoder -quiet -info comment='Arkivist' '%(video_source_filename)s' %(seek_filter)s %(chapter)s -aid %(audio_id)s -o '%(video_final_filename)s' -ffourcc %(video_fourcc_override)s -ovc lavc -oac mp3lame %(lavcopts)s %(video_filter)s %(audio_filter)s" % locals() - return cmd - -def compression_estimate (video_length, video_source_filename, video_final_filename, video_target_size, audio_id=128, video_bitrate=1000, video_codec='mpeg4', audio_codec='mp3', video_fourcc_override='FMP4', video_gray_flag=0, video_crop_area=None, video_aspect_ratio='16/9', video_scale=None, video_encode_passes=2, video_deinterlace_flag=0, audio_volume_boost=None, audio_sample_rate=None, audio_bitrate=None): - '''This attempts to figure out the best compression ratio for a given set of compression options. - ''' - # TODO Need to account for AVI overhead. - skip = int(video_length/9) # offset to skip (-ss option in mencoder) - sample_length = 10 - cmd1 = build_compression_command (video_source_filename, "compression_test_1.avi", video_target_size, audio_id, video_bitrate, video_codec, audio_codec, video_fourcc_override, video_gray_flag, video_crop_area, video_aspect_ratio, video_scale, video_encode_passes, video_deinterlace_flag, audio_volume_boost, audio_sample_rate, audio_bitrate, skip, sample_length) - cmd2 = build_compression_command (video_source_filename, "compression_test_2.avi", video_target_size, audio_id, video_bitrate, video_codec, audio_codec, video_fourcc_override, video_gray_flag, video_crop_area, video_aspect_ratio, video_scale, video_encode_passes, video_deinterlace_flag, audio_volume_boost, audio_sample_rate, audio_bitrate, skip*2, sample_length) - cmd3 = build_compression_command (video_source_filename, "compression_test_3.avi", video_target_size, audio_id, video_bitrate, video_codec, audio_codec, video_fourcc_override, video_gray_flag, video_crop_area, video_aspect_ratio, video_scale, video_encode_passes, video_deinterlace_flag, audio_volume_boost, audio_sample_rate, audio_bitrate, skip*4, sample_length) - cmd4 = build_compression_command (video_source_filename, "compression_test_4.avi", video_target_size, audio_id, video_bitrate, video_codec, audio_codec, video_fourcc_override, video_gray_flag, video_crop_area, video_aspect_ratio, video_scale, video_encode_passes, video_deinterlace_flag, audio_volume_boost, audio_sample_rate, audio_bitrate, skip*6, sample_length) - cmd5 = build_compression_command (video_source_filename, "compression_test_5.avi", video_target_size, audio_id, video_bitrate, video_codec, audio_codec, video_fourcc_override, video_gray_flag, video_crop_area, video_aspect_ratio, video_scale, video_encode_passes, video_deinterlace_flag, audio_volume_boost, audio_sample_rate, audio_bitrate, skip*8, sample_length) - run(cmd1) - run(cmd2) - run(cmd3) - run(cmd4) - run(cmd5) - size = get_filesize ("compression_test_1.avi")+get_filesize ("compression_test_2.avi")+get_filesize ("compression_test_3.avi")+get_filesize ("compression_test_4.avi")+get_filesize ("compression_test_5.avi") - return (size / 5.0) - -def compress_video (video_source_filename, video_final_filename, video_target_size, audio_id=128, video_bitrate=1000, video_codec='mpeg4', audio_codec='mp3', video_fourcc_override='FMP4', video_gray_flag=0, video_crop_area=None, video_aspect_ratio='16/9', video_scale=None, video_encode_passes=2, video_deinterlace_flag=0, audio_volume_boost=None, audio_sample_rate=None, audio_bitrate=None, seek_skip=None, seek_length=None, video_chapter=None, verbose_flag=0, dry_run_flag=0): - '''This compresses the video and audio of the given source video filename to the transcoded filename. - This does a two-pass compression (I'm assuming mpeg4, I should probably make this smarter for other formats). - ''' - # - # do the first pass video compression - # - #cmd = "mencoder -quiet '%(video_source_filename)s' -ss 65 -endpos 20 -aid %(audio_id)s -o '%(video_final_filename)s' -ffourcc %(video_fourcc_override)s -ovc lavc -oac lavc %(lavcopts)s %(video_filter)s %(audio_filter)s" % locals() - - cmd = build_compression_command (video_source_filename, video_final_filename, video_target_size, audio_id, video_bitrate, video_codec, audio_codec, video_fourcc_override, video_gray_flag, video_crop_area, video_aspect_ratio, video_scale, video_encode_passes, video_deinterlace_flag, audio_volume_boost, audio_sample_rate, audio_bitrate, seek_skip, seek_length, video_chapter) - if verbose_flag: print(cmd) - if not dry_run_flag: - run(cmd) - print() - - # If not doing two passes then return early. - if video_encode_passes!='2': - return - - if verbose_flag: - video_actual_size = get_filesize (video_final_filename) - if video_actual_size > video_target_size: - print("=======================================================") - print("WARNING!") - print("First pass compression resulted in") - print("actual file size greater than target size.") - print("Second pass will be too big.") - print("=======================================================") - - # - # do the second pass video compression - # - cmd = cmd.replace ('vpass=1', 'vpass=2') - if verbose_flag: print(cmd) - if not dry_run_flag: - run(cmd) - print() - return - -def compress_audio (audio_raw_filename, audio_compressed_filename, audio_lowpass_filter=None, audio_sample_rate=None, audio_bitrate=None, verbose_flag=0, dry_run_flag=0): - '''This is depricated. - This compresses the raw audio file to the compressed audio filename. - ''' - cmd = 'lame -h --athaa-sensitivity 1' # --cwlimit 11" - if audio_lowpass_filter: - cmd = cmd + ' --lowpass ' + audio_lowpass_filter - if audio_bitrate: - #cmd = cmd + ' --abr ' + audio_bitrate - cmd = cmd + ' --cbr -b ' + audio_bitrate - if audio_sample_rate: - cmd = cmd + ' --resample ' + audio_sample_rate - cmd = cmd + ' ' + audio_raw_filename + ' ' + audio_compressed_filename - if verbose_flag: print(cmd) - if not dry_run_flag: - (command_output, exitstatus) = run(cmd) - print() - if exitstatus != 0: - raise Exception('ERROR: lame failed to compress raw audio file.') - -def mux (video_final_filename, video_transcoded_filename, audio_compressed_filename, video_container_format, verbose_flag=0, dry_run_flag=0): - '''This is depricated. I used to use a three-pass encoding where I would mix the audio track separately, but - this never worked very well (loss of audio sync).''' - if video_container_format.lower() == 'mkv': # Matroska - mux_mkv (video_final_filename, video_transcoded_filename, audio_compressed_filename, verbose_flag, dry_run_flag) - if video_container_format.lower() == 'avi': - mux_avi (video_final_filename, video_transcoded_filename, audio_compressed_filename, verbose_flag, dry_run_flag) - -def mux_mkv (video_final_filename, video_transcoded_filename, audio_compressed_filename, verbose_flag=0, dry_run_flag=0): - '''This is depricated.''' - cmd = 'mkvmerge -o %s --noaudio %s %s' % (video_final_filename, video_transcoded_filename, audio_compressed_filename) - if verbose_flag: print(cmd) - if not dry_run_flag: - run(cmd) - print() - -def mux_avi (video_final_filename, video_transcoded_filename, audio_compressed_filename, verbose_flag=0, dry_run_flag=0): - '''This is depricated.''' - pass -# cmd = "mencoder -quiet -oac copy -ovc copy -o '%s' -audiofile %s '%s'" % (video_final_filename, audio_compressed_filename, video_transcoded_filename) -# if verbose_flag: print cmd -# if not dry_run_flag: -# run(cmd) -# print - -def delete_tmp_files (audio_raw_filename, verbose_flag=0, dry_run_flag=0): - global GLOBAL_LOGFILE_NAME - file_list = ' '.join([GLOBAL_LOGFILE_NAME, 'divx2pass.log', audio_raw_filename ]) - cmd = 'rm -f ' + file_list - if verbose_flag: print(cmd) - if not dry_run_flag: - run(cmd) - print() - -############################################################################## -# This is the interactive Q&A that is used if a conf file was not given. -############################################################################## -def interactive_convert (): - - global prompts, prompts_key_order - - print(globals()['__doc__']) - print() - print("==============================================") - print(" Enter '?' at any question to get extra help.") - print("==============================================") - print() - - # Ask for the level of options the user wants. - # A lot of code just to print a string! - level_sort = {0:'', 1:'', 2:''} - for k in prompts: - level = prompts[k][3] - if level < 0 or level > 2: - continue - level_sort[level] += " " + prompts[k][1] + "\n" - level_sort_string = "This sets the level for advanced options prompts. Set 0 for simple, 1 for advanced, or 2 for expert.\n" - level_sort_string += "[0] Basic options:\n" + str(level_sort[0]) + "\n" - level_sort_string += "[1] Advanced options:\n" + str(level_sort[1]) + "\n" - level_sort_string += "[2] Expert options:\n" + str(level_sort[2]) - c = input_option("Prompt level (0, 1, or 2)?", "1", level_sort_string) - max_prompt_level = int(c) - - options = {} - for k in prompts_key_order: - if k == 'video_aspect_ratio': - guess_aspect = get_aspect_ratio(options['video_source_filename']) - options[k] = input_option (prompts[k][1], guess_aspect, prompts[k][2], prompts[k][3], max_prompt_level) - elif k == 'audio_id': - aid_list = get_aid_list (options['video_source_filename']) - default_id = '128' - if max_prompt_level>=prompts[k][3]: - if len(aid_list) > 1: - print("This video has more than one audio stream. The following stream audio IDs were found:") - for aid in aid_list: - print(" " + aid) - default_id = aid_list[0] - else: - print("WARNING!") - print("Rippy was unable to get the list of audio streams from this video.") - print("If reading directly from a DVD then the DVD device might be busy.") - print("Using a default setting of stream id 128 (main audio on most DVDs).") - default_id = '128' - options[k] = input_option (prompts[k][1], default_id, prompts[k][2], prompts[k][3], max_prompt_level) - elif k == 'subtitle_id': - sid_list = get_sid_list (options['video_source_filename']) - default_id = 'None' - if max_prompt_level>=prompts[k][3]: - if len(sid_list) > 0: - print("This video has one or more subtitle streams. The following stream subtitle IDs were found:") - for sid in sid_list: - print(" " + sid) - #default_id = sid_list[0] - default_id = prompts[k][0] - else: - print("WARNING!") - print("Unable to get the list of subtitle streams from this video. It may have none.") - print("Setting default to None.") - default_id = 'None' - options[k] = input_option (prompts[k][1], default_id, prompts[k][2], prompts[k][3], max_prompt_level) - elif k == 'audio_lowpass_filter': - lowpass_default = "%.1f" % (math.floor(float(options['audio_sample_rate']) / 2.0)) - options[k] = input_option (prompts[k][1], lowpass_default, prompts[k][2], prompts[k][3], max_prompt_level) - elif k == 'video_bitrate': - if options['video_length'].lower() == 'none': - options[k] = input_option (prompts[k][1], '1000', prompts[k][2], prompts[k][3], max_prompt_level) - else: - options[k] = input_option (prompts[k][1], prompts[k][0], prompts[k][2], prompts[k][3], max_prompt_level) - else: - # don't bother asking for video_target_size or video_bitrate_overhead if video_bitrate was set - if (k=='video_target_size' or k=='video_bitrate_overhead') and options['video_bitrate']!='calc': - continue - # don't bother with crop area if video length is none - if k == 'video_crop_area' and options['video_length'].lower() == 'none': - options['video_crop_area'] = 'none' - continue - options[k] = input_option (prompts[k][1], prompts[k][0], prompts[k][2], prompts[k][3], max_prompt_level) - - #options['video_final_filename'] = options['video_final_filename'] + "." + options['video_container_format'] - - print("==========================================================================") - print("Ready to Rippy!") - print() - print("The following options will be used:") - for k,v in options.items(): - print("%27s : %s" % (k, v)) - - print() - c = input_option("Continue?", "Y") - c = c.strip().lower() - if c[0] != 'y': - print("Exiting...") - os._exit(1) - return options - -def clean_options (d): - '''This validates and cleans up the options dictionary. - After reading options interactively or from a conf file - we need to make sure that the values make sense and are - converted to the correct type. - 1. Any key with "_flag" in it becomes a boolean True or False. - 2. Values are normalized ("No", "None", "none" all become "none"; - "Calcluate", "c", "CALC" all become "calc"). - 3. Certain values are converted from string to int. - 4. Certain combinations of options are invalid or override each other. - This is a rather annoying function, but then so it most cleanup work. - ''' - for k in d: - d[k] = d[k].strip() - # convert all flag options to 0 or 1 - if '_flag' in k: - if type(d[k]) is types.StringType: - if d[k].strip().lower()[0] in 'yt1': #Yes, True, 1 - d[k] = 1 - else: - d[k] = 0 - d['video_bitrate'] = d['video_bitrate'].lower() - if d['video_bitrate'][0]=='c': - d['video_bitrate']='calc' - else: - d['video_bitrate'] = int(float(d['video_bitrate'])) - try: - d['video_target_size'] = int(d['video_target_size']) - # shorthand magic numbers get automatically expanded - if d['video_target_size'] == 180: - d['video_target_size'] = 193536000 - elif d['video_target_size'] == 550: - d['video_target_size'] = 580608000 - elif d['video_target_size'] == 650: - d['video_target_size'] = 681984000 - elif d['video_target_size'] == 700: - d['video_target_size'] = 737280000 - except: - d['video_target_size'] = 'none' - - try: - d['video_chapter'] = int(d['video_chapter']) - except: - d['video_chapter'] = None - - try: - d['subtitle_id'] = int(d['subtitle_id']) - except: - d['subtitle_id'] = None - - try: - d['video_bitrate_overhead'] = float(d['video_bitrate_overhead']) - except: - d['video_bitrate_overhead'] = -1.0 - - d['audio_bitrate'] = int(d['audio_bitrate']) - d['audio_sample_rate'] = int(d['audio_sample_rate']) - d['audio_volume_boost'] = d['audio_volume_boost'].lower() - if d['audio_volume_boost'][0]=='n': - d['audio_volume_boost'] = None - else: - d['audio_volume_boost'] = d['audio_volume_boost'].replace('db','') - d['audio_volume_boost'] = float(d['audio_volume_boost']) - -# assert (d['video_bitrate']=='calc' and d['video_target_size']!='none') -# or (d['video_bitrate']!='calc' and d['video_target_size']=='none') - - d['video_scale'] = d['video_scale'].lower() - if d['video_scale'][0]=='n': - d['video_scale']='none' - else: - al = re.findall("([0-9]+).*?([0-9]+)", d['video_scale']) - d['video_scale']=al[0][0]+':'+al[0][1] - d['video_crop_area'] = d['video_crop_area'].lower() - if d['video_crop_area'][0]=='n': - d['video_crop_area']='none' - d['video_length'] = d['video_length'].lower() - if d['video_length'][0]=='c': - d['video_length']='calc' - elif d['video_length'][0]=='n': - d['video_length']='none' - else: - d['video_length'] = int(float(d['video_length'])) - if d['video_length']==0: - d['video_length'] = 'none' - assert (not (d['video_length']=='none' and d['video_bitrate']=='calc')) - return d - -def main (): - try: - optlist, args = getopt.getopt(sys.argv[1:], 'h?', ['help','h','?']) - except Exception as e: - print(str(e)) - exit_with_usage() - command_line_options = dict(optlist) - # There are a million ways to cry for help. These are but a few of them. - if [elem for elem in command_line_options if elem in ['-h','--h','-?','--?','--help']]: - exit_with_usage(0) - - missing = check_missing_requirements() - if missing is not None: - print() - print("==========================================================================") - print("ERROR!") - print("Some required external commands are missing.") - print("please install the following packages:") - print(str(missing)) - print("==========================================================================") - print() - c = input_option("Continue?", "Y") - c = c.strip().lower() - if c[0] != 'y': - print("Exiting...") - os._exit(1) - - if len(args) > 0: - # cute one-line string-to-dictionary parser (two-lines if you count this comment): - options = dict(re.findall('([^: \t\n]*)\s*:\s*(".*"|[^ \t\n]*)', file(args[0]).read())) - options = clean_options(options) - convert (options) - else: - options = interactive_convert () - options = clean_options(options) - convert (options) - print("# Done!") - -if __name__ == "__main__": - try: - start_time = time.time() - print(time.asctime()) - main() - print(time.asctime()) - print("TOTAL TIME IN MINUTES:", end=' ') - print((time.time() - start_time) / 60.0) - except Exception as e: - tb_dump = traceback.format_exc() - print("==========================================================================") - print("ERROR -- Unexpected exception in script.") - print(str(e)) - print(str(tb_dump)) - print("==========================================================================") - print("==========================================================================", file=GLOBAL_LOGFILE) - print("ERROR -- Unexpected exception in script.", file=GLOBAL_LOGFILE) - print(str(e), file=GLOBAL_LOGFILE) - print(str(tb_dump), file=GLOBAL_LOGFILE) - print("==========================================================================", file=GLOBAL_LOGFILE) - exit_with_usage(3) - diff --git a/examples/ssh_session.py b/examples/ssh_session.py deleted file mode 100755 index f040c5c..0000000 --- a/examples/ssh_session.py +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env python - -''' - Eric S. Raymond - - Greatly modified by Nigel W. Moriarty - April 2003 - -PEXPECT LICENSE - - This license is approved by the OSI and FSF as GPL-compatible. - http://opensource.org/licenses/isc-license.txt - - Copyright (c) 2012, Noah Spurrier <noah@noah.org> - PERMISSION TO USE, COPY, MODIFY, AND/OR DISTRIBUTE THIS SOFTWARE FOR ANY - PURPOSE WITH OR WITHOUT FEE IS HEREBY GRANTED, PROVIDED THAT THE ABOVE - COPYRIGHT NOTICE AND THIS PERMISSION NOTICE APPEAR IN ALL COPIES. - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -''' - -from __future__ import absolute_import - -from pexpect import * -import os, sys -import getpass -import time - - -class ssh_session: - - '''Session with extra state including the password to be used.''' - - def __init__(self, user, host, password=None, verbose=0): - - self.user = user - self.host = host - self.verbose = verbose - self.password = password - self.keys = [ - 'authenticity', - 'assword:', - '@@@@@@@@@@@@', - 'Command not found.', - EOF, - ] - - self.f = open('ssh.out','w') - - def __repr__(self): - - outl = 'class :'+self.__class__.__name__ - for attr in self.__dict__: - if attr == 'password': - outl += '\n\t'+attr+' : '+'*'*len(self.password) - else: - outl += '\n\t'+attr+' : '+str(getattr(self, attr)) - return outl - - def __exec(self, command): - - '''Execute a command on the remote host. Return the output.''' - - child = spawn(command, - #timeout=10, - ) - if self.verbose: - sys.stderr.write("-> " + command + "\n") - seen = child.expect(self.keys) - self.f.write(str(child.before) + str(child.after)+'\n') - if seen == 0: - child.sendline('yes') - seen = child.expect(self.keys) - if seen == 1: - if not self.password: - self.password = getpass.getpass('Remote password: ') - child.sendline(self.password) - child.readline() - time.sleep(5) - # Added to allow the background running of remote process - if not child.isalive(): - seen = child.expect(self.keys) - if seen == 2: - lines = child.readlines() - self.f.write(lines) - if self.verbose: - sys.stderr.write("<- " + child.before + "|\n") - try: - self.f.write(str(child.before) + str(child.after)+'\n') - except: - pass - self.f.close() - return child.before - - def ssh(self, command): - - return self.__exec("ssh -l %s %s \"%s\"" \ - % (self.user,self.host,command)) - - def scp(self, src, dst): - - return self.__exec("scp %s %s@%s:%s" \ - % (src, session.user, session.host, dst)) - - def exists(self, file): - - '''Retrieve file permissions of specified remote file.''' - - seen = self.ssh("/bin/ls -ld %s" % file) - if string.find(seen, "No such file") > -1: - return None # File doesn't exist - else: - return seen.split()[0] # Return permission field of listing. - diff --git a/examples/sshls.py b/examples/sshls.py deleted file mode 100755 index 9f751f3..0000000 --- a/examples/sshls.py +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env python - -'''This runs 'ls -l' on a remote host using SSH. -At the prompts enter hostname, username, and password. - -PEXPECT LICENSE - - This license is approved by the OSI and FSF as GPL-compatible. - http://opensource.org/licenses/isc-license.txt - - Copyright (c) 2012, Noah Spurrier <noah@noah.org> - PERMISSION TO USE, COPY, MODIFY, AND/OR DISTRIBUTE THIS SOFTWARE FOR ANY - PURPOSE WITH OR WITHOUT FEE IS HEREBY GRANTED, PROVIDED THAT THE ABOVE - COPYRIGHT NOTICE AND THIS PERMISSION NOTICE APPEAR IN ALL COPIES. - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -''' - -from __future__ import print_function - -from __future__ import absolute_import - -import pexpect -import getpass, os - - -try: - raw_input -except NameError: - raw_input = input - - -def ssh_command (user, host, password, command): - - '''This runs a command on the remote host. This could also be done with the - pxssh class, but this demonstrates what that class does at a simpler level. - This returns a pexpect.spawn object. This handles the case when you try to - connect to a new host and ssh asks you if you want to accept the public key - fingerprint and continue connecting. ''' - - ssh_newkey = 'Are you sure you want to continue connecting' - child = pexpect.spawn('ssh -l %s %s %s'%(user, host, command)) - i = child.expect([pexpect.TIMEOUT, ssh_newkey, 'password: ']) - if i == 0: # Timeout - print('ERROR!') - print('SSH could not login. Here is what SSH said:') - print(child.before, child.after) - return None - if i == 1: # SSH does not have the public key. Just accept it. - child.sendline ('yes') - child.expect ('password: ') - i = child.expect([pexpect.TIMEOUT, 'password: ']) - if i == 0: # Timeout - print('ERROR!') - print('SSH could not login. Here is what SSH said:') - print(child.before, child.after) - return None - child.sendline(password) - return child - -def main (): - - host = raw_input('Hostname: ') - user = raw_input('User: ') - password = getpass.getpass('Password: ') - child = ssh_command (user, host, password, '/bin/ls -l') - child.expect(pexpect.EOF) - print(child.before) - -if __name__ == '__main__': - - try: - main() - except Exception as e: - print(str(e)) - traceback.print_exc() - os._exit(1) - |