Compare commits

...

134 Commits

Author SHA1 Message Date
dmunozv04
ce58552a89 test debug commands 2025-03-08 18:45:26 +01:00
dmunozv04
f486fec0bd Bump version 2025-02-22 00:49:06 +01:00
David
54015cf455 Merge pull request #255 from dmunozv04/fix-add-device
Fix adding devices
2025-02-22 00:48:45 +01:00
pre-commit-ci[bot]
114326e34c [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2025-02-21 23:45:26 +00:00
dmunozv04
3797200825 Fix adding devices 2025-02-22 00:44:29 +01:00
dmunozv04
eafedb7cf7 Move minimum supported version to python 3.9 2025-02-21 10:16:24 +01:00
dmunozv04
1cf539be9a Bump version 2025-02-21 10:15:07 +01:00
David
9d74a9b3ce Merge pull request #77 from boltgolt/feature-device-name
Allow setting device name to custom string
2025-02-19 18:26:12 +01:00
dmunozv04
9b5ea2b243 Merge remote-tracking branch 'origin/main' into pr/boltgolt/77 2025-02-19 10:18:04 +01:00
David
112a4faa50 Merge pull request #239 from dmunozv04/dependabot/pip/aiohttp-3.11.12
Bump aiohttp from 3.9.5 to 3.11.12
2025-02-19 00:29:03 +01:00
dependabot[bot]
027a8d7ebc Bump aiohttp from 3.9.5 to 3.11.12
---
updated-dependencies:
- dependency-name: aiohttp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-19 00:28:52 +01:00
David
a495cdf62e Merge pull request #247 from dmunozv04/dependabot/pip/pyytlounge-2.1.2
Bump pyytlounge from 2.1.1 to 2.1.2
2025-02-19 00:28:24 +01:00
dependabot[bot]
24f1612f20 Bump pyytlounge from 2.1.1 to 2.1.2
Bumps [pyytlounge](https://github.com/FabioGNR/pyytlounge) from 2.1.1 to 2.1.2.
- [Release notes](https://github.com/FabioGNR/pyytlounge/releases)
- [Changelog](https://github.com/FabioGNR/pyytlounge/blob/master/CHANGELOG.md)
- [Commits](https://github.com/FabioGNR/pyytlounge/compare/v2.1.1...v2.1.2)

---
updated-dependencies:
- dependency-name: pyytlounge
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-19 00:28:03 +01:00
David
c979d280a1 Merge pull request #250 from dmunozv04/create-web-session-async 2025-02-19 00:17:34 +01:00
pre-commit-ci[bot]
45bc7ff6e7 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2025-02-18 23:16:48 +00:00
dmunozv04
251a94f147 Make main function asnyc 2025-02-19 00:16:17 +01:00
pre-commit-ci[bot]
0ba8f4c3c5 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2025-02-13 20:44:53 +00:00
boltgolt
2ebc821ed9 Rename proposed device_name setting to join_name 2025-02-13 21:43:35 +01:00
pre-commit-ci[bot]
060fe7af5d [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2025-02-13 20:42:18 +00:00
boltgolt
879116e873 Merge branch 'main' into feature-device-name 2025-02-13 21:41:06 +01:00
David
1914afa432 Merge pull request #242 from dmunozv04/remove-argparse-dep
Remove argparse dependency
2025-02-12 13:49:39 +01:00
dmunozv04
82459c8986 Remove argparse dependency
Fixes argparse dependency unnecessary #195
2025-02-12 13:48:34 +01:00
David
c7cb4e8282 Merge pull request #221 from dmunozv04/dependabot/pip/pyytlounge-2.1.1
Bump pyytlounge from 2.0.0 to 2.1.1
2025-02-12 13:44:29 +01:00
dmunozv04
7ea0b8642c Update ytlounge client
to be compatible with pyytlounge v2.1.1
2025-02-12 13:43:03 +01:00
dependabot[bot]
79fc9c066c Bump pyytlounge from 2.0.0 to 2.1.1
Bumps [pyytlounge](https://github.com/FabioGNR/pyytlounge) from 2.0.0 to 2.1.1.
- [Release notes](https://github.com/FabioGNR/pyytlounge/releases)
- [Changelog](https://github.com/FabioGNR/pyytlounge/blob/master/CHANGELOG.md)
- [Commits](https://github.com/FabioGNR/pyytlounge/compare/v2.0.0...v2.1.1)

---
updated-dependencies:
- dependency-name: pyytlounge
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-12 12:29:42 +00:00
David
2b9e1f8bf4 Merge pull request #227 from dmunozv04/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2025-02-12 13:23:07 +01:00
pre-commit-ci[bot]
f5b183a679 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.8.6 → v0.9.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.6...v0.9.6)
- [github.com/igorshubovych/markdownlint-cli: v0.43.0 → v0.44.0](https://github.com/igorshubovych/markdownlint-cli/compare/v0.43.0...v0.44.0)
2025-02-12 13:22:46 +01:00
David
bececa5096 Merge pull request #234 from dmunozv04/dependabot/pip/textual-slider-0.2.0
Bump textual-slider from 0.1.2 to 0.2.0
2025-02-12 13:21:06 +01:00
dependabot[bot]
4589c612d1 Bump textual-slider from 0.1.2 to 0.2.0
Bumps [textual-slider](https://github.com/TomJGooding/textual-slider) from 0.1.2 to 0.2.0.
- [Changelog](https://github.com/TomJGooding/textual-slider/blob/main/CHANGELOG.md)
- [Commits](https://github.com/TomJGooding/textual-slider/compare/v0.1.2...v0.2.0)

---
updated-dependencies:
- dependency-name: textual-slider
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-12 12:17:23 +00:00
David
26d1d7e481 Merge pull request #230 from dmunozv04/dependabot/pip/textual-1.0.0
Bump textual from 0.58.0 to 1.0.0
2025-02-12 13:16:20 +01:00
dmunozv04
73b277706e Make deepsource happy 2025-02-12 13:09:05 +01:00
pre-commit-ci[bot]
5884844da1 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2025-02-12 12:06:38 +00:00
dmunozv04
38efe843de Update textual styles to work with textual 1.0 2025-02-12 13:06:11 +01:00
dependabot[bot]
ccfcd00aa9 Bump textual from 0.58.0 to 1.0.0
Bumps [textual](https://github.com/Textualize/textual) from 0.58.0 to 1.0.0.
- [Release notes](https://github.com/Textualize/textual/releases)
- [Changelog](https://github.com/Textualize/textual/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Textualize/textual/compare/v0.58.0...v1.0.0)

---
updated-dependencies:
- dependency-name: textual
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-12 11:14:22 +00:00
David
e34dbb0f80 Merge pull request #241 from dmunozv04/aiohttp-3.11-support
Aiohttp-3.11-support
2025-02-12 12:00:42 +01:00
dmunozv04
ae6da834e4 Fix #233 2025-02-12 11:42:38 +01:00
dmunozv04
7e80b41bbb Prepare for aiohttp 3.11 2025-02-12 11:42:16 +01:00
David
3c66dc3607 Merge pull request #232 from dmunozv04/dependabot/docker/python-3.13-alpine3.21
Bump python from 3.11-alpine3.21 to 3.13-alpine3.21
2025-01-30 11:33:56 +01:00
David
0c8427edc0 Update python3.11 references to 3.13 2025-01-30 10:14:27 +01:00
dependabot[bot]
e637e514b2 Bump python from 3.11-alpine3.21 to 3.13-alpine3.21
Bumps python from 3.11-alpine3.21 to 3.13-alpine3.21.

---
updated-dependencies:
- dependency-name: python
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-30 09:12:47 +00:00
David
c8b9fe157b Merge pull request #237 from dmunozv04/improve-exceptions
Improve exceptions
2025-01-30 10:08:13 +01:00
dmunozv04
dca9186d8b migrate to github actions cache 2025-01-24 16:56:31 +01:00
dmunozv04
adc7e1efe9 Pass markdownlint 2025-01-24 16:52:21 +01:00
dmunozv04
b96014840a ignore license on markdownlint 2025-01-24 16:47:26 +01:00
dmunozv04
147004e257 Remove unused variable 2025-01-24 16:44:37 +01:00
dmunozv04
6f4c27c0a5 Improve exceptions 2025-01-24 16:43:39 +01:00
David
f75dff8faf Merge pull request #229 from dmunozv04/dependabot/pip/xmltodict-0.14.2
Bump xmltodict from 0.13.0 to 0.14.2
2025-01-20 23:29:21 +01:00
dependabot[bot]
0e77418c2b Bump xmltodict from 0.13.0 to 0.14.2
Bumps [xmltodict](https://github.com/martinblech/xmltodict) from 0.13.0 to 0.14.2.
- [Changelog](https://github.com/martinblech/xmltodict/blob/master/CHANGELOG.md)
- [Commits](https://github.com/martinblech/xmltodict/compare/v0.13.0...v0.14.2)

---
updated-dependencies:
- dependency-name: xmltodict
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-20 20:21:03 +00:00
David
f1d1787511 Merge pull request #222 from dmunozv04/dependabot/pip/rich-click-1.8.5
Bump rich-click from 1.8.3 to 1.8.5
2025-01-16 19:17:26 +01:00
dependabot[bot]
49b1b902d3 Bump rich-click from 1.8.3 to 1.8.5
Bumps [rich-click](https://github.com/ewels/rich-click) from 1.8.3 to 1.8.5.
- [Release notes](https://github.com/ewels/rich-click/releases)
- [Changelog](https://github.com/ewels/rich-click/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ewels/rich-click/compare/v1.8.3...v1.8.5)

---
updated-dependencies:
- dependency-name: rich-click
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-16 18:09:22 +00:00
David
5116e6c1e0 Merge pull request #224 from dmunozv04/dependabot/pip/rich-13.9.4
Bump rich from 13.7.1 to 13.9.4
2025-01-16 19:08:00 +01:00
dependabot[bot]
0b0a235046 Bump rich from 13.7.1 to 13.9.4
Bumps [rich](https://github.com/Textualize/rich) from 13.7.1 to 13.9.4.
- [Release notes](https://github.com/Textualize/rich/releases)
- [Changelog](https://github.com/Textualize/rich/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Textualize/rich/compare/v13.7.1...v13.9.4)

---
updated-dependencies:
- dependency-name: rich
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-16 18:06:02 +00:00
David
4eaebd3006 Merge pull request #223 from dmunozv04/dependabot/docker/3.11-alpine3.21
Bump python from 3.11-alpine3.19 to 3.11-alpine3.21
2025-01-16 19:04:11 +01:00
David
58e10f0f82 Update base image to 3.11-alpine3.21 2025-01-16 18:59:34 +01:00
dependabot[bot]
a93eeaa1cf Bump python from 3.11-alpine3.19 to 3.13-alpine3.19
Bumps python from 3.11-alpine3.19 to 3.13-alpine3.19.

---
updated-dependencies:
- dependency-name: python
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-16 17:58:37 +00:00
David
77527ce4d5 Merge pull request #225 from dmunozv04/dependabot/pip/textual-slider-0.1.2
Bump textual-slider from 0.1.1 to 0.1.2
2025-01-16 18:56:40 +01:00
dmunozv04
6825ac6629 fix slider 2025-01-16 18:56:17 +01:00
dependabot[bot]
b59135316e Bump textual-slider from 0.1.1 to 0.1.2
Bumps [textual-slider](https://github.com/TomJGooding/textual-slider) from 0.1.1 to 0.1.2.
- [Changelog](https://github.com/TomJGooding/textual-slider/blob/main/CHANGELOG.md)
- [Commits](https://github.com/TomJGooding/textual-slider/commits)

---
updated-dependencies:
- dependency-name: textual-slider
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-16 16:59:21 +00:00
David
7b17d5a2da Merge pull request #220 from dmunozv04/dependabot/github_actions/docker/build-push-action-6
Bump docker/build-push-action from 5 to 6
2025-01-11 11:39:39 +01:00
dependabot[bot]
7e9f53e175 Bump docker/build-push-action from 5 to 6
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5 to 6.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-11 10:35:05 +00:00
David
84c22c2dde Merge pull request #219 from dmunozv04:add-dependabot
Add dependabot.yml
2025-01-09 15:04:33 +01:00
dmunozv04
a7fbcd3dd2 add dependabot.yml 2025-01-09 14:59:38 +01:00
David
dbf5e3ac1c Merge pull request #217 from dmunozv04/close-properly-part-2
Improve exit logic
2025-01-09 14:21:12 +01:00
David
23f65125e2 Merge branch 'main' into close-properly-part-2 2025-01-06 22:17:01 +01:00
David
4feecaa570 Merge pull request #218 from dmunozv04/improve-docker-image-creation
Improve docker image creation (now supports forks)
2025-01-06 19:23:34 +01:00
dmunozv04
52a3f238d6 Also build images on forks 2025-01-06 19:16:17 +01:00
David
afff2a44b9 Merge branch 'main' into close-properly-part-2 2025-01-06 19:09:58 +01:00
pre-commit-ci[bot]
15c165d89a [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2025-01-06 18:09:46 +00:00
David
e71598599a Merge pull request #215 from dmunozv04/pyapp-tests
Fix CI package builds
2025-01-06 19:09:04 +01:00
David
760970a751 Merge branch 'main' into pyapp-tests 2025-01-06 19:08:44 +01:00
David
fb598809da Merge pull request #216 from dmunozv04/update-pre-commit-hooks
Update .pre-commit-config.yaml
2025-01-06 19:08:23 +01:00
pre-commit-ci[bot]
35d13373f9 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2025-01-06 18:06:32 +00:00
David
0145b3ba8d fix indent 2025-01-06 19:06:26 +01:00
David
8fdd13da04 Update .pre-commit-config.yaml
Replace isort, black and pycln with ruff
2025-01-06 19:04:36 +01:00
David
6dcc12baed Merge pull request #131 from dmunozv04/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2025-01-06 18:52:06 +01:00
pre-commit-ci[bot]
afaced8f84 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2025-01-06 17:47:30 +00:00
dmunozv04
b3dd27748b better actions name 2025-01-06 18:35:01 +01:00
dmunozv04
73192a1171 add v1 variant for linux amd64 builds 2025-01-06 18:31:16 +01:00
David
c7dc54fd66 Merge pull request #214 from dmunozv04/update-readme
Add badges to README.md
2025-01-06 18:25:52 +01:00
dmunozv04
edca2bf11a Improve exit logic 2025-01-06 18:23:46 +01:00
pre-commit-ci[bot]
f3d3bdd432 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2025-01-06 17:20:40 +00:00
dmunozv04
67eb40ca9d Add badges to README.md 2025-01-06 18:20:16 +01:00
David
284b7a1d2f Merge pull request #207 from dmunozv04:close-properly
improve exit logic
2024-12-23 23:01:29 +01:00
dmunozv04
ec109e0f10 Fix x86_64 macOS and test aarch64-windows 2024-12-15 14:00:46 +01:00
David
658fad64eb Update pyapp 2024-12-15 02:33:04 +01:00
dmunozv04
7880e222b0 change python cpu variant in build 2024-12-15 02:09:08 +01:00
dmunozv04
dcf53dcca9 improve exit logic 2024-12-15 02:08:07 +01:00
pre-commit-ci[bot]
1a271c567a [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2024-10-08 00:26:21 +00:00
pre-commit-ci[bot]
4473fc925e [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/pre-commit-hooks: v4.3.0 → v5.0.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.3.0...v5.0.0)
- [github.com/pycqa/isort: 5.12.0 → 5.13.2](https://github.com/pycqa/isort/compare/5.12.0...5.13.2)
- [github.com/psf/black: 23.1.0 → 24.10.0](https://github.com/psf/black/compare/23.1.0...24.10.0)
- [github.com/hadialqattan/pycln: v2.3.0 → v2.4.0](https://github.com/hadialqattan/pycln/compare/v2.3.0...v2.4.0)
2024-10-08 00:26:04 +00:00
dmunozv04
d9ab2cd070 bump version 2024-09-18 17:20:27 +02:00
dmunozv04
ea2004ba94 fix release ci 2024-09-18 17:20:22 +02:00
David
63f5a3bc41 Bump version 2024-09-18 15:17:29 +02:00
David
e999a93503 Merge pull request #191 from dmunozv04/add-mutex-command
Implements mutex when sending commands to YouTube
2024-09-18 15:17:07 +02:00
pre-commit-ci[bot]
8cc3f8aa05 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2024-09-15 13:20:36 +00:00
dmunozv04
5fadc81a69 Fix occasional IndexError
in loungeScreenDisconnected event
2024-09-15 14:49:39 +02:00
dmunozv04
39aef5babf Test wrap command function in a mutex
to avoid race conditions with the _command_offset
2024-09-14 23:44:32 +02:00
David
c56cbfe095 Merge pull request #185 from dmunozv04/build-binaries
Build binaries
2024-08-18 17:30:34 +02:00
pre-commit-ci[bot]
bde4ecb72f [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2024-08-18 15:23:15 +00:00
dmunozv04
167383dea8 revert 2024-08-18 16:11:54 +02:00
dmunozv04
fb3c40d477 also build armv7 2024-08-18 15:58:15 +02:00
dmunozv04
cb738965a7 run on all events (and only publish on release) 2024-08-18 15:07:35 +02:00
dmunozv04
9ad335793a rename prefix 2024-08-18 14:35:02 +02:00
dmunozv04
fd400d077a update upload-artifact to v4 2024-08-18 14:19:39 +02:00
dmunozv04
f9c7b58ece add needs 2024-08-18 14:12:49 +02:00
dmunozv04
464baa7c59 trigger on test branch 2024-08-18 14:07:47 +02:00
dmunozv04
d9986e52b3 Merge remote-tracking branch 'origin/main' into build-binaries 2024-08-18 14:07:19 +02:00
dmunozv04
547a47b9ec modify action to create and publish binaries 2024-08-18 14:04:34 +02:00
dmunozv04
87d0e0e32e rework cli 2024-08-18 14:04:04 +02:00
David
854cb2462f Merge pull request #171 from guoard/dockerfile-issues
Fix buildx warnings when creating the docker image
2024-08-16 12:42:41 +02:00
David
662b71fc00 Merge pull request #177 from AN1MATEK/patch-1
Update config.json.template to correct the syntax error in auto_play
2024-07-14 11:12:08 +02:00
ANIMATEK
fd6b7cb43a Update config.json.template
Correct the error syntax from `autoplay` to the actual variable `auto_play`
2024-07-14 11:09:41 +02:00
dmunozv04
d17e59bf0d bump version 2024-07-07 17:36:01 +02:00
Ali Afsharzadeh
5bc6382f89 Fix buildx warnings when creating the docker image 2024-06-29 09:28:24 +03:30
David
205191f442 Merge pull request #150 from ryankupk/update-cli
Refactor CLI setup script, add prompts for muting and skipping native youtube ads
2024-06-21 17:08:47 +02:00
dmunozv04
810cd5eec3 change default options for autoplay and reporting, and mark default option 2024-06-21 17:08:15 +02:00
pre-commit-ci[bot]
e2ace8629f [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2024-06-21 15:01:56 +00:00
dmunozv04
e54ead26d2 Merge remote-tracking branch 'origin' into pr/ryankupk/150 2024-06-21 17:01:45 +02:00
David
49fba2f28f Merge pull request #161 from Ravioli8235/autoplay
Implement autoplay on/off toggle
2024-06-21 16:56:57 +02:00
pre-commit-ci[bot]
b1333a2f61 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2024-06-21 14:29:35 +00:00
dmunozv04
cfef219d32 fix autoplay padding 2024-06-21 16:29:17 +02:00
Ravioli8235
338e0479ba Merge remote-tracking branch 'origin/autoplay' into autoplay 2024-06-21 15:58:28 +02:00
Ravioli8235
bfefa94a7b fix panel fill 2024-06-21 15:58:15 +02:00
David
783e3d4240 Merge branch 'main' into autoplay 2024-06-17 14:55:07 +02:00
David
015f5a79c9 Merge pull request #168 from Shraymonks/fix-docker-root
Fix deps permissions in docker build
2024-06-16 14:22:25 +02:00
Raymond Ha
dc72db0609 Fix deps permissions in docker build 2024-06-05 16:18:10 -07:00
David
8de38cc92b Merge pull request #167 from dmunozv04/update-actions
Update actions versions
2024-06-05 19:29:42 +02:00
dmunozv04
94ba642af1 update actions versions to latest 2024-06-05 19:27:42 +02:00
David
d3341009a6 Merge branch 'main' into autoplay 2024-05-30 12:27:47 +02:00
David
5dbd16ddd5 Merge branch 'main' into autoplay 2024-05-30 09:05:18 +02:00
pre-commit-ci[bot]
faa0379b89 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2024-05-30 07:03:27 +00:00
Ravioli8235
fb3ed6b39a Implement autoplay 2024-05-30 08:53:35 +02:00
pre-commit-ci[bot]
865f5469a2 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2024-05-03 16:24:21 +00:00
Ryan Kupka
daa7026221 Refactor CLI setup script, add prompts for muting and skipping native youtube ads 2024-05-03 10:18:16 -06:00
boltgolt
4de056d2a8 Allow setting device name to custom string 2023-10-17 17:55:36 +02:00
22 changed files with 787 additions and 367 deletions

View File

@@ -7,12 +7,14 @@ assignees: ''
---
Before opening an issue make sure that there are no duplicates and that you are on the latest version.
Before opening an issue make sure that there are no duplicates and that you are
on the latest version.
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
@@ -25,13 +27,14 @@ A clear and concise description of what you expected to happen.
If applicable, add screenshots to help explain your problem.
**iSponsorBlockTV server (please complete the following information):**
- OS: [e.g. Docker on linux Arm64, windows]
- Python version [e.g. 3.7] (no need to fill if running on docker
- OS: [e.g. Docker on linux Arm64, windows]
- Python version [e.g. 3.7] (no need to fill if running on docker
**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**
Add any other context about the problem here.

View File

@@ -8,13 +8,15 @@ assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
A clear and concise description of what the problem is. Ex. I'm always
frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
A clear and concise description of any alternative solutions or features you've
considered.
**Additional context**
Add any other context or screenshots about the feature request here.

19
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
# 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"

View File

@@ -21,18 +21,22 @@ permissions:
jobs:
build:
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
runs-on: ubuntu-latest
steps:
# Get the repository's code
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
# Generate docker tags
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
uses: docker/metadata-action@v5
with:
images: ghcr.io/dmunozv04/isponsorblocktv, dmunozv04/isponsorblocktv
images: |
ghcr.io/${{ github.repository }}
${{ env.DOCKERHUB_USERNAME && 'dmunozv04/isponsorblocktv' || '' }}
tags: |
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
@@ -42,35 +46,34 @@ jobs:
# https://github.com/docker/setup-qemu-action
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
# https://github.com/docker/setup-buildx-action
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v4
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64, linux/arm64, linux/arm/v7
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=ghcr.io/dmunozv04/isponsorblocktv:buildcache
# 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' || '' }}
cache-from: type=gha
cache-to: type=gha,mode=max

205
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,205 @@
# This workflow will upload a Python Package using Twine when a release is created
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
name: Release Package
on:
push:
branches:
- '*'
tags:
- 'v*'
pull_request:
branches:
- '*'
release:
types: [published]
defaults:
run:
shell: bash
env:
PYTHON_VERSION: "3.11"
permissions:
contents: read
jobs:
build-sdist-and-wheel:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Hatch
run: |
python -m pip install --upgrade pip
pip install hatch
- name: Build package
run: python -m hatch build
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: sdist-and-wheel
path: dist/*
if-no-files-found: error
build-binaries:
name: Build binaries for ${{ matrix.job.release_suffix }} (${{ matrix.job.os }})
needs:
- build-sdist-and-wheel
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
matrix:
job:
# Linux
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
cross: true
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
os: ubuntu-latest
cross: true
release_suffix: aarch64-linux
# Windows
- target: x86_64-pc-windows-msvc
os: windows-latest
release_suffix: x86_64-windows
# macOS
- target: aarch64-apple-darwin
os: macos-latest
release_suffix: aarch64-osx
- target: x86_64-apple-darwin
os: macos-latest
cross: true
release_suffix: x86_64-osx
env:
PYAPP_PASS_LOCATION: "1"
PYAPP_UV_ENABLED: "1"
HATCH_BUILD_LOCATION: dist
CARGO: cargo
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_VERSION: v0.24.0
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Clone PyApp
run: git clone --depth 1 --branch $PYAPP_VERSION https://github.com/ofek/pyapp $PYAPP_REPO
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Hatch
run: |
python -m pip install --upgrade pip
pip install hatch
- name: Install Rust toolchain
if: ${{ !matrix.job.cross }}
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.job.target }}
- name: Set up cross compiling tools
if: matrix.job.cross
uses: taiki-e/setup-cross-toolchain-action@v1
with:
target: ${{ matrix.job.target}}
- name: Show toolchain information
run: |-
rustup toolchain list
rustup default
rustup -V
rustc -V
cargo -V
hatch --version
- name: Get artifact
uses: actions/download-artifact@v4
with:
name: sdist-and-wheel
path: ${{ github.workspace }}/dist
merge-multiple: true
- name: Build Binary
working-directory: ${{ github.workspace }}
run: |-
current_version=$(hatch version)
PYAPP_PROJECT_PATH="${{ github.workspace }}/dist/isponsorblocktv-${current_version}-py3-none-any.whl" hatch -v build -t binary
- name: Rename binary
working-directory: ${{ github.workspace }}
run: |-
mv dist/binary/iSponsorBlockTV* dist/binary/iSponsorBlockTV-${{ matrix.job.release_suffix }}
- name: Upload built binary package
uses: actions/upload-artifact@v4
with:
name: binaries-${{ matrix.job.release_suffix }}
path: dist/binary/*
if-no-files-found: error
publish-to-pypi:
needs: build-sdist-and-wheel
permissions:
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
# only run step if the event is a published release
if: github.event_name == 'release' && github.event.action == 'published'
runs-on: ubuntu-latest
steps:
- name: Get artifact
uses: actions/download-artifact@v4
with:
name: sdist-and-wheel
path: dist
merge-multiple: true
- name: Publish package
uses: pypa/gh-action-pypi-publish@release/v1
publish-to-release:
permissions:
contents: write
needs:
- build-sdist-and-wheel
- build-binaries
if: github.event_name == 'release' && github.event.action == 'published'
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
name: Get artifact
with:
path: dist
merge-multiple: true
- name: Add assets to release
uses: softprops/action-gh-release@v2
with:
files: dist/*

View File

@@ -1,37 +0,0 @@
# This workflow will upload a Python Package using Twine when a release is created
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
name: Upload Python Package
on:
release:
types: [published]
permissions:
contents: read
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install build wheel
- name: Build package
run: python -m build
- name: Publish package
uses: pypa/gh-action-pypi-publish@release/v1

View File

@@ -22,7 +22,7 @@ jobs:
# Update description
- name: Update repo description
uses: peter-evans/dockerhub-description@v3
uses: peter-evans/dockerhub-description@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

1
.markdownlintignore Normal file
View File

@@ -0,0 +1 @@
LICENSE.md

View File

@@ -3,7 +3,7 @@
# Inspired by textual pre-commit config
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
rev: v5.0.0
hooks:
- 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
@@ -18,22 +18,14 @@ repos:
- 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: trailing-whitespace # checks for trailing whitespace
- repo: https://github.com/pycqa/isort
rev: 5.12.0
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.6
hooks:
- id: isort
name: isort (python)
language_version: '3.11'
args: ["--profile", "black", "--filter-files"]
- repo: https://github.com/psf/black
rev: 23.1.0
- id: ruff
args: [ --fix, --exit-non-zero-on-fix ]
- id: ruff-format
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.44.0
hooks:
- id: black
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]
- id: markdownlint
args: ["--fix"]

View File

@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
FROM python:3.11-alpine3.19 as BASE
FROM python:3.13-alpine3.21 AS base
FROM base as compiler
FROM base AS compiler
WORKDIR /app
@@ -10,18 +10,18 @@ COPY src .
RUN python3 -m compileall -b -f . && \
find . -name "*.py" -type f -delete
FROM base as DEP_INSTALLER
FROM base AS dep_installer
COPY requirements.txt .
RUN apk add --no-cache gcc musl-dev && \
pip install --upgrade pip wheel && \
pip install --user -r requirements.txt && \
pip install -r requirements.txt && \
pip uninstall -y pip wheel && \
apk del gcc musl-dev && \
python3 -m compileall -b -f /root/.local/lib/python3.11/site-packages && \
find /root/.local/lib/python3.11/site-packages -name "*.py" -type f -delete && \
find /root/.local/lib/python3.11/ -name "__pycache__" -type d -exec rm -rf {} +
python3 -m compileall -b -f /usr/local/lib/python3.13/site-packages && \
find /usr/local/lib/python3.13/site-packages -name "*.py" -type f -delete && \
find /usr/local/lib/python3.13/ -name "__pycache__" -type d -exec rm -rf {} +
FROM base
@@ -29,7 +29,7 @@ ENV PIP_NO_CACHE_DIR=off iSPBTV_docker=True iSPBTV_data_dir=data TERM=xterm-256c
COPY requirements.txt .
COPY --from=dep_installer /root/.local /root/.local
COPY --from=dep_installer /usr/local /usr/local
WORKDIR /app

View File

@@ -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
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
might be different; for a GUI interface, you would use an "about box".

View File

@@ -1,14 +1,21 @@
# iSponsorBlockTV
Skip sponsor segments in YouTube videos playing on a YouTube TV device (see below for compatibility details).
[![ghcr.io Pulls](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fipitio.github.io%2Fbackage%2Fdmunozv04%2FiSponsorBlockTV%2Fisponsorblocktv.json&query=downloads&logo=github&label=ghcr.io%20pulls&style=flat)](https://ghcr.io/dmunozv04/isponsorblocktv)
[![Docker Pulls](https://img.shields.io/docker/pulls/dmunozv04/isponsorblocktv?logo=docker&style=flat)](https://hub.docker.com/r/dmunozv04/isponsorblocktv/)
[![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)
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.
## Installation
Check the [wiki](https://github.com/dmunozv04/iSponsorBlockTV/wiki/Installation)
Warning: docker armv7 builds have been deprecated. Amd64 and arm64 builds are still available.
## Compatibility
Legend: ✅ = Working, ❌ = Not working, ❔ = Not tested
Open an issue/pull request if you have tested a device that isn't listed here.
@@ -29,24 +36,33 @@ Open an issue/pull request if you have tested a device that isn't listed here.
| Playstation 4/5 | ✅ |
## Usage
Run iSponsorBlockTV on a computer that has network access.
Auto discovery will require the computer to be on the same network as the device 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.
It connects to the device, watches its activity and skips any sponsor segment using the [SponsorBlock](https://sponsor.ajay.app/) API.
Run iSponsorBlockTV on a computer that has network access.
Auto discovery will require the computer to be on the same network as the device
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.
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
- [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)
- [async-cache](https://github.com/iamsinghrajat/async-cache)
- [Textual](https://github.com/textualize/textual/) Used for the amazing new graphical configurator
- [Textual](https://github.com/textualize/textual/) Used for the amazing new
graphical configurator
- [ssdp](https://github.com/codingjoe/ssdp) Used for auto discovery
## Projects using this project
- [Home Assistant Addon](https://github.com/bertybuttface/addons/tree/main/isponsorblocktv)
## Contributing
1. Fork it (<https://github.com/dmunozv04/iSponsorBlockTV/fork>)
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
@@ -54,8 +70,13 @@ It can also skip/mute YouTube ads.
5. Create a new Pull Request
## Contributors
- [dmunozv04](https://github.com/dmunozv04) - creator and maintainer
- [HaltCatchFire](https://github.com/HaltCatchFire) - updated dependencies and improved skip logic
- [Oxixes](https://github.com/oxixes) - added support for channel whitelist and minor improvements
- [HaltCatchFire](https://github.com/HaltCatchFire) - updated dependencies and
improved skip logic
- [Oxixes](https://github.com/oxixes) - added support for channel whitelist and
minor improvements
## License
[![GNU GPLv3](https://www.gnu.org/graphics/gplv3-127x51.png)](https://www.gnu.org/licenses/gpl-3.0.en.html)

View File

@@ -12,6 +12,8 @@
"skip_count_tracking": true,
"mute_ads": true,
"skip_ads": true,
"auto_play": true,
"join_name": "iSponsorBlockTV",
"apikey": "",
"channel_whitelist": [
{"id": "",

View File

@@ -1,12 +1,12 @@
[project]
name = "iSponsorBlockTV"
version = "2.0.8"
version = "2.3.1"
authors = [
{"name" = "dmunozv04"}
]
description = "SponsorBlock client for all YouTube TV clients"
readme = "README.md"
requires-python = ">=3.7"
requires-python = ">=3.9"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",

View File

@@ -1,10 +1,10 @@
aiohttp==3.9.5
aiohttp==3.11.12
appdirs==1.4.4
argparse==1.4.0
async-cache==1.1.1
pyytlounge==2.0.0
rich==13.7.1
pyytlounge==2.1.2
rich==13.9.4
ssdp==1.3.0
textual==0.58.0
textual-slider==0.1.1
xmltodict==0.13.0
textual==1.0.0
textual-slider==0.2.0
xmltodict==0.14.2
rich_click==1.8.5

View File

@@ -1,169 +1,198 @@
import asyncio
import aiohttp
from . import api_helpers, ytlounge
async def pair_device(web_session):
try:
lounge_controller = ytlounge.YtLoungeApi(
"iSponsorBlockTV", web_session=web_session
)
pairing_code = input(
"Enter pairing code (found in Settings - Link with TV code): "
)
pairing_code = int(
pairing_code.replace("-", "").replace(" ", "")
) # remove dashes and spaces
print("Pairing...")
paired = await lounge_controller.pair(pairing_code)
if not paired:
print("Failed to pair device")
return
device = {
"screen_id": lounge_controller.auth.screen_id,
"name": lounge_controller.screen_name,
}
print(f"Paired device: {device['name']}")
return device
except Exception as e:
print(f"Failed to pair device: {e}")
return
def main(config, debug: bool) -> None:
print("Welcome to the iSponsorBlockTV cli setup wizard")
loop = asyncio.get_event_loop_policy().get_event_loop()
web_session = aiohttp.ClientSession()
if debug:
loop.set_debug(True)
asyncio.set_event_loop(loop)
if hasattr(config, "atvs"):
print(
"The atvs config option is deprecated and has stopped working. Please read"
" this for more information on how to upgrade to V2:"
" \nhttps://github.com/dmunozv04/iSponsorBlockTV/wiki/Migrate-from-V1-to-V2"
)
if (
input(
"Do you want to remove the legacy 'atvs' entry (the app won't start"
" with it present)? (y/n) "
)
== "y"
):
del config["atvs"]
devices = config.devices
while not input(f"Paired with {len(devices)} Device(s). Add more? (y/n) ") == "n":
task = loop.create_task(pair_device(web_session))
loop.run_until_complete(task)
device = task.result()
if device:
devices.append(device)
config.devices = devices
apikey = config.apikey
if apikey:
if input("API key already specified. Change it? (y/n) ") == "y":
apikey = input("Enter your API key: ")
else:
if (
input(
"API key only needed for the channel whitelist function. Add it? (y/n) "
)
== "y"
):
print(
"Get youtube apikey here:"
" https://developers.google.com/youtube/registering_an_application"
)
apikey = input("Enter your API key: ")
config.apikey = apikey
skip_categories = config.skip_categories
if skip_categories:
if input("Skip categories already specified. Change them? (y/n) ") == "y":
categories = input(
"Enter skip categories (space or comma sepparated) Options: [sponsor"
" selfpromo exclusive_access interaction poi_highlight intro outro"
" preview filler music_offtopic]:\n"
)
skip_categories = categories.replace(",", " ").split(" ")
skip_categories = [
x for x in skip_categories if x != ""
] # Remove empty strings
else:
categories = input(
"Enter skip categories (space or comma sepparated) Options: [sponsor,"
" selfpromo, exclusive_access, interaction, poi_highlight, intro, outro,"
" preview, filler, music_offtopic:\n"
)
skip_categories = categories.replace(",", " ").split(" ")
skip_categories = [
x for x in skip_categories if x != ""
] # Remove empty strings
config.skip_categories = skip_categories
channel_whitelist = config.channel_whitelist
if (
input("Do you want to whitelist any channels from being ad-blocked? (y/n) ")
== "y"
):
if not apikey:
print(
"WARNING: You need to specify an API key to use this function,"
" otherwise the program will fail to start.\nYou can add one by"
" re-running this setup wizard."
)
api_helper = api_helpers.ApiHelper(config, web_session)
while True:
channel_info = {}
channel = input('Enter a channel name or "/exit" to exit: ')
if channel == "/exit":
break
task = loop.create_task(
api_helper.search_channels(channel, apikey, web_session)
)
loop.run_until_complete(task)
results = task.result()
if len(results) == 0:
print("No channels found")
continue
for i, item in enumerate(results):
print(f"{i}: {item[1]} - Subs: {item[2]}")
print("5: Enter a custom channel ID")
print("6: Go back")
choice = -1
choice = input("Select one option of the above [0-6]: ")
while choice not in [str(x) for x in range(7)]:
print("Invalid choice")
choice = input("Select one option of the above [0-6]: ")
if choice == "5":
channel_info["id"] = input("Enter a channel ID: ")
channel_info["name"] = input("Enter the channel name: ")
channel_whitelist.append(channel_info)
continue
if choice == "6":
continue
channel_info["id"] = results[int(choice)][0]
channel_info["name"] = results[int(choice)][1]
channel_whitelist.append(channel_info)
# Close web session asynchronously
config.channel_whitelist = channel_whitelist
config.skip_count_tracking = (
not input(
"Do you want to report skipped segments to sponsorblock. Only the segment"
" UUID will be sent? (y/n) "
)
== "n"
)
print("Config finished")
config.save()
loop.run_until_complete(web_session.close())
import asyncio
import aiohttp
from . import api_helpers, ytlounge
# Constants for user input prompts
ATVS_REMOVAL_PROMPT = (
"Do you want to remove the legacy 'atvs' entry (the app won't start"
" with it present)? (y/N) "
)
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) "
CHANGE_API_KEY_PROMPT = "API key already specified. Change it? (y/N) "
ADD_API_KEY_PROMPT = (
"API key only needed for the channel whitelist function. Add it? (y/N) "
)
ENTER_API_KEY_PROMPT = "Enter your API key: "
CHANGE_SKIP_CATEGORIES_PROMPT = "Skip categories already specified. Change them? (y/N) "
ENTER_SKIP_CATEGORIES_PROMPT = (
"Enter skip categories (space or comma sepparated) Options: [sponsor,"
" selfpromo, exclusive_access, interaction, poi_highlight, intro, outro,"
" preview, filler, music_offtopic]:\n"
)
WHITELIST_CHANNELS_PROMPT = (
"Do you want to whitelist any channels from being ad-blocked? (y/N) "
)
SEARCH_CHANNEL_PROMPT = 'Enter a channel name or "/exit" to exit: '
SELECT_CHANNEL_PROMPT = "Select one option of the above [0-6]: "
ENTER_CHANNEL_ID_PROMPT = "Enter a channel ID: "
ENTER_CUSTOM_CHANNEL_NAME_PROMPT = "Enter the channel name: "
REPORT_SKIPPED_SEGMENTS_PROMPT = (
"Do you want to report skipped segments to sponsorblock. Only the segment"
" UUID will be sent? (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) "
AUTOPLAY_PROMPT = "Do you want to enable autoplay? (Y/n) "
def get_yn_input(prompt):
while choice := input(prompt):
if choice.lower() in ["y", "n"]:
return choice.lower()
print("Invalid input. Please enter 'y' or 'n'.")
async def create_web_session():
return aiohttp.ClientSession()
async def pair_device(web_session: aiohttp.ClientSession):
try:
lounge_controller = ytlounge.YtLoungeApi()
await lounge_controller.change_web_session(web_session)
pairing_code = input(PAIRING_CODE_PROMPT)
pairing_code = int(
pairing_code.replace("-", "").replace(" ", "")
) # remove dashes and spaces
print("Pairing...")
paired = await lounge_controller.pair(pairing_code)
if not paired:
print("Failed to pair device")
return
device = {
"screen_id": lounge_controller.auth.screen_id,
"name": lounge_controller.screen_name,
}
print(f"Paired device: {device['name']}")
return device
except Exception as e:
print(f"Failed to pair device: {e}")
return
def main(config, debug: bool) -> None:
print("Welcome to the iSponsorBlockTV cli setup wizard")
loop = asyncio.get_event_loop_policy().get_event_loop()
web_session = loop.run_until_complete(create_web_session())
if debug:
loop.set_debug(True)
asyncio.set_event_loop(loop)
if hasattr(config, "atvs"):
print(
"The atvs config option is deprecated and has stopped working. Please read"
" 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":
del config["atvs"]
devices = config.devices
choice = get_yn_input(ADD_MORE_DEVICES_PROMPT.format(num_devices=len(devices)))
while choice == "y":
device = loop.run_until_complete(pair_device(web_session))
if device:
devices.append(device)
choice = get_yn_input(ADD_MORE_DEVICES_PROMPT.format(num_devices=len(devices)))
config.devices = devices
apikey = config.apikey
if apikey:
choice = get_yn_input(CHANGE_API_KEY_PROMPT)
if choice == "y":
apikey = input(ENTER_API_KEY_PROMPT)
else:
choice = get_yn_input(ADD_API_KEY_PROMPT)
if choice == "y":
print(
"Get youtube apikey here:"
" https://developers.google.com/youtube/registering_an_application"
)
apikey = input(ENTER_API_KEY_PROMPT)
config.apikey = apikey
skip_categories = config.skip_categories
if skip_categories:
choice = get_yn_input(CHANGE_SKIP_CATEGORIES_PROMPT)
if choice == "y":
categories = input(ENTER_SKIP_CATEGORIES_PROMPT)
skip_categories = categories.replace(",", " ").split(" ")
skip_categories = [
x for x in skip_categories if x != ""
] # Remove empty strings
else:
categories = input(ENTER_SKIP_CATEGORIES_PROMPT)
skip_categories = categories.replace(",", " ").split(" ")
skip_categories = [
x for x in skip_categories if x != ""
] # Remove empty strings
config.skip_categories = skip_categories
channel_whitelist = config.channel_whitelist
choice = get_yn_input(WHITELIST_CHANNELS_PROMPT)
if choice == "y":
if not apikey:
print(
"WARNING: You need to specify an API key to use this function,"
" otherwise the program will fail to start.\nYou can add one by"
" re-running this setup wizard."
)
api_helper = api_helpers.ApiHelper(config, web_session)
while True:
channel_info = {}
channel = input(SEARCH_CHANNEL_PROMPT)
if channel == "/exit":
break
task = loop.create_task(
api_helper.search_channels(channel, apikey, web_session)
)
loop.run_until_complete(task)
results = task.result()
if len(results) == 0:
print("No channels found")
continue
for i, item in enumerate(results):
print(f"{i}: {item[1]} - Subs: {item[2]}")
print("5: Enter a custom channel ID")
print("6: Go back")
while choice := input(SELECT_CHANNEL_PROMPT):
if choice in [str(x) for x in range(7)]:
break
print("Invalid choice")
if choice == "5":
channel_info["id"] = input(ENTER_CHANNEL_ID_PROMPT)
channel_info["name"] = input(ENTER_CUSTOM_CHANNEL_NAME_PROMPT)
channel_whitelist.append(channel_info)
continue
if choice == "6":
continue
channel_info["id"] = results[int(choice)][0]
channel_info["name"] = results[int(choice)][1]
channel_whitelist.append(channel_info)
# Close web session asynchronously
config.channel_whitelist = channel_whitelist
choice = get_yn_input(REPORT_SKIPPED_SEGMENTS_PROMPT)
config.skip_count_tracking = choice != "n"
choice = get_yn_input(MUTE_ADS_PROMPT)
config.mute_ads = choice == "y"
choice = get_yn_input(SKIP_ADS_PROMPT)
config.skip_ads = choice == "y"
choice = get_yn_input(AUTOPLAY_PROMPT)
config.auto_play = choice != "n"
print("Config finished")
config.save()
loop.run_until_complete(web_session.close())

View File

@@ -1,4 +1,5 @@
"""Send out an M-SEARCH request and listening for responses."""
import asyncio
import socket

View File

@@ -1,10 +1,10 @@
import argparse
import json
import logging
import os
import sys
import time
import rich_click as click
from appdirs import user_data_dir
from . import config_setup, main, setup_wizard
@@ -41,6 +41,8 @@ class Config:
self.skip_count_tracking = True
self.mute_ads = False
self.skip_ads = False
self.auto_play = True
self.join_name = "iSponsorBlockTV"
self.__load()
def validate(self):
@@ -125,35 +127,93 @@ class Config:
return False
def app_start():
# If env has a data dir use that, otherwise use the default
default_data_dir = os.getenv("iSPBTV_data_dir") or user_data_dir(
"iSponsorBlockTV", "dmunozv04"
)
parser = argparse.ArgumentParser(description="iSponsorblockTV")
parser.add_argument(
"--data-dir", "-d", default=default_data_dir, help="data directory"
)
parser.add_argument(
"--setup", "-s", action="store_true", help="setup the program graphically"
)
parser.add_argument(
"--setup-cli",
"-sc",
action="store_true",
help="setup the program in the command line",
)
parser.add_argument("--debug", action="store_true", help="debug mode")
args = parser.parse_args()
config = Config(args.data_dir)
if args.debug:
@click.group(invoke_without_command=True)
@click.option(
"--data",
"-d",
default=lambda: os.getenv("iSPBTV_data_dir")
or user_data_dir("iSponsorBlockTV", "dmunozv04"),
help="data directory",
)
@click.option("--debug", is_flag=True, help="debug mode")
# legacy commands as arguments
@click.option(
"--setup", is_flag=True, help="Setup the program graphically", hidden=True
)
@click.option(
"--setup-cli",
is_flag=True,
help="Setup the program in the command line",
hidden=True,
)
@click.pass_context
def cli(ctx, data, debug, setup, setup_cli):
"""iSponsorblockTV"""
ctx.ensure_object(dict)
ctx.obj["data_dir"] = data
ctx.obj["debug"] = debug
if debug:
logging.basicConfig(level=logging.DEBUG)
if args.setup: # Set up the config file graphically
setup_wizard.main(config)
sys.exit()
if args.setup_cli: # Set up the config file
config_setup.main(config, args.debug)
else:
config.validate()
main.main(config, args.debug)
if ctx.invoked_subcommand is None:
if setup:
ctx.invoke(setup_command)
elif setup_cli:
ctx.invoke(setup_cli_command)
else:
ctx.invoke(start)
@cli.command()
@click.pass_context
def setup(ctx):
"""Setup the program graphically"""
config = Config(ctx.obj["data_dir"])
setup_wizard.main(config)
sys.exit()
setup_command = setup
@cli.command()
@click.pass_context
def setup_cli(ctx):
"""Setup the program in the command line"""
config = Config(ctx.obj["data_dir"])
config_setup.main(config, ctx.obj["debug"])
setup_cli_command = setup_cli
@cli.command()
@click.pass_context
def start(ctx):
"""Start the main program"""
config = Config(ctx.obj["data_dir"])
config.validate()
main.main(config, ctx.obj["debug"])
# Create fake "self" group to show pyapp options in help menu
# Subcommands remove, restore, update
pyapp_group = click.RichGroup("self", help="pyapp options (update, remove, restore)")
pyapp_group.add_command(
click.RichCommand("update", help="Update the package to the latest version")
)
pyapp_group.add_command(
click.Command(
"remove", help="Remove the package, wiping the installation but not the data"
)
)
pyapp_group.add_command(
click.RichCommand(
"restore", help="Restore the package to its original state by reinstalling it"
)
)
if os.getenv("PYAPP"):
cli.add_command(pyapp_group)
def app_start():
cli(obj={})

View File

@@ -27,9 +27,9 @@ class DeviceListener:
logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
)
self.logger.addHandler(sh)
self.logger.info(f"Starting device")
self.logger.info("Starting device")
self.lounge_controller = ytlounge.YtLoungeApi(
device.screen_id, config, api_helper, self.logger, self.web_session
device.screen_id, config, api_helper, self.logger
)
# Ensures that we have a valid auth token
@@ -38,14 +38,14 @@ class DeviceListener:
await asyncio.sleep(60 * 60 * 24) # Refresh every 24 hours
try:
await self.lounge_controller.refresh_auth()
except:
except BaseException:
# traceback.print_exc()
pass
async def is_available(self):
try:
return await self.lounge_controller.is_available()
except:
except BaseException:
# traceback.print_exc()
return False
@@ -57,20 +57,20 @@ class DeviceListener:
try:
self.logger.debug("Refreshing auth")
await lounge_controller.refresh_auth()
except:
except BaseException:
await asyncio.sleep(10)
while not (await self.is_available()) and not self.cancelled:
await asyncio.sleep(10)
try:
await lounge_controller.connect()
except:
except BaseException:
pass
while not lounge_controller.connected() and not self.cancelled:
# Doesn't connect to the device if it's a kids profile (it's broken)
await asyncio.sleep(10)
try:
await lounge_controller.connect()
except:
except BaseException:
pass
self.logger.info(
"Connected to device %s (%s)", lounge_controller.screen_name, self.name
@@ -79,14 +79,14 @@ class DeviceListener:
self.logger.info("Subscribing to lounge")
sub = await lounge_controller.subscribe_monitored(self)
await sub
except:
except BaseException:
pass
# Method called on playback state change
async def __call__(self, state):
try:
self.task.cancel()
except:
except BaseException:
pass
time_start = time.time()
self.task = asyncio.create_task(self.process_playstatus(state, time_start))
@@ -131,40 +131,71 @@ class DeviceListener:
await asyncio.create_task(self.api_helper.mark_viewed_segments(uuids))
await asyncio.create_task(self.lounge_controller.seek_to(position))
# Stops the connection to the device
async def cancel(self):
self.cancelled = True
try:
await self.lounge_controller.disconnect()
if self.task:
self.task.cancel()
except Exception:
pass
if self.lounge_controller.subscribe_task_watchdog:
self.lounge_controller.subscribe_task_watchdog.cancel()
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):
for i in devices:
await i.cancel()
async def finish(devices, web_session, tcp_connector):
await asyncio.gather(
*(device.cancel() for device in devices), return_exceptions=True
)
await web_session.close()
await tcp_connector.close()
def main(config, debug):
def handle_signal(signum, frame):
raise KeyboardInterrupt()
async def main_async(config, debug):
loop = asyncio.get_event_loop_policy().get_event_loop()
tasks = [] # Save the tasks so the interpreter doesn't garbage collect them
devices = [] # Save the devices to close them later
if debug:
loop.set_debug(True)
asyncio.set_event_loop(loop)
tcp_connector = aiohttp.TCPConnector(ttl_dns_cache=300)
web_session = aiohttp.ClientSession(loop=loop, connector=tcp_connector)
web_session = aiohttp.ClientSession(connector=tcp_connector)
api_helper = api_helpers.ApiHelper(config, web_session)
for i in config.devices:
device = DeviceListener(api_helper, config, i, debug, web_session)
devices.append(device)
await device.initialize_web_session()
tasks.append(loop.create_task(device.loop()))
tasks.append(loop.create_task(device.refresh_auth_loop()))
signal(SIGINT, lambda s, f: loop.stop())
signal(SIGTERM, lambda s, f: loop.stop())
loop.run_forever()
print("Cancelling tasks and exiting...")
loop.run_until_complete(finish(devices))
loop.run_until_complete(web_session.close())
loop.run_until_complete(tcp_connector.close())
signal(SIGTERM, handle_signal)
signal(SIGINT, handle_signal)
try:
await asyncio.gather(*tasks)
except KeyboardInterrupt:
print("Cancelling tasks and exiting...")
await finish(devices, web_session, tcp_connector)
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()

View File

@@ -21,9 +21,13 @@
scrollbar-gutter: stable;
}
.small-button{
.button-small {
height: 3;
border: none;
border-top: none;
border-bottom: none;
offset: 0 -1;
padding: 0;
}
.button-100 {
@@ -106,13 +110,14 @@ EditDevice {
}
Element {
background: $panel;
background: $panel-darken-1;
border-top: solid $panel-lighten-2;
layout: horizontal;
height: 2;
width: 100%;
margin: 0 1 0 1;
padding: 0;
}
Element > .element-name {
offset: 0 -1;
@@ -120,7 +125,11 @@ Element > .element-name {
width: 100%;
align: left middle;
text-align: left;
background: $panel-darken-1;
&:hover {
background: $panel-lighten-1;
border-top: tall $panel-lighten-3;
}
}
Element > .element-remove {
dock: right;
@@ -132,7 +141,7 @@ Element > .element-remove {
margin: 0 1 0 0;
}
#add-device {
#add-device, #add-channel {
text-style: bold;
width: 100%;
align: left middle;
@@ -140,6 +149,11 @@ Element > .element-remove {
dock: left;
text-align: left;
background: $panel-darken-1;
&:hover {
background: $panel-lighten-1;
border-top: tall $panel-lighten-3;
}
}
#add-device-button-container{
height: 1;
@@ -363,3 +377,9 @@ MigrationScreen {
width: 1fr;
content-align: center middle;
}
/* Autoplay */
#autoplay-container{
padding: 1;
height: auto;
}

View File

@@ -84,12 +84,15 @@ class Element(Static):
def compose(self) -> ComposeResult:
yield Button(
label=self.element_name,
classes="element-name",
classes="element-name button-small",
disabled=True,
id="element-name",
)
yield Button(
"Remove", classes="element-remove", variant="error", id="element-remove"
"Remove",
classes="element-remove button-small",
variant="error",
id="element-remove",
)
def on_mount(self) -> None:
@@ -102,7 +105,6 @@ class Device(Element):
"""A device element."""
def process_values_from_data(self):
print(self.element_data)
if "name" in self.element_data and self.element_data["name"]:
self.element_name = self.element_data["name"]
else:
@@ -285,7 +287,8 @@ class AddDevice(ModalWithClickExit):
" computer\nIf it isn't showing up, try restarting the"
" app.\nIf running in docker, make sure to use"
" `--network=host`\nTo refresh the list, close and open the"
" dialog again"
" dialog again\n[b][u]If it still doesn't work, "
"pair using a pairing code (it's much more reliable)"
),
classes="subtitle",
)
@@ -328,17 +331,18 @@ class AddDevice(ModalWithClickExit):
@on(Input.Changed, "#pairing-code-input")
def changed_pairing_code(self, event: Input.Changed):
self.query_one("#add-device-pin-add-button").disabled = (
not event.validation_result.is_valid
)
self.query_one(
"#add-device-pin-add-button"
).disabled = not event.validation_result.is_valid
@on(Input.Submitted, "#pairing-code-input")
@on(Button.Pressed, "#add-device-pin-add-button")
async def handle_add_device_pin(self) -> None:
self.query_one("#add-device-pin-add-button").disabled = True
lounge_controller = ytlounge.YtLoungeApi(
"iSponsorBlockTV", web_session=self.web_session
"iSponsorBlockTV",
)
await lounge_controller.change_web_session(self.web_session)
pairing_code = self.query_one("#pairing-code-input").value
pairing_code = int(
pairing_code.replace("-", "").replace(" ", "")
@@ -347,7 +351,7 @@ class AddDevice(ModalWithClickExit):
paired = False
try:
paired = await lounge_controller.pair(pairing_code)
except:
except BaseException:
pass
if paired:
device = {
@@ -377,9 +381,9 @@ class AddDevice(ModalWithClickExit):
@on(SelectionList.SelectedChanged, "#dial-devices-list")
def changed_device_list(self, event: SelectionList.SelectedChanged):
self.query_one("#add-device-dial-add-button").disabled = (
not event.selection_list.selected
)
self.query_one(
"#add-device-dial-add-button"
).disabled = not event.selection_list.selected
class AddChannel(ModalWithClickExit):
@@ -476,7 +480,6 @@ class AddChannel(ModalWithClickExit):
@on(Button.Pressed, "#add-channel-switch-buttons > *")
def handle_switch_buttons(self, event: Button.Pressed) -> None:
button_ = event.button.id
self.query_one("#add-channel-switcher").current = event.button.id.replace(
"-button", "-container"
)
@@ -496,7 +499,7 @@ class AddChannel(ModalWithClickExit):
self.query_one("#channel-search-results").remove_children()
try:
channels_list = await self.api_helper.search_channels(channel_name)
except:
except BaseException:
self.query_one("#add-channel-info").update(
"[#ff0000]Failed to search for channel"
)
@@ -585,7 +588,7 @@ class EditDevice(ModalWithClickExit):
)
def on_slider_changed(self, event: Slider.Changed) -> None:
offset_input = self.query_one("#device-offset-offset_input")
offset_input = self.query_one("#device-offset-input")
with offset_input.prevent(Input.Changed):
offset_input.value = str(event.slider.value)
@@ -619,7 +622,9 @@ class DevicesManager(Vertical):
def compose(self) -> ComposeResult:
yield Label("Devices", classes="title")
with Horizontal(id="add-device-button-container"):
yield Button("Add Device", id="add-device", classes="button-100")
yield Button(
"Add Device", id="add-device", classes="button-100 button-small"
)
for device in self.devices:
yield Device(device, tooltip="Click to edit")
@@ -813,7 +818,9 @@ class ChannelWhitelistManager(Vertical):
id="warning-no-key",
)
with Horizontal(id="add-channel-button-container"):
yield Button("Add Channel", id="add-channel", classes="button-100")
yield Button(
"Add Channel", id="add-channel", classes="button-100 button-small"
)
for channel in self.config.channel_whitelist:
yield Channel(channel)
@@ -850,6 +857,32 @@ class ChannelWhitelistManager(Vertical):
self.app.push_screen(AddChannel(self.config), callback=self.new_channel)
class AutoPlayManager(Vertical):
"""Manager for autoplay, allows enabling/disabling autoplay."""
def __init__(self, config, **kwargs) -> None:
super().__init__(**kwargs)
self.config = config
def compose(self) -> ComposeResult:
yield Label("Autoplay", classes="title")
yield Label(
"This feature allows you to enable/disable autoplay",
classes="subtitle",
id="autoplay-subtitle",
)
with Horizontal(id="autoplay-container"):
yield Checkbox(
value=self.config.auto_play,
id="autoplay-switch",
label="Enable autoplay",
)
@on(Checkbox.Changed, "#autoplay-switch")
def changed_skip(self, event: Checkbox.Changed):
self.config.auto_play = event.checkbox.value
class ISponsorBlockTVSetupMainScreen(Screen):
"""Making this a separate screen to avoid a bug: https://github.com/Textualize/textual/issues/3221"""
@@ -886,6 +919,9 @@ class ISponsorBlockTVSetupMainScreen(Screen):
yield ApiKeyManager(
config=self.config, id="api-key-manager", classes="container"
)
yield AutoPlayManager(
config=self.config, id="autoplay-manager", classes="container"
)
def on_mount(self) -> None:
if self.check_for_old_config_entries():
@@ -914,7 +950,7 @@ class ISponsorBlockTVSetupMainScreen(Screen):
self.app.query_one("#warning-no-key").display = (
not event.input.value
) and self.config.channel_whitelist
except:
except BaseException:
pass

View File

@@ -1,5 +1,6 @@
import asyncio
import json
from typing import Any, List
import pyytlounge
from aiohttp import ClientSession
@@ -12,15 +13,14 @@ create_task = asyncio.create_task
class YtLoungeApi(pyytlounge.YtLoungeApi):
def __init__(
self,
screen_id,
screen_id=None,
config=None,
api_helper=None,
logger=None,
web_session: ClientSession = None,
):
super().__init__("iSponsorBlockTV", logger=logger)
if web_session is not None:
self.session = web_session # And use the one we passed
super().__init__(
config.join_name if config else "iSponsorBlockTV", logger=logger
)
self.auth.screen_id = screen_id
self.auth.lounge_id_token = None
self.api_helper = api_helper
@@ -30,9 +30,12 @@ class YtLoungeApi(pyytlounge.YtLoungeApi):
self.callback = None
self.logger = logger
self.shorts_disconnected = False
self.auto_play = True
if config:
self.mute_ads = config.mute_ads
self.skip_ads = config.skip_ads
self.auto_play = config.auto_play
self._command_mutex = asyncio.Lock()
# Ensures that we still are subscribed to the lounge
async def _watchdog(self):
@@ -41,7 +44,7 @@ class YtLoungeApi(pyytlounge.YtLoungeApi):
) # YouTube sends at least a message every 30 seconds (no-op or any other)
try:
self.subscribe_task.cancel()
except Exception:
except BaseException:
pass
# Subscribe to the lounge and start the watchdog
@@ -49,25 +52,27 @@ class YtLoungeApi(pyytlounge.YtLoungeApi):
self.callback = callback
try:
self.subscribe_task_watchdog.cancel()
except:
except BaseException:
pass # No watchdog task
self.subscribe_task = asyncio.create_task(super().subscribe(callback))
self.subscribe_task_watchdog = asyncio.create_task(self._watchdog())
create_task(self.debug_command("bugchomp "))
return self.subscribe_task
# Process a lounge subscription event
def _process_event(self, event_id: int, event_type: str, args):
self.logger.debug(f"process_event({event_id}, {event_type}, {args})")
def _process_event(self, event_type: str, args: List[Any]):
self.logger.debug(f"process_event({event_type}, {args})")
# (Re)start the watchdog
try:
self.subscribe_task_watchdog.cancel()
except:
except BaseException:
pass
finally:
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
# can get the segments)
if event_type == "onStateChange":
create_task(self.debug_command("exp 0 "))
data = args[0]
# print(data)
# Unmute when the video starts playing
@@ -143,11 +148,16 @@ class YtLoungeApi(pyytlounge.YtLoungeApi):
self.shorts_disconnected = False
create_task(self.play_video(video_id_saved))
elif event_type == "loungeScreenDisconnected":
data = args[0]
if data["reason"] == "disconnectedByUserScreenInitiated": # Short playing?
self.shorts_disconnected = True
if args: # Sometimes it's empty
data = args[0]
if (
data["reason"] == "disconnectedByUserScreenInitiated"
): # Short playing?
self.shorts_disconnected = True
elif event_type == "onAutoplayModeChanged":
create_task(self.set_auto_play_mode(self.auto_play))
super()._process_event(event_id, event_type, args)
super()._process_event(event_type, args)
# Set the volume to a specific value (0-100)
async def set_volume(self, volume: int) -> None:
@@ -177,3 +187,25 @@ class YtLoungeApi(pyytlounge.YtLoungeApi):
async def play_video(self, video_id: str) -> bool:
return await self._command("setPlaylist", {"videoId": video_id})
# Test to wrap the command function in a mutex to avoid race conditions with
# the _command_offset (TODO: move to upstream if it works)
async def _command(self, command: str, command_parameters: dict = None) -> bool:
async with self._command_mutex:
self.logger.debug(
f"Send command: {command}, Parameters: {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 debug_command(self, debug_command: str):
await super()._command(
"sendDebugCommand",
{"debugCommand": debug_command},
)