Compare commits

...

7 Commits

Author SHA1 Message Date
Salastil
5ea24f3b0b fix
All checks were successful
Build Release Binaries / build-and-release (push) Successful in 10m31s
2025-11-23 12:40:15 -05:00
Salastil
37bfd30a84 Unique filenames for builds
Some checks failed
Build Release Binaries / build-and-release (push) Failing after 10m29s
2025-11-23 05:42:55 -05:00
Salastil
1feedc4488 Update permissions on token
All checks were successful
Build Release Binaries / build-and-release (push) Successful in 10m22s
2025-11-23 05:16:13 -05:00
Salastil
cccbf4ab63 1
Some checks failed
Build Release Binaries / build-and-release (push) Failing after 5m42s
2025-11-23 05:00:11 -05:00
Salastil
0f7f0219aa Update Gitea workflow
Some checks failed
Build Release Binaries / build-and-release (push) Failing after 5m43s
2025-11-23 04:47:19 -05:00
Salastil
653326177c Github Workflow 2025-11-23 04:28:39 -05:00
Salastil
d661aee36c Enhance README with usage and build instructions
Expanded README to include detailed usage instructions, debugging tips, and building from source.
2025-11-23 02:17:56 -05:00
6 changed files with 314 additions and 17 deletions

View File

@@ -0,0 +1,147 @@
name: Build Release Binaries
on:
push:
tags:
- "v*"
permissions:
contents: write
env:
BINARY_NAME: streamed-tui
API_BASE: https://git.salastil.com/api/v1
GITEA_ENV: gitea.env
jobs:
build-and-release:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
#######################################################
# 1. Bundle puppeteer-extra + stealth plugin
#######################################################
- name: Bundle extractor node_modules
run: |
chmod +x ./scripts/build_node_modules.sh
./scripts/build_node_modules.sh
#######################################################
# 2. Build all platform binaries
#######################################################
- name: Build binaries
run: |
mkdir -p dist
build() {
GOOS="$1" GOARCH="$2"
OUT="${BINARY_NAME}_${1}_${2}"
echo "Building $1 $2 => $OUT"
env \
GOOS="$1" \
GOARCH="$2" \
CGO_ENABLED=0 \
go build -o "dist/${OUT}" .
}
build linux amd64
build linux arm64
build darwin amd64
build darwin arm64
#######################################################
# 3. Check if release exists → create if needed
#######################################################
- name: Ensure release exists
id: ensure_release
env:
GITEA_TOKEN: ${{ secrets.CI_TOKEN }}
TAG: ${{ github.ref_name }}
REPO: ${{ github.repository }}
run: |
set -e
echo "Checking if release exists: $TAG"
# Query
STATUS=$(curl -s -o resp.json -w "%{http_code}" \
-H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/repos/${REPO}/releases/tags/${TAG}")
if [ "$STATUS" = "200" ]; then
echo "Release already exists."
RELEASE_ID=$(jq -r '.id' resp.json)
else
echo "Release does not exist — creating"
CREATE=$(curl -s -o create.json -w "%{http_code}" \
-X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"tag_name\": \"${TAG}\",
\"name\": \"${TAG}\",
\"body\": \"Automated release ${TAG}\",
\"draft\": false,
\"prerelease\": false
}" \
"${API_BASE}/repos/${REPO}/releases")
echo "Gitea API Response Code: $CREATE"
RELEASE_ID=$(jq -r '.id' create.json)
echo "New release id: $RELEASE_ID"
fi
if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "null" ]; then
echo "ERROR: Could not determine release ID"
exit 1
fi
# Write to environment file (Gitea-compatible)
echo "RELEASE_ID=${RELEASE_ID}" >> "$GITEA_ENV"
#######################################################
# 4. Load RELEASE_ID into environment (Gitea-compatible)
#######################################################
- name: Load RELEASE_ID
run: |
if [ -f "$GITEA_ENV" ]; then
cat "$GITEA_ENV" >> "$GITHUB_ENV"
echo "Loaded RELEASE_ID=$RELEASE_ID"
else
echo "ERROR: gitea.env missing"
exit 1
fi
#######################################################
# 5. Upload all binaries as release assets
#######################################################
- name: Upload binaries to Gitea Release
env:
GITEA_TOKEN: ${{ secrets.CI_TOKEN }}
REPO: ${{ github.repository }}
RELEASE_ID: ${{ env.RELEASE_ID }}
run: |
echo "Uploading binaries..."
cd dist
for file in *; do
echo "Uploading $file..."
curl -s -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/octet-stream" \
--data-binary @"${file}" \
"${API_BASE}/repos/${REPO}/releases/${RELEASE_ID}/assets?name=${file}"
done
echo "Upload complete."

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

@@ -0,0 +1,82 @@
name: Build & Release
on:
push:
tags:
- "v*"
permissions:
contents: write
env:
BINARY_NAME: streamed-tui
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
# -------------------------------------------------
# Build node_modules bundle for puppeteer-extra
# -------------------------------------------------
- name: Build node_modules archive
run: |
chmod +x scripts/build_node_modules.sh
./scripts/build_node_modules.sh
# -------------------------------------------------
# Compile all platform binaries
# -------------------------------------------------
- name: Build binaries
run: |
mkdir -p out
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o out/${BINARY_NAME}_linux_amd64 .
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o out/${BINARY_NAME}_linux_arm64 .
GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -o out/${BINARY_NAME}_darwin_amd64 .
GOOS=darwin GOARCH=arm64 CGO_ENABLED=0 go build -o out/${BINARY_NAME}_darwin_arm64 .
# -------------------------------------------------
# Create source code bundle for this version
# -------------------------------------------------
- name: Create source tarball
run: |
mkdir -p release
tar -czf release/${BINARY_NAME}_${GITHUB_REF_NAME}_source.tar.gz .
# -------------------------------------------------
# Install GitHub CLI
# -------------------------------------------------
- name: Install GitHub CLI
run: sudo apt-get install -y gh
# -------------------------------------------------
# Create Release + upload binaries and source
# -------------------------------------------------
- name: Create Release and Upload Assets
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG="${GITHUB_REF_NAME}"
# Remove old release if re-tagged
gh release delete "$TAG" --yes --cleanup-tag || true
# Create release and upload all assets
gh release create "$TAG" \
out/${BINARY_NAME}_linux_amd64 \
out/${BINARY_NAME}_linux_arm64 \
out/${BINARY_NAME}_darwin_amd64 \
out/${BINARY_NAME}_darwin_arm64 \
release/${BINARY_NAME}_${TAG}_source.tar.gz \
--title "$TAG" \
--notes "Release $TAG" \
--latest

1
.gitignore vendored
View File

@@ -30,5 +30,4 @@ go.work.sum
# Editor/IDE
# .idea/
# .vscode/
go.sum
streamed-tui

View File

@@ -1,17 +1,43 @@
# streamed-tui
TUI Application for launching streamed.pk feeds
Terminal UI for browsing streamed.pk sports streams, opening the selected embed URL in your browser or passing it straight to mpv.
<img width="1910" height="1038" alt="20251123_015918" src="https://github.com/user-attachments/assets/d0bf328b-9139-44ef-b764-25f1a53c7da7" />
## How it works
The client talks to the https://streamed.pk/ API to load sports, popular matches, and per-match streams in a three column layout: sports on the left, matches in the middle, and available streams on the right. Focus moves with the arrow keys or vim keys hjkl, and selecting a match triggers a stream lookup for that event. Press `o` to open the highlighted stream in your default browser, `p` or enter to pipe the embed URL to mpv. You can also bypass the TUI entirely with `-e <embed-url>` to extract and launch a single stream from the command line if you know the url from embed.top
**Debugging** Start the app with `--debug` to append verbose extractor logs into the debug panel at the bottom of the UI, useful when diagnosing failed stream loads, best used with the -e flag so it will not render the TUI and the debug log is placed in stdout.
**Admin Streams** - Streams flagged as Admin are not capable of being forwarded to mpv. These streams have heavier obsfucation and the typical m3u8 extraction method does not work as the javascript on these pages continously issue new m3u8 rather than the typical follow-along type on other streams. These streams can only be watched in the browser, hitting 'o' on the stream will open your browser as set by $XDG_OPEN.
## Building from source
1. Install Go 1.24+ (matching the module version) and ensure your `$GOPATH/bin` is on `PATH`.
2. Refresh bundled Node.js dependencies (only needed if you change extractor packages):
```bash
scripts/build_node_modules.sh
```
This repacks the `puppeteer-extra` toolchain into `internal/assets/node_modules.tar.gz` so the Go binary can unpack it at runtime without requiring npm on the target machine.
3. Compile the TUI:
```bash
go build -o streamed-tui .
```
4. Run it:
```bash
./streamed-tui # launches the full TUI
./streamed-tui -e URL # extracts and launches a single embed URL
./streamed-tui --debug # shows extractor debug log in the footer
```
## Bundled Puppeteer dependencies
The extractor relies on `puppeteer-extra`, `puppeteer-extra-plugin-stealth`, and `puppeteer`. These Node.js packages are
bundled into the final binary via `internal/assets/node_modules.tar.gz`. To refresh the archive (for example after updating
dependency versions), run:
The extractor relies on `puppeteer-extra`, `puppeteer-extra-plugin-stealth`, and `puppeteer`. These Node.js packages are bundled into the final binary via `internal/assets/node_modules.tar.gz`. To refresh the archive (for example after updating dependency versions), run:
```
scripts/build_node_modules.sh
```
The script installs the dependencies into a temporary directory and regenerates the tarball so the Go binary can extract
them at runtime without requiring `npm install` on the target system. When the binary starts it will automatically unpack the
archive into the user's cache directory (or `$TMPDIR` fallback) and point Puppeteer at that cached `node_modules` tree, so the
program can run as a single self-contained executable even when no dependencies exist alongside it.
The script installs the dependencies into a temporary directory and regenerates the tarball so the Go binary can extract them at runtime without requiring `npm install` on the target system. When the binary starts it will automatically unpack the archive into the user's cache directory (or `$TMPDIR` fallback) and point Puppeteer at that cached `node_modules` tree, so the program can run as a single self-contained executable even when no dependencies exist alongside it.

12
go.mod
View File

@@ -3,9 +3,9 @@ module github.com/Salastil/streamed-tui
go 1.24
require (
github.com/charmbracelet/bubbles v0.18.0
github.com/charmbracelet/bubbletea v0.26.6
github.com/charmbracelet/lipgloss v0.13.0
github.com/charmbracelet/bubbles v0.18.0
github.com/charmbracelet/bubbletea v0.26.6
github.com/charmbracelet/lipgloss v0.13.0
)
require (
@@ -14,11 +14,7 @@ require (
github.com/charmbracelet/x/input v0.1.0 // indirect
github.com/charmbracelet/x/term v0.1.1 // indirect
github.com/charmbracelet/x/windows v0.1.0 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2 // indirect
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.4.0 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect

47
go.sum Normal file
View File

@@ -0,0 +1,47 @@
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw=
github.com/charmbracelet/bubbletea v0.26.6 h1:zTCWSuST+3yZYZnVSvbXwKOPRSNZceVeqpzOLN2zq1s=
github.com/charmbracelet/bubbletea v0.26.6/go.mod h1:dz8CWPlfCCGLFbBlTY4N7bjLiyOGDJEnd2Muu7pOWhk=
github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw=
github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY=
github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM=
github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/charmbracelet/x/input v0.1.0 h1:TEsGSfZYQyOtp+STIjyBq6tpRaorH0qpwZUj8DavAhQ=
github.com/charmbracelet/x/input v0.1.0/go.mod h1:ZZwaBxPF7IG8gWWzPUVqHEtWhc1+HXJPNuerJGRGZ28=
github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI=
github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw=
github.com/charmbracelet/x/windows v0.1.0 h1:gTaxdvzDM5oMa/I2ZNF7wN78X/atWemG9Wph7Ika2k4=
github.com/charmbracelet/x/windows v0.1.0/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=