mirror of
https://github.com/dmunozv04/iSponsorBlockTV.git
synced 2025-12-13 23:46:43 +03:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d17e59bf0d | ||
|
|
205191f442 | ||
|
|
810cd5eec3 | ||
|
|
e2ace8629f | ||
|
|
e54ead26d2 | ||
|
|
49fba2f28f | ||
|
|
b1333a2f61 | ||
|
|
cfef219d32 | ||
|
|
338e0479ba | ||
|
|
bfefa94a7b | ||
|
|
783e3d4240 | ||
|
|
015f5a79c9 | ||
|
|
dc72db0609 | ||
|
|
8de38cc92b | ||
|
|
94ba642af1 | ||
|
|
6e09db9994 | ||
|
|
b56d7443d1 | ||
|
|
e92ba897c4 | ||
|
|
5214190fd0 | ||
|
|
d3341009a6 | ||
|
|
adc0f5b95d | ||
|
|
5dbd16ddd5 | ||
|
|
faa0379b89 | ||
|
|
fb3ed6b39a | ||
|
|
1ab7e73b52 | ||
|
|
d310e4c817 | ||
|
|
d21bb6320f | ||
|
|
dd42e20dc4 | ||
|
|
213ae97bf2 | ||
|
|
865f5469a2 | ||
|
|
daa7026221 | ||
|
|
582b9bf725 | ||
|
|
ce95b6dbf0 | ||
|
|
80196b19aa | ||
|
|
4dd6aa1c4d | ||
|
|
1a5f29fe2a | ||
|
|
e689a713ef | ||
|
|
e6b1e14d80 | ||
|
|
4934eff8b7 | ||
|
|
674a13e43a |
16
.github/workflows/build_docker_images.yml
vendored
16
.github/workflows/build_docker_images.yml
vendored
@@ -25,12 +25,12 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
# Get the repository's code
|
# Get the repository's code
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# Generate docker tags
|
# Generate docker tags
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: ghcr.io/dmunozv04/isponsorblocktv, dmunozv04/isponsorblocktv
|
images: ghcr.io/dmunozv04/isponsorblocktv, dmunozv04/isponsorblocktv
|
||||||
tags: |
|
tags: |
|
||||||
@@ -42,32 +42,32 @@ jobs:
|
|||||||
|
|
||||||
# https://github.com/docker/setup-qemu-action
|
# https://github.com/docker/setup-qemu-action
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
# https://github.com/docker/setup-buildx-action
|
# https://github.com/docker/setup-buildx-action
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
id: buildx
|
id: buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Login to GHCR
|
- name: Login to GHCR
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64, linux/arm64
|
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 }}
|
||||||
|
|||||||
4
.github/workflows/release_pypi.yml
vendored
4
.github/workflows/release_pypi.yml
vendored
@@ -22,9 +22,9 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v3
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: '3.11'
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
|
|||||||
2
.github/workflows/update_docker_readme.yml
vendored
2
.github/workflows/update_docker_readme.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
|||||||
|
|
||||||
# Update description
|
# Update description
|
||||||
- name: Update repo description
|
- name: Update repo description
|
||||||
uses: peter-evans/dockerhub-description@v3
|
uses: peter-evans/dockerhub-description@v4
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|||||||
26
Dockerfile
26
Dockerfile
@@ -1,5 +1,7 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
FROM python:3.11-alpine3.19 as compiler
|
FROM python:3.11-alpine3.19 as BASE
|
||||||
|
|
||||||
|
FROM base as compiler
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
@@ -8,18 +10,26 @@ COPY src .
|
|||||||
RUN python3 -m compileall -b -f . && \
|
RUN python3 -m compileall -b -f . && \
|
||||||
find . -name "*.py" -type f -delete
|
find . -name "*.py" -type f -delete
|
||||||
|
|
||||||
FROM python:3.11-alpine3.19
|
FROM base as DEP_INSTALLER
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
|
||||||
|
RUN apk add --no-cache gcc musl-dev && \
|
||||||
|
pip install --upgrade pip wheel && \
|
||||||
|
pip install -r requirements.txt && \
|
||||||
|
pip uninstall -y pip wheel && \
|
||||||
|
apk del gcc musl-dev && \
|
||||||
|
python3 -m compileall -b -f /usr/local/lib/python3.11/site-packages && \
|
||||||
|
find /usr/local/lib/python3.11/site-packages -name "*.py" -type f -delete && \
|
||||||
|
find /usr/local/lib/python3.11/ -name "__pycache__" -type d -exec rm -rf {} +
|
||||||
|
|
||||||
|
FROM base
|
||||||
|
|
||||||
ENV PIP_NO_CACHE_DIR=off iSPBTV_docker=True iSPBTV_data_dir=data TERM=xterm-256color COLORTERM=truecolor
|
ENV PIP_NO_CACHE_DIR=off iSPBTV_docker=True iSPBTV_data_dir=data TERM=xterm-256color COLORTERM=truecolor
|
||||||
|
|
||||||
COPY requirements.txt .
|
COPY requirements.txt .
|
||||||
|
|
||||||
RUN pip install --upgrade pip wheel && \
|
COPY --from=dep_installer /usr/local /usr/local
|
||||||
pip install -r requirements.txt && \
|
|
||||||
pip uninstall -y pip wheel && \
|
|
||||||
python3 -m compileall -b -f /usr/local/lib/python3.11/site-packages && \
|
|
||||||
find /usr/local/lib/python3.11/site-packages -name "*.py" -type f -delete && \
|
|
||||||
find /usr/local/lib/python3.11/ -name "__pycache__" -type d -exec rm -rf {} +
|
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"skip_count_tracking": true,
|
"skip_count_tracking": true,
|
||||||
"mute_ads": true,
|
"mute_ads": true,
|
||||||
"skip_ads": true,
|
"skip_ads": true,
|
||||||
|
"autoplay": true,
|
||||||
"apikey": "",
|
"apikey": "",
|
||||||
"channel_whitelist": [
|
"channel_whitelist": [
|
||||||
{"id": "",
|
{"id": "",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "iSponsorBlockTV"
|
name = "iSponsorBlockTV"
|
||||||
version = "2.0.6"
|
version = "2.1.0"
|
||||||
authors = [
|
authors = [
|
||||||
{"name" = "dmunozv04"}
|
{"name" = "dmunozv04"}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
aiohttp==3.9.0
|
aiohttp==3.9.5
|
||||||
appdirs==1.4.4
|
appdirs==1.4.4
|
||||||
argparse==1.4.0
|
argparse==1.4.0
|
||||||
async-cache==1.1.1
|
async-cache==1.1.1
|
||||||
pyytlounge==1.6.3
|
pyytlounge==2.0.0
|
||||||
rich==13.6.0
|
rich==13.7.1
|
||||||
ssdp==1.3.0
|
ssdp==1.3.0
|
||||||
textual==0.40.0
|
textual==0.58.0
|
||||||
textual-slider==0.1.1
|
textual-slider==0.1.1
|
||||||
xmltodict==0.13.0
|
xmltodict==0.13.0
|
||||||
|
|||||||
@@ -1,167 +1,195 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
|
||||||
from . import api_helpers, ytlounge
|
from . import api_helpers, ytlounge
|
||||||
|
|
||||||
|
# Constants for user input prompts
|
||||||
async def pair_device():
|
ATVS_REMOVAL_PROMPT = (
|
||||||
try:
|
"Do you want to remove the legacy 'atvs' entry (the app won't start"
|
||||||
lounge_controller = ytlounge.YtLoungeApi("iSponsorBlockTV")
|
" with it present)? (y/N) "
|
||||||
pairing_code = input(
|
)
|
||||||
"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) "
|
||||||
pairing_code = int(
|
CHANGE_API_KEY_PROMPT = "API key already specified. Change it? (y/N) "
|
||||||
pairing_code.replace("-", "").replace(" ", "")
|
ADD_API_KEY_PROMPT = (
|
||||||
) # remove dashes and spaces
|
"API key only needed for the channel whitelist function. Add it? (y/N) "
|
||||||
print("Pairing...")
|
)
|
||||||
paired = await lounge_controller.pair(pairing_code)
|
ENTER_API_KEY_PROMPT = "Enter your API key: "
|
||||||
if not paired:
|
CHANGE_SKIP_CATEGORIES_PROMPT = "Skip categories already specified. Change them? (y/N) "
|
||||||
print("Failed to pair device")
|
ENTER_SKIP_CATEGORIES_PROMPT = (
|
||||||
return
|
"Enter skip categories (space or comma sepparated) Options: [sponsor,"
|
||||||
device = {
|
" selfpromo, exclusive_access, interaction, poi_highlight, intro, outro,"
|
||||||
"screen_id": lounge_controller.auth.screen_id,
|
" preview, filler, music_offtopic]:\n"
|
||||||
"name": lounge_controller.screen_name,
|
)
|
||||||
}
|
WHITELIST_CHANNELS_PROMPT = (
|
||||||
print(f"Paired device: {device['name']}")
|
"Do you want to whitelist any channels from being ad-blocked? (y/N) "
|
||||||
return device
|
)
|
||||||
except Exception as e:
|
SEARCH_CHANNEL_PROMPT = 'Enter a channel name or "/exit" to exit: '
|
||||||
print(f"Failed to pair device: {e}")
|
SELECT_CHANNEL_PROMPT = "Select one option of the above [0-6]: "
|
||||||
return
|
ENTER_CHANNEL_ID_PROMPT = "Enter a channel ID: "
|
||||||
|
ENTER_CUSTOM_CHANNEL_NAME_PROMPT = "Enter the channel name: "
|
||||||
|
REPORT_SKIPPED_SEGMENTS_PROMPT = (
|
||||||
def main(config, debug: bool) -> None:
|
"Do you want to report skipped segments to sponsorblock. Only the segment"
|
||||||
print("Welcome to the iSponsorBlockTV cli setup wizard")
|
" UUID will be sent? (Y/n) "
|
||||||
loop = asyncio.get_event_loop_policy().get_event_loop()
|
)
|
||||||
if debug:
|
MUTE_ADS_PROMPT = "Do you want to mute native YouTube ads automatically? (y/N) "
|
||||||
loop.set_debug(True)
|
SKIP_ADS_PROMPT = "Do you want to skip native YouTube ads automatically? (y/N) "
|
||||||
asyncio.set_event_loop(loop)
|
AUTOPLAY_PROMPT = "Do you want to enable autoplay? (Y/n) "
|
||||||
if hasattr(config, "atvs"):
|
|
||||||
print(
|
|
||||||
"The atvs config option is deprecated and has stopped working. Please read"
|
def get_yn_input(prompt):
|
||||||
" this for more information on how to upgrade to V2:"
|
while choice := input(prompt):
|
||||||
" \nhttps://github.com/dmunozv04/iSponsorBlockTV/wiki/Migrate-from-V1-to-V2"
|
if choice.lower() in ["y", "n"]:
|
||||||
)
|
return choice.lower()
|
||||||
if (
|
print("Invalid input. Please enter 'y' or 'n'.")
|
||||||
input(
|
|
||||||
"Do you want to remove the legacy 'atvs' entry (the app won't start"
|
|
||||||
" with it present)? (y/n) "
|
async def pair_device():
|
||||||
)
|
try:
|
||||||
== "y"
|
lounge_controller = ytlounge.YtLoungeApi("iSponsorBlockTV")
|
||||||
):
|
pairing_code = input(PAIRING_CODE_PROMPT)
|
||||||
del config["atvs"]
|
pairing_code = int(
|
||||||
devices = config.devices
|
pairing_code.replace("-", "").replace(" ", "")
|
||||||
while not input(f"Paired with {len(devices)} Device(s). Add more? (y/n) ") == "n":
|
) # remove dashes and spaces
|
||||||
task = loop.create_task(pair_device())
|
print("Pairing...")
|
||||||
loop.run_until_complete(task)
|
paired = await lounge_controller.pair(pairing_code)
|
||||||
device = task.result()
|
if not paired:
|
||||||
if device:
|
print("Failed to pair device")
|
||||||
devices.append(device)
|
return
|
||||||
config.devices = devices
|
device = {
|
||||||
|
"screen_id": lounge_controller.auth.screen_id,
|
||||||
apikey = config.apikey
|
"name": lounge_controller.screen_name,
|
||||||
if apikey:
|
}
|
||||||
if input("API key already specified. Change it? (y/n) ") == "y":
|
print(f"Paired device: {device['name']}")
|
||||||
apikey = input("Enter your API key: ")
|
return device
|
||||||
else:
|
except Exception as e:
|
||||||
if (
|
print(f"Failed to pair device: {e}")
|
||||||
input(
|
return
|
||||||
"API key only needed for the channel whitelist function. Add it? (y/n) "
|
|
||||||
)
|
|
||||||
== "y"
|
def main(config, debug: bool) -> None:
|
||||||
):
|
print("Welcome to the iSponsorBlockTV cli setup wizard")
|
||||||
print(
|
loop = asyncio.get_event_loop_policy().get_event_loop()
|
||||||
"Get youtube apikey here:"
|
web_session = aiohttp.ClientSession()
|
||||||
" https://developers.google.com/youtube/registering_an_application"
|
if debug:
|
||||||
)
|
loop.set_debug(True)
|
||||||
apikey = input("Enter your API key: ")
|
asyncio.set_event_loop(loop)
|
||||||
config.apikey = apikey
|
if hasattr(config, "atvs"):
|
||||||
|
print(
|
||||||
skip_categories = config.skip_categories
|
"The atvs config option is deprecated and has stopped working. Please read"
|
||||||
if skip_categories:
|
" this for more information on how to upgrade to V2:"
|
||||||
if input("Skip categories already specified. Change them? (y/n) ") == "y":
|
" \nhttps://github.com/dmunozv04/iSponsorBlockTV/wiki/Migrate-from-V1-to-V2"
|
||||||
categories = input(
|
)
|
||||||
"Enter skip categories (space or comma sepparated) Options: [sponsor"
|
choice = get_yn_input(ATVS_REMOVAL_PROMPT)
|
||||||
" selfpromo exclusive_access interaction poi_highlight intro outro"
|
if choice == "y":
|
||||||
" preview filler music_offtopic]:\n"
|
del config["atvs"]
|
||||||
)
|
|
||||||
skip_categories = categories.replace(",", " ").split(" ")
|
devices = config.devices
|
||||||
skip_categories = [
|
choice = get_yn_input(ADD_MORE_DEVICES_PROMPT.format(num_devices=len(devices)))
|
||||||
x for x in skip_categories if x != ""
|
while choice == "y":
|
||||||
] # Remove empty strings
|
task = loop.create_task(pair_device())
|
||||||
else:
|
loop.run_until_complete(task)
|
||||||
categories = input(
|
device = task.result()
|
||||||
"Enter skip categories (space or comma sepparated) Options: [sponsor,"
|
if device:
|
||||||
" selfpromo, exclusive_access, interaction, poi_highlight, intro, outro,"
|
devices.append(device)
|
||||||
" preview, filler, music_offtopic:\n"
|
choice = get_yn_input(ADD_MORE_DEVICES_PROMPT.format(num_devices=len(devices)))
|
||||||
)
|
config.devices = devices
|
||||||
skip_categories = categories.replace(",", " ").split(" ")
|
|
||||||
skip_categories = [
|
apikey = config.apikey
|
||||||
x for x in skip_categories if x != ""
|
if apikey:
|
||||||
] # Remove empty strings
|
choice = get_yn_input(CHANGE_API_KEY_PROMPT)
|
||||||
config.skip_categories = skip_categories
|
if choice == "y":
|
||||||
|
apikey = input(ENTER_API_KEY_PROMPT)
|
||||||
channel_whitelist = config.channel_whitelist
|
else:
|
||||||
if (
|
choice = get_yn_input(ADD_API_KEY_PROMPT)
|
||||||
input("Do you want to whitelist any channels from being ad-blocked? (y/n) ")
|
if choice == "y":
|
||||||
== "y"
|
print(
|
||||||
):
|
"Get youtube apikey here:"
|
||||||
if not apikey:
|
" https://developers.google.com/youtube/registering_an_application"
|
||||||
print(
|
)
|
||||||
"WARNING: You need to specify an API key to use this function,"
|
apikey = input(ENTER_API_KEY_PROMPT)
|
||||||
" otherwise the program will fail to start.\nYou can add one by"
|
config.apikey = apikey
|
||||||
" re-running this setup wizard."
|
|
||||||
)
|
skip_categories = config.skip_categories
|
||||||
web_session = aiohttp.ClientSession()
|
if skip_categories:
|
||||||
api_helper = api_helpers.ApiHelper(config, web_session)
|
choice = get_yn_input(CHANGE_SKIP_CATEGORIES_PROMPT)
|
||||||
while True:
|
if choice == "y":
|
||||||
channel_info = {}
|
categories = input(ENTER_SKIP_CATEGORIES_PROMPT)
|
||||||
channel = input('Enter a channel name or "/exit" to exit: ')
|
skip_categories = categories.replace(",", " ").split(" ")
|
||||||
if channel == "/exit":
|
skip_categories = [
|
||||||
break
|
x for x in skip_categories if x != ""
|
||||||
|
] # Remove empty strings
|
||||||
task = loop.create_task(
|
else:
|
||||||
api_helper.search_channels(channel, apikey, web_session)
|
categories = input(ENTER_SKIP_CATEGORIES_PROMPT)
|
||||||
)
|
skip_categories = categories.replace(",", " ").split(" ")
|
||||||
loop.run_until_complete(task)
|
skip_categories = [
|
||||||
results = task.result()
|
x for x in skip_categories if x != ""
|
||||||
if len(results) == 0:
|
] # Remove empty strings
|
||||||
print("No channels found")
|
config.skip_categories = skip_categories
|
||||||
continue
|
|
||||||
|
channel_whitelist = config.channel_whitelist
|
||||||
for i, item in enumerate(results):
|
choice = get_yn_input(WHITELIST_CHANNELS_PROMPT)
|
||||||
print(f"{i}: {item[1]} - Subs: {item[2]}")
|
if choice == "y":
|
||||||
print("5: Enter a custom channel ID")
|
if not apikey:
|
||||||
print("6: Go back")
|
print(
|
||||||
|
"WARNING: You need to specify an API key to use this function,"
|
||||||
choice = -1
|
" otherwise the program will fail to start.\nYou can add one by"
|
||||||
choice = input("Select one option of the above [0-6]: ")
|
" re-running this setup wizard."
|
||||||
while choice not in [str(x) for x in range(7)]:
|
)
|
||||||
print("Invalid choice")
|
api_helper = api_helpers.ApiHelper(config, web_session)
|
||||||
choice = input("Select one option of the above [0-6]: ")
|
while True:
|
||||||
|
channel_info = {}
|
||||||
if choice == "5":
|
channel = input(SEARCH_CHANNEL_PROMPT)
|
||||||
channel_info["id"] = input("Enter a channel ID: ")
|
if channel == "/exit":
|
||||||
channel_info["name"] = input("Enter the channel name: ")
|
break
|
||||||
channel_whitelist.append(channel_info)
|
|
||||||
continue
|
task = loop.create_task(
|
||||||
if choice == "6":
|
api_helper.search_channels(channel, apikey, web_session)
|
||||||
continue
|
)
|
||||||
|
loop.run_until_complete(task)
|
||||||
channel_info["id"] = results[int(choice)][0]
|
results = task.result()
|
||||||
channel_info["name"] = results[int(choice)][1]
|
if len(results) == 0:
|
||||||
channel_whitelist.append(channel_info)
|
print("No channels found")
|
||||||
# Close web session asynchronously
|
continue
|
||||||
loop.run_until_complete(web_session.close())
|
|
||||||
|
for i, item in enumerate(results):
|
||||||
config.channel_whitelist = channel_whitelist
|
print(f"{i}: {item[1]} - Subs: {item[2]}")
|
||||||
|
print("5: Enter a custom channel ID")
|
||||||
config.skip_count_tracking = (
|
print("6: Go back")
|
||||||
not input(
|
|
||||||
"Do you want to report skipped segments to sponsorblock. Only the segment"
|
while choice := input(SELECT_CHANNEL_PROMPT):
|
||||||
" UUID will be sent? (y/n) "
|
if choice in [str(x) for x in range(7)]:
|
||||||
)
|
break
|
||||||
== "n"
|
print("Invalid choice")
|
||||||
)
|
|
||||||
print("Config finished")
|
if choice == "5":
|
||||||
config.save()
|
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())
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ class Config:
|
|||||||
self.skip_count_tracking = True
|
self.skip_count_tracking = True
|
||||||
self.mute_ads = False
|
self.mute_ads = False
|
||||||
self.skip_ads = False
|
self.skip_ads = False
|
||||||
|
self.auto_play = True
|
||||||
self.__load()
|
self.__load()
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
|||||||
@@ -1,160 +0,0 @@
|
|||||||
import logging
|
|
||||||
|
|
||||||
from rich.logging import RichHandler
|
|
||||||
from rich.style import Style
|
|
||||||
from rich.text import Text
|
|
||||||
|
|
||||||
"""
|
|
||||||
Copyright (c) 2020 Will McGugan
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
Modified code from rich (https://github.com/textualize/rich)
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class LogHandler(RichHandler):
|
|
||||||
def __init__(self, device_name, log_name_len, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.filter_strings = []
|
|
||||||
self._log_render = LogRender(
|
|
||||||
device_name=device_name,
|
|
||||||
log_name_len=log_name_len,
|
|
||||||
show_time=True,
|
|
||||||
show_level=True,
|
|
||||||
show_path=False,
|
|
||||||
time_format="[%x %X]",
|
|
||||||
omit_repeated_times=True,
|
|
||||||
level_width=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
def add_filter_string(self, s):
|
|
||||||
self.filter_strings.append(s)
|
|
||||||
|
|
||||||
def _filter(self, s):
|
|
||||||
for i in self.filter_strings:
|
|
||||||
s = s.replace(i, "REDACTED")
|
|
||||||
return s
|
|
||||||
|
|
||||||
def format(self, record):
|
|
||||||
original = super().format(record)
|
|
||||||
return self._filter(original)
|
|
||||||
|
|
||||||
|
|
||||||
class LogRender:
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
device_name,
|
|
||||||
log_name_len,
|
|
||||||
show_time=True,
|
|
||||||
show_level=False,
|
|
||||||
show_path=True,
|
|
||||||
time_format="[%x %X]",
|
|
||||||
omit_repeated_times=True,
|
|
||||||
level_width=8,
|
|
||||||
):
|
|
||||||
self.filter_strings = []
|
|
||||||
self.log_name_len = log_name_len
|
|
||||||
self.device_name = device_name
|
|
||||||
self.show_time = show_time
|
|
||||||
self.show_level = show_level
|
|
||||||
self.show_path = show_path
|
|
||||||
self.time_format = time_format
|
|
||||||
self.omit_repeated_times = omit_repeated_times
|
|
||||||
self.level_width = level_width
|
|
||||||
self._last_time = None
|
|
||||||
|
|
||||||
def __call__(
|
|
||||||
self,
|
|
||||||
console,
|
|
||||||
renderables,
|
|
||||||
log_time,
|
|
||||||
time_format=None,
|
|
||||||
level="",
|
|
||||||
path=None,
|
|
||||||
line_no=None,
|
|
||||||
link_path=None,
|
|
||||||
):
|
|
||||||
from rich.containers import Renderables
|
|
||||||
from rich.table import Table
|
|
||||||
|
|
||||||
output = Table.grid(padding=(0, 1))
|
|
||||||
output.expand = True
|
|
||||||
if self.show_time:
|
|
||||||
output.add_column(style="log.time")
|
|
||||||
if self.show_level:
|
|
||||||
output.add_column(style="log.level", width=self.level_width)
|
|
||||||
output.add_column(
|
|
||||||
width=self.log_name_len, style=Style(color="yellow"), overflow="fold"
|
|
||||||
)
|
|
||||||
output.add_column(ratio=1, style="log.message", overflow="fold")
|
|
||||||
if self.show_path and path:
|
|
||||||
output.add_column(style="log.path")
|
|
||||||
row = []
|
|
||||||
if self.show_time:
|
|
||||||
log_time = log_time or console.get_datetime()
|
|
||||||
time_format = time_format or self.time_format
|
|
||||||
if callable(time_format):
|
|
||||||
log_time_display = time_format(log_time)
|
|
||||||
else:
|
|
||||||
log_time_display = Text(log_time.strftime(time_format))
|
|
||||||
if log_time_display == self._last_time and self.omit_repeated_times:
|
|
||||||
row.append(Text(" " * len(log_time_display)))
|
|
||||||
else:
|
|
||||||
row.append(log_time_display)
|
|
||||||
self._last_time = log_time_display
|
|
||||||
if self.show_level:
|
|
||||||
row.append(level)
|
|
||||||
row.append(Text(self.device_name))
|
|
||||||
row.append(Renderables(renderables))
|
|
||||||
if self.show_path and path:
|
|
||||||
path_text = Text()
|
|
||||||
path_text.append(
|
|
||||||
path, style=f"link file://{link_path}" if link_path else ""
|
|
||||||
)
|
|
||||||
if line_no:
|
|
||||||
path_text.append(":")
|
|
||||||
path_text.append(
|
|
||||||
f"{line_no}",
|
|
||||||
style=f"link file://{link_path}#{line_no}" if link_path else "",
|
|
||||||
)
|
|
||||||
row.append(path_text)
|
|
||||||
|
|
||||||
output.add_row(*row)
|
|
||||||
return output
|
|
||||||
|
|
||||||
|
|
||||||
class LogFormatter(logging.Formatter):
|
|
||||||
def __init__(
|
|
||||||
self, fmt=None, datefmt=None, style="%", validate=True, filter_strings=None
|
|
||||||
):
|
|
||||||
super().__init__(fmt, datefmt, style, validate)
|
|
||||||
self.filter_strings = filter_strings or []
|
|
||||||
|
|
||||||
def add_filter_string(self, s):
|
|
||||||
self.filter_strings.append(s)
|
|
||||||
|
|
||||||
def _filter(self, s):
|
|
||||||
print(s)
|
|
||||||
for i in self.filter_strings:
|
|
||||||
s = s.replace(i, "REDACTED")
|
|
||||||
return s
|
|
||||||
|
|
||||||
def format(self, record):
|
|
||||||
original = logging.Formatter.format(self, record)
|
|
||||||
return self._filter(original)
|
|
||||||
@@ -5,29 +5,31 @@ from signal import SIGINT, SIGTERM, signal
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import rich
|
|
||||||
|
|
||||||
from . import api_helpers, logging_helpers, ytlounge
|
from . import api_helpers, ytlounge
|
||||||
|
|
||||||
|
|
||||||
class DeviceListener:
|
class DeviceListener:
|
||||||
def __init__(self, api_helper, config, device, log_name_len, debug: bool):
|
def __init__(self, api_helper, config, device, debug: bool, web_session):
|
||||||
self.task: Optional[asyncio.Task] = None
|
self.task: Optional[asyncio.Task] = None
|
||||||
self.api_helper = api_helper
|
self.api_helper = api_helper
|
||||||
self.offset = device.offset
|
self.offset = device.offset
|
||||||
self.name = device.name
|
self.name = device.name
|
||||||
self.cancelled = False
|
self.cancelled = False
|
||||||
self.logger = logging.getLogger(f"iSponsorBlockTV-{device.screen_id}")
|
self.logger = logging.getLogger(f"iSponsorBlockTV-{device.screen_id}")
|
||||||
|
self.web_session = web_session
|
||||||
if debug:
|
if debug:
|
||||||
self.logger.setLevel(logging.DEBUG)
|
self.logger.setLevel(logging.DEBUG)
|
||||||
else:
|
else:
|
||||||
self.logger.setLevel(logging.INFO)
|
self.logger.setLevel(logging.INFO)
|
||||||
rh = logging_helpers.LogHandler(device.name, log_name_len, level=logging.DEBUG)
|
sh = logging.StreamHandler()
|
||||||
rh.add_filter_string(device.screen_id)
|
sh.setFormatter(
|
||||||
self.logger.addHandler(rh)
|
logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
||||||
|
)
|
||||||
|
self.logger.addHandler(sh)
|
||||||
self.logger.info(f"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
|
||||||
@@ -50,13 +52,13 @@ class DeviceListener:
|
|||||||
# Main subscription loop
|
# Main subscription loop
|
||||||
async def loop(self):
|
async def loop(self):
|
||||||
lounge_controller = self.lounge_controller
|
lounge_controller = self.lounge_controller
|
||||||
while not lounge_controller.linked():
|
|
||||||
try:
|
|
||||||
self.logger.debug("Refreshing auth")
|
|
||||||
await lounge_controller.refresh_auth()
|
|
||||||
except:
|
|
||||||
await asyncio.sleep(10)
|
|
||||||
while not self.cancelled:
|
while not self.cancelled:
|
||||||
|
while not lounge_controller.linked():
|
||||||
|
try:
|
||||||
|
self.logger.debug("Refreshing auth")
|
||||||
|
await lounge_controller.refresh_auth()
|
||||||
|
except:
|
||||||
|
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:
|
||||||
@@ -74,7 +76,7 @@ class DeviceListener:
|
|||||||
"Connected to device %s (%s)", lounge_controller.screen_name, self.name
|
"Connected to device %s (%s)", lounge_controller.screen_name, self.name
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
# print("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:
|
except:
|
||||||
@@ -153,16 +155,16 @@ def main(config, debug):
|
|||||||
tcp_connector = aiohttp.TCPConnector(ttl_dns_cache=300)
|
tcp_connector = aiohttp.TCPConnector(ttl_dns_cache=300)
|
||||||
web_session = aiohttp.ClientSession(loop=loop, 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)
|
||||||
longest_name_len = len(list(sorted([i.name for i in config.devices]))[-1])
|
|
||||||
for i in config.devices:
|
for i in config.devices:
|
||||||
device = DeviceListener(api_helper, config, i, longest_name_len, debug)
|
device = DeviceListener(api_helper, config, i, debug, web_session)
|
||||||
devices.append(device)
|
devices.append(device)
|
||||||
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(SIGINT, lambda s, f: loop.stop())
|
signal(SIGINT, lambda s, f: loop.stop())
|
||||||
signal(SIGTERM, lambda s, f: loop.stop())
|
signal(SIGTERM, lambda s, f: loop.stop())
|
||||||
rich.reconfigure(color_system="standard")
|
|
||||||
loop.run_forever()
|
loop.run_forever()
|
||||||
print("Cancelling tasks and exiting...")
|
print("Cancelling tasks and exiting...")
|
||||||
loop.run_until_complete(finish(devices))
|
loop.run_until_complete(finish(devices))
|
||||||
loop.run_until_complete(web_session.close())
|
loop.run_until_complete(web_session.close())
|
||||||
|
loop.run_until_complete(tcp_connector.close())
|
||||||
|
loop.close()
|
||||||
|
|||||||
@@ -363,3 +363,9 @@ MigrationScreen {
|
|||||||
width: 1fr;
|
width: 1fr;
|
||||||
content-align: center middle;
|
content-align: center middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Autoplay */
|
||||||
|
#autoplay-container{
|
||||||
|
padding: 1;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|||||||
@@ -102,7 +102,6 @@ class Device(Element):
|
|||||||
"""A device element."""
|
"""A device element."""
|
||||||
|
|
||||||
def process_values_from_data(self):
|
def process_values_from_data(self):
|
||||||
print("HIIII")
|
|
||||||
print(self.element_data)
|
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"]
|
||||||
@@ -235,8 +234,8 @@ class AddDevice(ModalWithClickExit):
|
|||||||
def __init__(self, config, **kwargs) -> None:
|
def __init__(self, config, **kwargs) -> None:
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.config = config
|
self.config = config
|
||||||
web_session = aiohttp.ClientSession()
|
self.web_session = aiohttp.ClientSession()
|
||||||
self.api_helper = api_helpers.ApiHelper(config, web_session)
|
self.api_helper = api_helpers.ApiHelper(config, self.web_session)
|
||||||
self.devices_discovered_dial = []
|
self.devices_discovered_dial = []
|
||||||
|
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
@@ -337,7 +336,9 @@ class AddDevice(ModalWithClickExit):
|
|||||||
@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("iSponsorBlockTV")
|
lounge_controller = ytlounge.YtLoungeApi(
|
||||||
|
"iSponsorBlockTV", 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(" ", "")
|
||||||
@@ -849,6 +850,32 @@ class ChannelWhitelistManager(Vertical):
|
|||||||
self.app.push_screen(AddChannel(self.config), callback=self.new_channel)
|
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):
|
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"""
|
||||||
|
|
||||||
@@ -885,6 +912,9 @@ class ISponsorBlockTVSetupMainScreen(Screen):
|
|||||||
yield ApiKeyManager(
|
yield ApiKeyManager(
|
||||||
config=self.config, id="api-key-manager", classes="container"
|
config=self.config, id="api-key-manager", classes="container"
|
||||||
)
|
)
|
||||||
|
yield AutoPlayManager(
|
||||||
|
config=self.config, id="autoplay-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():
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import asyncio
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
import pyytlounge
|
import pyytlounge
|
||||||
|
from aiohttp import ClientSession
|
||||||
|
|
||||||
from .constants import youtube_client_blacklist
|
from .constants import youtube_client_blacklist
|
||||||
|
|
||||||
@@ -9,8 +10,17 @@ create_task = asyncio.create_task
|
|||||||
|
|
||||||
|
|
||||||
class YtLoungeApi(pyytlounge.YtLoungeApi):
|
class YtLoungeApi(pyytlounge.YtLoungeApi):
|
||||||
def __init__(self, screen_id, config=None, api_helper=None, logger=None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
screen_id,
|
||||||
|
config=None,
|
||||||
|
api_helper=None,
|
||||||
|
logger=None,
|
||||||
|
web_session: ClientSession = None,
|
||||||
|
):
|
||||||
super().__init__("iSponsorBlockTV", logger=logger)
|
super().__init__("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
|
||||||
@@ -20,9 +30,11 @@ class YtLoungeApi(pyytlounge.YtLoungeApi):
|
|||||||
self.callback = None
|
self.callback = None
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
self.shorts_disconnected = False
|
self.shorts_disconnected = False
|
||||||
|
self.auto_play = True
|
||||||
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
|
||||||
|
self.auto_play = config.auto_play
|
||||||
|
|
||||||
# Ensures that we still are subscribed to the lounge
|
# Ensures that we still are subscribed to the lounge
|
||||||
async def _watchdog(self):
|
async def _watchdog(self):
|
||||||
@@ -67,23 +79,23 @@ class YtLoungeApi(pyytlounge.YtLoungeApi):
|
|||||||
data = args[0]
|
data = args[0]
|
||||||
# Unmute when the video starts playing
|
# Unmute when the video starts playing
|
||||||
if self.mute_ads and data.get("state", "0") == "1":
|
if self.mute_ads and data.get("state", "0") == "1":
|
||||||
# print("Ad has ended, unmuting")
|
self.logger.info("Ad has ended, unmuting")
|
||||||
create_task(self.mute(False, override=True))
|
create_task(self.mute(False, override=True))
|
||||||
elif event_type == "onAdStateChange":
|
elif event_type == "onAdStateChange":
|
||||||
data = args[0]
|
data = args[0]
|
||||||
if data["adState"] == "0": # Ad is not playing
|
if data["adState"] == "0": # Ad is not playing
|
||||||
# print("Ad has ended, unmuting")
|
self.logger.info("Ad has ended, unmuting")
|
||||||
create_task(self.mute(False, override=True))
|
create_task(self.mute(False, override=True))
|
||||||
elif (
|
elif (
|
||||||
self.skip_ads and data["isSkipEnabled"] == "true"
|
self.skip_ads and data["isSkipEnabled"] == "true"
|
||||||
): # YouTube uses strings for booleans
|
): # YouTube uses strings for booleans
|
||||||
self._logger.info("Ad can be skipped, skipping")
|
self.logger.info("Ad can be skipped, skipping")
|
||||||
create_task(self.skip_ad())
|
create_task(self.skip_ad())
|
||||||
create_task(self.mute(False, override=True))
|
create_task(self.mute(False, override=True))
|
||||||
elif (
|
elif (
|
||||||
self.mute_ads
|
self.mute_ads
|
||||||
): # Seen multiple other adStates, assuming they are all ads
|
): # Seen multiple other adStates, assuming they are all ads
|
||||||
self._logger.info("Ad has started, muting")
|
self.logger.info("Ad has started, muting")
|
||||||
create_task(self.mute(True, override=True))
|
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)
|
# Manages volume, useful since YouTube wants to know the volume when unmuting (even if they already have it)
|
||||||
elif event_type == "onVolumeChanged":
|
elif event_type == "onVolumeChanged":
|
||||||
@@ -94,7 +106,7 @@ class YtLoungeApi(pyytlounge.YtLoungeApi):
|
|||||||
if len(args) > 0 and (
|
if len(args) > 0 and (
|
||||||
vid_id := args[0]["videoId"]
|
vid_id := args[0]["videoId"]
|
||||||
): # if video id is not empty
|
): # if video id is not empty
|
||||||
print(f"Getting segments for next video: {vid_id}")
|
self.logger.info(f"Getting segments for next video: {vid_id}")
|
||||||
create_task(self.api_helper.get_segments(vid_id))
|
create_task(self.api_helper.get_segments(vid_id))
|
||||||
|
|
||||||
# #Used to know if an ad is skippable or not
|
# #Used to know if an ad is skippable or not
|
||||||
@@ -102,18 +114,18 @@ class YtLoungeApi(pyytlounge.YtLoungeApi):
|
|||||||
data = args[0]
|
data = args[0]
|
||||||
# Gets segments for the next video (after the ad) before it starts playing
|
# Gets segments for the next video (after the ad) before it starts playing
|
||||||
if vid_id := data["contentVideoId"]:
|
if vid_id := data["contentVideoId"]:
|
||||||
self._logger.info(f"Getting segments for next video: {vid_id}")
|
self.logger.info(f"Getting segments for next video: {vid_id}")
|
||||||
create_task(self.api_helper.get_segments(vid_id))
|
create_task(self.api_helper.get_segments(vid_id))
|
||||||
elif (
|
elif (
|
||||||
self.skip_ads and data["isSkipEnabled"] == "true"
|
self.skip_ads and data["isSkipEnabled"] == "true"
|
||||||
): # YouTube uses strings for booleans
|
): # YouTube uses strings for booleans
|
||||||
self._logger.info("Ad can be skipped, skipping")
|
self.logger.info("Ad can be skipped, skipping")
|
||||||
create_task(self.skip_ad())
|
create_task(self.skip_ad())
|
||||||
create_task(self.mute(False, override=True))
|
create_task(self.mute(False, override=True))
|
||||||
elif (
|
elif (
|
||||||
self.mute_ads
|
self.mute_ads
|
||||||
): # Seen multiple other adStates, assuming they are all ads
|
): # Seen multiple other adStates, assuming they are all ads
|
||||||
self._logger.info("Ad has started, muting")
|
self.logger.info("Ad has started, muting")
|
||||||
create_task(self.mute(True, override=True))
|
create_task(self.mute(True, override=True))
|
||||||
|
|
||||||
elif event_type == "loungeStatus":
|
elif event_type == "loungeStatus":
|
||||||
@@ -136,6 +148,8 @@ class YtLoungeApi(pyytlounge.YtLoungeApi):
|
|||||||
data = args[0]
|
data = args[0]
|
||||||
if data["reason"] == "disconnectedByUserScreenInitiated": # Short playing?
|
if data["reason"] == "disconnectedByUserScreenInitiated": # Short playing?
|
||||||
self.shorts_disconnected = True
|
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_id, event_type, args)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user