mirror of
https://github.com/dmunozv04/iSponsorBlockTV.git
synced 2025-12-06 11:56:45 +03:00
Compare commits
1 Commits
add-custom
...
pr/161
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
afc4ee7410 |
198
.github/workflows/release.yml
vendored
198
.github/workflows/release.yml
vendored
@@ -1,198 +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: 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.target }} (${{ 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: aarch64-unknown-linux-gnu
|
||||
os: ubuntu-latest
|
||||
cross: true
|
||||
release_suffix: aarch64-linux
|
||||
# Windows
|
||||
- target: x86_64-pc-windows-msvc
|
||||
os: windows-2022
|
||||
release_suffix: x86_64-windows
|
||||
# macOS
|
||||
- target: aarch64-apple-darwin
|
||||
os: macos-14
|
||||
release_suffix: aarch64-osx
|
||||
- target: x86_64-apple-darwin
|
||||
os: macos-12
|
||||
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_REPO: pyapp # Use local copy of pyapp (needed for cross-compiling)
|
||||
PYAPP_VERSION: v0.23.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/*
|
||||
37
.github/workflows/release_pypi.yml
vendored
Normal file
37
.github/workflows/release_pypi.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
# 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@v4
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
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
|
||||
@@ -1,7 +1,7 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM python:3.11-alpine3.19 AS base
|
||||
FROM python:3.11-alpine3.19 as BASE
|
||||
|
||||
FROM base AS compiler
|
||||
FROM base as compiler
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
@@ -10,7 +10,7 @@ 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 .
|
||||
|
||||
|
||||
@@ -12,12 +12,11 @@
|
||||
"skip_count_tracking": true,
|
||||
"mute_ads": true,
|
||||
"skip_ads": true,
|
||||
"auto_play": true,
|
||||
"autoplay": true,
|
||||
"apikey": "",
|
||||
"channel_whitelist": [
|
||||
{"id": "",
|
||||
"name": ""
|
||||
}
|
||||
],
|
||||
"api_server": "https://sponsor.ajay.app"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "iSponsorBlockTV"
|
||||
version = "2.2.1"
|
||||
version = "2.0.8"
|
||||
authors = [
|
||||
{"name" = "dmunozv04"}
|
||||
]
|
||||
|
||||
@@ -8,4 +8,3 @@ ssdp==1.3.0
|
||||
textual==0.58.0
|
||||
textual-slider==0.1.1
|
||||
xmltodict==0.13.0
|
||||
rich_click==1.8.3
|
||||
|
||||
@@ -27,7 +27,6 @@ class ApiHelper:
|
||||
self.skip_count_tracking = config.skip_count_tracking
|
||||
self.web_session = web_session
|
||||
self.num_devices = len(config.devices)
|
||||
self.api_server = config.api_server
|
||||
|
||||
# Not used anymore, maybe it can stay here a little longer
|
||||
@AsyncLRU(maxsize=10)
|
||||
@@ -131,7 +130,7 @@ class ApiHelper:
|
||||
"service": constants.SponsorBlock_service,
|
||||
}
|
||||
headers = {"Accept": "application/json"}
|
||||
url = self.api_server + "/api/skipSegments/" + vid_id_hashed
|
||||
url = constants.SponsorBlock_api + "skipSegments/" + vid_id_hashed
|
||||
async with self.web_session.get(
|
||||
url, headers=headers, params=params
|
||||
) as response:
|
||||
@@ -202,7 +201,7 @@ class ApiHelper:
|
||||
Lets the contributor know that someone skipped the segment (thanks)"""
|
||||
if self.skip_count_tracking:
|
||||
for i in uuids:
|
||||
url = self.api_server + "/api/viewedVideoSponsorTime/"
|
||||
url = constants.SponsorBlock_api + "viewedVideoSponsorTime/"
|
||||
params = {"UUID": i}
|
||||
await self.web_session.post(url, params=params)
|
||||
|
||||
|
||||
@@ -4,54 +4,15 @@ 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) "
|
||||
ENTER_API_SERVER_PROMPT = (
|
||||
"Enter the custom API server URL (leave blank to use default): "
|
||||
)
|
||||
|
||||
|
||||
def get_yn_input(prompt):
|
||||
while choice := input(prompt):
|
||||
if choice.lower() in ["y", "n"]:
|
||||
return choice.lower()
|
||||
print("Invalid input. Please enter 'y' or 'n'.")
|
||||
|
||||
|
||||
async def pair_device():
|
||||
async def pair_device(web_session):
|
||||
try:
|
||||
lounge_controller = ytlounge.YtLoungeApi("iSponsorBlockTV")
|
||||
pairing_code = input(PAIRING_CODE_PROMPT)
|
||||
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
|
||||
@@ -84,47 +45,59 @@ def main(config, debug: bool) -> None:
|
||||
" 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":
|
||||
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
|
||||
choice = get_yn_input(ADD_MORE_DEVICES_PROMPT.format(num_devices=len(devices)))
|
||||
while choice == "y":
|
||||
task = loop.create_task(pair_device())
|
||||
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)
|
||||
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)
|
||||
if input("API key already specified. Change it? (y/n) ") == "y":
|
||||
apikey = input("Enter your API key: ")
|
||||
else:
|
||||
choice = get_yn_input(ADD_API_KEY_PROMPT)
|
||||
if choice == "y":
|
||||
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_API_KEY_PROMPT)
|
||||
apikey = input("Enter your API key: ")
|
||||
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)
|
||||
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_PROMPT)
|
||||
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 != ""
|
||||
@@ -132,8 +105,10 @@ def main(config, debug: bool) -> None:
|
||||
config.skip_categories = skip_categories
|
||||
|
||||
channel_whitelist = config.channel_whitelist
|
||||
choice = get_yn_input(WHITELIST_CHANNELS_PROMPT)
|
||||
if choice == "y":
|
||||
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,"
|
||||
@@ -143,7 +118,7 @@ def main(config, debug: bool) -> None:
|
||||
api_helper = api_helpers.ApiHelper(config, web_session)
|
||||
while True:
|
||||
channel_info = {}
|
||||
channel = input(SEARCH_CHANNEL_PROMPT)
|
||||
channel = input('Enter a channel name or "/exit" to exit: ')
|
||||
if channel == "/exit":
|
||||
break
|
||||
|
||||
@@ -161,14 +136,15 @@ def main(config, debug: bool) -> None:
|
||||
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
|
||||
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_CHANNEL_ID_PROMPT)
|
||||
channel_info["name"] = input(ENTER_CUSTOM_CHANNEL_NAME_PROMPT)
|
||||
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":
|
||||
@@ -181,22 +157,15 @@ def main(config, debug: bool) -> None:
|
||||
|
||||
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"
|
||||
|
||||
api_server = input(ENTER_API_SERVER_PROMPT)
|
||||
if api_server:
|
||||
config.api_server = api_server
|
||||
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"
|
||||
)
|
||||
|
||||
config.auto_play = not input("Do you want to enable autoplay? (y/n) ") == "n"
|
||||
print("Config finished")
|
||||
config.save()
|
||||
loop.run_until_complete(web_session.close())
|
||||
|
||||
@@ -2,6 +2,7 @@ userAgent = "iSponsorBlockTV/0.1"
|
||||
SponsorBlock_service = "youtube"
|
||||
SponsorBlock_actiontype = "skip"
|
||||
|
||||
SponsorBlock_api = "https://sponsor.ajay.app/api/"
|
||||
Youtube_api = "https://www.googleapis.com/youtube/v3/"
|
||||
|
||||
skip_categories = (
|
||||
@@ -19,4 +20,5 @@ skip_categories = (
|
||||
|
||||
youtube_client_blacklist = ["TVHTML5_FOR_KIDS"]
|
||||
|
||||
|
||||
config_file_blacklist_keys = ["config_file", "data_dir"]
|
||||
|
||||
@@ -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
|
||||
@@ -42,7 +42,6 @@ class Config:
|
||||
self.mute_ads = False
|
||||
self.skip_ads = False
|
||||
self.auto_play = True
|
||||
self.api_server = "https://sponsor.ajay.app"
|
||||
self.__load()
|
||||
|
||||
def validate(self):
|
||||
@@ -127,93 +126,35 @@ class Config:
|
||||
return False
|
||||
|
||||
|
||||
@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 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={})
|
||||
# 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:
|
||||
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)
|
||||
|
||||
@@ -368,4 +368,4 @@ MigrationScreen {
|
||||
#autoplay-container{
|
||||
padding: 1;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
@@ -876,31 +876,6 @@ class AutoPlayManager(Vertical):
|
||||
self.config.auto_play = event.checkbox.value
|
||||
|
||||
|
||||
class ApiServerManager(Vertical):
|
||||
"""Manager for the custom API server URL."""
|
||||
|
||||
def __init__(self, config, **kwargs) -> None:
|
||||
super().__init__(**kwargs)
|
||||
self.config = config
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Label("Custom API Server", classes="title")
|
||||
yield Label(
|
||||
"You can specify a custom SponsorBlock API server URL here.",
|
||||
classes="subtitle",
|
||||
)
|
||||
with Grid(id="api-server-grid"):
|
||||
yield Input(
|
||||
placeholder="Custom API Server URL",
|
||||
id="api-server-input",
|
||||
value=self.config.api_server,
|
||||
)
|
||||
|
||||
@on(Input.Changed, "#api-server-input")
|
||||
def changed_api_server(self, event: Input.Changed):
|
||||
self.config.api_server = event.input.value
|
||||
|
||||
|
||||
class ISponsorBlockTVSetupMainScreen(Screen):
|
||||
"""Making this a separate screen to avoid a bug: https://github.com/Textualize/textual/issues/3221"""
|
||||
|
||||
@@ -940,9 +915,6 @@ class ISponsorBlockTVSetupMainScreen(Screen):
|
||||
yield AutoPlayManager(
|
||||
config=self.config, id="autoplay-manager", classes="container"
|
||||
)
|
||||
yield ApiServerManager(
|
||||
config=self.config, id="api-server-manager", classes="container"
|
||||
)
|
||||
|
||||
def on_mount(self) -> None:
|
||||
if self.check_for_old_config_entries():
|
||||
|
||||
@@ -35,7 +35,6 @@ class YtLoungeApi(pyytlounge.YtLoungeApi):
|
||||
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):
|
||||
@@ -146,12 +145,9 @@ class YtLoungeApi(pyytlounge.YtLoungeApi):
|
||||
self.shorts_disconnected = False
|
||||
create_task(self.play_video(video_id_saved))
|
||||
elif event_type == "loungeScreenDisconnected":
|
||||
if args: # Sometimes it's empty
|
||||
data = args[0]
|
||||
if (
|
||||
data["reason"] == "disconnectedByUserScreenInitiated"
|
||||
): # Short playing?
|
||||
self.shorts_disconnected = True
|
||||
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))
|
||||
|
||||
@@ -185,9 +181,3 @@ 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:
|
||||
return await super()._command(command, command_parameters)
|
||||
|
||||
Reference in New Issue
Block a user