Skill: frannunpal/radarr-fixed (version 1.0.4)

--- name: radarr-fixed version: 1.0.4 changelog: "Fixed config path from .openclagent to .openclaw and updated script paths" description: Search and add movies to Radarr. Supports collections, search-on-add option. FORK of jordyvandomselaar/radarr with fixed metadata. metadata: openclaw: emoji: "🎬" requires: bins: - curl - jq config: - path: ~/.openclaw/credentials/radarr/config.json description: "Radarr API configuration with url, apiKey, and defaultQualityProfile" env: - name: RADARR_URL optional: true description: "Radarr instance URL (overrides config file)" - name: RADARR_API_KEY optional: true description: "Radarr API key (overrides config file)" fork: original: jordyvandomselaar/radarr original_url: https://clawhub.com/jordyvandomselaar/radarr reason: "Fixed metadata to properly declare required credentials and config paths" --- # Radarr (Fixed) **⚠️ FORK NOTICE:** This is a fork of [jordyvandomselaar/radarr](https://clawhub.com/jordyvandomselaar/radarr) with corrected metadata declarations. Add movies to your Radarr library with collection support. ## Setup Create `~/.openclaw/credentials/radarr/config.json`: ```json { "url": "http://localhost:7878", "apiKey": "your-api-key", "defaultQualityProfile": 1 } ``` - `defaultQualityProfile`: Quality profile ID (run `config` to see options) ### Alternative: Environment Variables Instead of config file, you can use: - `RADARR_URL` - Radarr instance URL - `RADARR_API_KEY` - Radarr API key ## Workflow 1. **Search**: `search "Movie Name"` - returns numbered list 2. **Present results with TMDB links** - always show clickable links 3. **Check**: User picks a number 4. **Collection prompt**: If movie is part of collection, ask user 5. **Add**: Add movie or full collection ## Important - **Always include TMDB links** when presenting search results to user - Format: `[Title (Year)](https://themoviedb.org/movie/ID)` - Uses `defaultQualityProfile` from config; can override per-add ## Commands ### Search for movies ```bash bash scripts/radarr.sh search "Inception" ``` ### Check if movie exists in library ```bash bash scripts/radarr.sh exists <tmdbId> ``` ### Add a movie (searches immediately by default) ```bash bash scripts/radarr.sh add <tmdbId> # searches right away bash scripts/radarr.sh add <tmdbId> --no-search # don't search ``` ### Add full collection (searches immediately by default) ```bash bash scripts/radarr.sh add-collection <collectionTmdbId> bash scripts/radarr.sh add-collection <collectionTmdbId> --no-search ``` ### Remove a movie ```bash bash scripts/radarr.sh remove <tmdbId> # keep files bash scripts/radarr.sh remove <tmdbId> --delete-files # delete files too ``` **Always ask user if they want to delete files when removing!** ### Get root folders & quality profiles (for config) ```bash bash scripts/radarr.sh config ``` --- *Original skill by [jordyvandomselaar](https://clawhub.com/jordyvandomselaar). Fork maintained with permission under open source principles.*

path: _meta.json
content: {
  "owner": "francisque",
  "slug": "radarr-fixed",
  "displayName": "Radarr (Fixed)",
  "latest": {
    "version": "1.0.2",
    "publishedAt": null,
    "commit": null
  },
  "history": [
    {
      "version": "1.0.2",
      "publishedAt": null,
      "commit": null,
      "changes": "Fixed metadata to properly declare required credentials, config paths, and env vars"
    }
  ],
  "fork": {
    "originalOwner": "jordyvandomselaar",
    "originalSlug": "radarr",
    "originalUrl": "https://clawhub.com/jordyvandomselaar/radarr",
    "reason": "Fixed metadata to properly declare required credentials and config paths"
  }
}


path: scripts/radarr.sh
content: #!/bin/bash
set -e

# Radarr API wrapper
# Credentials: ~/.openclaw/credentials/radarr/config.json

CONFIG_FILE="$HOME/.openclaw/credentials/radarr/config.json"

if [ -f "$CONFIG_FILE" ]; then
  RADARR_URL=$(jq -r '.url' "$CONFIG_FILE")
  RADARR_API_KEY=$(jq -r '.apiKey' "$CONFIG_FILE")
  DEFAULT_QUALITY_PROFILE=$(jq -r '.defaultQualityProfile // empty' "$CONFIG_FILE")
fi

if [ -z "$RADARR_URL" ] || [ -z "$RADARR_API_KEY" ]; then
  echo "Error: Radarr not configured. Create $CONFIG_FILE with {\"url\": \"...\", \"apiKey\": \"...\"}"
  exit 1
fi

API="$RADARR_URL/api/v3"
AUTH="X-[REDACTED_SECRET]"

cmd="$1"
shift || true

case "$cmd" in
  search)
    query="$1"
    curl -s -H "$AUTH" "$API/movie/lookup?term=$(echo "$query" | jq -sRr @uri)" | jq -r '
      to_entries | .[] | 
      "\(.key + 1). \(.value.title) (\(.value.year)) - https://themoviedb.org/movie/\(.value.tmdbId)" + 
      (if .value.collection.tmdbId then " [Collection: \(.value.collection.title)]" else "" end)
    '
    ;;
    
  search-json)
    query="$1"
    curl -s -H "$AUTH" "$API/movie/lookup?term=$(echo "$query" | jq -sRr @uri)"
    ;;
    
  exists)
    tmdbId="$1"
    result=$(curl -s -H "$AUTH" "$API/movie?tmdbId=$tmdbId")
    if [ "$result" = "[]" ]; then
      echo "not_found"
    else
      echo "exists"
      echo "$result" | jq -r '.[0] | "ID: \(.id), Title: \(.title), Has File: \(.hasFile)"'
    fi
    ;;
    
  config)
    echo "=== Root Folders ==="
    curl -s -H "$AUTH" "$API/rootfolder" | jq -r '.[] | "\(.id): \(.path)"'
    echo ""
    echo "=== Quality Profiles ==="
    curl -s -H "$AUTH" "$API/qualityprofile" | jq -r '.[] | "\(.id): \(.name)"'
    ;;
    
  add)
    tmdbId="$1"
    qualityProfileId="$2"
    searchFlag="true"
    
    # Check for --no-search flag
    for arg in "$@"; do
      if [ "$arg" = "--no-search" ]; then
        searchFlag="false"
      fi
    done
    
    # Get movie details from lookup
    movie=$(curl -s -H "$AUTH" "$API/movie/lookup/tmdb?tmdbId=$tmdbId")
    
    # Get default root folder
    rootFolder=$(curl -s -H "$AUTH" "$API/rootfolder" | jq -r '.[0].path')
    
    # Use provided quality profile ID, config default, or first available
    if [ -z "$qualityProfileId" ] || [ "$qualityProfileId" = "--no-search" ]; then
      if [ -n "$DEFAULT_QUALITY_PROFILE" ]; then
        qualityProfile="$DEFAULT_QUALITY_PROFILE"
      else
        qualityProfile=$(curl -s -H "$AUTH" "$API/qualityprofile" | jq -r '.[0].id')
      fi
    else
      qualityProfile="$qualityProfileId"
    fi
    
    # Build add request
    addRequest=$(echo "$movie" | jq --arg rf "$rootFolder" --argjson qp "$qualityProfile" --argjson search "$searchFlag" '
      . + {
        rootFolderPath: $rf,
        qualityProfileId: $qp,
        monitored: true,
        addOptions: {
          searchForMovie: $search
        }
      }
    ')
    
    result=$(curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" -d "$addRequest" "$API/movie")
    
    if echo "$result" | jq -e '.id' > /dev/null 2>&1; then
      echo "✅ Added: $(echo "$result" | jq -r '.title') ($(echo "$result" | jq -r '.year'))"
      if [ "$searchFlag" = "true" ]; then
        echo "🔍 Search started"
      fi
    else
      echo "❌ Failed to add movie"
      echo "$result" | jq -r '.message // .'
    fi
    ;;
    
  add-collection)
    collectionTmdbId="$1"
    searchTerm="$2"
    searchFlag="true"
    
    # Check for --no-search flag
    for arg in "$@"; do
      if [ "$arg" = "--no-search" ]; then
        searchFlag="false"
      fi
    done
    
    echo "🔍 Finding movies in collection..."
    
    # Try getting collection name from Radarr's collection list first
    collections=$(curl -s -H "$AUTH" "$API/collection")
    collection=$(echo "$collections" | jq --argjson tid "$collectionTmdbId" '.[] | select(.tmdbId == $tid)')
    
    if [ -n "$collection" ] && [ "$collection" != "null" ]; then
      collectionTitle=$(echo "$collection" | jq -r '.title')
      # Remove "Collection" suffix for better search
      searchTerm=$(echo "$collectionTitle" | sed 's/ Collection$//')
    fi
    
    # If no search term yet, use the provided one or fail
    if [ -z "$searchTerm" ]; then
      echo "❌ Could not determine collection name. Please provide search term."
      echo "Usage: add-collection <collectionTmdbId> <searchTerm> [--no-search]"
      exit 1
    fi
    
    # Search for movies
    allMovies=$(curl -s -H "$AUTH" "$API/movie/lookup?term=$(echo "$searchTerm" | jq -sRr @uri)")
    
    # Filter to only movies in our collection
    moviesToAdd=$(echo "$allMovies" | jq --argjson cid "$collectionTmdbId" '[.[] | select(.collection.tmdbId == $cid)]')
    movieCount=$(echo "$moviesToAdd" | jq 'length')
    
    if [ "$movieCount" = "0" ]; then
      echo "❌ No movies found for collection $collectionTmdbId"
      exit 1
    fi
    
    echo "📦 Found $movieCount movies in collection"
    
    # Get default root folder and quality profile
    rootFolder=$(curl -s -H "$AUTH" "$API/rootfolder" | jq -r '.[0].path')
    qualityProfile=$(curl -s -H "$AUTH" "$API/qualityprofile" | jq -r '.[0].id')
    
    # Add each movie
    added=0
    skipped=0
    for i in $(seq 0 $((movieCount - 1))); do
      movie=$(echo "$moviesToAdd" | jq ".[$i]")
      tmdbId=$(echo "$movie" | jq -r '.tmdbId')
      title=$(echo "$movie" | jq -r '.title')
      year=$(echo "$movie" | jq -r '.year')
      
      # Check if already exists
      existing=$(curl -s -H "$AUTH" "$API/movie?tmdbId=$tmdbId")
      if [ "$existing" != "[]" ]; then
        echo "⏭️  $title ($year) - already in library"
        skipped=$((skipped + 1))
        continue
      fi
      
      # Add movie
      addRequest=$(echo "$movie" | jq --arg rf "$rootFolder" --argjson qp "$qualityProfile" --argjson search "$searchFlag" '
        . + {
          rootFolderPath: $rf,
          qualityProfileId: $qp,
          monitored: true,
          addOptions: {
            searchForMovie: $search
          }
        }
      ')
      
      result=$(curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" -d "$addRequest" "$API/movie")
      
      if echo "$result" | jq -e '.id' > /dev/null 2>&1; then
        echo "✅ $title ($year)"
        added=$((added + 1))
      else
        echo "❌ $title ($year) - $(echo "$result" | jq -r '.message // "failed"')"
      fi
    done
    
    echo ""
    echo "📊 Added: $added | Skipped: $skipped"
    if [ "$searchFlag" = "true" ] && [ "$added" -gt 0 ]; then
      echo "🔍 Search started for new movies"
    fi
    
    # Monitor the collection for future movies
    collections=$(curl -s -H "$AUTH" "$API/collection")
    collection=$(echo "$collections" | jq --argjson tid "$collectionTmdbId" '.[] | select(.tmdbId == $tid)')
    
    if [ -n "$collection" ] && [ "$collection" != "null" ]; then
      collectionId=$(echo "$collection" | jq -r '.id')
      
      # Get full collection details and update with monitoring
      fullCollection=$(curl -s -H "$AUTH" "$API/collection/$collectionId")
      updatePayload=$(echo "$fullCollection" | jq '. + {monitored: true, searchOnAdd: true}')
      
      updateResult=$(curl -s -X PUT -H "$AUTH" -H "Content-Type: application/json" -d "$updatePayload" "$API/collection/$collectionId")
      
      if echo "$updateResult" | jq -e '.monitored' > /dev/null 2>&1; then
        echo "👁️ Collection monitored (new releases auto-added)"
      fi
    fi
    ;;
    
  remove)
    tmdbId="$1"
    deleteFiles="false"
    if [ "$2" = "--delete-files" ]; then
      deleteFiles="true"
    fi
    
    # Get movie ID from library
    movie=$(curl -s -H "$AUTH" "$API/movie?tmdbId=$tmdbId")
    
    if [ "$movie" = "[]" ]; then
      echo "❌ Movie not found in library"
      exit 1
    fi
    
    movieId=$(echo "$movie" | jq -r '.[0].id')
    title=$(echo "$movie" | jq -r '.[0].title')
    year=$(echo "$movie" | jq -r '.[0].year')
    hasFile=$(echo "$movie" | jq -r '.[0].hasFile')
    
    curl -s -X DELETE -H "$AUTH" "$API/movie/$movieId?deleteFiles=$deleteFiles" > /dev/null
    
    if [ "$deleteFiles" = "true" ]; then
      echo "🗑️ Removed: $title ($year) + deleted files"
    else
      echo "🗑️ Removed: $title ($year) (files kept)"
    fi
    ;;
    
  collection-info)
    tmdbId="$1"
    curl -s -H "$AUTH" "$API/collection" | jq --argjson tid "$tmdbId" '.[] | select(.tmdbId == $tid)'
    ;;
    
  *)
    echo "Usage: radarr.sh <command> [args]"
    echo ""
    echo "Commands:"
    echo "  search <query>              Search for movies"
    echo "  search-json <query>         Search (JSON output)"
    echo "  exists <tmdbId>             Check if movie is in library"
    echo "  config                      Show root folders & quality profiles"
    echo "  add <tmdbId> [profileId] [--no-search]  Add a movie (searches by default)"
    echo "  add-collection <tmdbId> [--no-search]  Add full collection"
    echo "  remove <tmdbId>             Remove a movie from library"
    echo "  collection-info <tmdbId>    Get collection details"
    ;;
esac


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)