-
Notifications
You must be signed in to change notification settings - Fork 5
Hotfix issue core 29184 #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
4f5dd80
99bba01
690e1d9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ | |
| import logging | ||
| import telnetlib | ||
| import re | ||
| import time | ||
|
|
||
| # Error Imports | ||
| from socket import error as sockerr | ||
|
|
@@ -46,11 +47,14 @@ class VLCTelnet(object): | |
| """Conection to VLC using Telnet.""" | ||
|
|
||
| # Non commands | ||
| def __init__(self, host="localhost", password="admin", port=4212, connect=True, login=True): | ||
| def __init__(self, host="localhost", password="admin", port=4212, connect=True, login=True, retries=10, timeout=10): | ||
| self.tn = telnetlib.Telnet() | ||
| self.host = host | ||
| self.port = port | ||
| self.password = password | ||
| self.retries = retries | ||
| self.timeout = timeout | ||
|
|
||
| if connect: | ||
| self.connect() | ||
| # Login to VLC using password provided in the arguments | ||
|
|
@@ -61,7 +65,7 @@ def connect(self): | |
| """Connect to VLC.""" | ||
| # Connect to telnet. | ||
| try: | ||
| self.tn.open(self.host, port=self.port, timeout=10) | ||
| self.tn.open(self.host, self.port, self.timeout) | ||
| except sockerr: | ||
| raise ConnectionError("Could not connect to VLC. Make sure the Telnet interface is enabled and accessible.") | ||
|
|
||
|
|
@@ -77,7 +81,7 @@ def login(self): | |
| full_command = self.password.encode('utf-8') + b'\n' | ||
| self.tn.write(full_command) | ||
| for _ in range(2): | ||
| command_output = self.tn.read_until(b'\n', timeout=10).decode('utf-8').strip('\r\n') | ||
| command_output = self.tn.read_until(b'\n', self.timeout).decode('utf-8').strip('\r\n') | ||
| if command_output: # discard empty line once. | ||
| break | ||
| _LOGGER.debug("Password response: %s", command_output) | ||
|
|
@@ -91,15 +95,31 @@ def login(self): | |
| # Read until prompt | ||
| self.tn.read_until(b'> ') | ||
|
|
||
| def is_connected(self): | ||
| try: | ||
| self.tn.write(b'\n') | ||
| answer = self.tn.read_until(b'> ', self.timeout) | ||
| if answer: | ||
| _LOGGER.debug("is_connected: answer obtained. Value: %s", answer) | ||
| return True | ||
| else: | ||
| _LOGGER.debug("is_connected: failed to obtain answer.") | ||
| return False | ||
| except Exception: | ||
| return False | ||
|
|
||
| def run_command(self, command): | ||
| return self.do_run_command(command, self.retries) | ||
|
|
||
| def do_send_string(self, string): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: "string" is also the name of a Python module -- maybe "message" is a better name here? |
||
| """Run a command and return a list with the output lines.""" | ||
| # Put the command in a nice byte-encoded variable | ||
| full_command = command.encode('utf-8') + b'\n' | ||
| _LOGGER.debug("Sending command: %s", command) | ||
| full_command = string.encode('utf-8') + b'\n' | ||
| _LOGGER.debug("Sending command: %s", string) | ||
| # Write out the command to telnet | ||
| self.tn.write(full_command) | ||
| # Get the command output, decode it, and split out the junk | ||
| command_output = self.tn.read_until(b'> ').decode('utf-8').split('\r\n')[:-1] | ||
| command_output = self.tn.read_until(b'> ', self.timeout).decode('utf-8').split('\r\n')[:-1] | ||
| # Raise command error if VLC does not recognize the command. | ||
| _LOGGER.debug("Command output: %s", command_output) | ||
| if command_output: | ||
|
|
@@ -111,6 +131,30 @@ def run_command(self, command): | |
| # Return the split output of the command | ||
| return command_output | ||
|
|
||
| def do_run_command(self, command, retries): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Add a short doc string? |
||
| _LOGGER.debug("do_run_command: retries: %s", retries) | ||
|
|
||
| if self.is_connected(): | ||
| return self.do_send_string(command) | ||
| else: | ||
| _LOGGER.debug("do_run_command: retrying connection. Iteration # %s", retries) | ||
|
|
||
| if retries > 0: | ||
| try: | ||
| self.disconnect() | ||
| time.sleep(1) | ||
| except Exception: | ||
| pass | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want to log the exception here? (or at least that there was one?) If this is a thing that happens commonly, maybe we can tighten it up to the specific Exception that happens here. (same for line 153) |
||
|
|
||
| try: | ||
| self.connect() | ||
| self.login() | ||
| except Exception: | ||
| pass | ||
|
|
||
| return self.do_run_command(command, retries - 1) | ||
| else: | ||
| raise ConnectionError("Could not connect to VLC. Make sure the Telnet interface is enabled and accessible.") | ||
| # Commands | ||
| # Block 1 | ||
| def add(self, xyz): | ||
|
|
@@ -192,14 +236,45 @@ def clear(self): | |
| def status(self): | ||
| """Current playlist status.""" | ||
| status_output = self.run_command('status') | ||
| _LOGGER.debug("status: status output: %s", status_output) | ||
|
|
||
| if status_output is None: | ||
| raise ParseError("Could not get status.") | ||
| if len(status_output) == 3: | ||
| inputloc = '%20'.join(status_output[0].split(' ')[3:-1]) | ||
| volume = int(status_output[1].split(' ')[3]) | ||
| state = status_output[2].split(' ')[2] | ||
| output_inputloc = status_output[0].split(' ') | ||
| output_volume = status_output[1].split(' ') | ||
| output_status = status_output[2].split(' ') | ||
|
|
||
| if len(output_inputloc) >= 4: | ||
| inputloc = '%20'.join(output_inputloc[3:-1]) | ||
| else: | ||
| raise ParseError("Received malformed status message") | ||
|
|
||
| if len(output_volume) >= 4: | ||
| volume = int(output_volume[3]) | ||
| else: | ||
| raise ParseError("Received malformed status message") | ||
|
|
||
| if len(output_status) >= 3: | ||
| state = output_status[2] | ||
| else: | ||
| raise ParseError("Received malformed status message") | ||
|
|
||
| returndict = {'input': inputloc, 'volume': volume, 'state': state} | ||
| elif len(status_output) == 2: | ||
| volume = int(status_output[0].split(' ')[3]) | ||
| state = status_output[1].split(' ')[2] | ||
| output_volume = status_output[0].split(' ') | ||
| output_status = status_output[1].split(' ') | ||
|
|
||
| if len(output_volume) >= 4: | ||
| volume = int(output_volume[3]) | ||
| else: | ||
| raise ParseError("Received malformed status message") | ||
|
|
||
| if len(output_status) >= 3: | ||
| state = output_status[2] | ||
| else: | ||
| raise ParseError("Received malformed status message") | ||
|
|
||
| returndict = {'volume': volume, 'state': state} | ||
| else: | ||
| raise ParseError("Could not get status.") | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Could we combine
run_commandanddo_run_command? I think we can wrap much ofdo_run_commandinfor _ in range(self.retries): ...Alternatively,
maybe_reconnect_and_run_commandmight be a clearer name fordo_run_command?