From 80ac169126d0bd96c2e5cd8f72390d7b96af91f3 Mon Sep 17 00:00:00 2001 From: zphinx Date: Sun, 22 Feb 2026 01:00:11 +0100 Subject: [PATCH] upload --- README.md | 93 ++++++++++- svt.sh | 458 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 550 insertions(+), 1 deletion(-) create mode 100755 svt.sh diff --git a/README.md b/README.md index 3b61598..20a72dd 100644 --- a/README.md +++ b/README.md @@ -1 +1,92 @@ -# Random scripts +# SVT Play Stream Launcher + +A simple tool that lets you watch SVT Play programs directly in a video player. + +## What It Does + +This script makes it easy to search for and watch SVT Play programs. Instead of opening a web browser, you can: + +1. Search for a program by name +2. Browse popular programs +3. Select what you want to watch +4. It opens in your video player automatically + +## What You Need + +Before using this script, make sure you have these programs installed: + +- `yt-dlp` - Downloads video information +- `mpv` - Video player +- `zenity` - Creates the dialog windows +- `jq` - Processes data +- `curl` - Downloads information + +The script will tell you if something is missing and how to install it. + +## How to Use It + +### Start the script + +```bash +./svt.sh +``` + +A window will appear with two options: +- **Search for Program** - Type the name of what you want to watch +- **Browse Popular Programs** - See what's trending on SVT Play + +### Example: Searching for a program + +1. Click "Search for Program" +2. Type what you want (like "Wallander" or "News") +3. Wait 10-20 seconds for results (this is normal, the server is a bit slow) +4. Click on the program you want +5. It automatically opens in your video player +6. Enjoy! + +### Example: Browsing popular programs + +1. Click "Browse Popular Programs" +2. Wait for the list to load (10-20 seconds) +3. Scroll through and pick something +4. Press OK and it starts playing + +## If Something Goes Wrong + +### "Missing required tools" +You need to install something. The script will tell you exactly what to install. On Arch Linux: +```bash +sudo pacman -S yt-dlp mpv zenity jq curl +``` + +On Ubuntu/Debian: +```bash +sudo apt install yt-dlp mpv zenity jq curl +``` + +### "Program not found" +The program you searched for doesn't exist or isn't available on SVT Play right now. + +### "Nothing happens after clicking OK" +This is usually just the server being slow. Wait a bit longer. The script is still working. + +### "Video player doesn't open" +Make sure mpv is installed. Check with: `which mpv` + +## See What's Happening + +The script keeps a log of everything it's doing. If you want to see what's going on: + +```bash +tail -f ~/.svtplay.log +``` + +This shows you all the activity in real-time. Good for understanding what the script is doing. + +## Notes + +- Video plays in mpv, which is a lightweight video player +- Everything is logged to `~/.svtplay.log` +- The program searches the actual SVT Play website, not a saved list +- Searching and loading takes 10-20 seconds because the server is slow - this is normal + diff --git a/svt.sh b/svt.sh new file mode 100755 index 0000000..7187415 --- /dev/null +++ b/svt.sh @@ -0,0 +1,458 @@ +#!/bin/bash + +# SVT Play Stream Launcher - 100% GUI Based +# Search and stream SVT Play content with zenity interface + +set -euo pipefail + +# Debug mode +DEBUG="${DEBUG:-0}" +WAIT_FOR_MPV=0 + +# Logging +LOG_FILE="${HOME}/.svtplay.log" +mkdir -p "$(dirname "$LOG_FILE")" + +# Parse command line options +while [[ $# -gt 0 ]]; do + case "$1" in + --debug) DEBUG=1; shift ;; + --wait) WAIT_FOR_MPV=1; shift ;; + --log) tail -f "$LOG_FILE"; exit 0 ;; + --clear-log) rm -f "$LOG_FILE"; echo "Log cleared"; exit 0 ;; + --help|-h) + zenity --info --title="SVT Play Launcher" --text="SVT Play Stream Launcher\n\nA GUI-based tool to search and stream SVT Play content.\n\nUsage: ./svt.sh [OPTIONS]\n\nOptions:\n --debug Enable debug output\n --wait Wait for playback to finish\n --log View live log\n --help Show this message" + exit 0 + ;; + *) break ;; + esac +done + +# Color definitions +readonly RED='\033[0;31m' +readonly GREEN='\033[0;32m' +readonly YELLOW='\033[1;33m' +readonly BLUE='\033[0;34m' +readonly NC='\033[0m' + +# Zenity dark mode helper +zenity_dark() { + GTK_THEME=Adwaita:dark zenity "$@" +} + +# Logging functions +log_info() { + local msg="[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $*" + echo -e "${BLUE}[INFO]${NC} $*" >&2 + echo "$msg" >> "$LOG_FILE" +} + +log_success() { + local msg="[$(date '+%Y-%m-%d %H:%M:%S')] [✓] $*" + echo -e "${GREEN}[✓]${NC} $*" >&2 + echo "$msg" >> "$LOG_FILE" +} + +log_error() { + local msg="[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $*" + echo -e "${RED}[ERROR]${NC} $*" >&2 + echo "$msg" >> "$LOG_FILE" +} + +log_debug() { + if [[ "$DEBUG" == "1" ]]; then + local msg="[$(date '+%Y-%m-%d %H:%M:%S')] [DEBUG] $*" + echo -e "${BLUE}[DEBUG]${NC} $*" >&2 + echo "$msg" >> "$LOG_FILE" + fi +} + +# Check dependencies +check_dependencies() { + log_info "Checking dependencies..." + + local required=("yt-dlp" "mpv" "zenity" "jq" "curl") + local missing=() + + for cmd in "${required[@]}"; do + if ! command -v "$cmd" &>/dev/null; then + missing+=("$cmd") + fi + done + + if [[ ${#missing[@]} -gt 0 ]]; then + log_error "Missing required tools: ${missing[*]}" + zenity_dark --error --title="Missing Dependencies" --text="Please install: ${missing[*]}\n\nArch: sudo pacman -S ${missing[*]}\nDebian: sudo apt install ${missing[*]}" + exit 1 + fi + + log_success "Dependencies OK" +} + +# Search programs via SVT Play GraphQL API +search_programs() { + local query="$1" + log_info "API: Querying searchPage with: $query" + + # Make API call with searchPage (with longer timeout since SVT API can be slow) + local response + response=$(curl -s --connect-timeout 10 --max-time 30 -w "\n%{http_code}" -X POST "https://api.svt.se/contento/graphql" \ + -H "Content-Type: application/json" \ + -d "{\"query\": \"{ searchPage(query: \\\"${query}\\\", maxHits: 50) { flat { hits { teaser { heading } } } } }\"}" 2>/dev/null) || { + log_error "Failed to query SVT Play API (curl error)" + return 1 + } + + local http_code=$(echo "$response" | tail -1) + response=$(echo "$response" | sed '$d') + + log_info "API: Response HTTP $http_code" + + if [[ "$http_code" != "200" ]]; then + log_error "API returned HTTP $http_code" + return 1 + fi + + # Check for GraphQL errors + if echo "$response" | jq -e '.errors' >/dev/null 2>&1; then + log_error "GraphQL error: $(echo "$response" | jq -r '.errors[0].message // "Unknown error"')" + return 1 + fi + + # Parse response - extract headings, clean HTML, and convert to slugs + local results + results=$(echo "$response" | jq -r '.data.searchPage.flat.hits[].teaser | select(.heading != null) | .heading as $title | ($title | ascii_downcase | gsub("<[^>]*>"; "") | gsub("[^a-z0-9 ]"; ""; "g") | gsub("^ +| +$"; "") | gsub(" +"; "-")) as $slug | $title + "|" + $slug' 2>/dev/null) + local jq_ret=$? + + if [[ $jq_ret -ne 0 ]]; then + log_error "jq parsing failed (ret=$jq_ret)" + return 1 + fi + + if [[ -z "$results" ]]; then + log_warn "No results from search" + return 1 + fi + + local result_count=$(echo "$results" | grep -c '|') + log_info "API: Search returned $result_count results" + + echo "$results" +} + +# Get all active programs from Tablåtjänsten +get_popular_programs() { + log_info "API: Fetching popular programs" + + # Use GraphQL API with broad search query "a" to get popular/varied content (with longer timeout) + local response + response=$(curl -s --connect-timeout 10 --max-time 30 -w "\n%{http_code}" -X POST "https://api.svt.se/contento/graphql" \ + -H "Content-Type: application/json" \ + -d '{"query": "{ searchPage(query: \"a\", maxHits: 50) { flat { hits { teaser { heading } } } } }"}' 2>/dev/null) || { + log_error "API: Failed to query (curl error)" + return 1 + } + + local http_code=$(echo "$response" | tail -1) + response=$(echo "$response" | sed '$d') + + log_info "API: Popular programs response HTTP $http_code" + + if [[ "$http_code" != "200" ]]; then + log_error "API returned HTTP $http_code for popular programs" + return 1 + fi + + # Check response is not empty + local resp_len=$(echo "$response" | wc -c) + log_info "API: Response size: $resp_len bytes" + + if [[ $resp_len -lt 50 ]]; then + log_error "API: Response too small, likely error: $response" + return 1 + fi + + # Parse flat hits (popular programs) + # Format output: title|slug + local results + log_info "API: Parsing results with jq" + results=$(echo "$response" | jq -r '.data.searchPage.flat.hits[].teaser | select(.heading != null) | .heading as $title | ($title | ascii_downcase | gsub("<[^>]*>"; "") | gsub("[^a-z0-9 ]"; ""; "g") | gsub("^ +| +$"; "") | gsub(" +"; "-")) as $slug | $title + "|" + $slug' 2>/dev/null) + local jq_ret=$? + + if [[ $jq_ret -ne 0 ]]; then + log_error "jq parsing failed (ret=$jq_ret), response: $response" + return 1 + fi + + if [[ -z "$results" ]]; then + log_warn "API: jq returned empty results" + log_info "API: Full response: $response" + return 1 + fi + + local result_count=$(echo "$results" | grep -c '|') + log_info "API: Popular programs parsed $result_count results" + + echo "$results" +} + +# Show search dialog +show_search_dialog() { + log_info "Showing search dialog" + + local search_term + search_term=$(zenity_dark --entry \ + --title="Search SVT Play" \ + --text="Enter program name to search:" \ + --width=500 \ + 2>/dev/null) + + if [[ -z "$search_term" ]]; then + log_debug "Search cancelled" + return 1 + fi + + log_debug "Search term: $search_term" + echo "$search_term" +} + +# Show results in zenity list +show_results_list() { + local results="$1" + log_debug "Showing results list" + + if [[ -z "$results" ]]; then + log_warn "No results provided" + zenity_dark --error --title="No Results" --text="No programs found." + return 1 + fi + + local result_count=$(echo "$results" | wc -l) + log_debug "Found $result_count results" + + # Create temp file with formatted list for zenity + local temp_file="/tmp/svt_search_$$.txt" + local idx=0 + + while IFS='|' read -r title slug; do + [[ -z "$title" || -z "$slug" ]] && continue + ((idx++)) + + # Clean HTML tags + title=$(echo "$title" | sed 's/<[^>]*>//g' | sed 's/&/\&/g' | sed 's/"/"/g' | sed "s/'/'/g" | sed 's/<//g') + + # Save title->slug mapping + echo "$title=$slug" >> "$temp_file.map" + + if [[ $idx -eq 1 ]]; then + echo "TRUE" >> "$temp_file" + else + echo "FALSE" >> "$temp_file" + fi + echo "$title" >> "$temp_file" + done <<< "$results" + + [[ $idx -eq 0 ]] && { rm -f "$temp_file"*; return 1; } + + log_debug "Built list with $idx items" + + # Display zenity dialog + local selected + selected=$(zenity_dark --list --title="Search Results" --text="Select a program:" --column="Select" --column="Program" --radiolist --width=700 --height=500 < "$temp_file" 2>/dev/null) + + if [[ -z "$selected" ]]; then + log_debug "User cancelled" + rm -f "$temp_file"* + return 1 + fi + + # Look up slug + local slug + slug=$(grep "^$selected=" "$temp_file.map" 2>/dev/null | cut -d= -f2) + + rm -f "$temp_file"* + + if [[ -z "$slug" ]]; then + log_error "Could not map selection to slug" + return 1 + fi + + log_success "Selected: $selected" + echo "$slug" +} + +# Show popular programs +show_popular_programs() { + log_info "Fetching popular programs from SVT Play..." + + # Get programs (API call takes 10-20 seconds) + local programs + programs=$(get_popular_programs) + + if [[ -z "$programs" ]]; then + log_error "No popular programs fetched" + zenity_dark --error --title="Error" --text="Failed to fetch popular programs from SVT Play." + return 1 + fi + + log_info "Got $(echo "$programs" | wc -l) results, preparing list..." + + # Create temp file with formatted list for zenity + local temp_file="/tmp/svt_list_$$.txt" + local idx=0 + local first_title="" + + while IFS='|' read -r title slug; do + [[ -z "$title" || -z "$slug" ]] && continue + ((idx++)) + + # Clean HTML tags + title=$(echo "$title" | sed 's/<[^>]*>//g' | sed 's/&/\&/g' | sed 's/"/"/g' | sed "s/'/'/g" | sed 's/<//g') + + # Save title->slug mapping + echo "$title=$slug" >> "$temp_file.map" + + if [[ $idx -eq 1 ]]; then + echo "TRUE" >> "$temp_file" + first_title="$title" + else + echo "FALSE" >> "$temp_file" + fi + echo "$title" >> "$temp_file" + done <<< "$programs" + + [[ $idx -eq 0 ]] && { log_error "No valid items"; rm -f "$temp_file"*; return 1; } + + log_info "Built list with $idx items" + + # Display zenity dialog with the list + log_info "Showing zenity list dialog..." + local selected + selected=$(zenity_dark --list --title="Popular Programs" --text="Select a program to stream:" --column="Select" --column="Program" --radiolist --width=700 --height=600 < "$temp_file" 2>/dev/null) + + local ret=$? + + if [[ $ret -ne 0 ]] || [[ -z "$selected" ]]; then + log_debug "User cancelled or dialog failed (ret=$ret)" + rm -f "$temp_file"* + return 1 + fi + + # Look up slug from the selected title + local slug + slug=$(grep "^$selected=" "$temp_file.map" 2>/dev/null | cut -d= -f2) + + rm -f "$temp_file"* + + if [[ -z "$slug" ]]; then + log_error "Could not find slug for: $selected" + return 1 + fi + + log_success "Selected: $selected" + echo "$slug" +} + +# Show main menu +show_main_menu() { + log_info "Showing main menu" + + local choice + choice=$(zenity_dark --list \ + --title="SVT Play Stream Launcher" \ + --text="Select an option:" \ + --column="Option" \ + "Search for Program" \ + "Browse Popular Programs" \ + --width=400 \ + --height=200 \ + 2>/dev/null) + + if [[ -z "$choice" ]]; then + log_debug "User cancelled main menu" + return 1 + fi + + echo "$choice" +} + +# Play program directly +play_program() { + local slug="$1" + local url="https://www.svtplay.se/$slug" + + log_info "Playing program: $slug" + log_debug "URL: $url" + + # Verify URL is accessible + if ! yt-dlp --quiet --dump-json "$url" &>/dev/null; then + log_error "Failed to access program: $slug" + zenity_dark --error --title="Error" --text="Failed to access program. It may not be available." + return 1 + fi + + log_success "Starting playback for: $slug" + + # Launch mpv with yt-dlp integration + mpv --force-window=immediate "$url" & + local pid=$! + + log_success "Playback started (PID: $pid)" + + if [[ "$WAIT_FOR_MPV" == "1" ]]; then + log_info "Waiting for mpv to finish..." + wait $pid 2>/dev/null + log_success "Playback finished" + else + log_info "Playback continues in background" + fi +} + +# Main function +main() { + log_info "═════ SVT Play Stream Launcher started ═════" + log_info "PID: $$, DIR: $PWD" + + check_dependencies + + # Show main menu + local menu_choice + menu_choice=$(show_main_menu) || exit 0 + + local program_slug + + case "$menu_choice" in + "Search for Program") + local search_term + search_term=$(show_search_dialog) || exit 0 + + log_info "Searching for: $search_term" + + # Get search results (API call takes 10-20 seconds) + search_results=$(search_programs "$search_term") + + if [[ -z "$search_results" ]]; then + zenity_dark --error --title="No Results" --text="No programs found for: $search_term" + exit 0 + fi + + program_slug=$(show_results_list "$search_results") || exit 0 + ;; + "Browse Popular Programs") + program_slug=$(show_popular_programs) || exit 0 + ;; + *) + log_error "Invalid menu choice: $menu_choice" + exit 1 + ;; + esac + + # Play the selected program + play_program "$program_slug" + + log_success "Session complete" + log_info "═════════════════════════════════════════════════════" +} + +# Only run main if script is executed directly (not sourced) +[[ "${BASH_SOURCE[0]}" == "${0}" ]] && main "$@"