Giter Club home page Giter Club logo

skippex's Introduction

Skippex

Skippex automatically skips intros for you on Plex, with support for the Chromecast.

IMPORTANT NOTE: This is still pretty much beta software. Expect bugs and please report them!

Installation

Installing Skippex through Docker is the easiest way to get started:

$ docker pull ghcr.io/sprt/skippex

Docker-compose example coming soon.

If you prefer not to use Docker, you can also use pipx, which will install Skippex in its own virtual environment:

$ pipx install skippex

Or you can just use pip:

$ pip install --user skippex

Usage

The first time you use Skippex, you'll first have to authorize the application with Plex using the following command. This will open a new tab in your Web browser allowing you to authenticate and authorize the application to access your Plex account.

Docker pipx & pip
$ docker run -v skippex:/data --network host ghcr.io/sprt/skippex auth $ skippex auth

Once that's done, you can simply run Skippex and it'll start monitoring your playback sessions and automatically skip intros for you on supported devices:

Docker pipx & pip
$ docker run -v skippex:/data --network host ghcr.io/sprt/skippex run $ skippex run

Et voilà! When this command says "Ready", Skippex is monitoring your shows and will automatically skip intros for you.

Note: Due to a Chromecast limitation, the Docker container has to run with host mode networking.

Things to know

  • Clients need to have "Advertise as player" enabled.
  • Only works for players on the local network.
  • Only skips once per playback session.
  • Solely based on the intro markers detected by Plex; Skippex does not attempt to detect intros itself.

Tested and supported players

  • Plex Web App
  • Plex for iOS (both iPhone and iPad)
  • Chromecast v3

The NVIDIA SHIELD might be supported as well, but I don't have one so I can't test it. Other players might also be supported. In any case, please inform me by creating a new issue, so I can add your player to this list.

Known issues

  • With a Chromecast, when seeking to a position, the WebSocket only receives the notification 10 seconds later. Likewise, the HTTP API starts returning the correct position only after 10 seconds. This means that if, before the intro, the user seeks to within 10 seconds of the intro, they may view it for a few seconds (before the notification comes in and saves us).

    One workaround would be to listen to Chromecast status updates using pychromecast, but that would necessitate a rearchitecture of the code.

skippex's People

Contributors

sprt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

skippex's Issues

Can't skip intro from inside container

$ docker run -v skippex:/config ghcr.io/sprt/skippex run
2021-01-17 16:21:41 - INFO - Verifying token...
2021-01-17 16:21:41 - INFO - Connecting to Plex server...
2021-01-17 16:21:43 - INFO - Ready
2021-01-17 16:21:54 - INFO - New session 86: <PlexClient:Chrome> is playing <Episode:XXX:YYY> (intro marker = True)
2021-01-17 16:21:55 - INFO - Session 86: skipped intro (seeked from 1000 to 21383)
Exception in thread Thread-11:
Traceback (most recent call last):
  File "/venv/lib/python3.9/site-packages/urllib3/connection.py", line 169, in _new_conn
    conn = connection.create_connection(
  File "/venv/lib/python3.9/site-packages/urllib3/util/connection.py", line 96, in create_connection
    raise err
  File "/venv/lib/python3.9/site-packages/urllib3/util/connection.py", line 86, in create_connection
    sock.connect(sa)
ConnectionRefusedError: [Errno 111] Connection refused

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/venv/lib/python3.9/site-packages/urllib3/connectionpool.py", line 699, in urlopen
    httplib_response = self._make_request(
  File "/venv/lib/python3.9/site-packages/urllib3/connectionpool.py", line 394, in _make_request
    conn.request(method, url, **httplib_request_kw)
  File "/venv/lib/python3.9/site-packages/urllib3/connection.py", line 234, in request
    super(HTTPConnection, self).request(method, url, body=body, headers=headers)
  File "/usr/local/lib/python3.9/http/client.py", line 1255, in request
    self._send_request(method, url, body, headers, encode_chunked)
  File "/usr/local/lib/python3.9/http/client.py", line 1301, in _send_request
    self.endheaders(body, encode_chunked=encode_chunked)
  File "/usr/local/lib/python3.9/http/client.py", line 1250, in endheaders
    self._send_output(message_body, encode_chunked=encode_chunked)
  File "/usr/local/lib/python3.9/http/client.py", line 1010, in _send_output
    self.send(msg)
  File "/usr/local/lib/python3.9/http/client.py", line 950, in send
    self.connect()
  File "/venv/lib/python3.9/site-packages/urllib3/connection.py", line 200, in connect
    conn = self._new_conn()
  File "/venv/lib/python3.9/site-packages/urllib3/connection.py", line 181, in _new_conn
    raise NewConnectionError(
urllib3.exceptions.NewConnectionError: <urllib3.connection.HTTPConnection object at 0x7f30661440d0>: Failed to establish a new connection: [Errno 111] Connection refused

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/venv/lib/python3.9/site-packages/requests/adapters.py", line 439, in send
    resp = conn.urlopen(
  File "/venv/lib/python3.9/site-packages/urllib3/connectionpool.py", line 755, in urlopen
    retries = retries.increment(
  File "/venv/lib/python3.9/site-packages/urllib3/util/retry.py", line 573, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='127.0.0.1', port=32400): Max retries exceeded with url: /player/playback/seekTo?commandID=1&offset=21383&type=video (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f30661440d0>: Failed to establish a new connection: [Errno 111] Connection refused'))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.9/threading.py", line 954, in _bootstrap_inner
    self.run()
  File "/usr/local/lib/python3.9/threading.py", line 892, in run
    self._target(*self._args, **self._kwargs)
  File "/app/skippex/seekables.py", line 103, in _seek
    self._client.seekTo(offset_ms, mtype=DEFAULT_MTYPE+self._TIMEOUT_SUFFIX)
  File "/venv/lib/python3.9/site-packages/plexapi/client.py", line 355, in seekTo
    self.sendCommand('playback/seekTo', offset=offset, type=mtype)
  File "/venv/lib/python3.9/site-packages/plexapi/client.py", line 210, in sendCommand
    return query(key, headers=headers)
  File "/app/skippex/seekables.py", line 69, in _patched_query
    return self._original_query(
  File "/venv/lib/python3.9/site-packages/plexapi/client.py", line 161, in query
    response = method(url, headers=headers, timeout=timeout, **kwargs)
  File "/venv/lib/python3.9/site-packages/requests/sessions.py", line 555, in get
    return self.request('GET', url, **kwargs)
  File "/venv/lib/python3.9/site-packages/requests/sessions.py", line 542, in request
    resp = self.send(prep, **send_kwargs)
  File "/venv/lib/python3.9/site-packages/requests/sessions.py", line 655, in send
    r = adapter.send(request, **kwargs)
  File "/venv/lib/python3.9/site-packages/requests/adapters.py", line 516, in send
    raise ConnectionError(e, request=request)
requests.exceptions.ConnectionError: HTTPConnectionPool(host='127.0.0.1', port=32400): Max retries exceeded with url: /player/playback/seekTo?commandID=1&offset=21383&type=video (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f30661440d0>: Failed to establish a new connection: [Errno 111] Connection refused'))

Avoid skipping intro if session started during intro?

This would, as a consequence, avoid skipping the intro in these scenarios:

  • When resuming an episode from Continue Watching when the user was previously viewing the intro for some reason. (This can happen if the user came back to the intro -- for whatever reason -- after Skippex skipped it.)
  • Untested, but probably when the user switches from say the Plex Web App to a Chromecast during the intro.
  • If in the future we decide to automatically restart the program after a crash, then we wouldn't skip forward if the crash happened during the intro.
  • Prevents from skipping forward in the middle of a show if Skippex was just started.

Just food for thought, not sure if I wanna implement this. Also, the above list will probably grow.

Risks:

  • For new users who have shows in Continue Watching that are currently seeked within the intro, Skippex wouldn't skip the intro when they resume playback at that position. This could lead new users to believe Skippex isn't working properly, so let's clearly document this if we make it happen.

Does not appear to work on Roku clients

skippex.seekables.SeekableNotFoundErrorChain: [PlexPlayerNotFoundError('could not find Plex player with machine ID 2732133810633fd9de7622aecc83e201'), SeekableNotFoundError('could not find Chromecast with address 192.168.2.107')]
2021-03-12 23:21:08 - ERROR - Plex player not found for session; ensure "advertise as player" is enabled
2021-03-12 23:21:08 - ERROR - Cannot skip intro for session 10

Tested and it is working as expected on web client. I don't think Roku client has the option to advertise as player.

Container fatally crashes

Hello, first off thank you for this. Glad to know someone appreciates Chromecasts as Plex sure doesn't.

I've had my container fatally crash a few times and has to be manually restarted. It looks like it has to do when a video starts to buffer. My Plex logs were cleared so I am not sure if it was my local stream of watching an episode of ER or the remote user watching the movie 'The Tomorrow War' that caused this.

Here is the log:

today at 12:01:53 AM 2021-07-02 00:01:53 - INFO - Connecting to Plex server...
today at 12:01:57 AM 2021-07-02 00:01:57 - INFO - Ready
today at 12:01:59 AM 2021-07-02 00:01:59 - INFO - New session 11: <PlexClient:Chromecast> is playing <Episode:19745:ER-s05e18> (intro marker = True)
today at 12:01:59 AM 2021-07-02 00:01:59 - INFO - Session 11: skipped intro (seeked from 280000 to 286687)
today at 12:32:44 AM 2021-07-02 00:32:44 - WARNING - No session found for 'buffering' notification
today at 12:32:47 AM 2021-07-02 00:32:47 - WARNING - No session found for 'buffering' notification
today at 12:32:47 AM Traceback (most recent call last):
today at 12:32:47 AM   File "/usr/local/lib/python3.9/runpy.py", line 197, in _run_module_as_main
today at 12:32:47 AM     return _run_code(code, main_globals, None,
today at 12:32:47 AM   File "/usr/local/lib/python3.9/runpy.py", line 87, in _run_code
today at 12:32:47 AM     exec(code, run_globals)
today at 12:32:47 AM   File "/venv/lib/python3.9/site-packages/skippex/__main__.py", line 3, in <module>
today at 12:32:47 AM     main()
today at 12:32:47 AM   File "/venv/lib/python3.9/site-packages/skippex/cmd.py", line 202, in main
today at 12:32:47 AM     _main()
today at 12:32:47 AM   File "/venv/lib/python3.9/site-packages/skippex/cmd.py", line 196, in _main
today at 12:32:47 AM     sys.exit(args.func(args))
today at 12:32:47 AM   File "/venv/lib/python3.9/site-packages/skippex/cmd.py", line 146, in cmd_run
today at 12:32:47 AM     notif_listener.run_forever()
today at 12:32:47 AM   File "/venv/lib/python3.9/site-packages/skippex/notifications.py", line 75, in run_forever
today at 12:32:47 AM     ws_app.run_forever()
today at 12:32:47 AM   File "/venv/lib/python3.9/site-packages/websocket/_app.py", line 308, in run_forever
today at 12:32:47 AM     self._callback(self.on_error, e)
today at 12:32:47 AM   File "/venv/lib/python3.9/site-packages/skippex/notifications.py", line 50, in _callback
today at 12:32:47 AM     callback(*args)
today at 12:32:47 AM   File "/venv/lib/python3.9/site-packages/skippex/notifications.py", line 82, in _on_error
today at 12:32:47 AM     raise e
today at 12:32:47 AM   File "/venv/lib/python3.9/site-packages/websocket/_app.py", line 306, in run_forever
today at 12:32:47 AM     dispatcher.read(self.sock.sock, read, check)
today at 12:32:47 AM   File "/venv/lib/python3.9/site-packages/websocket/_app.py", line 53, in read
today at 12:32:47 AM     if not read_callback():
today at 12:32:47 AM   File "/venv/lib/python3.9/site-packages/websocket/_app.py", line 290, in read
today at 12:32:47 AM     self._callback(self.on_message, data)
today at 12:32:47 AM   File "/venv/lib/python3.9/site-packages/skippex/notifications.py", line 50, in _callback
today at 12:32:47 AM     callback(*args)
today at 12:32:47 AM   File "/venv/lib/python3.9/site-packages/skippex/notifications.py", line 79, in _on_message
today at 12:32:47 AM     self._callback(msg_dict['NotificationContainer'])
today at 12:32:47 AM   File "/venv/lib/python3.9/site-packages/wrapt/decorators.py", line 502, in _synchronized_wrapper
today at 12:32:47 AM     return wrapped(*args, **kwargs)
today at 12:32:47 AM   File "/venv/lib/python3.9/site-packages/skippex/sessions.py", line 211, in alert_callback
today at 12:32:47 AM     self._handle_notification(notification)
today at 12:32:47 AM   File "/venv/lib/python3.9/site-packages/wrapt/decorators.py", line 502, in _synchronized_wrapper
today at 12:32:47 AM     return wrapped(*args, **kwargs)
today at 12:32:47 AM   File "/venv/lib/python3.9/site-packages/skippex/sessions.py", line 276, in _handle_notification
today at 12:32:47 AM     session = self._provider.provide(session_key)
today at 12:32:47 AM   File "/venv/lib/python3.9/site-packages/skippex/sessions.py", line 184, in provide
today at 12:32:47 AM     raise SessionNotFoundError(f'could not find session key {session_key} among {sessions}')
today at 12:32:47 AM skippex.sessions.SessionNotFoundError: could not find session key 12 among [<Movie:78110:The-Tomorrow-War>, <Episode:19745:ER-s05e18>]

PlexAPI sometimes stuck connecting

PlexAPI is sometimes struggling to connect to the server and will keep trying with seemingly no timeout. Let's at least signal this to the user and at best work around it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.