Spaces:
Paused
Paused
Download scripts/seed.sh from stremio-db/a: direct link, hf CLI and curl.
- Browser
- Download file 8.91 kB
-
https://huggingface.co/spaces/stremio-db/a/resolve/main/scripts/seed.sh
- Command line
-
hf download hf://spaces/stremio-db/a/scripts/seed.sh
-
curl -L -o seed.sh https://huggingface.co/spaces/stremio-db/a/resolve/main/scripts/seed.sh
8.91 kB
| set -euo pipefail | |
| SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" | |
| DATA_FILE="$SCRIPT_DIR/title.basics.tsv" | |
| # ββ defaults ββ | |
| LIMIT=100 | |
| LIMIT_PER_TYPE="" # if set, cap each type independently | |
| TYPE="" # "" = both, "movie", "tvSeries" | |
| GENRES="" # comma-separated, e.g. "Action,Horror" | |
| YEAR_FROM="" | |
| YEAR_TO="$(($(date +%Y) - 1))" | |
| API_KEY="t0-free-rpdb" | |
| ASSETS="poster" # poster, logo, backdrop, all | |
| DRY_RUN=false | |
| usage() { | |
| cat <<EOF | |
| Usage: $(basename "$0") <BASE_URL> [OPTIONS] | |
| Seed an OpenPosterDB instance by requesting posters to warm the cache. | |
| Entries are processed newest-first (by year descending). | |
| Sort year uses endYear for series (if available), startYear otherwise. | |
| Optional: | |
| BASE_URL Server base URL (default: http://localhost:3000) | |
| Options: | |
| -n, --limit NUM Total entries to seed (default: $LIMIT, 0 = unlimited) | |
| -N, --limit-per-type NUM Cap each type independently (e.g. 100k movies + 100k series) | |
| -t, --type TYPE Title type: movie, tv, or both (default: both) | |
| -g, --genres GENRES Comma-separated genres to include (e.g. "Action,Horror") | |
| -f, --year-from YEAR Minimum year (inclusive) | |
| -y, --year-to YEAR Maximum year (inclusive, default: current year - 1) | |
| -k, --key KEY API key to use (default: $API_KEY) | |
| -a, --assets ASSETS What to fetch: poster, logo, backdrop, all (default: $ASSETS) | |
| -d, --dry-run Print what would be seeded without making requests | |
| -h, --help Show this help | |
| Examples: | |
| $(basename "$0") http://localhost:3000 | |
| $(basename "$0") http://localhost:3000 -n 500 -t movie -f 2000 | |
| $(basename "$0") http://localhost:3000 -n 0 -g "Horror,Thriller" -a all | |
| $(basename "$0") http://localhost:3000 -N 100000 | |
| EOF | |
| exit 0 | |
| } | |
| # ββ parse args ββ | |
| if [[ "${1:-}" == "-h" ]] || [[ "${1:-}" == "--help" ]]; then | |
| usage | |
| fi | |
| # First arg is BASE_URL if it doesn't start with - | |
| if [[ $# -gt 0 ]] && [[ "$1" != -* ]]; then | |
| BASE_URL="${1%/}" | |
| shift | |
| else | |
| BASE_URL="http://localhost:3000" | |
| fi | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| -n|--limit) LIMIT="$2"; shift 2 ;; | |
| -N|--limit-per-type) LIMIT_PER_TYPE="$2"; shift 2 ;; | |
| -t|--type) TYPE="$2"; shift 2 ;; | |
| -g|--genres) GENRES="$2"; shift 2 ;; | |
| -f|--year-from) YEAR_FROM="$2"; shift 2 ;; | |
| -y|--year-to) YEAR_TO="$2"; shift 2 ;; | |
| -k|--key) API_KEY="$2"; shift 2 ;; | |
| -a|--assets) ASSETS="$2"; shift 2 ;; | |
| -d|--dry-run) DRY_RUN=true; shift ;; | |
| -h|--help) usage ;; | |
| *) echo "Unknown option: $1"; usage ;; | |
| esac | |
| done | |
| if [[ ! -f "$DATA_FILE" ]]; then | |
| echo "Error: Data file not found at $DATA_FILE" | |
| echo "Download it from https://datasets.imdbws.com/title.basics.tsv.gz and extract it." | |
| exit 1 | |
| fi | |
| # ββ build awk filter & extract to temp file ββ | |
| # Columns: 1=tconst 2=titleType 3=primaryTitle 4=originalTitle | |
| # 5=isAdult 6=startYear 7=endYear 8=runtimeMinutes 9=genres | |
| # | |
| # Output is tab-separated: sort_year \t tconst \t titleType \t primaryTitle | |
| TMPFILE=$(mktemp) | |
| trap 'rm -f "$TMPFILE"' EXIT | |
| AWK_CONDS=() | |
| case "$TYPE" in | |
| movie) AWK_CONDS+=('$2 == "movie"') ;; | |
| tv) AWK_CONDS+=('$2 == "tvSeries"') ;; | |
| both|"") ;; | |
| *) echo "Error: --type must be movie, tv, or both"; exit 1 ;; | |
| esac | |
| if [[ -n "$YEAR_FROM" ]]; then | |
| AWK_CONDS+=("sort_year >= $YEAR_FROM") | |
| fi | |
| if [[ -n "$YEAR_TO" ]]; then | |
| AWK_CONDS+=("sort_year <= $YEAR_TO") | |
| fi | |
| if [[ -n "$GENRES" ]]; then | |
| IFS=',' read -ra GENRE_ARR <<< "$GENRES" | |
| GENRE_PARTS=() | |
| for g in "${GENRE_ARR[@]}"; do | |
| g=$(echo "$g" | xargs) | |
| GENRE_PARTS+=("index(\$9, \"$g\") > 0") | |
| done | |
| GENRE_COND="" | |
| for part in "${GENRE_PARTS[@]}"; do | |
| if [[ -n "$GENRE_COND" ]]; then | |
| GENRE_COND="$GENRE_COND || $part" | |
| else | |
| GENRE_COND="$part" | |
| fi | |
| done | |
| AWK_CONDS+=("($GENRE_COND)") | |
| fi | |
| # Join conditions with && | |
| AWK_FILTER="NR > 1" | |
| for cond in "${AWK_CONDS[@]+"${AWK_CONDS[@]}"}"; do | |
| AWK_FILTER="$AWK_FILTER && $cond" | |
| done | |
| # Write awk script to a file to avoid shell escaping issues | |
| AWK_SCRIPT=$(mktemp) | |
| trap 'rm -f "$TMPFILE" "$AWK_SCRIPT"' EXIT | |
| cat > "$AWK_SCRIPT" << 'AWKEOF' | |
| BEGIN { FS="\t"; OFS="\t" } | |
| NR > 1 { | |
| sort_year = ($7 != "\\N" && $7 + 0 > 0) ? $7 + 0 : ($6 != "\\N" ? $6 + 0 : 0) | |
| AWKEOF | |
| # Append the dynamic filter | |
| echo " if ($AWK_FILTER) print sort_year, \$1, \$2, \$3" >> "$AWK_SCRIPT" | |
| echo "}" >> "$AWK_SCRIPT" | |
| echo "Filtering titles..." | |
| awk -f "$AWK_SCRIPT" "$DATA_FILE" | sort -t$'\t' -k1 -rn > "$TMPFILE" | |
| TOTAL=$(wc -l < "$TMPFILE") | |
| if [[ "$TOTAL" -eq 0 ]]; then | |
| echo "No entries match the given filters." | |
| exit 0 | |
| fi | |
| # Apply limits: --limit-per-type takes precedence over --limit | |
| if [[ -n "$LIMIT_PER_TYPE" ]]; then | |
| # Split by type, cap each independently, then merge back (still sorted by year desc) | |
| TMPFILE_LPT=$(mktemp) | |
| trap 'rm -f "$TMPFILE" "$AWK_SCRIPT" "$TMPFILE_LPT"' EXIT | |
| # Use awk to cap each type independently (avoids SIGPIPE from head) | |
| awk -F$'\t' -v cap="$LIMIT_PER_TYPE" '{ | |
| count[$3]++ | |
| if (count[$3] <= cap) print | |
| }' "$TMPFILE" > "$TMPFILE_LPT" | |
| # Re-sort merged results by year descending | |
| sort -t$'\t' -k1 -rn "$TMPFILE_LPT" > "$TMPFILE" | |
| rm -f "$TMPFILE_LPT" | |
| SELECTED=$(wc -l < "$TMPFILE") | |
| # Build per-type summary | |
| TYPE_SUMMARY=$(awk -F$'\t' '{count[$3]++} END {for (t in count) printf "%s: %d, ", t, count[t]}' "$TMPFILE") | |
| TYPE_SUMMARY="${TYPE_SUMMARY%, }" | |
| echo "Matched $TOTAL titles, seeding $SELECTED β $TYPE_SUMMARY (newest first, capped at $LIMIT_PER_TYPE per type)" | |
| elif [[ "$LIMIT" -gt 0 ]] && [[ "$LIMIT" -lt "$TOTAL" ]]; then | |
| SELECTED="$LIMIT" | |
| echo "Matched $TOTAL titles, seeding $SELECTED (newest first)" | |
| else | |
| SELECTED="$TOTAL" | |
| echo "Matched $TOTAL titles, seeding $SELECTED (newest first)" | |
| fi | |
| echo "Server: $BASE_URL" | |
| echo "API key: $API_KEY" | |
| echo "Assets: $ASSETS" | |
| echo "" | |
| if [[ "$DRY_RUN" == true ]]; then | |
| echo "ββ Dry run ββ" | |
| head -n "$SELECTED" "$TMPFILE" | while IFS=$'\t' read -r year id type title; do | |
| echo " [$year] $type $id - $title" | |
| done | |
| exit 0 | |
| fi | |
| # ββ seeding ββ | |
| echo "ββ Seeding ββ" | |
| fetch_asset() { | |
| local base_url="$1" api_key="$2" id="$3" asset_type="$4" ext="$5" | |
| local url="${base_url}/${api_key}/imdb/${asset_type}-default/${id}.${ext}" | |
| local output | |
| output=$(curl -sL --max-time 60 -o /dev/null -w '%{http_code}\t%{time_total}\t%{content_type}' "$url" 2>/dev/null) | |
| local http_code time_total content_type | |
| http_code=$(echo "$output" | cut -f1) | |
| time_total=$(echo "$output" | cut -f2) | |
| content_type=$(echo "$output" | cut -f3) | |
| # Must be 200 with an image content type β not a Cloudflare error page | |
| if [[ "$http_code" == "200" ]] && [[ "$content_type" == image/* ]]; then | |
| echo "$time_total" | |
| return 0 | |
| else | |
| echo "$time_total $http_code" | |
| return 1 | |
| fi | |
| } | |
| COUNT=0 | |
| OK=0 | |
| FAIL=0 | |
| TOTAL_TIME=0 | |
| head -n "$SELECTED" "$TMPFILE" | while IFS=$'\t' read -r year id type title; do | |
| COUNT=$((COUNT + 1)) | |
| result="OK" | |
| latency="" | |
| case "$ASSETS" in | |
| poster) | |
| fetch_out=$(fetch_asset "$BASE_URL" "$API_KEY" "$id" "poster" "jpg") || result="FAIL" | |
| ;; | |
| logo) | |
| fetch_out=$(fetch_asset "$BASE_URL" "$API_KEY" "$id" "logo" "png") || result="SKIP" | |
| ;; | |
| backdrop) | |
| fetch_out=$(fetch_asset "$BASE_URL" "$API_KEY" "$id" "backdrop" "jpg") || result="SKIP" | |
| ;; | |
| all) | |
| fetch_out=$(fetch_asset "$BASE_URL" "$API_KEY" "$id" "poster" "jpg") || result="FAIL" | |
| logo_result="OK" | |
| fetch_out_logo=$(fetch_asset "$BASE_URL" "$API_KEY" "$id" "logo" "png") || logo_result="SKIP" | |
| backdrop_result="OK" | |
| fetch_out_backdrop=$(fetch_asset "$BASE_URL" "$API_KEY" "$id" "backdrop" "jpg") || backdrop_result="SKIP" | |
| ;; | |
| esac | |
| if [[ "$result" == "OK" ]]; then | |
| OK=$((OK + 1)) | |
| else | |
| FAIL=$((FAIL + 1)) | |
| fi | |
| # Print a log line for a single asset fetch | |
| log_asset() { | |
| local out="$1" res="$2" asset_label="$3" | |
| local lat hc lat_str | |
| lat=$(echo "$out" | awk '{print $1}') | |
| hc=$(echo "$out" | awk '{print $2}') | |
| if [[ -n "$lat" ]]; then | |
| lat_str="$(awk "BEGIN {printf \"%.0f\", $lat * 1000}")ms" | |
| else | |
| lat_str="-" | |
| fi | |
| if [[ "$res" == "OK" ]]; then | |
| printf "[%d/%d] %-4s %6s [%s] %-10s %s - %s%s\n" "$COUNT" "$SELECTED" "$res" "$lat_str" "$year" "$type" "$id" "$title" "$asset_label" | |
| else | |
| printf "[%d/%d] %-4s %6s [%s] %-10s %s - %s%s (HTTP %s)\n" "$COUNT" "$SELECTED" "$res" "$lat_str" "$year" "$type" "$id" "$title" "$asset_label" "${hc:-?}" | |
| fi | |
| } | |
| if [[ "$ASSETS" == "all" ]]; then | |
| log_asset "$fetch_out" "$result" " [poster]" | |
| log_asset "${fetch_out_logo:-}" "$logo_result" " [logo]" | |
| log_asset "${fetch_out_backdrop:-}" "$backdrop_result" " [backdrop]" | |
| else | |
| log_asset "$fetch_out" "$result" "" | |
| fi | |
| done | |
| echo "" | |
| echo "Done." | |