123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- #!/usr/bin/env python3
- import sys
- import logging
- import traceback
- from typing import Optional
- from time import sleep
- from datetime import datetime, timezone, timedelta
- import json
- from python_mpv_jsonipc import MPV
- import argparse
- from aw_core import dirs
- from aw_core.models import Event
- from aw_client.client import ActivityWatchClient
- logging.basicConfig(level=logging.WARN)
- parser = argparse.ArgumentParser("A watcher for MPV")
- parser.add_argument("--testing", action="store_true",
- help='run in testing mode')
- args, unknown = parser.parse_known_args()
- logger = logging.getLogger("aw-watcher-mpv")
- DEFAULT_CONFIG = """
- [aw-watcher-mpv]
- ipc_socket = "/tmp/mpv-socket"
- poll_time = 3.0"""
- def get_current_video(mpv) -> Optional[dict]:
- if (mpv.command("get_property", "duration") == None):
- return None
-
- current_video = {}
- current_video["duration"] = float(mpv.command("get_property", "duration"))
- current_video["filename"] = mpv.command("get_property", "filename")
- current_video["time-pos"] = float(mpv.command("get_property", "time-pos"))
- current_video["volume"] = float(mpv.command("get_property", "volume"))
- current_video["mute"] = mpv.command("get_property", "mute")
- current_video["audio"] = "{} - {}".format(
- mpv.command("get_property", "current-tracks/audio/id"),
- mpv.command("get_property", "current-tracks/audio/lang"),
- )
- current_video["sub"] = "{} - {}".format(
- mpv.command("get_property", "current-tracks/sub/id"),
- mpv.command("get_property", "current-tracks/sub/lang")
- )
- pause = mpv.command("get_property", "pause")
- if pause == False:
- return current_video
- return None
- def data_from_video(video: dict) -> dict:
- data = {}
- data["filename"] = video["filename"]
- data["duration"] = video["duration"]
- data["volume"] = video["volume"]
- data["mute"] = video["mute"]
- data["audio"] = video["audio"]
- data["sub"] = video["sub"]
- logging.debug("VIDEO: {}".format(data["filename"]))
- return data
- def mpvConnect(ipc_socket=None):
- mpv = MPV(start_mpv=False, ipc_socket=ipc_socket)
- return mpv
- def load_config():
- from aw_core.config import load_config_toml as _load_config
- return _load_config("aw-watcher-mpv", DEFAULT_CONFIG)
- def print_statusline(msg):
- last_msg_length = (
- len(print_statusline.last_msg) if hasattr(print_statusline, "last_msg") else 0
- )
- print(" " * last_msg_length, end="\r")
- print(msg, end="\r")
- print_statusline.last_msg = msg
- def main():
- logging.basicConfig(level=logging.INFO)
- config_dir = dirs.get_config_dir("aw-watcher-mpv")
- config = load_config()
- poll_time = float(config["aw-watcher-mpv"].get("poll_time"))
- ipc_socket = config["aw-watcher-mpv"].get("ipc_socket", None)
- if not ipc_socket:
- logger.warning(
- "ipc_socket not specified in config file (in folder {}).".format(
- config_dir
- )
- )
- sys.exit(1)
- # TODO: Fix --testing flag and set testing as appropriate
- aw = ActivityWatchClient("aw-watcher-mpv", testing=args.testing)
- bucketname = "{}_{}".format(aw.client_name, aw.client_hostname)
- aw.create_bucket(bucketname, "mpv-currently-playing", queued=True)
- aw.connect()
- mpv = None
- last_video = None
- video = None
- last_start_duration = 0
- last_elapsed = 0
- while True:
- try:
- if not mpv:
- print_statusline("Connecting to mpv...")
- mpv = mpvConnect(ipc_socket)
- else:
- try:
- mpv.command("get_property", "duration")
- except BrokenPipeError as e:
- mpv = mpvConnect(ipc_socket)
- except Exception as e:
- print_statusline("Connection to mpv was refused, attempting to reconnect")
- sleep(poll_time)
- continue
- try:
- video = get_current_video(mpv)
- except Exception as e:
- logger.error("Unknown Error")
- logger.error(traceback.format_exc())
- sleep(0.1)
- continue
- try:
- # Outputs a new line when a video ends, giving a short history directly in the log
- if last_video:
- last_video_data = data_from_video(last_video)
- if not video or (
- video
- and last_video_data["filename"] != data_from_video(video)["filename"]
- ):
- video_td = timedelta(seconds=last_video["time-pos"])
- video_time = int(video_td.seconds / 60), int(video_td.seconds % 60)
- print_statusline(
- "Video ended ({}:{:02d}): {filename}\n".format(
- *video_time, **last_video_data
- )
- )
- last_video_data["startedDuration"] = last_start_duration
- event = Event(timestamp=datetime.now(timezone.utc), data=last_video_data)
- aw.heartbeat(bucketname, event, pulsetime=poll_time + 1, queued=True)
- elif video and not (0 < (video["time-pos"] - last_elapsed) < 6):
- video_td = timedelta(seconds=last_video["time-pos"])
- video_time = int(video_td.seconds / 60), int(video_td.seconds % 60)
- print_statusline(
- "Video seeked ahead/backwards ({}:{:02d}): {filename}\n".format(
- *video_time, **last_video_data
- )
- )
- last_video_data["startedDuration"] = last_start_duration
- event = Event(timestamp=datetime.now(timezone.utc), data=last_video_data)
- aw.heartbeat(bucketname, event, pulsetime=poll_time + 1, queued=True)
- if video:
- video_data = data_from_video(video)
-
- if not last_video or (last_video and last_video_data["filename"] != video_data["filename"]) or not (0 < (video["time-pos"] - last_elapsed) < 6):
- last_start_duration = video["time-pos"]
- last_elapsed = video["time-pos"]
- video_data["startedDuration"] = last_start_duration
- video_td = timedelta(seconds=video["time-pos"])
- video_time = int(video_td.seconds / 60), int(video_td.seconds % 60)
- print_statusline(
- "Current video ({}:{:02d}): {filename}".format(
- *video_time, **video_data
- )
- )
- event = Event(timestamp=datetime.now(timezone.utc), data=video_data)
- aw.heartbeat(bucketname, event, pulsetime=poll_time + 1, queued=True)
- else:
- print_statusline("Waiting for video to start playing...")
- last_video = video
- except Exception as e:
- print("An exception occurred: {}".format(e))
- traceback.print_exc()
- sleep(poll_time)
- if __name__ == "__main__":
- main()
|