sane-cast: implement --device option to choose a specific device non-interactively

This commit is contained in:
Colin 2024-05-11 00:40:38 +00:00
parent 4b04c283b6
commit e62365436c

View File

@ -40,6 +40,11 @@ class Device:
url = self.url or other.url,
)
def matches(self, filter: str) -> bool:
""" return True if the filter string occurs inside the identifier for this device """
clean = lambda s: "".join(c for c in s.lower() if c in "abcdefghijklmnopqrstuvwxyz0123456789")
return clean(filter) in clean(self.model)
# ranked in order of preference
KNOWN_DEVICES = [
Device("Theater TV", Compat.RenameToMp4),
@ -84,8 +89,7 @@ class Go2TvParser:
self.partial_url = None
class Go2TvDriver:
visible_devices = None
def scan_devices(self) -> None:
def scan_devices(self) -> list[Device]:
go2tv = subprocess.Popen(
[ "go2tv", "-l" ],
stdout=subprocess.PIPE,
@ -94,20 +98,7 @@ class Go2TvDriver:
for line in iter(go2tv.stdout.readline, b''):
parser.feed_line(line.decode("utf-8"))
self.visible_devices = parser.into_devices()
def rank_devices(self) -> list[Device]:
ranked = []
for known in KNOWN_DEVICES:
for vis in self.visible_devices:
if vis.model == known.model:
ranked.append(known.augmented(vis))
for vis in self.visible_devices:
if not any(vis.model == r.model for r in ranked):
ranked.append(vis)
return ranked
return parser.into_devices()
def cast_to(self, dev: Device, media: str) -> None:
logger.info(f"casting to {dev.model} at {dev.url} with compat {dev.compat}")
@ -134,6 +125,23 @@ class Go2TvDriver:
os.execvp("go2tv", cli_args)
def filter_devices(devices: list[Device], filter: str) -> list[Device]:
return [d for d in devices if d.matches(filter)]
def rank_devices(devices: list[Device]) -> list[Device]:
ranked = []
for known in KNOWN_DEVICES:
for vis in devices:
if vis.model == known.model:
ranked.append(known.augmented(vis))
for vis in devices:
if not any(vis.model == r.model for r in ranked):
ranked.append(vis)
return ranked
def ask_device(devices: list[Device]) -> Device | None:
dev = None
@ -170,6 +178,7 @@ def main():
parser = argparse.ArgumentParser(description="cast media to a DLNA receiver in range")
parser.add_argument("--verbose", action="store_true", help="more logging")
parser.add_argument("--always-ask", action="store_true", help="always ask which device to cast to, regardless how many are available")
parser.add_argument("--device", help="filter devices based on if this string is contained in their name (case-insensitive)")
parser.add_argument("media", help="file or URL to send to the DLNA device")
args = parser.parse_args()
@ -178,8 +187,11 @@ def main():
logger.setLevel(logging.DEBUG)
go2tv = Go2TvDriver()
go2tv.scan_devices()
devices = go2tv.rank_devices()
devices = go2tv.scan_devices()
if args.device:
devices = filter_devices(devices, args.device)
devices = rank_devices(devices)
dev = get_default_device(devices)
if dev is None or args.always_ask: