Compare commits

..

1 Commits

Author SHA1 Message Date
dmunozv04
30cac71820 Fix watchdog triggering too much 2025-07-04 19:14:08 +02:00
15 changed files with 102 additions and 134 deletions

View File

@@ -6,8 +6,7 @@ on:
branches: branches:
- '*' - '*'
tags: tags:
- 'v*.*.*' - 'v*'
- 'v*.*.*-*'
pull_request: pull_request:
branches: branches:
- '*' - '*'
@@ -28,7 +27,7 @@ jobs:
steps: steps:
# Get the repository's code # Get the repository's code
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v4
# Generate docker tags # Generate docker tags
- name: Docker meta - name: Docker meta
@@ -41,9 +40,7 @@ jobs:
tags: | tags: |
type=raw,value=develop,priority=900,enable=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} type=raw,value=develop,priority=900,enable=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
type=ref,enable=true,priority=600,prefix=pr-,suffix=,event=pr type=ref,enable=true,priority=600,prefix=pr-,suffix=,event=pr
type=semver,pattern=v{{version}} type=ref,event=tag
type=semver,pattern=v{{major}}
type=semver,pattern=v{{major}}.{{minor}}
type=ref,event=branch type=ref,event=branch
type=schedule type=schedule
@@ -56,7 +53,7 @@ jobs:
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
- name: Login to DockerHub - name: Login to DockerHub
if: github.event_name != 'pull_request' && env.DOCKERHUB_USERNAME != '' if: github.event_name != 'pull_request'
uses: docker/login-action@v3 uses: docker/login-action@v3
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
@@ -74,7 +71,7 @@ jobs:
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
context: . context: .
platforms: linux/amd64, linux/arm64, linux/arm/v7, linux/386, linux/arm/v6 platforms: linux/amd64, linux/arm64, linux/arm/v7
push: ${{ github.event_name != 'pull_request' }} push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}

View File

@@ -11,9 +11,12 @@ name: Release Package
on: on:
push: push:
branches: branches:
- 'main' - '*'
tags: tags:
- 'v*' - 'v*'
pull_request:
branches:
- '*'
release: release:
types: [published] types: [published]
@@ -30,10 +33,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v4
- name: Set up Python ${{ env.PYTHON_VERSION }} - name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v6 uses: actions/setup-python@v5
with: with:
python-version: ${{ env.PYTHON_VERSION }} python-version: ${{ env.PYTHON_VERSION }}
@@ -46,7 +49,7 @@ jobs:
run: python -m hatch build run: python -m hatch build
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v5 uses: actions/upload-artifact@v4
with: with:
name: sdist-and-wheel name: sdist-and-wheel
path: dist/* path: dist/*
@@ -100,17 +103,17 @@ jobs:
CARGO_BUILD_TARGET: ${{ matrix.job.target }} CARGO_BUILD_TARGET: ${{ matrix.job.target }}
PYAPP_DISTRIBUTION_VARIANT_CPU: ${{ matrix.job.cpu_variant }} PYAPP_DISTRIBUTION_VARIANT_CPU: ${{ matrix.job.cpu_variant }}
PYAPP_REPO: pyapp # Use local copy of pyapp (needed for cross-compiling) PYAPP_REPO: pyapp # Use local copy of pyapp (needed for cross-compiling)
PYAPP_VERSION: v0.28.0 PYAPP_VERSION: v0.27.0
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v4
- name: Clone PyApp - name: Clone PyApp
run: git clone --depth 1 --branch $PYAPP_VERSION https://github.com/ofek/pyapp $PYAPP_REPO run: git clone --depth 1 --branch $PYAPP_VERSION https://github.com/ofek/pyapp $PYAPP_REPO
- name: Set up Python ${{ env.PYTHON_VERSION }} - name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v6 uses: actions/setup-python@v5
with: with:
python-version: ${{ env.PYTHON_VERSION }} python-version: ${{ env.PYTHON_VERSION }}
@@ -141,7 +144,7 @@ jobs:
hatch --version hatch --version
- name: Get artifact - name: Get artifact
uses: actions/download-artifact@v6 uses: actions/download-artifact@v4
with: with:
name: sdist-and-wheel name: sdist-and-wheel
path: ${{ github.workspace }}/dist path: ${{ github.workspace }}/dist
@@ -159,13 +162,12 @@ jobs:
mv dist/binary/iSponsorBlockTV* dist/binary/iSponsorBlockTV-${{ matrix.job.release_suffix }} mv dist/binary/iSponsorBlockTV* dist/binary/iSponsorBlockTV-${{ matrix.job.release_suffix }}
- name: Attest build provenance - name: Attest build provenance
uses: actions/attest-build-provenance@v3 uses: actions/attest-build-provenance@v2
with: with:
subject-path: dist/binary/* subject-path: dist/binary/*
continue-on-error: true # Continue if attestation fails (it will fail on forks)
- name: Upload built binary package - name: Upload built binary package
uses: actions/upload-artifact@v5 uses: actions/upload-artifact@v4
with: with:
name: binaries-${{ matrix.job.release_suffix }} name: binaries-${{ matrix.job.release_suffix }}
path: dist/binary/* path: dist/binary/*
@@ -181,7 +183,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Get artifact - name: Get artifact
uses: actions/download-artifact@v6 uses: actions/download-artifact@v4
with: with:
name: sdist-and-wheel name: sdist-and-wheel
path: dist path: dist
@@ -200,7 +202,7 @@ jobs:
if: github.event_name == 'release' && github.event.action == 'published' if: github.event_name == 'release' && github.event.action == 'published'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/download-artifact@v6 - uses: actions/download-artifact@v4
name: Get artifact name: Get artifact
with: with:
path: dist path: dist

View File

@@ -18,11 +18,11 @@ jobs:
steps: steps:
# Get the repository's code # Get the repository's code
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v4
# Update description # Update description
- name: Update repo description - name: Update repo description
uses: peter-evans/dockerhub-description@v5 uses: peter-evans/dockerhub-description@v4
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}

View File

@@ -3,7 +3,7 @@
# Inspired by textual pre-commit config # Inspired by textual pre-commit config
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0 rev: v5.0.0
hooks: hooks:
- id: check-ast # simply checks whether the files parse as valid python - id: check-ast # simply checks whether the files parse as valid python
- id: check-builtin-literals # requires literal syntax when initializing empty or zero python builtin types - id: check-builtin-literals # requires literal syntax when initializing empty or zero python builtin types
@@ -19,13 +19,13 @@ repos:
- id: mixed-line-ending # replaces or checks mixed line ending - id: mixed-line-ending # replaces or checks mixed line ending
- id: trailing-whitespace # checks for trailing whitespace - id: trailing-whitespace # checks for trailing whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.10 rev: v0.11.9
hooks: hooks:
- id: ruff - id: ruff
args: [ --fix, --exit-non-zero-on-fix ] args: [ --fix, --exit-non-zero-on-fix ]
- id: ruff-format - id: ruff-format
- repo: https://github.com/igorshubovych/markdownlint-cli - repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.47.0 rev: v0.44.0
hooks: hooks:
- id: markdownlint - id: markdownlint
args: ["--fix"] args: ["--fix"]

View File

@@ -5,11 +5,10 @@
[![GitHub Release](https://img.shields.io/github/v/release/dmunozv04/isponsorblocktv?logo=GitHub&style=flat)](https://github.com/dmunozv04/iSponsorBlockTV/releases/latest) [![GitHub Release](https://img.shields.io/github/v/release/dmunozv04/isponsorblocktv?logo=GitHub&style=flat)](https://github.com/dmunozv04/iSponsorBlockTV/releases/latest)
[![GitHub Repo stars](https://img.shields.io/github/stars/dmunozv04/isponsorblocktv?style=flat)](https://github.com/dmunozv04/isponsorblocktv) [![GitHub Repo stars](https://img.shields.io/github/stars/dmunozv04/isponsorblocktv?style=flat)](https://github.com/dmunozv04/isponsorblocktv)
iSponsorBlockTV is a self-hosted application that connects to your YouTube TV Skip sponsor segments in YouTube videos playing on a YouTube TV device (see
app (see compatibility below) and automatically skips segments (like Sponsors below for compatibility details).
or intros) in YouTube videos using the [SponsorBlock](https://sponsor.ajay.app/)
API. It can also auto mute and press the "Skip Ad" button the moment it becomes This project is written in asynchronous python and should be pretty quick.
available on YouTube ads.
## Installation ## Installation
@@ -23,7 +22,7 @@ Open an issue/pull request if you have tested a device that isn't listed here.
| Device | Status | | Device | Status |
|:-------------------|:------:| |:-------------------|:------:|
| Apple TV | ✅* | | Apple TV | ✅ |
| Samsung TV (Tizen) | ✅ | | Samsung TV (Tizen) | ✅ |
| LG TV (WebOS) | ✅ | | LG TV (WebOS) | ✅ |
| Android TV | ✅ | | Android TV | ✅ |
@@ -36,22 +35,17 @@ Open an issue/pull request if you have tested a device that isn't listed here.
| Xbox One/Series | ✅ | | Xbox One/Series | ✅ |
| Playstation 4/5 | ✅ | | Playstation 4/5 | ✅ |
*Ad muting won't work when using AirPlay to send the audio to another speaker.
** Shorts aren't fully supported due to limitations on YouTube's side.
A single short can be seen by either selecting the "Disconnect" option in the
warning shown
or by long pressing the thumbnail to open the menu and clicking play from there
## Usage ## Usage
Run iSponsorBlockTV on a computer that has network access. It doesn't need to Run iSponsorBlockTV on a computer that has network access.
be on the same network as the device, only access to youtube.com is required.
Auto discovery will require the computer to be on the same network as the device Auto discovery will require the computer to be on the same network as the device
during setup. during setup.
The device can also be manually added to iSponsorBlockTV with a YouTube TV code. The device can also be manually added to iSponsorBlockTV with a YouTube TV code.
This code can be found in the settings page of your YouTube TV application. This code can be found in the settings page of your YouTube application.
It connects to the device, watches its activity and skips any sponsor segment
using the [SponsorBlock](https://sponsor.ajay.app/) API.
It can also skip/mute YouTube ads.
## Libraries used ## Libraries used
@@ -77,9 +71,11 @@ This code can be found in the settings page of your YouTube TV application.
## Contributors ## Contributors
[![Contributors](https://contrib.rocks/image?repo=dmunozv04/iSponsorBlockTV)](https://github.com/dmunozv04/iSponsorBlockTV/graphs/contributors) - [dmunozv04](https://github.com/dmunozv04) - creator and maintainer
- [HaltCatchFire](https://github.com/HaltCatchFire) - updated dependencies and
Made with [contrib.rocks](https://contrib.rocks). improved skip logic
- [Oxixes](https://github.com/oxixes) - added support for channel whitelist and
minor improvements
## License ## License

View File

@@ -20,6 +20,5 @@
{"id": "", {"id": "",
"name": "" "name": ""
} }
], ]
"use_proxy": false
} }

View File

@@ -1,7 +1,8 @@
version: '3.3'
services: services:
iSponsorBlockTV: iSponsorBlockTV:
image: ghcr.io/dmunozv04/isponsorblocktv image: ghcr.io/dmunozv04/isponsorblocktv
container_name: iSponsorBlockTV container_name: iSponsorBlockTV
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- /PATH_TO_YOUR_DATA_DIR:/app/data - /PATH_TO_YOUR_DATA_DIR:/app/data

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "iSponsorBlockTV" name = "iSponsorBlockTV"
version = "2.6.1" version = "2.5.3"
authors = [ authors = [
{"name" = "dmunozv04"} {"name" = "dmunozv04"}
] ]

View File

@@ -1,10 +1,10 @@
aiohttp==3.12.15 aiohttp==3.11.18
appdirs==1.4.4 appdirs==1.4.4
async-cache==1.1.1 async-cache==1.1.1
pyytlounge==2.3.0 pyytlounge==2.3.0
rich==14.1.0 rich==14.0.0
ssdp==1.3.1 ssdp==1.3.0
textual==5.3.0 textual==2.1.2
textual-slider==0.2.0 textual-slider==0.2.0
xmltodict==0.15.1 xmltodict==0.14.2
rich_click==1.8.9 rich_click==1.8.8

View File

@@ -5,7 +5,6 @@ import aiohttp
from . import api_helpers, ytlounge from . import api_helpers, ytlounge
# Constants for user input prompts # Constants for user input prompts
USE_PROXY_PROMPT = "Do you want to use system-wide proxy? (y/N)"
ATVS_REMOVAL_PROMPT = ( ATVS_REMOVAL_PROMPT = (
"Do you want to remove the legacy 'atvs' entry (the app won't start with it present)? (y/N) " "Do you want to remove the legacy 'atvs' entry (the app won't start with it present)? (y/N) "
) )
@@ -46,8 +45,8 @@ def get_yn_input(prompt):
return None return None
async def create_web_session(use_proxy): async def create_web_session():
return aiohttp.ClientSession(trust_env=use_proxy) return aiohttp.ClientSession()
async def pair_device(web_session: aiohttp.ClientSession): async def pair_device(web_session: aiohttp.ClientSession):
@@ -76,12 +75,8 @@ async def pair_device(web_session: aiohttp.ClientSession):
def main(config, debug: bool) -> None: def main(config, debug: bool) -> None:
print("Welcome to the iSponsorBlockTV cli setup wizard") print("Welcome to the iSponsorBlockTV cli setup wizard")
choice = get_yn_input(USE_PROXY_PROMPT)
config.use_proxy = choice == "y"
loop = asyncio.get_event_loop_policy().get_event_loop() loop = asyncio.get_event_loop_policy().get_event_loop()
web_session = loop.run_until_complete(create_web_session(config.use_proxy)) web_session = loop.run_until_complete(create_web_session())
if debug: if debug:
loop.set_debug(True) loop.set_debug(True)
asyncio.set_event_loop(loop) asyncio.set_event_loop(loop)

View File

@@ -44,7 +44,6 @@ class Config:
self.minimum_skip_length = 1 self.minimum_skip_length = 1
self.auto_play = True self.auto_play = True
self.join_name = "iSponsorBlockTV" self.join_name = "iSponsorBlockTV"
self.use_proxy = False
self.__load() self.__load()
def validate(self): def validate(self):

View File

@@ -172,11 +172,9 @@ async def main_async(config, debug, http_tracing):
trace_config.on_response_chunk_received.append(tracer.on_response_chunk_received) trace_config.on_response_chunk_received.append(tracer.on_response_chunk_received)
trace_config.on_request_end.append(tracer.on_request_end) trace_config.on_request_end.append(tracer.on_request_end)
trace_config.on_request_exception.append(tracer.on_request_exception) trace_config.on_request_exception.append(tracer.on_request_exception)
web_session = aiohttp.ClientSession( web_session = aiohttp.ClientSession(connector=tcp_connector, trace_configs=[trace_config])
trust_env=config.use_proxy, connector=tcp_connector, trace_configs=[trace_config]
)
else: else:
web_session = aiohttp.ClientSession(trust_env=config.use_proxy, connector=tcp_connector) web_session = aiohttp.ClientSession(connector=tcp_connector)
api_helper = api_helpers.ApiHelper(config, web_session) api_helper = api_helpers.ApiHelper(config, web_session)
for i in config.devices: for i in config.devices:

View File

@@ -383,9 +383,3 @@ MigrationScreen {
padding: 1; padding: 1;
height: auto; height: auto;
} }
/* Use Proxy */
#useproxy-container{
padding: 1;
height: auto;
}

View File

@@ -13,7 +13,6 @@ from textual.containers import (
ScrollableContainer, ScrollableContainer,
Vertical, Vertical,
) )
from textual.css.query import NoMatches
from textual.events import Click from textual.events import Click
from textual.screen import Screen from textual.screen import Screen
from textual.validation import Function from textual.validation import Function
@@ -234,7 +233,7 @@ class AddDevice(ModalWithClickExit):
def __init__(self, config, **kwargs) -> None: def __init__(self, config, **kwargs) -> None:
super().__init__(**kwargs) super().__init__(**kwargs)
self.config = config self.config = config
self.web_session = aiohttp.ClientSession(trust_env=config.use_proxy) self.web_session = aiohttp.ClientSession()
self.api_helper = api_helpers.ApiHelper(config, self.web_session) self.api_helper = api_helpers.ApiHelper(config, self.web_session)
self.devices_discovered_dial = [] self.devices_discovered_dial = []
@@ -302,11 +301,7 @@ class AddDevice(ModalWithClickExit):
async def task_discover_devices(self): async def task_discover_devices(self):
devices_found = await self.api_helper.discover_youtube_devices_dial() devices_found = await self.api_helper.discover_youtube_devices_dial()
try: list_widget: SelectionList = self.query_one("#dial-devices-list")
list_widget: SelectionList = self.query_one("#dial-devices-list")
except NoMatches:
# The widget was not found, probably the screen was dismissed
return
list_widget.clear_options() list_widget.clear_options()
if devices_found: if devices_found:
# print(devices_found) # print(devices_found)
@@ -341,7 +336,7 @@ class AddDevice(ModalWithClickExit):
pairing_code = int( pairing_code = int(
pairing_code.replace("-", "").replace(" ", "") pairing_code.replace("-", "").replace(" ", "")
) # remove dashes and spaces ) # remove dashes and spaces
device_name = self.query_one("#device-name-input").value device_name = self.parent.query_one("#device-name-input").value
paired = False paired = False
try: try:
paired = await lounge_controller.pair(pairing_code) paired = await lounge_controller.pair(pairing_code)
@@ -387,7 +382,7 @@ class AddChannel(ModalWithClickExit):
def __init__(self, config, **kwargs) -> None: def __init__(self, config, **kwargs) -> None:
super().__init__(**kwargs) super().__init__(**kwargs)
self.config = config self.config = config
web_session = aiohttp.ClientSession(trust_env=config.use_proxy) web_session = aiohttp.ClientSession()
self.api_helper = api_helpers.ApiHelper(config, web_session) self.api_helper = api_helpers.ApiHelper(config, web_session)
def compose(self) -> ComposeResult: def compose(self) -> ComposeResult:
@@ -664,7 +659,7 @@ class ApiKeyManager(Vertical):
@on(Button.Pressed, "#api-key-view") @on(Button.Pressed, "#api-key-view")
def pressed_api_key_view(self, event: Button.Pressed): def pressed_api_key_view(self, event: Button.Pressed):
if "Show" in str(event.button.label): if "Show" in event.button.label:
event.button.label = "Hide key" event.button.label = "Hide key"
self.query_one("#api-key-input").password = False self.query_one("#api-key-input").password = False
else: else:
@@ -825,7 +820,10 @@ class ChannelWhitelistManager(Vertical):
id="channel-whitelist-subtitle", id="channel-whitelist-subtitle",
) )
yield Label( yield Label(
("⚠️ [#FF0000]You need to set your YouTube Api Key in order to use this feature"), (
":warning: [#FF0000]You need to set your YouTube Api Key in order to"
" use this feature"
),
id="warning-no-key", id="warning-no-key",
) )
with Horizontal(id="add-channel-button-container"): with Horizontal(id="add-channel-button-container"):
@@ -892,45 +890,11 @@ class AutoPlayManager(Vertical):
self.config.auto_play = event.checkbox.value self.config.auto_play = event.checkbox.value
class UseProxyManager(Vertical): class ISponsorBlockTVSetupMainScreen(Screen):
"""Manager for proxy use, allows enabling/disabling use of proxy."""
def __init__(self, config, **kwargs) -> None:
super().__init__(**kwargs)
self.config = config
def compose(self) -> ComposeResult:
yield Label("Use proxy", classes="title")
yield Label(
"This feature allows application to use system proxy,"
" if it is set in environment variables."
" This parameter will be passed in all [i]aiohttp.ClientSession[/i]"
' calls. For further information, see "[i]trust_env[/i]" section at'
" [link='https://docs.aiohttp.org/en/stable/client_reference.html']"
"aiohttp documentation[/link].",
classes="subtitle",
id="useproxy-subtitle",
)
with Horizontal(id="useproxy-container"):
yield Checkbox(
value=self.config.use_proxy,
id="useproxy-switch",
label="Use proxy",
)
@on(Checkbox.Changed, "#useproxy-switch")
def changed_skip(self, event: Checkbox.Changed):
self.config.use_proxy = event.checkbox.value
class ISponsorBlockTVSetup(App):
TITLE = "iSponsorBlockTV" TITLE = "iSponsorBlockTV"
SUB_TITLE = "Setup Wizard" SUB_TITLE = "Setup Wizard"
BINDINGS = [("q,ctrl+c", "exit_modal", "Exit"), ("s", "save", "Save")] BINDINGS = [("q,ctrl+c", "exit_modal", "Exit"), ("s", "save", "Save")]
AUTO_FOCUS = None AUTO_FOCUS = None
CSS_PATH = ( # tcss is the recommended extension for textual css files
"setup-wizard-style.tcss"
)
def __init__(self, config, **kwargs) -> None: def __init__(self, config, **kwargs) -> None:
super().__init__(**kwargs) super().__init__(**kwargs)
@@ -962,7 +926,6 @@ class ISponsorBlockTVSetup(App):
) )
yield ApiKeyManager(config=self.config, id="api-key-manager", classes="container") yield ApiKeyManager(config=self.config, id="api-key-manager", classes="container")
yield AutoPlayManager(config=self.config, id="autoplay-manager", classes="container") yield AutoPlayManager(config=self.config, id="autoplay-manager", classes="container")
yield UseProxyManager(config=self.config, id="useproxy-manager", classes="container")
def on_mount(self) -> None: def on_mount(self) -> None:
if self.check_for_old_config_entries(): if self.check_for_old_config_entries():
@@ -986,13 +949,36 @@ class ISponsorBlockTVSetup(App):
@on(Input.Changed, "#api-key-input") @on(Input.Changed, "#api-key-input")
def changed_api_key(self, event: Input.Changed): def changed_api_key(self, event: Input.Changed):
try: # ChannelWhitelist might not be mounted try: # ChannelWhitelist might not be mounted
self.app.query_one("#warning-no-key").display = bool( # Show if no api key is set and at least one channel is in the whitelist
(not event.input.value) and self.config.channel_whitelist self.app.query_one("#warning-no-key").display = (
) not event.input.value
except NoMatches: ) and self.config.channel_whitelist
except BaseException:
pass pass
class ISponsorBlockTVSetup(App):
CSS_PATH = ( # tcss is the recommended extension for textual css files
"setup-wizard-style.tcss"
)
# Bindings for the whole app here, so they are available in all screens
BINDINGS = [("q,ctrl+c", "exit_modal", "Exit"), ("s", "save", "Save")]
def __init__(self, config, **kwargs) -> None:
super().__init__(**kwargs)
self.config = config
self.main_screen = ISponsorBlockTVSetupMainScreen(config=self.config)
def on_mount(self) -> None:
self.push_screen(self.main_screen)
def action_save(self) -> None:
self.main_screen.action_save()
def action_exit_modal(self) -> None:
self.main_screen.action_exit_modal()
def main(config): def main(config):
app = ISponsorBlockTVSetup(config) app = ISponsorBlockTVSetup(config)
app.run() app.run()

View File

@@ -1,6 +1,7 @@
import asyncio import asyncio
import json import json
import sys import sys
import time
from typing import Any, List from typing import Any, List
import pyytlounge import pyytlounge
@@ -50,12 +51,12 @@ class YtLoungeApi(pyytlounge.YtLoungeApi):
it cancels the current subscription. it cancels the current subscription.
""" """
self.watchdog_running = True self.watchdog_running = True
self.last_event_time = asyncio.get_event_loop().time() self.last_event_time = time.time()
try: try:
while self.watchdog_running: while self.watchdog_running:
await asyncio.sleep(10) await asyncio.sleep(10)
current_time = asyncio.get_event_loop().time() current_time = time.time()
time_since_last_event = current_time - self.last_event_time time_since_last_event = current_time - self.last_event_time
# YouTube sends a message at least every 30 seconds # YouTube sends a message at least every 30 seconds
@@ -105,7 +106,7 @@ class YtLoungeApi(pyytlounge.YtLoungeApi):
def _process_event(self, event_type: str, args: List[Any]): def _process_event(self, event_type: str, args: List[Any]):
self.logger.debug(f"process_event({event_type}, {args})") self.logger.debug(f"process_event({event_type}, {args})")
# Update last event time for the watchdog # Update last event time for the watchdog
self.last_event_time = asyncio.get_event_loop().time() self.last_event_time = time.time()
# A bunch of events useful to detect ads playing, # A bunch of events useful to detect ads playing,
# and the next video before it starts playing # and the next video before it starts playing