Compare commits
7 Commits
d99e44e996
...
v1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ea24f3b0b | ||
|
|
37bfd30a84 | ||
|
|
1feedc4488 | ||
|
|
cccbf4ab63 | ||
|
|
0f7f0219aa | ||
|
|
653326177c | ||
|
|
d661aee36c |
147
.gitea/workflows/release.yml
Normal file
147
.gitea/workflows/release.yml
Normal 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
82
.github/workflows/release.yml
vendored
Normal 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
1
.gitignore
vendored
@@ -30,5 +30,4 @@ go.work.sum
|
||||
# Editor/IDE
|
||||
# .idea/
|
||||
# .vscode/
|
||||
go.sum
|
||||
streamed-tui
|
||||
|
||||
42
README.md
42
README.md
@@ -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
12
go.mod
@@ -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
47
go.sum
Normal 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=
|
||||
Reference in New Issue
Block a user