Clone
9
Services
Sp5rky edited this page 2025-07-19 23:57:27 -06:00

unshackle doesn't include any services by default. You must create your own services for the platforms you have legal access to.

Unlike similar project's such as yt-dlp, unshackle does not currently come with any Services. You must develop your own Services and only use unshackle with Services you have the legal right to do so.

Note

If you made a Service for unshackle that does not use widevine or any other DRM systems, feel free to make a Pull Request and make your service available to others. Any Service on yt-dlp would be able to be added to the unshackle repository as they both use the [Unlicense license] therefore direct reading and porting of their code would be legal.

Creating a Service

To create a new service, you'll need to understand the basic structure and components. The best way to learn is by examining the included EXAMPLE service, which demonstrates all the core concepts and patterns used in unshackle services.

Service Structure

Each service consists of two main files:

  1. __init__.py - Contains the main service class and logic
  2. config.yaml - Contains service configuration (endpoints, client settings, etc.)

Service Directory Layout

unshackle/services/YOUR_SERVICE/
├── __init__.py
└── config.yaml

Key Components

1. Service Class

Your service must inherit from the Service base class and implement several required methods:

from unshackle.core.service import Service

class YOUR_SERVICE(Service):
    """
    Service code for your-domain.com
    Version: 1.0.0

    Authorization: Cookies/Credentials
    Security: FHD@L3/UHD@L1

    Usage instructions here.
    """

    TITLE_RE = r"^(?:https?://?your-domain\.com/details/)?(?P<title_id>[^/]+)"
    GEOFENCE = ("US", "UK")  # Optional: specify supported regions

2. Required Methods

  • authenticate() - Handle authentication (cookies, credentials)
  • search() - Search for content (returns SearchResult objects)
  • get_titles() - Extract title metadata and return Movies/Series
  • get_tracks() - Extract video/audio tracks from manifests
  • get_widevine_license() - Handle Widevine DRM licensing
  • get_playready_license() - Handle PlayReady DRM licensing (optional)
  • get_chapters() - Extract chapter information (optional)

3. CLI Interface

Each service needs a CLI command interface:

@staticmethod
@click.command(name="YOUR_SERVICE", short_help="https://your-domain.com")
@click.argument("title", type=str)
@click.option("-m", "--movie", is_flag=True, default=False, help="Specify if it's a movie")
@click.option("-d", "--device", type=str, default="android_tv", help="Select device from the config file")
@click.pass_context
def cli(ctx, **kwargs):
    return YOUR_SERVICE(ctx, **kwargs)

Configuration File

The config.yaml file contains:

endpoints:
  login: https://api.your-domain.com/v1/login
  metadata: https://api.your-domain.com/v1/metadata/{title_id}.json
  streams: https://api.your-domain.com/v1/streams
  widevine_license: https://api.your-domain.com/v1/license/widevine

client:
  android_tv:
    user_agent: USER_AGENT_STRING
    license_user_agent: LICENSE_USER_AGENT_STRING
    type: DEVICE_TYPE

Study the EXAMPLE Service

The EXAMPLE service (unshackle/services/EXAMPLE/) provides a complete, working template that demonstrates:

  • Cookie-based authentication with JWT tokens
  • Token caching and refresh logic
  • Search functionality
  • Movie and series handling
  • DASH manifest parsing
  • Subtitle extraction
  • Chapter extraction
  • Both Widevine and PlayReady license handling

Getting Started

  1. Copy the EXAMPLE service directory to create your new service
  2. Rename the directory and class to match your service
  3. Update the configuration endpoints in config.yaml
  4. Modify the authentication logic for your platform
  5. Adjust the metadata parsing to match your API responses
  6. Update the DRM license handling as needed

The EXAMPLE service serves as both documentation and a functional template - study it carefully to understand the patterns and adapt them to your specific streaming platform.

Service Tags

Service tags generally follow these rules:

  • Tag can be between 2-4 characters long, consisting of just [A-Z0-9i]{2,4}.
    • Lower-case i is only used for select services. Specifically BBC iPlayer and iTunes.
  • If the Service's commercial name has a + or Plus, the last character should be a P. E.g., ATVP for Apple TV+, DSCP for Discovery+, DSNP for Disney+, and PMTP for Paramount+.

These rules are not exhaustive and should only be used as a guide. You don't strictly have to follow these rules, but we recommend doing so for consistency.

Cookies & Credentials

unshackle can authenticate with Services using Cookies and/or Credentials. Credentials are stored in the config, and Cookies are stored in the data directory which can be found by running unshackle env info.

To add a Credential to a Service, take a look at the Credentials Config for information on setting up one or more credentials per-service. You can add one or more Credential per-service and use -p/--profile to choose which Credential to use.

To add a Cookie to a Service, use a Cookie file extension to make a cookies.txt file and move it into the Cookies directory. You must rename the cookies.txt file to that of the Service tag (case-sensitive), e.g., NF.txt. You can also place it in a Service Cookie folder, e.g., /Cookies/NF/default.txt or /Cookies/NF/.txt.

You can add multiple Cookies to the /Cookies/NF/ folder with their own unique name and then use -p/--profile to choose which one to use. E.g., /Cookies/NF/sam.txt and then use it with --profile sam. If you make a Service Cookie folder without a .txt or default.txt, but with another file, then no Cookies will be loaded unless you use -p/--profile like shown. This allows you to opt in to authentication at whim.

  • If your Service does not require Authentication, then do not define any Credential or Cookie for that Service.
  • You can use both Cookies and Credentials at the same time, so long as your Service takes and uses both.
  • If you are using profiles, then make sure you use the same name on the Credential name and Cookie file name when using -p/--profile. [!WARNING] Profile names are case-sensitive and unique per-service. They have no arbitrary character or length limit, but for convenience sake we don't recommend using any special characters as your terminal may get confused.

Cookies must be in the standard Netscape cookies file format.
Recommended Cookie exporter extensions:

  • Firefox: "[Export Cookies]" by Rotem Dan
  • Chromium: "[Open Cookies.txt]" by Ninh Pham

Any other extension that exports to the standard Netscape format should theoretically work.

Sharing Services

Sending and receiving zipped Service folders is quite cumbersome. Let's explore alternative routes to collaborating on Service Code.

Warning

Please be careful with who you trust and what you run. The users you collaborate with on Service code could update it with malicious code that you would run via unshackle on the next call.

Forking

If you are collaborating with a team on multiple services then forking the project is the best way to go.

  1. Create a new Private GitHub Repository without README, .gitignore, or LICENSE files. Note: Do NOT use the GitHub Fork button, or you will not be able to make the repository private.
  2. git clone <your repo url here> and then cd into it.
  3. git remote add upstream https://github.com/unshackle-dl/unshackle
  4. git remote set-url --push upstream DISABLE
  5. git fetch upstream
  6. git pull upstream master
  7. (optionally) Hard reset to the latest stable version by tag. E.g., git reset --hard v1.0.1.

Now commit your Services or other changes to your forked repository.
Once committed all your other team members can easily pull changes as well as push new changes.

When a new update comes out you can easily rebase your fork to that commit to update.

  1. git fetch upstream
  2. git rebase upstream/master

However, please make sure you look at changes between each version before rebasing and resolve any breaking changes and deprecations when rebasing to a new version.

If you are new to git then take a look at GitHub Desktop.

Tip

A huge benefit with this method is that you can also sync dependencies by your own Services as well! Just use uv to add or modify dependencies appropriately and commit the changed uv.lock. However, if the core project also has dependency changes your uv.lock changes will conflict and you will need to learn how to do conflict resolution/rebasing. It is worth it though!

Symlinking

This is a great option for those who wish to do something like the forking method, but may not care what changes happened or when and just want changes synced across a team.

This also opens up the ways you can host or collaborate on Service code. As long as you can receive a directory that updates with just the services within it, then you're good to go. Options could include an FTP server, Shared Google Drive, a non-fork repository with just services, and more.

  1. Use any Cloud Source that gives you a pseudo-directory to access the Service files like a normal drive. E.g., rclone, Google Drive Desktop (aka File Stream), Air Drive, CloudPool, etc.
  2. Create a services directory somewhere in it and have all your services within it.
  3. Symlink the services directory to the /unshackle folder. You should end up with /unshackle/services folder containing services, not /unshackle/services/services.

You have to make sure the original folder keeps receiving and downloading/streaming those changes. You must also make sure that the version of unshackle you have locally is supported by the Service code.

Note

If you're using a cloud source that downloads the file once it gets opened, you don't have to worry as those will automatically download. Python importing the files triggers the download to begin. However, it may cause a delay on startup.