mirror of
https://github.com/dmunozv04/iSponsorBlockTV.git
synced 2025-12-06 11:56:45 +03:00
Final v2 commit before launch?
Adds better logging and modifies README.md
This commit is contained in:
6
.github/workflows/main.yml
vendored
6
.github/workflows/main.yml
vendored
@@ -31,9 +31,9 @@ jobs:
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: ghcr.io/dmunozv04/isponsorblocktv, dmunozv04/isponsorblocktv
|
||||
# tags: |
|
||||
# type=raw,value=latest,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
|
||||
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
|
||||
|
||||
# https://github.com/docker/setup-qemu-action
|
||||
- name: Set up QEMU
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -155,7 +155,7 @@ cython_debug/
|
||||
|
||||
#config folder
|
||||
data/
|
||||
config.json
|
||||
data/config.json
|
||||
|
||||
.DS_Store
|
||||
|
||||
|
||||
47
README.md
47
README.md
@@ -1,33 +1,49 @@
|
||||
# iSponsorBlockTV
|
||||
Skip sponsor segments in YouTube videos playing on a YouTube TV device (see below for compatibility details).
|
||||
|
||||
Skip sponsor segments in YouTube videos playing on an Apple TV. Sponsor Block in YouTube for apple TV
|
||||
This project is written in asynchronous python and should be pretty quick.
|
||||
|
||||
This project is written in asycronous python and should be pretty quick.
|
||||
|
||||
# Installation
|
||||
## Installation
|
||||
Check the [wiki](https://github.com/dmunozv04/iSponsorBlockTV/wiki/Installation)
|
||||
|
||||
Warning: armv7 builds have been deprecated.
|
||||
Warning: docker armv7 builds have been deprecated. Amd64 and arm64 builds are still available.
|
||||
|
||||
# Usage
|
||||
## Compatibility
|
||||
Leyend: ✅ = Working, ❌ = Not working, ❔ = Not tested
|
||||
|
||||
Run iSponsorBLockTV on the same network as the Apple TV.
|
||||
Open an issue/pull request if you have tested a device that isn't listed here.
|
||||
|
||||
It connects to the Apple TV, watches its activity and skips any sponsor segment using the [SponsorBlock](https://sponsor.ajay.app/) API.
|
||||
| Device | Status |
|
||||
|:-------------------|:------:|
|
||||
| Apple TV | ✅ |
|
||||
| Samsung TV (Tizen) | ✅ |
|
||||
| LG TV (WebOS) | ✅ |
|
||||
| Android TV | ❔ |
|
||||
| Chromecast | ❔ |
|
||||
| Roku | ❔ |
|
||||
| Fire TV | ❔ |
|
||||
| Nintendo Switch | ✅ |
|
||||
| Xbox One/Series | ❔ |
|
||||
| Playstation 4/5 | ❔ |
|
||||
|
||||
The last 5 videos' segments are cached to limit the number on queries on SponsorBlock and YouTube.
|
||||
## 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.
|
||||
|
||||
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
|
||||
- [pyatv](https://github.com/postlund/pyatv) Used to connect to the Apple TV
|
||||
## Libraries used
|
||||
- [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
|
||||
- [ssdp](https://github.com/codingjoe/ssdp) Used for auto discovery
|
||||
|
||||
# Projects using this proect
|
||||
## Projects using this project
|
||||
- [Home Assistant Addon](https://github.com/bertybuttface/addons/tree/main/isponsorblocktv)
|
||||
|
||||
# Contributing
|
||||
|
||||
## 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'`)
|
||||
@@ -35,9 +51,8 @@ The last 5 videos' segments are cached to limit the number on queries on Sponsor
|
||||
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
|
||||
# License
|
||||
## License
|
||||
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
||||
|
||||
@@ -8,6 +8,7 @@ import traceback
|
||||
|
||||
class DeviceListener:
|
||||
def __init__(self, api_helper, config, screen_id, offset):
|
||||
self.task: asyncio.Task = None
|
||||
self.api_helper = api_helper
|
||||
self.lounge_controller = ytlounge.YtLoungeApi(screen_id, config, api_helper)
|
||||
self.offset = offset
|
||||
@@ -53,12 +54,12 @@ class DeviceListener:
|
||||
await lounge_controller.connect()
|
||||
except:
|
||||
pass
|
||||
# print(f"Connected to device {lounge_controller.screen_name}")
|
||||
print(f"Connected to device {lounge_controller.screen_name}")
|
||||
try:
|
||||
print("Subscribing to lounge")
|
||||
#print("Subscribing to lounge")
|
||||
sub = await lounge_controller.subscribe_monitored(self)
|
||||
await sub
|
||||
print("Subscription ended")
|
||||
#print("Subscription ended")
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -77,9 +78,10 @@ class DeviceListener:
|
||||
segments = []
|
||||
if state.videoId:
|
||||
segments = await self.api_helper.get_segments(state.videoId)
|
||||
print(segments)
|
||||
if state.state.value == 1 and segments: # Playing and has segments to skip
|
||||
await self.time_to_segment(segments, state.currentTime, time_start)
|
||||
if state.state.value == 1: # Playing
|
||||
print(f"Playing {state.videoId} with {len(segments)} segments")
|
||||
if segments: # If there are segments
|
||||
await self.time_to_segment(segments, state.currentTime, time_start)
|
||||
|
||||
# Finds the next segment to skip to and skips to it
|
||||
async def time_to_segment(self, segments, position, time_start):
|
||||
@@ -108,13 +110,14 @@ class DeviceListener:
|
||||
self.api_helper.mark_viewed_segments(UUID)
|
||||
) # Don't wait for this to finish
|
||||
|
||||
|
||||
# Stops the connection to the device
|
||||
async def cancel(self):
|
||||
self.cancelled = True
|
||||
try:
|
||||
self.task.cancel()
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
pass
|
||||
|
||||
|
||||
async def finish(devices):
|
||||
@@ -141,7 +144,6 @@ def main(config, debug):
|
||||
loop.run_forever()
|
||||
except KeyboardInterrupt as e:
|
||||
print("Keyboard interrupt detected, cancelling tasks and exiting...")
|
||||
traceback.print_exc()
|
||||
loop.run_until_complete(finish(devices))
|
||||
finally:
|
||||
loop.run_until_complete(web_session.close())
|
||||
|
||||
@@ -63,12 +63,15 @@ class YtLoungeApi(pyytlounge.YtLoungeApi):
|
||||
self._update_state()
|
||||
# Unmute when the video starts playing
|
||||
if self.mute_ads and data.get("state", "0") == "1":
|
||||
#print("Ad has ended, unmuting")
|
||||
create_task(self.mute(False, override=True))
|
||||
elif self.mute_ads and event_type == "onAdStateChange":
|
||||
data = args[0]
|
||||
if data["adState"] == '0': # Ad is not playing
|
||||
#print("Ad has ended, unmuting")
|
||||
create_task(self.mute(False, override=True))
|
||||
else: # Seen multiple other adStates, assuming they are all ads
|
||||
print("Ad has started, muting")
|
||||
create_task(self.mute(True, override=True))
|
||||
# Manages volume, useful since YouTube wants to know the volume when unmuting (even if they already have it)
|
||||
elif event_type == "onVolumeChanged":
|
||||
@@ -77,6 +80,7 @@ class YtLoungeApi(pyytlounge.YtLoungeApi):
|
||||
# Gets segments for the next video before it starts playing
|
||||
elif event_type == "autoplayUpNext":
|
||||
if len(args) > 0 and (vid_id := args[0]["videoId"]): # if video id is not empty
|
||||
print(f"Getting segments for next video: {vid_id}")
|
||||
create_task(self.api_helper.get_segments(vid_id))
|
||||
|
||||
# #Used to know if an ad is skippable or not
|
||||
@@ -84,6 +88,7 @@ class YtLoungeApi(pyytlounge.YtLoungeApi):
|
||||
data = args[0]
|
||||
# Gets segments for the next video (after the ad) before it starts playing
|
||||
if vid_id := data["contentVideoId"]:
|
||||
print(f"Getting segments for next video: {vid_id}")
|
||||
create_task(self.api_helper.get_segments(vid_id))
|
||||
|
||||
if data["isSkippable"] == "true": # YouTube uses strings for booleans
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from iSponsorBlockTV import setup_wizard
|
||||
from iSponsorBlockTV.helpers import Config
|
||||
|
||||
config = Config("config.json")
|
||||
config = Config("data/config.json")
|
||||
setup_wizard.main(config)
|
||||
Reference in New Issue
Block a user