mirror of
https://github.com/dmunozv04/iSponsorBlockTV.git
synced 2025-12-09 13:26:44 +03:00
Compare commits
2 Commits
better-wat
...
add-custom
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e00c62af1 | ||
|
|
1567a33e51 |
13
.github/ISSUE_TEMPLATE/bug_report.md
vendored
13
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -7,14 +7,12 @@ assignees: ''
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Before opening an issue make sure that there are no duplicates and that you are
|
Before opening an issue make sure that there are no duplicates and that you are on the latest version.
|
||||||
on the latest version.
|
|
||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
A clear and concise description of what the bug is.
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
**To Reproduce**
|
**To Reproduce**
|
||||||
Steps to reproduce the behavior:
|
Steps to reproduce the behavior:
|
||||||
|
|
||||||
1. Go to '...'
|
1. Go to '...'
|
||||||
2. Click on '....'
|
2. Click on '....'
|
||||||
3. Scroll down to '....'
|
3. Scroll down to '....'
|
||||||
@@ -27,14 +25,13 @@ A clear and concise description of what you expected to happen.
|
|||||||
If applicable, add screenshots to help explain your problem.
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
**iSponsorBlockTV server (please complete the following information):**
|
**iSponsorBlockTV server (please complete the following information):**
|
||||||
|
- OS: [e.g. Docker on linux Arm64, windows]
|
||||||
- OS: [e.g. Docker on linux Arm64, windows]
|
- Python version [e.g. 3.7] (no need to fill if running on docker
|
||||||
- Python version [e.g. 3.7] (no need to fill if running on docker
|
|
||||||
|
|
||||||
**Apple TV (please complete the following information):**
|
**Apple TV (please complete the following information):**
|
||||||
|
- Device: [e.g. Apple TV 4]
|
||||||
|
- OS: [e.g. tvOS 15.4]
|
||||||
|
|
||||||
- Device: [e.g. Apple TV 4]
|
|
||||||
- OS: [e.g. tvOS 15.4]
|
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context about the problem here.
|
Add any other context about the problem here.
|
||||||
|
|||||||
6
.github/ISSUE_TEMPLATE/feature_request.md
vendored
6
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -8,15 +8,13 @@ assignees: ''
|
|||||||
---
|
---
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
**Is your feature request related to a problem? Please describe.**
|
||||||
A clear and concise description of what the problem is. Ex. I'm always
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
frustrated when [...]
|
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
**Describe the solution you'd like**
|
||||||
A clear and concise description of what you want to happen.
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
**Describe alternatives you've considered**
|
||||||
A clear and concise description of any alternative solutions or features you've
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
considered.
|
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context or screenshots about the feature request here.
|
Add any other context or screenshots about the feature request here.
|
||||||
|
|||||||
19
.github/dependabot.yml
vendored
19
.github/dependabot.yml
vendored
@@ -1,19 +0,0 @@
|
|||||||
# To get started with Dependabot version updates, you'll need to specify which
|
|
||||||
# package ecosystems to update and where the package manifests are located.
|
|
||||||
# Please see the documentation for all configuration options:
|
|
||||||
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
|
||||||
|
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: "pip"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "weekly"
|
|
||||||
- package-ecosystem: "docker"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "weekly"
|
|
||||||
- package-ecosystem: "github-actions"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "weekly"
|
|
||||||
13
.github/workflows/build_docker_images.yml
vendored
13
.github/workflows/build_docker_images.yml
vendored
@@ -21,8 +21,6 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
env:
|
|
||||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
# Get the repository's code
|
# Get the repository's code
|
||||||
@@ -34,9 +32,7 @@ jobs:
|
|||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: |
|
images: ghcr.io/dmunozv04/isponsorblocktv, dmunozv04/isponsorblocktv
|
||||||
ghcr.io/${{ github.repository }}
|
|
||||||
${{ env.DOCKERHUB_USERNAME && 'dmunozv04/isponsorblocktv' || '' }}
|
|
||||||
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
|
||||||
@@ -68,12 +64,13 @@ jobs:
|
|||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64, linux/arm64, linux/arm/v7
|
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 }}
|
||||||
cache-from: type=gha
|
cache-from: type=registry,ref=ghcr.io/dmunozv04/isponsorblocktv:buildcache
|
||||||
cache-to: type=gha,mode=max
|
# Only cache if it's not a pull request
|
||||||
|
cache-to: ${{ github.event_name != 'pull_request' && 'type=registry,ref=ghcr.io/dmunozv04/isponsorblocktv:buildcache,mode=max' || '' }}
|
||||||
|
|||||||
17
.github/workflows/release.yml
vendored
17
.github/workflows/release.yml
vendored
@@ -57,7 +57,7 @@ jobs:
|
|||||||
|
|
||||||
|
|
||||||
build-binaries:
|
build-binaries:
|
||||||
name: Build binaries for ${{ matrix.job.release_suffix }} (${{ matrix.job.os }})
|
name: Build binaries for ${{ matrix.job.target }} (${{ matrix.job.os }})
|
||||||
needs:
|
needs:
|
||||||
- build-sdist-and-wheel
|
- build-sdist-and-wheel
|
||||||
runs-on: ${{ matrix.job.os }}
|
runs-on: ${{ matrix.job.os }}
|
||||||
@@ -70,26 +70,20 @@ jobs:
|
|||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
cross: true
|
cross: true
|
||||||
release_suffix: x86_64-linux
|
release_suffix: x86_64-linux
|
||||||
- target: x86_64-unknown-linux-gnu
|
|
||||||
os: ubuntu-latest
|
|
||||||
cross: true
|
|
||||||
cpu_variant: v1
|
|
||||||
release_suffix: x86_64-linux-v1
|
|
||||||
- target: aarch64-unknown-linux-gnu
|
- target: aarch64-unknown-linux-gnu
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
cross: true
|
cross: true
|
||||||
release_suffix: aarch64-linux
|
release_suffix: aarch64-linux
|
||||||
# Windows
|
# Windows
|
||||||
- target: x86_64-pc-windows-msvc
|
- target: x86_64-pc-windows-msvc
|
||||||
os: windows-latest
|
os: windows-2022
|
||||||
release_suffix: x86_64-windows
|
release_suffix: x86_64-windows
|
||||||
# macOS
|
# macOS
|
||||||
- target: aarch64-apple-darwin
|
- target: aarch64-apple-darwin
|
||||||
os: macos-latest
|
os: macos-14
|
||||||
release_suffix: aarch64-osx
|
release_suffix: aarch64-osx
|
||||||
- target: x86_64-apple-darwin
|
- target: x86_64-apple-darwin
|
||||||
os: macos-latest
|
os: macos-12
|
||||||
cross: true
|
|
||||||
release_suffix: x86_64-osx
|
release_suffix: x86_64-osx
|
||||||
|
|
||||||
env:
|
env:
|
||||||
@@ -98,9 +92,8 @@ jobs:
|
|||||||
HATCH_BUILD_LOCATION: dist
|
HATCH_BUILD_LOCATION: dist
|
||||||
CARGO: cargo
|
CARGO: cargo
|
||||||
CARGO_BUILD_TARGET: ${{ matrix.job.target }}
|
CARGO_BUILD_TARGET: ${{ matrix.job.target }}
|
||||||
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.24.0
|
PYAPP_VERSION: v0.23.0
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
LICENSE.md
|
|
||||||
@@ -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: v5.0.0
|
rev: v4.3.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
|
||||||
@@ -18,14 +18,22 @@ repos:
|
|||||||
- id: end-of-file-fixer # ensures that a file is either empty, or ends with one newline
|
- id: end-of-file-fixer # ensures that a file is either empty, or ends with one newline
|
||||||
- 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/pycqa/isort
|
||||||
rev: v0.9.6
|
rev: 5.12.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: isort
|
||||||
args: [ --fix, --exit-non-zero-on-fix ]
|
name: isort (python)
|
||||||
- id: ruff-format
|
language_version: '3.11'
|
||||||
- repo: https://github.com/igorshubovych/markdownlint-cli
|
args: ["--profile", "black", "--filter-files"]
|
||||||
rev: v0.44.0
|
- repo: https://github.com/psf/black
|
||||||
|
rev: 23.1.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: markdownlint
|
- id: black
|
||||||
args: ["--fix"]
|
language_version: '3.11'
|
||||||
|
args: ["--preview"]
|
||||||
|
- repo: https://github.com/hadialqattan/pycln # removes unused imports
|
||||||
|
rev: v2.3.0
|
||||||
|
hooks:
|
||||||
|
- id: pycln
|
||||||
|
language_version: "3.11"
|
||||||
|
args: [--all]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
FROM python:3.13-alpine3.21 AS base
|
FROM python:3.11-alpine3.19 AS base
|
||||||
|
|
||||||
FROM base AS compiler
|
FROM base AS compiler
|
||||||
|
|
||||||
@@ -19,9 +19,9 @@ RUN apk add --no-cache gcc musl-dev && \
|
|||||||
pip install -r requirements.txt && \
|
pip install -r requirements.txt && \
|
||||||
pip uninstall -y pip wheel && \
|
pip uninstall -y pip wheel && \
|
||||||
apk del gcc musl-dev && \
|
apk del gcc musl-dev && \
|
||||||
python3 -m compileall -b -f /usr/local/lib/python3.13/site-packages && \
|
python3 -m compileall -b -f /usr/local/lib/python3.11/site-packages && \
|
||||||
find /usr/local/lib/python3.13/site-packages -name "*.py" -type f -delete && \
|
find /usr/local/lib/python3.11/site-packages -name "*.py" -type f -delete && \
|
||||||
find /usr/local/lib/python3.13/ -name "__pycache__" -type d -exec rm -rf {} +
|
find /usr/local/lib/python3.11/ -name "__pycache__" -type d -exec rm -rf {} +
|
||||||
|
|
||||||
FROM base
|
FROM base
|
||||||
|
|
||||||
|
|||||||
@@ -657,7 +657,7 @@ notice like this when it starts in an interactive mode:
|
|||||||
This is free software, and you are welcome to redistribute it
|
This is free software, and you are welcome to redistribute it
|
||||||
under certain conditions; type `show c' for details.
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
The hypothetical commands `show w' and`show c' should show the appropriate
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
parts of the General Public License. Of course, your program's commands
|
parts of the General Public License. Of course, your program's commands
|
||||||
might be different; for a GUI interface, you would use an "about box".
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
|||||||
41
README.md
41
README.md
@@ -1,21 +1,14 @@
|
|||||||
# iSponsorBlockTV
|
# iSponsorBlockTV
|
||||||
|
Skip sponsor segments in YouTube videos playing on a YouTube TV device (see below for compatibility details).
|
||||||
[](https://ghcr.io/dmunozv04/isponsorblocktv)
|
|
||||||
[](https://hub.docker.com/r/dmunozv04/isponsorblocktv/)
|
|
||||||
[](https://github.com/dmunozv04/iSponsorBlockTV/releases/latest)
|
|
||||||
[](https://github.com/dmunozv04/isponsorblocktv)
|
|
||||||
|
|
||||||
Skip sponsor segments in YouTube videos playing on a YouTube TV device (see
|
|
||||||
below for compatibility details).
|
|
||||||
|
|
||||||
This project is written in asynchronous python and should be pretty quick.
|
This project is written in asynchronous python and should be pretty quick.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Check the [wiki](https://github.com/dmunozv04/iSponsorBlockTV/wiki/Installation)
|
Check the [wiki](https://github.com/dmunozv04/iSponsorBlockTV/wiki/Installation)
|
||||||
|
|
||||||
## Compatibility
|
Warning: docker armv7 builds have been deprecated. Amd64 and arm64 builds are still available.
|
||||||
|
|
||||||
|
## Compatibility
|
||||||
Legend: ✅ = Working, ❌ = Not working, ❔ = Not tested
|
Legend: ✅ = Working, ❌ = Not working, ❔ = Not tested
|
||||||
|
|
||||||
Open an issue/pull request if you have tested a device that isn't listed here.
|
Open an issue/pull request if you have tested a device that isn't listed here.
|
||||||
@@ -36,33 +29,24 @@ Open an issue/pull request if you have tested a device that isn't listed here.
|
|||||||
| Playstation 4/5 | ✅ |
|
| Playstation 4/5 | ✅ |
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Run iSponsorBlockTV on a computer that has network access.
|
Run iSponsorBlockTV on a computer that has network access.
|
||||||
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. This code can be found in the settings page of your YouTube application.
|
||||||
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 application.
|
|
||||||
|
|
||||||
It connects to the device, watches its activity and skips any sponsor segment
|
It connects to the device, watches its activity and skips any sponsor segment using the [SponsorBlock](https://sponsor.ajay.app/) API.
|
||||||
using the [SponsorBlock](https://sponsor.ajay.app/) API.
|
|
||||||
It can also skip/mute YouTube ads.
|
It can also skip/mute YouTube ads.
|
||||||
|
|
||||||
## Libraries used
|
## Libraries used
|
||||||
|
- [pyytlounge](https://github.com/FabioGNR/pyytlounge) Used to interact with the device
|
||||||
- [pyytlounge](https://github.com/FabioGNR/pyytlounge) Used to interact with the
|
|
||||||
device
|
|
||||||
- asyncio and [aiohttp](https://github.com/aio-libs/aiohttp)
|
- asyncio and [aiohttp](https://github.com/aio-libs/aiohttp)
|
||||||
- [async-cache](https://github.com/iamsinghrajat/async-cache)
|
- [async-cache](https://github.com/iamsinghrajat/async-cache)
|
||||||
- [Textual](https://github.com/textualize/textual/) Used for the amazing new
|
- [Textual](https://github.com/textualize/textual/) Used for the amazing new graphical configurator
|
||||||
graphical configurator
|
|
||||||
- [ssdp](https://github.com/codingjoe/ssdp) Used for auto discovery
|
- [ssdp](https://github.com/codingjoe/ssdp) Used for auto discovery
|
||||||
|
|
||||||
## Projects using this project
|
## Projects using this project
|
||||||
|
|
||||||
- [Home Assistant Addon](https://github.com/bertybuttface/addons/tree/main/isponsorblocktv)
|
- [Home Assistant Addon](https://github.com/bertybuttface/addons/tree/main/isponsorblocktv)
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
1. Fork it (<https://github.com/dmunozv04/iSponsorBlockTV/fork>)
|
1. Fork it (<https://github.com/dmunozv04/iSponsorBlockTV/fork>)
|
||||||
2. Create your feature branch (`git checkout -b my-new-feature`)
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||||
3. Commit your changes (`git commit -am 'Add some feature'`)
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
||||||
@@ -70,13 +54,8 @@ It can also skip/mute YouTube ads.
|
|||||||
5. Create a new Pull Request
|
5. Create a new Pull Request
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
||||||
- [dmunozv04](https://github.com/dmunozv04) - creator and maintainer
|
- [dmunozv04](https://github.com/dmunozv04) - creator and maintainer
|
||||||
- [HaltCatchFire](https://github.com/HaltCatchFire) - updated dependencies and
|
- [HaltCatchFire](https://github.com/HaltCatchFire) - updated dependencies and improved skip logic
|
||||||
improved skip logic
|
- [Oxixes](https://github.com/oxixes) - added support for channel whitelist and minor improvements
|
||||||
- [Oxixes](https://github.com/oxixes) - added support for channel whitelist and
|
|
||||||
minor improvements
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
||||||
|
|||||||
@@ -13,11 +13,11 @@
|
|||||||
"mute_ads": true,
|
"mute_ads": true,
|
||||||
"skip_ads": true,
|
"skip_ads": true,
|
||||||
"auto_play": true,
|
"auto_play": true,
|
||||||
"join_name": "iSponsorBlockTV",
|
|
||||||
"apikey": "",
|
"apikey": "",
|
||||||
"channel_whitelist": [
|
"channel_whitelist": [
|
||||||
{"id": "",
|
{"id": "",
|
||||||
"name": ""
|
"name": ""
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"api_server": "https://sponsor.ajay.app"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "iSponsorBlockTV"
|
name = "iSponsorBlockTV"
|
||||||
version = "2.3.1"
|
version = "2.2.1"
|
||||||
authors = [
|
authors = [
|
||||||
{"name" = "dmunozv04"}
|
{"name" = "dmunozv04"}
|
||||||
]
|
]
|
||||||
description = "SponsorBlock client for all YouTube TV clients"
|
description = "SponsorBlock client for all YouTube TV clients"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.9"
|
requires-python = ">=3.7"
|
||||||
classifiers = [
|
classifiers = [
|
||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
aiohttp==3.11.12
|
aiohttp==3.9.5
|
||||||
appdirs==1.4.4
|
appdirs==1.4.4
|
||||||
|
argparse==1.4.0
|
||||||
async-cache==1.1.1
|
async-cache==1.1.1
|
||||||
pyytlounge==2.1.2
|
pyytlounge==2.0.0
|
||||||
rich==13.9.4
|
rich==13.7.1
|
||||||
ssdp==1.3.0
|
ssdp==1.3.0
|
||||||
textual==1.0.0
|
textual==0.58.0
|
||||||
textual-slider==0.2.0
|
textual-slider==0.1.1
|
||||||
xmltodict==0.14.2
|
xmltodict==0.13.0
|
||||||
rich_click==1.8.5
|
rich_click==1.8.3
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class ApiHelper:
|
|||||||
self.skip_count_tracking = config.skip_count_tracking
|
self.skip_count_tracking = config.skip_count_tracking
|
||||||
self.web_session = web_session
|
self.web_session = web_session
|
||||||
self.num_devices = len(config.devices)
|
self.num_devices = len(config.devices)
|
||||||
|
self.api_server = config.api_server
|
||||||
|
|
||||||
# Not used anymore, maybe it can stay here a little longer
|
# Not used anymore, maybe it can stay here a little longer
|
||||||
@AsyncLRU(maxsize=10)
|
@AsyncLRU(maxsize=10)
|
||||||
@@ -130,7 +131,7 @@ class ApiHelper:
|
|||||||
"service": constants.SponsorBlock_service,
|
"service": constants.SponsorBlock_service,
|
||||||
}
|
}
|
||||||
headers = {"Accept": "application/json"}
|
headers = {"Accept": "application/json"}
|
||||||
url = constants.SponsorBlock_api + "skipSegments/" + vid_id_hashed
|
url = self.api_server + "/api/skipSegments/" + vid_id_hashed
|
||||||
async with self.web_session.get(
|
async with self.web_session.get(
|
||||||
url, headers=headers, params=params
|
url, headers=headers, params=params
|
||||||
) as response:
|
) as response:
|
||||||
@@ -201,7 +202,7 @@ class ApiHelper:
|
|||||||
Lets the contributor know that someone skipped the segment (thanks)"""
|
Lets the contributor know that someone skipped the segment (thanks)"""
|
||||||
if self.skip_count_tracking:
|
if self.skip_count_tracking:
|
||||||
for i in uuids:
|
for i in uuids:
|
||||||
url = constants.SponsorBlock_api + "viewedVideoSponsorTime/"
|
url = self.api_server + "/api/viewedVideoSponsorTime/"
|
||||||
params = {"UUID": i}
|
params = {"UUID": i}
|
||||||
await self.web_session.post(url, params=params)
|
await self.web_session.post(url, params=params)
|
||||||
|
|
||||||
|
|||||||
@@ -1,198 +1,202 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
|
||||||
from . import api_helpers, ytlounge
|
from . import api_helpers, ytlounge
|
||||||
|
|
||||||
# Constants for user input prompts
|
# Constants for user input prompts
|
||||||
ATVS_REMOVAL_PROMPT = (
|
ATVS_REMOVAL_PROMPT = (
|
||||||
"Do you want to remove the legacy 'atvs' entry (the app won't start"
|
"Do you want to remove the legacy 'atvs' entry (the app won't start"
|
||||||
" with it present)? (y/N) "
|
" with it present)? (y/N) "
|
||||||
)
|
)
|
||||||
PAIRING_CODE_PROMPT = "Enter pairing code (found in Settings - Link with TV code): "
|
PAIRING_CODE_PROMPT = "Enter pairing code (found in Settings - Link with TV code): "
|
||||||
ADD_MORE_DEVICES_PROMPT = "Paired with {num_devices} Device(s). Add more? (y/N) "
|
ADD_MORE_DEVICES_PROMPT = "Paired with {num_devices} Device(s). Add more? (y/N) "
|
||||||
CHANGE_API_KEY_PROMPT = "API key already specified. Change it? (y/N) "
|
CHANGE_API_KEY_PROMPT = "API key already specified. Change it? (y/N) "
|
||||||
ADD_API_KEY_PROMPT = (
|
ADD_API_KEY_PROMPT = (
|
||||||
"API key only needed for the channel whitelist function. Add it? (y/N) "
|
"API key only needed for the channel whitelist function. Add it? (y/N) "
|
||||||
)
|
)
|
||||||
ENTER_API_KEY_PROMPT = "Enter your API key: "
|
ENTER_API_KEY_PROMPT = "Enter your API key: "
|
||||||
CHANGE_SKIP_CATEGORIES_PROMPT = "Skip categories already specified. Change them? (y/N) "
|
CHANGE_SKIP_CATEGORIES_PROMPT = "Skip categories already specified. Change them? (y/N) "
|
||||||
ENTER_SKIP_CATEGORIES_PROMPT = (
|
ENTER_SKIP_CATEGORIES_PROMPT = (
|
||||||
"Enter skip categories (space or comma sepparated) Options: [sponsor,"
|
"Enter skip categories (space or comma sepparated) Options: [sponsor,"
|
||||||
" selfpromo, exclusive_access, interaction, poi_highlight, intro, outro,"
|
" selfpromo, exclusive_access, interaction, poi_highlight, intro, outro,"
|
||||||
" preview, filler, music_offtopic]:\n"
|
" preview, filler, music_offtopic]:\n"
|
||||||
)
|
)
|
||||||
WHITELIST_CHANNELS_PROMPT = (
|
WHITELIST_CHANNELS_PROMPT = (
|
||||||
"Do you want to whitelist any channels from being ad-blocked? (y/N) "
|
"Do you want to whitelist any channels from being ad-blocked? (y/N) "
|
||||||
)
|
)
|
||||||
SEARCH_CHANNEL_PROMPT = 'Enter a channel name or "/exit" to exit: '
|
SEARCH_CHANNEL_PROMPT = 'Enter a channel name or "/exit" to exit: '
|
||||||
SELECT_CHANNEL_PROMPT = "Select one option of the above [0-6]: "
|
SELECT_CHANNEL_PROMPT = "Select one option of the above [0-6]: "
|
||||||
ENTER_CHANNEL_ID_PROMPT = "Enter a channel ID: "
|
ENTER_CHANNEL_ID_PROMPT = "Enter a channel ID: "
|
||||||
ENTER_CUSTOM_CHANNEL_NAME_PROMPT = "Enter the channel name: "
|
ENTER_CUSTOM_CHANNEL_NAME_PROMPT = "Enter the channel name: "
|
||||||
REPORT_SKIPPED_SEGMENTS_PROMPT = (
|
REPORT_SKIPPED_SEGMENTS_PROMPT = (
|
||||||
"Do you want to report skipped segments to sponsorblock. Only the segment"
|
"Do you want to report skipped segments to sponsorblock. Only the segment"
|
||||||
" UUID will be sent? (Y/n) "
|
" UUID will be sent? (Y/n) "
|
||||||
)
|
)
|
||||||
MUTE_ADS_PROMPT = "Do you want to mute native YouTube ads automatically? (y/N) "
|
MUTE_ADS_PROMPT = "Do you want to mute native YouTube ads automatically? (y/N) "
|
||||||
SKIP_ADS_PROMPT = "Do you want to skip native YouTube ads automatically? (y/N) "
|
SKIP_ADS_PROMPT = "Do you want to skip native YouTube ads automatically? (y/N) "
|
||||||
AUTOPLAY_PROMPT = "Do you want to enable autoplay? (Y/n) "
|
AUTOPLAY_PROMPT = "Do you want to enable autoplay? (Y/n) "
|
||||||
|
ENTER_API_SERVER_PROMPT = (
|
||||||
|
"Enter the custom API server URL (leave blank to use default): "
|
||||||
def get_yn_input(prompt):
|
)
|
||||||
while choice := input(prompt):
|
|
||||||
if choice.lower() in ["y", "n"]:
|
|
||||||
return choice.lower()
|
def get_yn_input(prompt):
|
||||||
print("Invalid input. Please enter 'y' or 'n'.")
|
while choice := input(prompt):
|
||||||
|
if choice.lower() in ["y", "n"]:
|
||||||
|
return choice.lower()
|
||||||
async def create_web_session():
|
print("Invalid input. Please enter 'y' or 'n'.")
|
||||||
return aiohttp.ClientSession()
|
|
||||||
|
|
||||||
|
async def pair_device():
|
||||||
async def pair_device(web_session: aiohttp.ClientSession):
|
try:
|
||||||
try:
|
lounge_controller = ytlounge.YtLoungeApi("iSponsorBlockTV")
|
||||||
lounge_controller = ytlounge.YtLoungeApi()
|
pairing_code = input(PAIRING_CODE_PROMPT)
|
||||||
await lounge_controller.change_web_session(web_session)
|
pairing_code = int(
|
||||||
pairing_code = input(PAIRING_CODE_PROMPT)
|
pairing_code.replace("-", "").replace(" ", "")
|
||||||
pairing_code = int(
|
) # remove dashes and spaces
|
||||||
pairing_code.replace("-", "").replace(" ", "")
|
print("Pairing...")
|
||||||
) # remove dashes and spaces
|
paired = await lounge_controller.pair(pairing_code)
|
||||||
print("Pairing...")
|
if not paired:
|
||||||
paired = await lounge_controller.pair(pairing_code)
|
print("Failed to pair device")
|
||||||
if not paired:
|
return
|
||||||
print("Failed to pair device")
|
device = {
|
||||||
return
|
"screen_id": lounge_controller.auth.screen_id,
|
||||||
device = {
|
"name": lounge_controller.screen_name,
|
||||||
"screen_id": lounge_controller.auth.screen_id,
|
}
|
||||||
"name": lounge_controller.screen_name,
|
print(f"Paired device: {device['name']}")
|
||||||
}
|
return device
|
||||||
print(f"Paired device: {device['name']}")
|
except Exception as e:
|
||||||
return device
|
print(f"Failed to pair device: {e}")
|
||||||
except Exception as e:
|
return
|
||||||
print(f"Failed to pair device: {e}")
|
|
||||||
return
|
|
||||||
|
def main(config, debug: bool) -> None:
|
||||||
|
print("Welcome to the iSponsorBlockTV cli setup wizard")
|
||||||
def main(config, debug: bool) -> None:
|
loop = asyncio.get_event_loop_policy().get_event_loop()
|
||||||
print("Welcome to the iSponsorBlockTV cli setup wizard")
|
web_session = aiohttp.ClientSession()
|
||||||
loop = asyncio.get_event_loop_policy().get_event_loop()
|
if debug:
|
||||||
web_session = loop.run_until_complete(create_web_session())
|
loop.set_debug(True)
|
||||||
if debug:
|
asyncio.set_event_loop(loop)
|
||||||
loop.set_debug(True)
|
if hasattr(config, "atvs"):
|
||||||
asyncio.set_event_loop(loop)
|
print(
|
||||||
if hasattr(config, "atvs"):
|
"The atvs config option is deprecated and has stopped working. Please read"
|
||||||
print(
|
" this for more information on how to upgrade to V2:"
|
||||||
"The atvs config option is deprecated and has stopped working. Please read"
|
" \nhttps://github.com/dmunozv04/iSponsorBlockTV/wiki/Migrate-from-V1-to-V2"
|
||||||
" this for more information on how to upgrade to V2:"
|
)
|
||||||
" \nhttps://github.com/dmunozv04/iSponsorBlockTV/wiki/Migrate-from-V1-to-V2"
|
choice = get_yn_input(ATVS_REMOVAL_PROMPT)
|
||||||
)
|
if choice == "y":
|
||||||
choice = get_yn_input(ATVS_REMOVAL_PROMPT)
|
del config["atvs"]
|
||||||
if choice == "y":
|
|
||||||
del config["atvs"]
|
devices = config.devices
|
||||||
|
choice = get_yn_input(ADD_MORE_DEVICES_PROMPT.format(num_devices=len(devices)))
|
||||||
devices = config.devices
|
while choice == "y":
|
||||||
choice = get_yn_input(ADD_MORE_DEVICES_PROMPT.format(num_devices=len(devices)))
|
task = loop.create_task(pair_device())
|
||||||
while choice == "y":
|
loop.run_until_complete(task)
|
||||||
device = loop.run_until_complete(pair_device(web_session))
|
device = task.result()
|
||||||
if device:
|
if device:
|
||||||
devices.append(device)
|
devices.append(device)
|
||||||
choice = get_yn_input(ADD_MORE_DEVICES_PROMPT.format(num_devices=len(devices)))
|
choice = get_yn_input(ADD_MORE_DEVICES_PROMPT.format(num_devices=len(devices)))
|
||||||
config.devices = devices
|
config.devices = devices
|
||||||
|
|
||||||
apikey = config.apikey
|
apikey = config.apikey
|
||||||
if apikey:
|
if apikey:
|
||||||
choice = get_yn_input(CHANGE_API_KEY_PROMPT)
|
choice = get_yn_input(CHANGE_API_KEY_PROMPT)
|
||||||
if choice == "y":
|
if choice == "y":
|
||||||
apikey = input(ENTER_API_KEY_PROMPT)
|
apikey = input(ENTER_API_KEY_PROMPT)
|
||||||
else:
|
else:
|
||||||
choice = get_yn_input(ADD_API_KEY_PROMPT)
|
choice = get_yn_input(ADD_API_KEY_PROMPT)
|
||||||
if choice == "y":
|
if choice == "y":
|
||||||
print(
|
print(
|
||||||
"Get youtube apikey here:"
|
"Get youtube apikey here:"
|
||||||
" https://developers.google.com/youtube/registering_an_application"
|
" https://developers.google.com/youtube/registering_an_application"
|
||||||
)
|
)
|
||||||
apikey = input(ENTER_API_KEY_PROMPT)
|
apikey = input(ENTER_API_KEY_PROMPT)
|
||||||
config.apikey = apikey
|
config.apikey = apikey
|
||||||
|
|
||||||
skip_categories = config.skip_categories
|
skip_categories = config.skip_categories
|
||||||
if skip_categories:
|
if skip_categories:
|
||||||
choice = get_yn_input(CHANGE_SKIP_CATEGORIES_PROMPT)
|
choice = get_yn_input(CHANGE_SKIP_CATEGORIES_PROMPT)
|
||||||
if choice == "y":
|
if choice == "y":
|
||||||
categories = input(ENTER_SKIP_CATEGORIES_PROMPT)
|
categories = input(ENTER_SKIP_CATEGORIES_PROMPT)
|
||||||
skip_categories = categories.replace(",", " ").split(" ")
|
skip_categories = categories.replace(",", " ").split(" ")
|
||||||
skip_categories = [
|
skip_categories = [
|
||||||
x for x in skip_categories if x != ""
|
x for x in skip_categories if x != ""
|
||||||
] # Remove empty strings
|
] # Remove empty strings
|
||||||
else:
|
else:
|
||||||
categories = input(ENTER_SKIP_CATEGORIES_PROMPT)
|
categories = input(ENTER_SKIP_CATEGORIES_PROMPT)
|
||||||
skip_categories = categories.replace(",", " ").split(" ")
|
skip_categories = categories.replace(",", " ").split(" ")
|
||||||
skip_categories = [
|
skip_categories = [
|
||||||
x for x in skip_categories if x != ""
|
x for x in skip_categories if x != ""
|
||||||
] # Remove empty strings
|
] # Remove empty strings
|
||||||
config.skip_categories = skip_categories
|
config.skip_categories = skip_categories
|
||||||
|
|
||||||
channel_whitelist = config.channel_whitelist
|
channel_whitelist = config.channel_whitelist
|
||||||
choice = get_yn_input(WHITELIST_CHANNELS_PROMPT)
|
choice = get_yn_input(WHITELIST_CHANNELS_PROMPT)
|
||||||
if choice == "y":
|
if choice == "y":
|
||||||
if not apikey:
|
if not apikey:
|
||||||
print(
|
print(
|
||||||
"WARNING: You need to specify an API key to use this function,"
|
"WARNING: You need to specify an API key to use this function,"
|
||||||
" otherwise the program will fail to start.\nYou can add one by"
|
" otherwise the program will fail to start.\nYou can add one by"
|
||||||
" re-running this setup wizard."
|
" re-running this setup wizard."
|
||||||
)
|
)
|
||||||
api_helper = api_helpers.ApiHelper(config, web_session)
|
api_helper = api_helpers.ApiHelper(config, web_session)
|
||||||
while True:
|
while True:
|
||||||
channel_info = {}
|
channel_info = {}
|
||||||
channel = input(SEARCH_CHANNEL_PROMPT)
|
channel = input(SEARCH_CHANNEL_PROMPT)
|
||||||
if channel == "/exit":
|
if channel == "/exit":
|
||||||
break
|
break
|
||||||
|
|
||||||
task = loop.create_task(
|
task = loop.create_task(
|
||||||
api_helper.search_channels(channel, apikey, web_session)
|
api_helper.search_channels(channel, apikey, web_session)
|
||||||
)
|
)
|
||||||
loop.run_until_complete(task)
|
loop.run_until_complete(task)
|
||||||
results = task.result()
|
results = task.result()
|
||||||
if len(results) == 0:
|
if len(results) == 0:
|
||||||
print("No channels found")
|
print("No channels found")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for i, item in enumerate(results):
|
for i, item in enumerate(results):
|
||||||
print(f"{i}: {item[1]} - Subs: {item[2]}")
|
print(f"{i}: {item[1]} - Subs: {item[2]}")
|
||||||
print("5: Enter a custom channel ID")
|
print("5: Enter a custom channel ID")
|
||||||
print("6: Go back")
|
print("6: Go back")
|
||||||
|
|
||||||
while choice := input(SELECT_CHANNEL_PROMPT):
|
while choice := input(SELECT_CHANNEL_PROMPT):
|
||||||
if choice in [str(x) for x in range(7)]:
|
if choice in [str(x) for x in range(7)]:
|
||||||
break
|
break
|
||||||
print("Invalid choice")
|
print("Invalid choice")
|
||||||
|
|
||||||
if choice == "5":
|
if choice == "5":
|
||||||
channel_info["id"] = input(ENTER_CHANNEL_ID_PROMPT)
|
channel_info["id"] = input(ENTER_CHANNEL_ID_PROMPT)
|
||||||
channel_info["name"] = input(ENTER_CUSTOM_CHANNEL_NAME_PROMPT)
|
channel_info["name"] = input(ENTER_CUSTOM_CHANNEL_NAME_PROMPT)
|
||||||
channel_whitelist.append(channel_info)
|
channel_whitelist.append(channel_info)
|
||||||
continue
|
continue
|
||||||
if choice == "6":
|
if choice == "6":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
channel_info["id"] = results[int(choice)][0]
|
channel_info["id"] = results[int(choice)][0]
|
||||||
channel_info["name"] = results[int(choice)][1]
|
channel_info["name"] = results[int(choice)][1]
|
||||||
channel_whitelist.append(channel_info)
|
channel_whitelist.append(channel_info)
|
||||||
# Close web session asynchronously
|
# Close web session asynchronously
|
||||||
|
|
||||||
config.channel_whitelist = channel_whitelist
|
config.channel_whitelist = channel_whitelist
|
||||||
|
|
||||||
choice = get_yn_input(REPORT_SKIPPED_SEGMENTS_PROMPT)
|
choice = get_yn_input(REPORT_SKIPPED_SEGMENTS_PROMPT)
|
||||||
config.skip_count_tracking = choice != "n"
|
config.skip_count_tracking = choice != "n"
|
||||||
|
|
||||||
choice = get_yn_input(MUTE_ADS_PROMPT)
|
choice = get_yn_input(MUTE_ADS_PROMPT)
|
||||||
config.mute_ads = choice == "y"
|
config.mute_ads = choice == "y"
|
||||||
|
|
||||||
choice = get_yn_input(SKIP_ADS_PROMPT)
|
choice = get_yn_input(SKIP_ADS_PROMPT)
|
||||||
config.skip_ads = choice == "y"
|
config.skip_ads = choice == "y"
|
||||||
|
|
||||||
choice = get_yn_input(AUTOPLAY_PROMPT)
|
choice = get_yn_input(AUTOPLAY_PROMPT)
|
||||||
config.auto_play = choice != "n"
|
config.auto_play = choice != "n"
|
||||||
|
|
||||||
print("Config finished")
|
api_server = input(ENTER_API_SERVER_PROMPT)
|
||||||
config.save()
|
if api_server:
|
||||||
loop.run_until_complete(web_session.close())
|
config.api_server = api_server
|
||||||
|
|
||||||
|
print("Config finished")
|
||||||
|
config.save()
|
||||||
|
loop.run_until_complete(web_session.close())
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ userAgent = "iSponsorBlockTV/0.1"
|
|||||||
SponsorBlock_service = "youtube"
|
SponsorBlock_service = "youtube"
|
||||||
SponsorBlock_actiontype = "skip"
|
SponsorBlock_actiontype = "skip"
|
||||||
|
|
||||||
SponsorBlock_api = "https://sponsor.ajay.app/api/"
|
|
||||||
Youtube_api = "https://www.googleapis.com/youtube/v3/"
|
Youtube_api = "https://www.googleapis.com/youtube/v3/"
|
||||||
|
|
||||||
skip_categories = (
|
skip_categories = (
|
||||||
@@ -20,5 +19,4 @@ skip_categories = (
|
|||||||
|
|
||||||
youtube_client_blacklist = ["TVHTML5_FOR_KIDS"]
|
youtube_client_blacklist = ["TVHTML5_FOR_KIDS"]
|
||||||
|
|
||||||
|
|
||||||
config_file_blacklist_keys = ["config_file", "data_dir"]
|
config_file_blacklist_keys = ["config_file", "data_dir"]
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
"""Send out an M-SEARCH request and listening for responses."""
|
"""Send out an M-SEARCH request and listening for responses."""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class Config:
|
|||||||
self.mute_ads = False
|
self.mute_ads = False
|
||||||
self.skip_ads = False
|
self.skip_ads = False
|
||||||
self.auto_play = True
|
self.auto_play = True
|
||||||
self.join_name = "iSponsorBlockTV"
|
self.api_server = "https://sponsor.ajay.app"
|
||||||
self.__load()
|
self.__load()
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ class DeviceListener:
|
|||||||
logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
||||||
)
|
)
|
||||||
self.logger.addHandler(sh)
|
self.logger.addHandler(sh)
|
||||||
self.logger.info("Starting device")
|
self.logger.info(f"Starting device")
|
||||||
self.lounge_controller = ytlounge.YtLoungeApi(
|
self.lounge_controller = ytlounge.YtLoungeApi(
|
||||||
device.screen_id, config, api_helper, self.logger
|
device.screen_id, config, api_helper, self.logger, self.web_session
|
||||||
)
|
)
|
||||||
|
|
||||||
# Ensures that we have a valid auth token
|
# Ensures that we have a valid auth token
|
||||||
@@ -38,14 +38,14 @@ class DeviceListener:
|
|||||||
await asyncio.sleep(60 * 60 * 24) # Refresh every 24 hours
|
await asyncio.sleep(60 * 60 * 24) # Refresh every 24 hours
|
||||||
try:
|
try:
|
||||||
await self.lounge_controller.refresh_auth()
|
await self.lounge_controller.refresh_auth()
|
||||||
except BaseException:
|
except:
|
||||||
# traceback.print_exc()
|
# traceback.print_exc()
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def is_available(self):
|
async def is_available(self):
|
||||||
try:
|
try:
|
||||||
return await self.lounge_controller.is_available()
|
return await self.lounge_controller.is_available()
|
||||||
except BaseException:
|
except:
|
||||||
# traceback.print_exc()
|
# traceback.print_exc()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -57,20 +57,20 @@ class DeviceListener:
|
|||||||
try:
|
try:
|
||||||
self.logger.debug("Refreshing auth")
|
self.logger.debug("Refreshing auth")
|
||||||
await lounge_controller.refresh_auth()
|
await lounge_controller.refresh_auth()
|
||||||
except BaseException:
|
except:
|
||||||
await asyncio.sleep(10)
|
await asyncio.sleep(10)
|
||||||
while not (await self.is_available()) and not self.cancelled:
|
while not (await self.is_available()) and not self.cancelled:
|
||||||
await asyncio.sleep(10)
|
await asyncio.sleep(10)
|
||||||
try:
|
try:
|
||||||
await lounge_controller.connect()
|
await lounge_controller.connect()
|
||||||
except BaseException:
|
except:
|
||||||
pass
|
pass
|
||||||
while not lounge_controller.connected() and not self.cancelled:
|
while not lounge_controller.connected() and not self.cancelled:
|
||||||
# Doesn't connect to the device if it's a kids profile (it's broken)
|
# Doesn't connect to the device if it's a kids profile (it's broken)
|
||||||
await asyncio.sleep(10)
|
await asyncio.sleep(10)
|
||||||
try:
|
try:
|
||||||
await lounge_controller.connect()
|
await lounge_controller.connect()
|
||||||
except BaseException:
|
except:
|
||||||
pass
|
pass
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
"Connected to device %s (%s)", lounge_controller.screen_name, self.name
|
"Connected to device %s (%s)", lounge_controller.screen_name, self.name
|
||||||
@@ -79,14 +79,14 @@ class DeviceListener:
|
|||||||
self.logger.info("Subscribing to lounge")
|
self.logger.info("Subscribing to lounge")
|
||||||
sub = await lounge_controller.subscribe_monitored(self)
|
sub = await lounge_controller.subscribe_monitored(self)
|
||||||
await sub
|
await sub
|
||||||
except BaseException:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Method called on playback state change
|
# Method called on playback state change
|
||||||
async def __call__(self, state):
|
async def __call__(self, state):
|
||||||
try:
|
try:
|
||||||
self.task.cancel()
|
self.task.cancel()
|
||||||
except BaseException:
|
except:
|
||||||
pass
|
pass
|
||||||
time_start = time.time()
|
time_start = time.time()
|
||||||
self.task = asyncio.create_task(self.process_playstatus(state, time_start))
|
self.task = asyncio.create_task(self.process_playstatus(state, time_start))
|
||||||
@@ -131,71 +131,40 @@ class DeviceListener:
|
|||||||
await asyncio.create_task(self.api_helper.mark_viewed_segments(uuids))
|
await asyncio.create_task(self.api_helper.mark_viewed_segments(uuids))
|
||||||
await asyncio.create_task(self.lounge_controller.seek_to(position))
|
await asyncio.create_task(self.lounge_controller.seek_to(position))
|
||||||
|
|
||||||
|
# Stops the connection to the device
|
||||||
async def cancel(self):
|
async def cancel(self):
|
||||||
self.cancelled = True
|
self.cancelled = True
|
||||||
await self.lounge_controller.disconnect()
|
try:
|
||||||
if self.task:
|
|
||||||
self.task.cancel()
|
self.task.cancel()
|
||||||
if self.lounge_controller.subscribe_task_watchdog:
|
except Exception:
|
||||||
self.lounge_controller.subscribe_task_watchdog.cancel()
|
pass
|
||||||
if self.lounge_controller.subscribe_task:
|
|
||||||
self.lounge_controller.subscribe_task.cancel()
|
|
||||||
await asyncio.gather(
|
|
||||||
self.task,
|
|
||||||
self.lounge_controller.subscribe_task_watchdog,
|
|
||||||
self.lounge_controller.subscribe_task,
|
|
||||||
return_exceptions=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
async def initialize_web_session(self):
|
|
||||||
await self.lounge_controller.change_web_session(self.web_session)
|
|
||||||
|
|
||||||
|
|
||||||
async def finish(devices, web_session, tcp_connector):
|
async def finish(devices):
|
||||||
await asyncio.gather(
|
for i in devices:
|
||||||
*(device.cancel() for device in devices), return_exceptions=True
|
await i.cancel()
|
||||||
)
|
|
||||||
await web_session.close()
|
|
||||||
await tcp_connector.close()
|
|
||||||
|
|
||||||
|
|
||||||
def handle_signal(signum, frame):
|
def main(config, debug):
|
||||||
raise KeyboardInterrupt()
|
|
||||||
|
|
||||||
|
|
||||||
async def main_async(config, debug):
|
|
||||||
loop = asyncio.get_event_loop_policy().get_event_loop()
|
loop = asyncio.get_event_loop_policy().get_event_loop()
|
||||||
tasks = [] # Save the tasks so the interpreter doesn't garbage collect them
|
tasks = [] # Save the tasks so the interpreter doesn't garbage collect them
|
||||||
devices = [] # Save the devices to close them later
|
devices = [] # Save the devices to close them later
|
||||||
if debug:
|
if debug:
|
||||||
loop.set_debug(True)
|
loop.set_debug(True)
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
tcp_connector = aiohttp.TCPConnector(ttl_dns_cache=300)
|
tcp_connector = aiohttp.TCPConnector(ttl_dns_cache=300)
|
||||||
web_session = aiohttp.ClientSession(connector=tcp_connector)
|
web_session = aiohttp.ClientSession(loop=loop, 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:
|
||||||
device = DeviceListener(api_helper, config, i, debug, web_session)
|
device = DeviceListener(api_helper, config, i, debug, web_session)
|
||||||
devices.append(device)
|
devices.append(device)
|
||||||
await device.initialize_web_session()
|
|
||||||
tasks.append(loop.create_task(device.loop()))
|
tasks.append(loop.create_task(device.loop()))
|
||||||
tasks.append(loop.create_task(device.refresh_auth_loop()))
|
tasks.append(loop.create_task(device.refresh_auth_loop()))
|
||||||
signal(SIGTERM, handle_signal)
|
signal(SIGINT, lambda s, f: loop.stop())
|
||||||
signal(SIGINT, handle_signal)
|
signal(SIGTERM, lambda s, f: loop.stop())
|
||||||
try:
|
loop.run_forever()
|
||||||
await asyncio.gather(*tasks)
|
print("Cancelling tasks and exiting...")
|
||||||
except KeyboardInterrupt:
|
loop.run_until_complete(finish(devices))
|
||||||
print("Cancelling tasks and exiting...")
|
loop.run_until_complete(web_session.close())
|
||||||
await finish(devices, web_session, tcp_connector)
|
loop.run_until_complete(tcp_connector.close())
|
||||||
for task in tasks:
|
|
||||||
task.cancel()
|
|
||||||
await asyncio.gather(*tasks, return_exceptions=True)
|
|
||||||
finally:
|
|
||||||
await web_session.close()
|
|
||||||
await tcp_connector.close()
|
|
||||||
loop.close()
|
|
||||||
print("Exited")
|
|
||||||
|
|
||||||
|
|
||||||
def main(config, debug):
|
|
||||||
loop = asyncio.get_event_loop()
|
|
||||||
loop.run_until_complete(main_async(config, debug))
|
|
||||||
loop.close()
|
loop.close()
|
||||||
|
|||||||
@@ -21,13 +21,9 @@
|
|||||||
scrollbar-gutter: stable;
|
scrollbar-gutter: stable;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-small {
|
.small-button{
|
||||||
height: 3;
|
height: 3;
|
||||||
border: none;
|
|
||||||
border-top: none;
|
|
||||||
border-bottom: none;
|
|
||||||
offset: 0 -1;
|
|
||||||
padding: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-100 {
|
.button-100 {
|
||||||
@@ -110,14 +106,13 @@ EditDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Element {
|
Element {
|
||||||
background: $panel-darken-1;
|
background: $panel;
|
||||||
border-top: solid $panel-lighten-2;
|
border-top: solid $panel-lighten-2;
|
||||||
layout: horizontal;
|
layout: horizontal;
|
||||||
height: 2;
|
height: 2;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0 1 0 1;
|
margin: 0 1 0 1;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
Element > .element-name {
|
Element > .element-name {
|
||||||
offset: 0 -1;
|
offset: 0 -1;
|
||||||
@@ -125,11 +120,7 @@ Element > .element-name {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
align: left middle;
|
align: left middle;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
background: $panel-darken-1;
|
|
||||||
&:hover {
|
|
||||||
background: $panel-lighten-1;
|
|
||||||
border-top: tall $panel-lighten-3;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Element > .element-remove {
|
Element > .element-remove {
|
||||||
dock: right;
|
dock: right;
|
||||||
@@ -141,7 +132,7 @@ Element > .element-remove {
|
|||||||
margin: 0 1 0 0;
|
margin: 0 1 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#add-device, #add-channel {
|
#add-device {
|
||||||
text-style: bold;
|
text-style: bold;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
align: left middle;
|
align: left middle;
|
||||||
@@ -149,11 +140,6 @@ Element > .element-remove {
|
|||||||
dock: left;
|
dock: left;
|
||||||
|
|
||||||
text-align: left;
|
text-align: left;
|
||||||
background: $panel-darken-1;
|
|
||||||
&:hover {
|
|
||||||
background: $panel-lighten-1;
|
|
||||||
border-top: tall $panel-lighten-3;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#add-device-button-container{
|
#add-device-button-container{
|
||||||
height: 1;
|
height: 1;
|
||||||
|
|||||||
@@ -84,15 +84,12 @@ class Element(Static):
|
|||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
yield Button(
|
yield Button(
|
||||||
label=self.element_name,
|
label=self.element_name,
|
||||||
classes="element-name button-small",
|
classes="element-name",
|
||||||
disabled=True,
|
disabled=True,
|
||||||
id="element-name",
|
id="element-name",
|
||||||
)
|
)
|
||||||
yield Button(
|
yield Button(
|
||||||
"Remove",
|
"Remove", classes="element-remove", variant="error", id="element-remove"
|
||||||
classes="element-remove button-small",
|
|
||||||
variant="error",
|
|
||||||
id="element-remove",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def on_mount(self) -> None:
|
def on_mount(self) -> None:
|
||||||
@@ -105,6 +102,7 @@ class Device(Element):
|
|||||||
"""A device element."""
|
"""A device element."""
|
||||||
|
|
||||||
def process_values_from_data(self):
|
def process_values_from_data(self):
|
||||||
|
print(self.element_data)
|
||||||
if "name" in self.element_data and self.element_data["name"]:
|
if "name" in self.element_data and self.element_data["name"]:
|
||||||
self.element_name = self.element_data["name"]
|
self.element_name = self.element_data["name"]
|
||||||
else:
|
else:
|
||||||
@@ -287,8 +285,7 @@ class AddDevice(ModalWithClickExit):
|
|||||||
" computer\nIf it isn't showing up, try restarting the"
|
" computer\nIf it isn't showing up, try restarting the"
|
||||||
" app.\nIf running in docker, make sure to use"
|
" app.\nIf running in docker, make sure to use"
|
||||||
" `--network=host`\nTo refresh the list, close and open the"
|
" `--network=host`\nTo refresh the list, close and open the"
|
||||||
" dialog again\n[b][u]If it still doesn't work, "
|
" dialog again"
|
||||||
"pair using a pairing code (it's much more reliable)"
|
|
||||||
),
|
),
|
||||||
classes="subtitle",
|
classes="subtitle",
|
||||||
)
|
)
|
||||||
@@ -331,18 +328,17 @@ class AddDevice(ModalWithClickExit):
|
|||||||
|
|
||||||
@on(Input.Changed, "#pairing-code-input")
|
@on(Input.Changed, "#pairing-code-input")
|
||||||
def changed_pairing_code(self, event: Input.Changed):
|
def changed_pairing_code(self, event: Input.Changed):
|
||||||
self.query_one(
|
self.query_one("#add-device-pin-add-button").disabled = (
|
||||||
"#add-device-pin-add-button"
|
not event.validation_result.is_valid
|
||||||
).disabled = not event.validation_result.is_valid
|
)
|
||||||
|
|
||||||
@on(Input.Submitted, "#pairing-code-input")
|
@on(Input.Submitted, "#pairing-code-input")
|
||||||
@on(Button.Pressed, "#add-device-pin-add-button")
|
@on(Button.Pressed, "#add-device-pin-add-button")
|
||||||
async def handle_add_device_pin(self) -> None:
|
async def handle_add_device_pin(self) -> None:
|
||||||
self.query_one("#add-device-pin-add-button").disabled = True
|
self.query_one("#add-device-pin-add-button").disabled = True
|
||||||
lounge_controller = ytlounge.YtLoungeApi(
|
lounge_controller = ytlounge.YtLoungeApi(
|
||||||
"iSponsorBlockTV",
|
"iSponsorBlockTV", web_session=self.web_session
|
||||||
)
|
)
|
||||||
await lounge_controller.change_web_session(self.web_session)
|
|
||||||
pairing_code = self.query_one("#pairing-code-input").value
|
pairing_code = self.query_one("#pairing-code-input").value
|
||||||
pairing_code = int(
|
pairing_code = int(
|
||||||
pairing_code.replace("-", "").replace(" ", "")
|
pairing_code.replace("-", "").replace(" ", "")
|
||||||
@@ -351,7 +347,7 @@ class AddDevice(ModalWithClickExit):
|
|||||||
paired = False
|
paired = False
|
||||||
try:
|
try:
|
||||||
paired = await lounge_controller.pair(pairing_code)
|
paired = await lounge_controller.pair(pairing_code)
|
||||||
except BaseException:
|
except:
|
||||||
pass
|
pass
|
||||||
if paired:
|
if paired:
|
||||||
device = {
|
device = {
|
||||||
@@ -381,9 +377,9 @@ class AddDevice(ModalWithClickExit):
|
|||||||
|
|
||||||
@on(SelectionList.SelectedChanged, "#dial-devices-list")
|
@on(SelectionList.SelectedChanged, "#dial-devices-list")
|
||||||
def changed_device_list(self, event: SelectionList.SelectedChanged):
|
def changed_device_list(self, event: SelectionList.SelectedChanged):
|
||||||
self.query_one(
|
self.query_one("#add-device-dial-add-button").disabled = (
|
||||||
"#add-device-dial-add-button"
|
not event.selection_list.selected
|
||||||
).disabled = not event.selection_list.selected
|
)
|
||||||
|
|
||||||
|
|
||||||
class AddChannel(ModalWithClickExit):
|
class AddChannel(ModalWithClickExit):
|
||||||
@@ -480,6 +476,7 @@ class AddChannel(ModalWithClickExit):
|
|||||||
|
|
||||||
@on(Button.Pressed, "#add-channel-switch-buttons > *")
|
@on(Button.Pressed, "#add-channel-switch-buttons > *")
|
||||||
def handle_switch_buttons(self, event: Button.Pressed) -> None:
|
def handle_switch_buttons(self, event: Button.Pressed) -> None:
|
||||||
|
button_ = event.button.id
|
||||||
self.query_one("#add-channel-switcher").current = event.button.id.replace(
|
self.query_one("#add-channel-switcher").current = event.button.id.replace(
|
||||||
"-button", "-container"
|
"-button", "-container"
|
||||||
)
|
)
|
||||||
@@ -499,7 +496,7 @@ class AddChannel(ModalWithClickExit):
|
|||||||
self.query_one("#channel-search-results").remove_children()
|
self.query_one("#channel-search-results").remove_children()
|
||||||
try:
|
try:
|
||||||
channels_list = await self.api_helper.search_channels(channel_name)
|
channels_list = await self.api_helper.search_channels(channel_name)
|
||||||
except BaseException:
|
except:
|
||||||
self.query_one("#add-channel-info").update(
|
self.query_one("#add-channel-info").update(
|
||||||
"[#ff0000]Failed to search for channel"
|
"[#ff0000]Failed to search for channel"
|
||||||
)
|
)
|
||||||
@@ -588,7 +585,7 @@ class EditDevice(ModalWithClickExit):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def on_slider_changed(self, event: Slider.Changed) -> None:
|
def on_slider_changed(self, event: Slider.Changed) -> None:
|
||||||
offset_input = self.query_one("#device-offset-input")
|
offset_input = self.query_one("#device-offset-offset_input")
|
||||||
with offset_input.prevent(Input.Changed):
|
with offset_input.prevent(Input.Changed):
|
||||||
offset_input.value = str(event.slider.value)
|
offset_input.value = str(event.slider.value)
|
||||||
|
|
||||||
@@ -622,9 +619,7 @@ class DevicesManager(Vertical):
|
|||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
yield Label("Devices", classes="title")
|
yield Label("Devices", classes="title")
|
||||||
with Horizontal(id="add-device-button-container"):
|
with Horizontal(id="add-device-button-container"):
|
||||||
yield Button(
|
yield Button("Add Device", id="add-device", classes="button-100")
|
||||||
"Add Device", id="add-device", classes="button-100 button-small"
|
|
||||||
)
|
|
||||||
for device in self.devices:
|
for device in self.devices:
|
||||||
yield Device(device, tooltip="Click to edit")
|
yield Device(device, tooltip="Click to edit")
|
||||||
|
|
||||||
@@ -818,9 +813,7 @@ class ChannelWhitelistManager(Vertical):
|
|||||||
id="warning-no-key",
|
id="warning-no-key",
|
||||||
)
|
)
|
||||||
with Horizontal(id="add-channel-button-container"):
|
with Horizontal(id="add-channel-button-container"):
|
||||||
yield Button(
|
yield Button("Add Channel", id="add-channel", classes="button-100")
|
||||||
"Add Channel", id="add-channel", classes="button-100 button-small"
|
|
||||||
)
|
|
||||||
for channel in self.config.channel_whitelist:
|
for channel in self.config.channel_whitelist:
|
||||||
yield Channel(channel)
|
yield Channel(channel)
|
||||||
|
|
||||||
@@ -883,6 +876,31 @@ class AutoPlayManager(Vertical):
|
|||||||
self.config.auto_play = event.checkbox.value
|
self.config.auto_play = event.checkbox.value
|
||||||
|
|
||||||
|
|
||||||
|
class ApiServerManager(Vertical):
|
||||||
|
"""Manager for the custom API server URL."""
|
||||||
|
|
||||||
|
def __init__(self, config, **kwargs) -> None:
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.config = config
|
||||||
|
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
yield Label("Custom API Server", classes="title")
|
||||||
|
yield Label(
|
||||||
|
"You can specify a custom SponsorBlock API server URL here.",
|
||||||
|
classes="subtitle",
|
||||||
|
)
|
||||||
|
with Grid(id="api-server-grid"):
|
||||||
|
yield Input(
|
||||||
|
placeholder="Custom API Server URL",
|
||||||
|
id="api-server-input",
|
||||||
|
value=self.config.api_server,
|
||||||
|
)
|
||||||
|
|
||||||
|
@on(Input.Changed, "#api-server-input")
|
||||||
|
def changed_api_server(self, event: Input.Changed):
|
||||||
|
self.config.api_server = event.input.value
|
||||||
|
|
||||||
|
|
||||||
class ISponsorBlockTVSetupMainScreen(Screen):
|
class ISponsorBlockTVSetupMainScreen(Screen):
|
||||||
"""Making this a separate screen to avoid a bug: https://github.com/Textualize/textual/issues/3221"""
|
"""Making this a separate screen to avoid a bug: https://github.com/Textualize/textual/issues/3221"""
|
||||||
|
|
||||||
@@ -922,6 +940,9 @@ class ISponsorBlockTVSetupMainScreen(Screen):
|
|||||||
yield AutoPlayManager(
|
yield AutoPlayManager(
|
||||||
config=self.config, id="autoplay-manager", classes="container"
|
config=self.config, id="autoplay-manager", classes="container"
|
||||||
)
|
)
|
||||||
|
yield ApiServerManager(
|
||||||
|
config=self.config, id="api-server-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():
|
||||||
@@ -950,7 +971,7 @@ class ISponsorBlockTVSetupMainScreen(Screen):
|
|||||||
self.app.query_one("#warning-no-key").display = (
|
self.app.query_one("#warning-no-key").display = (
|
||||||
not event.input.value
|
not event.input.value
|
||||||
) and self.config.channel_whitelist
|
) and self.config.channel_whitelist
|
||||||
except BaseException:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
from typing import Any, List
|
|
||||||
|
|
||||||
import pyytlounge
|
import pyytlounge
|
||||||
from aiohttp import ClientSession
|
from aiohttp import ClientSession
|
||||||
@@ -13,14 +12,15 @@ create_task = asyncio.create_task
|
|||||||
class YtLoungeApi(pyytlounge.YtLoungeApi):
|
class YtLoungeApi(pyytlounge.YtLoungeApi):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
screen_id=None,
|
screen_id,
|
||||||
config=None,
|
config=None,
|
||||||
api_helper=None,
|
api_helper=None,
|
||||||
logger=None,
|
logger=None,
|
||||||
|
web_session: ClientSession = None,
|
||||||
):
|
):
|
||||||
super().__init__(
|
super().__init__("iSponsorBlockTV", logger=logger)
|
||||||
config.join_name if config else "iSponsorBlockTV", logger=logger
|
if web_session is not None:
|
||||||
)
|
self.session = web_session # And use the one we passed
|
||||||
self.auth.screen_id = screen_id
|
self.auth.screen_id = screen_id
|
||||||
self.auth.lounge_id_token = None
|
self.auth.lounge_id_token = None
|
||||||
self.api_helper = api_helper
|
self.api_helper = api_helper
|
||||||
@@ -31,7 +31,6 @@ class YtLoungeApi(pyytlounge.YtLoungeApi):
|
|||||||
self.logger = logger
|
self.logger = logger
|
||||||
self.shorts_disconnected = False
|
self.shorts_disconnected = False
|
||||||
self.auto_play = True
|
self.auto_play = True
|
||||||
self.noop_attempted = False # Track if we've already tried noop
|
|
||||||
if config:
|
if config:
|
||||||
self.mute_ads = config.mute_ads
|
self.mute_ads = config.mute_ads
|
||||||
self.skip_ads = config.skip_ads
|
self.skip_ads = config.skip_ads
|
||||||
@@ -43,28 +42,9 @@ class YtLoungeApi(pyytlounge.YtLoungeApi):
|
|||||||
await asyncio.sleep(
|
await asyncio.sleep(
|
||||||
35
|
35
|
||||||
) # YouTube sends at least a message every 30 seconds (no-op or any other)
|
) # YouTube sends at least a message every 30 seconds (no-op or any other)
|
||||||
|
|
||||||
if not self.noop_attempted:
|
|
||||||
# First time the watchdog is triggered, try sending a noop to keep connection alive
|
|
||||||
# YouTube responds with a LoungeStatus event after a noop if it is still connected
|
|
||||||
self.noop_attempted = True
|
|
||||||
self.logger.info("Watchdog triggered - sending noop command")
|
|
||||||
try:
|
|
||||||
await self.noop()
|
|
||||||
self.subscribe_task_watchdog = asyncio.create_task(self._watchdog())
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(f"Error sending noop command: {e}")
|
|
||||||
self._cancel_subscription()
|
|
||||||
else:
|
|
||||||
# If we already tried noop and the watchdog is triggered again, cancel subscription
|
|
||||||
self.logger.warning("Watchdog triggered again after noop attempt, cancelling subscription")
|
|
||||||
self._cancel_subscription()
|
|
||||||
|
|
||||||
def _cancel_subscription(self):
|
|
||||||
try:
|
try:
|
||||||
self.subscribe_task.cancel()
|
self.subscribe_task.cancel()
|
||||||
self.noop_attempted = False
|
except Exception:
|
||||||
except BaseException:
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Subscribe to the lounge and start the watchdog
|
# Subscribe to the lounge and start the watchdog
|
||||||
@@ -72,26 +52,22 @@ class YtLoungeApi(pyytlounge.YtLoungeApi):
|
|||||||
self.callback = callback
|
self.callback = callback
|
||||||
try:
|
try:
|
||||||
self.subscribe_task_watchdog.cancel()
|
self.subscribe_task_watchdog.cancel()
|
||||||
except BaseException:
|
except:
|
||||||
pass # No watchdog task
|
pass # No watchdog task
|
||||||
|
|
||||||
self.noop_attempted = False
|
|
||||||
self.subscribe_task = asyncio.create_task(super().subscribe(callback))
|
self.subscribe_task = asyncio.create_task(super().subscribe(callback))
|
||||||
self.subscribe_task_watchdog = asyncio.create_task(self._watchdog())
|
self.subscribe_task_watchdog = asyncio.create_task(self._watchdog())
|
||||||
return self.subscribe_task
|
return self.subscribe_task
|
||||||
|
|
||||||
# Process a lounge subscription event
|
# Process a lounge subscription event
|
||||||
def _process_event(self, event_type: str, args: List[Any]):
|
def _process_event(self, event_id: int, event_type: str, args):
|
||||||
self.logger.debug(f"process_event({event_type}, {args})")
|
self.logger.debug(f"process_event({event_id}, {event_type}, {args})")
|
||||||
# (Re)start the watchdog and reset noop attempt flag
|
# (Re)start the watchdog
|
||||||
try:
|
try:
|
||||||
self.subscribe_task_watchdog.cancel()
|
self.subscribe_task_watchdog.cancel()
|
||||||
except BaseException:
|
except:
|
||||||
pass
|
pass
|
||||||
finally:
|
finally:
|
||||||
self.noop_attempted = False
|
|
||||||
self.subscribe_task_watchdog = asyncio.create_task(self._watchdog())
|
self.subscribe_task_watchdog = asyncio.create_task(self._watchdog())
|
||||||
|
|
||||||
# A bunch of events useful to detect ads playing, and the next video before it starts playing (that way we
|
# A bunch of events useful to detect ads playing, and the next video before it starts playing (that way we
|
||||||
# can get the segments)
|
# can get the segments)
|
||||||
if event_type == "onStateChange":
|
if event_type == "onStateChange":
|
||||||
@@ -179,7 +155,7 @@ class YtLoungeApi(pyytlounge.YtLoungeApi):
|
|||||||
elif event_type == "onAutoplayModeChanged":
|
elif event_type == "onAutoplayModeChanged":
|
||||||
create_task(self.set_auto_play_mode(self.auto_play))
|
create_task(self.set_auto_play_mode(self.auto_play))
|
||||||
|
|
||||||
super()._process_event(event_type, args)
|
super()._process_event(event_id, event_type, args)
|
||||||
|
|
||||||
# Set the volume to a specific value (0-100)
|
# Set the volume to a specific value (0-100)
|
||||||
async def set_volume(self, volume: int) -> None:
|
async def set_volume(self, volume: int) -> None:
|
||||||
@@ -215,14 +191,3 @@ class YtLoungeApi(pyytlounge.YtLoungeApi):
|
|||||||
async def _command(self, command: str, command_parameters: dict = None) -> bool:
|
async def _command(self, command: str, command_parameters: dict = None) -> bool:
|
||||||
async with self._command_mutex:
|
async with self._command_mutex:
|
||||||
return await super()._command(command, command_parameters)
|
return await super()._command(command, command_parameters)
|
||||||
|
|
||||||
async def change_web_session(self, web_session: ClientSession):
|
|
||||||
if self.session is not None:
|
|
||||||
await self.session.close()
|
|
||||||
if self.conn is not None:
|
|
||||||
await self.conn.close()
|
|
||||||
self.session = web_session
|
|
||||||
|
|
||||||
async def noop(self):
|
|
||||||
# No-op command to keep the connection alive
|
|
||||||
await super()._command("noop")
|
|
||||||
Reference in New Issue
Block a user