Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
c931cf0
feat: add Live TV sources, program guides, and DVR
ReenigneArcher Oct 30, 2026
4efe063
feat: refine Live TV settings and source discovery
ReenigneArcher Oct 30, 2026
18bfdb0
fix: keep tuner discovery listening after UDP resets
ReenigneArcher Oct 30, 2026
eab6a9c
feat: show TV discoveries as selectable cards
ReenigneArcher Oct 30, 2026
a1ffac0
feat: add buffered Live TV playback and shared guide hero
ReenigneArcher Oct 30, 2026
8edcda1
fix: improve Live TV playback and guide controls
ReenigneArcher Oct 30, 2026
daac992
fix: stabilize Live TV controls and align the guide
ReenigneArcher Oct 30, 2026
93bd6d0
feat: add an in-player TV guide and stable guide navigation
ReenigneArcher Oct 31, 2026
4636e26
fix: honor guide artwork selection and center TV player navigation
ReenigneArcher Oct 31, 2026
a619063
feat: enrich Live TV artwork and bound guide retention
ReenigneArcher Oct 31, 2026
6ecd869
feat: scale Live TV imports and add IPTV provider management
ReenigneArcher Oct 31, 2026
6e1221a
feat: polish Live TV provider settings and fix IPTV playback
ReenigneArcher Nov 1, 2026
83dcefc
feat: refine Live TV guide navigation and recording dialogs
ReenigneArcher Nov 1, 2026
f39cca6
feat: add Live TV multiview playback
ReenigneArcher Nov 1, 2026
191cd86
fix: improve Live TV startup and source error recovery
ReenigneArcher Nov 1, 2026
f4fd53e
fix: stabilize live TV buffering and source recovery
ReenigneArcher Nov 1, 2026
1ce7dc1
style: remove extra blank line from Live TV setup
ReenigneArcher Nov 1, 2026
1551ff7
fix: repair native TV builds and common lint configuration
ReenigneArcher Nov 1, 2026
5aaf95a
refactor: resolve Live TV static analysis findings
ReenigneArcher Nov 1, 2026
72dfb72
fix: isolate native SDK release builds from Cargo debug settings
ReenigneArcher Nov 1, 2026
f593b60
refactor: simplify DVR capture workers and guide focus restoration
ReenigneArcher Nov 1, 2026
d167412
ci: generate API docs without compiling the native tuner runtime
ReenigneArcher Nov 1, 2026
5a2a0e5
ci: build the playback server before starting browser tests
ReenigneArcher Nov 1, 2026
f63e84a
ci: provide the GNU build tools required by TSDuck on macOS
ReenigneArcher Nov 2, 2026
76f890a
fix: restore incomplete cached native SDK headers
ReenigneArcher Nov 2, 2026
2cb5d46
build: use a pinned TSDuck submodule and C++23 bridge
ReenigneArcher Nov 2, 2026
905f979
ci: initialize build submodules through checkout
ReenigneArcher Nov 2, 2026
48d1553
build: derive TSDuck metadata and share offline packaging
ReenigneArcher Nov 2, 2026
64dd49c
fix: make native packaging tests and license bundling reliable
ReenigneArcher Nov 2, 2026
361c077
style: normalize the bundled GCC license ending
ReenigneArcher Nov 2, 2026
6fc587e
fix: validate SDK version metadata for safe cache paths
ReenigneArcher Nov 2, 2026
b68ecdb
refactor: use Zap2It names throughout Live TV
ReenigneArcher Nov 2, 2026
82c227c
test: use neutral fictional TV guide examples
ReenigneArcher Nov 2, 2026
61c0907
fix: keep release metadata out of cache path names
ReenigneArcher Nov 2, 2026
a95c099
ci: allow cold native builds in browser playback jobs
ReenigneArcher Nov 2, 2026
764142c
refactor: write release JSON through a typed file handle
ReenigneArcher Nov 2, 2026
5a5a25a
ci: authenticate TSDuck release metadata requests
ReenigneArcher Nov 2, 2026
3be66cd
test: resolve media tools and await guide and seek updates
ReenigneArcher Nov 2, 2026
c451f6e
fix: preserve Live TV setup and station choices across refreshes
ReenigneArcher Nov 2, 2026
058d654
fix: rebind retained tuner choices after background refreshes
ReenigneArcher Nov 2, 2026
9258e4b
refactor: read owned control keys through dataset
ReenigneArcher Nov 2, 2026
b5d6f2b
test: await saved playback progress after session deletion
ReenigneArcher Nov 2, 2026
9d01c76
fix: refresh the buffer before skipping to the live edge
ReenigneArcher Nov 2, 2026
2a2e857
test(live-tv): cover capture and provider lifecycles
ReenigneArcher Nov 2, 2026
bbe4f43
fix(live-tv): recover stalled playback and preserve pending seeks
ReenigneArcher Nov 2, 2026
1536f20
style(live-tv): use explicit capture-test imports
ReenigneArcher Nov 2, 2026
c0f503e
test(podcasts): assert cancellation of the pending search timer
ReenigneArcher Nov 2, 2026
ee733f1
fix(packaging): make Alpine media tools usable for TV capture
ReenigneArcher Nov 2, 2026
009a8c1
refactor(build): replace native TV Python helpers with Rust
ReenigneArcher Nov 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# TODO: static link glibc

[alias]
xtask = "run --quiet -p xtask --"
new-migration = "run --quiet -p xtask -- new-migration"
msi-version = "run --quiet -p xtask -- msi-version"

Expand Down
116 changes: 116 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
---
# This file is centrally managed in https://github.com/<organization>/.github/
# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in
# the above-mentioned repo.

# Generated from CLion C/C++ Code Style settings
BasedOnStyle: LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: BlockIndent
AlignConsecutiveAssignments: None
AlignEscapedNewlines: DontAlign
AlignOperands: Align
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: None
AllowShortLoopsOnASingleLine: true
AlignTrailingComments: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: MultiLine
BinPackArguments: false
BinPackParameters: false
BracedInitializerIndentWidth: 2
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterExternBlock: true
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterUnion: false
BeforeCatch: true
BeforeElse: true
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: true
BreakArrays: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: false
BreakConstructorInitializers: AfterColon
BreakInheritanceList: AfterColon
ColumnLimit: 0
CompactNamespaces: false
ContinuationIndentWidth: 2
Cpp11BracedListStyle: true
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: Always
ExperimentalAutoDetectBinPacking: true
FixNamespaceComments: true
IncludeBlocks: Regroup
IndentAccessModifiers: false
IndentCaseBlocks: true
IndentCaseLabels: true
IndentExternBlock: Indent
IndentGotoLabels: true
IndentPPDirectives: BeforeHash
IndentWidth: 2
IndentWrappedFunctionNames: true
InsertBraces: true
InsertNewlineAtEOF: true
KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 1
NamespaceIndentation: All
ObjCBinPackProtocolList: Never
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
PackConstructorInitializers: Never
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 1
PenaltyBreakString: 1
PenaltyBreakFirstLessLess: 0
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 100000000
PointerAlignment: Right
ReferenceAlignment: Pointer
ReflowComments: true
RemoveBracesLLVM: false
RemoveSemicolon: false
SeparateDefinitionBlocks: Always
SortIncludes: CaseInsensitive
SortUsingDeclarations: Lexicographic
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: true
SpaceBeforeCtorInitializerColon: false
SpaceBeforeInheritanceColon: false
SpaceBeforeJsonColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: Never
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInLineCommentPrefix:
Maximum: 3
Minimum: 1
SpacesInParentheses: false
SpacesInSquareBrackets: false
TabWidth: 2
UseTab: Never
7 changes: 7 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[flake8]
filename =
*.py
max-line-length = 120
extend-exclude =
.venv/
venv/
40 changes: 35 additions & 5 deletions .github/workflows/ci-rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,12 @@ jobs:
env:
CARGO_TERM_COLOR: always
INPUT_RELEASE_VERSION: ${{ inputs.release_version }}
TEST_TOOLCHAIN: nightly # libtest's JSON output requires nightly.
# Temporary pin pending https://github.com/rust-lang/rust/pull/165266; tracked in PR #351's checklist.
TEST_TOOLCHAIN: nightly-2026-10-31 # libtest JSON needs nightly; November 1 has a compiler regression.
# musl must support dlopen for Koko's bundled native TV library.
RUSTFLAGS: >-
${{ contains(matrix.target, 'musl') &&
'-C target-feature=-crt-static -C link-arg=-Wl,-rpath,$ORIGIN/live-tv' || '' }}
steps:
- name: Fix arm64 Alpine container
if: runner.arch == 'ARM64' && contains(matrix.container, 'alpine')
Expand All @@ -121,11 +126,15 @@ jobs:
apk add --no-cache \
bash \
build-base \
cmake \
curl \
git \
jq \
linux-headers \
nodejs \
npm \
numactl \
python3 \
openssl-dev \
openssl-libs-static \
p7zip \
Expand All @@ -137,15 +146,15 @@ jobs:

- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
submodules: recursive

- name: Trust Alpine checkout
if: contains(matrix.container, 'alpine')
run: |
git config --global --add safe.directory "${GITHUB_WORKSPACE}"
git config --global --add safe.directory "${GITHUB_WORKSPACE}/third-party/build-deps"

- name: Initialize build-deps
run: git submodule update --init --depth=1 third-party/build-deps
git config --global --add safe.directory "${GITHUB_WORKSPACE}/third-party/tsduck"

- name: Install Ubuntu dependencies
if: matrix.container == '' && contains(matrix.os, 'ubuntu')
Expand All @@ -162,6 +171,10 @@ jobs:
p7zip-full \
pkg-config

- name: Install macOS native build tools
if: contains(matrix.os, 'macos')
run: brew install bash coreutils gnu-sed grep make

- name: Setup Rust
if: matrix.container == ''
uses: actions-rust-lang/setup-rust-toolchain@ecabd13d1c56bd1345c230e542e9144811ad706f # v2.0.0
Expand Down Expand Up @@ -216,6 +229,8 @@ jobs:
- name: Test
id: test
if: matrix.run == 'test'
env:
GH_TOKEN: ${{ github.token }}
run: |
tarpaulin_args=()
case "${{ matrix.target }}" in
Expand Down Expand Up @@ -267,12 +282,24 @@ jobs:
- name: Build
if: matrix.run == 'build'
env:
GH_TOKEN: ${{ github.token }}
KOKO_FANART_PROJECT_API_KEY: ${{ secrets.FANART_PROJECT_API_KEY }}
KOKO_LRCLIB_APPLICATION_ID: ${{ secrets.LRCLIB_APPLICATION_ID }}
run: |
cargo build --locked ${{ matrix.cargo_features }} --target ${{ matrix.target }} --release \
--message-format=json | tee "${RUNNER_TEMP}/cargo-build.json"

- name: Bundle native TV library
if: matrix.run == 'build'
run: |
out_dir="$(jq -ber '
select(.reason == "build-script-executed" and (.package_id | contains("#koko@"))) | .out_dir
' "${RUNNER_TEMP}/cargo-build.json")"
package_dir="$(cat "${out_dir}/native-tv-root.txt")"
destination="target/${{ matrix.target }}/release/live-tv"
mkdir -p "${destination}"
cp -R "${package_dir}/." "${destination}/"

- name: Bundle shared FFmpeg tools
if: matrix.run == 'build'
env:
Expand Down Expand Up @@ -304,7 +331,8 @@ jobs:
7z a "./artifacts/koko-${{ matrix.target }}.7z" \
"./assets" \
"./target/${{ matrix.target }}/release/koko${extension}" \
"./target/${{ matrix.target }}/release/ffmpeg"
"./target/${{ matrix.target }}/release/ffmpeg" \
"./target/${{ matrix.target }}/release/live-tv"

- name: Prepare build artifact
if: matrix.run == 'build' && matrix.build_artifact_path == 'build-artifact'
Expand All @@ -323,6 +351,8 @@ jobs:
"build-artifact/target/${{ matrix.target }}/release/koko${extension}"
cp -R "crates/client-web/dist" "build-artifact/crates/client-web/dist"
cp -R "target/${{ matrix.target }}/release/ffmpeg" "build-artifact/target/${{ matrix.target }}/release/ffmpeg"
cp -R "target/${{ matrix.target }}/release/live-tv" \
"build-artifact/target/${{ matrix.target }}/release/live-tv"

- name: Upload build artifact
if: matrix.run == 'build'
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/ci-windows-installer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ jobs:
7z a "artifacts\koko-${{ matrix.target }}.7z" `
".\assets" `
".\target\${{ matrix.target }}\release\koko.exe" `
".\target\${{ matrix.target }}\release\ffmpeg"
".\target\${{ matrix.target }}\release\ffmpeg" `
".\target\${{ matrix.target }}\release\live-tv"

- name: Resolve MSI package version
shell: pwsh
Expand Down
19 changes: 18 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ jobs:
- name: Check licenses
run: cargo bin cargo-deny check licenses

- name: Test media tool package compatibility
run: |
rustc --test --edition=2021 -D warnings packaging/ffmpeg_stack.rs -o "${RUNNER_TEMP}/ffmpeg-stack-tests"
"${RUNNER_TEMP}/ffmpeg-stack-tests"

- name: Test native packaging metadata and downloads
run: cargo test -p xtask --locked

clippy:
name: Clippy
permissions:
Expand All @@ -78,7 +86,8 @@ jobs:
permissions:
contents: read
runs-on: ubuntu-latest
timeout-minutes: 30
# Cold native SDK builds and the playback suite can exceed thirty minutes together.
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
Expand All @@ -88,9 +97,12 @@ jobs:
env:
KOKO_SKIP_FFMPEG_DOWNLOAD: '1'
KOKO_E2E_BROWSER: ${{ matrix.browser }}
KOKO_E2E_SERVER: ${{ github.workspace }}/target/debug/koko
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
submodules: recursive

- name: Setup Rust
uses: actions-rust-lang/setup-rust-toolchain@ecabd13d1c56bd1345c230e542e9144811ad706f # v2.0.0
Expand Down Expand Up @@ -119,6 +131,11 @@ jobs:
node node_modules/playwright/cli.js install --with-deps chrome
fi

- name: Build browser test server
env:
KOKO_SKIP_FFMPEG_DOWNLOAD: '1'
run: cargo build -p koko --locked --no-default-features

- name: Test real browser playback
working-directory: crates/client-web
run: npm run test:e2e
Expand Down
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@
path = third-party/dockle
url = https://github.com/LizardByte/dockle.git
branch = master
[submodule "third-party/tsduck"]
path = third-party/tsduck
url = https://github.com/tsduck/tsduck.git
branch = master
2 changes: 2 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ build:
fi
chmod +x "${dockle_dir}/readthedocs_build.sh"
export DOCKLE_DIR="${dockle_dir}"
# Rustdoc generates the TV APIs without building or loading the native tuner runtime.
export KOKO_SKIP_TSDUCK_BUILD=1
"${dockle_dir}/readthedocs_build.sh"

python:
Expand Down
Loading
Loading