{"id":51463149,"url":"https://github.com/vinefeeder/TwinVine","last_synced_at":"2026-07-25T01:00:36.926Z","repository":{"id":311431268,"uuid":"1043697007","full_name":"vinefeeder/TwinVine","owner":"vinefeeder","description":"A single uv install of two packages. Use the VineFeeder GUI for searching and batch downloading; use Envied - with a little bit extra - for command line downloading.","archived":false,"fork":false,"pushed_at":"2026-07-13T09:15:56.000Z","size":11040,"stargazers_count":107,"open_issues_count":2,"forks_count":15,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-07-13T10:07:23.383Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vinefeeder.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-24T12:33:46.000Z","updated_at":"2026-07-13T09:16:06.000Z","dependencies_parsed_at":"2025-09-29T12:07:47.021Z","dependency_job_id":"79c8db01-7a1b-4032-b4dc-3166174ec138","html_url":"https://github.com/vinefeeder/TwinVine","commit_stats":null,"previous_names":["vinefeeder/twinvine"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/vinefeeder/TwinVine","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vinefeeder%2FTwinVine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vinefeeder%2FTwinVine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vinefeeder%2FTwinVine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vinefeeder%2FTwinVine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vinefeeder","download_url":"https://codeload.github.com/vinefeeder/TwinVine/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vinefeeder%2FTwinVine/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35861394,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-07-20T02:08:10.276Z","status":"online","status_checked_at":"2026-07-24T02:00:07.870Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-06T08:00:23.213Z","updated_at":"2026-07-25T01:00:36.919Z","avatar_url":"https://github.com/vinefeeder.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"# TwinVine\n\n![TwinVine GUI](https://github.com/vinefeeder/TwinVine/blob/main/images/vinefeederA.png)\n\nTwinVine combines two Python packages:\n\n- [Vinefeeder](https://github.com/vinefeeder/TwinVine/blob/main/packages/vinefeeder/src/vinefeeder/README.md)\n- [Envied](https://github.com/vinefeeder/TwinVine/blob/main/packages/envied/README.md)\n\nTwinVine helps you find, select, and download media. Use the graphical front end when you want search and selection assistance, and use the command-line downloader for exact URLs.\n\nEnvied is forked from unshackle github.com/unshackle-dl/unshackle and I thank the developers for their effort.\n\n## Key workflows\n\n- Use `envied` when you already have an exact program URL.\n- Use `vinefeeder` search when you only know a program name.\n- Use the browse feature when you want to explore categories like Film, Drama, or Sport.\n- Use Batch Mode to select and download multiple items from several services.\n\n## Usage\n\nTwinVine runs through the Python package manager `uv`.\n\n### Run the main tools\n\n```bash\nuv run vinefeeder\nuv run envied dl --select-titles \u003cservice\u003e \u003curl\u003e\n```\n\n### Access envied from the GUI\n\n- On Linux: choose **run envied** after clicking the GUI `envied` button.\n- On Windows: close the GUI or press `Ctrl+C` to return to the terminal.\n\n## Installation\n\n### Prerequisites\n\nInstall `uv` if needed:\n\n```bash\npip install uv\npython3 -m pip install uv\n```\n\nOr install `python-uv` through your system package manager.\n\n### Install TwinVine\n\n```bash\ngit clone https://github.com/vinefeeder/TwinVine.git\ncd TwinVine\nuv clean\nuv lock\nuv sync\nuv run vinefeeder --help\nuv run envied --help\nuv run envied dl -?\n```\n\n## Windows installation\n\nThis section is aimed at novice Windows users.\nA printer-friendly checklist is available here:\n\n[WINDOWS_INSTALL_CHECKLIST.txt](WINDOWS_INSTALL_CHECKLIST.txt)\n\n### Windows quick overview\n\n1. Install Git for Windows.\n2. Clone the TwinVine repository.\n3. Run the Windows installer script.\n4. Verify that `uv` is installed.\n5. Initialize TwinVine and verify the commands.\n\n## Linux installation\n\nOn Linux, use the bundled shell script to install required binaries.\n\n1. Open `Install-media-tools.sh` in a text editor.\n2. Change the package manager command on lines 6 and 7 from `apt-get` to your distro's package manager (`dnf`, `pacman`, `zypper`, etc.).\n3. Save the file.\n4. Run:\n\n```bash\nsudo bash ./Install-media-tools.sh\n```\n\n5. If `uv` is still not installed, run:\n\n```bash\nwget -qO- https://astral.sh/uv/install.sh | sh\n```\n\n6. Then initialize TwinVine:\n\n```bash\ngit clone https://github.com/vinefeeder/TwinVine.git\ncd TwinVine\nuv lock\nuv sync\ncp ./packages/envied/src/envied/envied-working-example.yaml ./packages/envied/src/envied/envied.yaml\nuv run vinefeeder --help\nuv run envied --help\nuv run envied dl -?\n```\n\n## Locations\n\nBy default, downloaded files are saved to:\n\n- `TwinVine/packages/envied/src/downloads/`\n\nEdit `packages/envied/src/envied/envied.yaml` to change the media-download location and other values like email:password for a service.\n\n- Use a full path.\n- On Windows, use forward slashes: `C:/Users/Downloads`.\n- On Linux, use `/home/user/Downloads`.\n\nCookies live in `packages/envied/src/`. Each cookie file should be named exactly for the service, for example `DNSP.txt`, and contain the service login cookie.\n\nWVD files are stored in `TwinVine/WVDs/`, such as `device.wvd`.\n\nVaults are not configured locally by default. TwinVine uses a remote vault for caching and license retrieval and may display `DRMLab` as the license source.\n\n## Linux note\n\nLinux terminals can sometimes freeze after `envied` completes a download. If that happens, ensure `TERMINAL_RESET: True` is set in:\n\n- `TwinVine/packages/vinefeeder/src/vinefeeder/config.yaml`\n\n## Services\n\n### Vinefeeder services\n\nVinefeeder currently supports search, browse, and list-select for these services:\n\n- ALL4\n- BBC\n- ITVX\n- MY5\n- PLEX\n- RTE\n- STV\n- TPTV\n- TVNZ\n- U\n\n### Envied services\n\nEnvied supports a broader set of direct-download services:\n\n- ALL4\n- AUBC\n- CBS\n- CWTV\n- DSCP\n- iP\n- MAX\n- MY5\n- NF\n- PCOK\n- PLEX\n- RTE\n- ROKU\n- SPOT\n- TPTV\n- TVNZ\n- YTBE\n- ARD\n- CBC\n- CTV\n- DSNP\n- ITV\n- MTSP\n- NBLA\n- NRK\n- PLUTO\n- STV\n- TUBI\n- UKTV\n- ZDF\n\nThese services have web origins and not all have been tested.\n\n## You can use AI to create your own Vinefeeder service!!\n\nThe prompt below was written by ChatGPT after refactoring a Vinefeeder service to reuse Envied service logic.\n\n```\nI am working on the TwinVine project:\n\nhttps://github.com/vinefeeder/TwinVine\n\nTwinVine contains two Python packages:\n\n* `envied`: a command-line video downloader.\n* `vinefeeder`: a graphical / interactive front end that searches, lists, selects, and then calls `envied` by subprocess.\n\nI want to create or rewrite a Vinefeeder service by reusing the matching envied service instead of duplicating API logic.\n\nTarget service:\n\n* Envied service path:\n  `packages/envied/src/envied/services/\u003cSERVICE\u003e/`\n\n* Vinefeeder service path:\n  `packages/vinefeeder/src/vinefeeder/services/\u003cSERVICE\u003e/`\n\nPlease inspect the current code in both locations.\n\nGoal:\n\nRewrite the Vinefeeder `\u003cSERVICE\u003e` service so that it imports and reuses the existing envied `\u003cSERVICE\u003e` service class wherever possible.\n\nThe Vinefeeder service should:\n\n1. Preserve the standard Vinefeeder service interface:\n\n   * `receive()`\n   * `fetch_videos()`\n   * `second_fetch()`\n   * `fetch_videos_by_category()`\n\n2. Use the envied service for:\n\n   * authentication / token handling\n   * search, if the envied service provides `search()`\n   * programme/title expansion via `get_titles()` or `get_titles_cached()`\n   * service-specific API calls already implemented in envied\n\n3. Avoid reimplementing API endpoints that already exist in envied.\n\n4. Keep Vinefeeder responsible only for:\n\n   * presenting search results\n   * letting the user choose a title\n   * expanding a multi-episode series into selectable individual episodes\n   * building one envied subprocess command per selected single title\n\n5. Use `beaupy.select_multiple()` when there are multiple episodes available.\n\n6. If the selected item is a movie, sport event, clip, highlight, or single episode, skip the beaupy episode list and call envied directly.\n\n7. Preserve Vinefeeder’s existing download behaviour by calling:\n\n   `self.runsubprocess(command)`\n\n   where command should normally be shaped like:\n\n   `[\"uv\", \"run\", \"envied\", \"dl\", *self.options_list, \"\u003cSERVICE\u003e\", url]`\n\n8. Preserve support for Vinefeeder service options from config, using `split_options()` as existing services do.\n\n9. If an envied service object needs a Click context, create the smallest safe adapter context needed rather than copying envied command-line internals unnecessarily.\n\n10. If the envied service requires credentials, token cache, proxy state, or service config, reuse envied’s existing config-loading patterns as closely as possible.\n\n11. Be careful not to authenticate twice unnecessarily. If `fetch_videos()` and `second_fetch()` are sequential, prefer reusing an already-authenticated envied service instance where safe.\n\n12. Produce a complete replacement `__init__.py` for the Vinefeeder service.\n\n13. Also explain:\n\n* which envied methods/classes are being reused\n* what Vinefeeder-specific logic remains\n* any assumptions or caveats\n* how I should test the rewritten service\n\nPlease do not merely describe the approach. Provide the actual rewritten Vinefeeder service code.\n\n```\n## Linux users only: Adding a little automation\n\nIn .bashrc in your home directory you can add useful aliases and scripts to speed the start-up of TwinVine.\n\nAdd the following lines at the end of your .bashrc, (or your system's equivalent), to start Vinefeeder by typing 'v' into a terminal window;  \nand 'e' to start envied with a pre-filled command.  BE SURE TO EDIT your/path/to/Twinvine to reflect your needs.\n```\nalias v=\"cd /your/path/to/TwinVine/;uv run vinefeeder\"\n\nunalias e 2\u003e/dev/null  \n\ne() {\n    cd /your/path/to/TwinVine || return\n\n    local cmd\n    read -e -i \"uv run envied dl --select-titles \" -p \"TwinVine\u003e \" cmd || return\n\n    [[ -n \"$cmd\" ]] \u0026\u0026 history -s \"$cmd\" \u0026\u0026 eval \"$cmd\"\n}\n```\n  \n## Other README's\n    TwinVine/packages/vinefeeder/src/vinefeeder/README.md  \n    for details for confuring Envied download options on a service by service basis.\n    TwinVine/packages/envied/README.md  links to wiki (envied - envied's parent)\n\n\n\nImages\n    ![TwinVine GUI](https://github.com/vinefeeder/TwinVine/blob/main/images/vinefeederA1.png)\n    ![TwinVine GUI](https://github.com/vinefeeder/TwinVine/blob/main/images/vinefeeder1.png)\n    ![TwinVine GUI](https://github.com/vinefeeder/TwinVine/blob/main/images/vinefeeder2.png)\n    ![TwinVine GUI](https://github.com/vinefeeder/TwinVine/blob/main/images/vinefeeder4.png)\n    ![TwinVine GUI](https://github.com/vinefeeder/TwinVine/blob/main/images/vinefeeder5.png)\n    ![TwinVine GUI](https://github.com/vinefeeder/TwinVine/blob/main/images/vinefeeder6.png)\n    ![TwinVine GUI](https://github.com/vinefeeder/TwinVine/blob/main/images/vinefeeder7.png)\n    ![TwinVine GUI](https://github.com/vinefeeder/TwinVine/blob/main/images/vinefeeder8.png)\n    ![TwinVine GUI](https://github.com/vinefeeder/TwinVine/blob/main/images/vinefeeder9.png)\n    ![TwinVine GUI](https://github.com/vinefeeder/TwinVine/blob/main/images/vinefeeder10.png)\n    ![TwinVine GUI](https://github.com/vinefeeder/TwinVine/blob/main/images/vinefeeder11.png)\n    ![TwinVine GUI](https://github.com/vinefeeder/TwinVine/blob/main/images/vinefeederB.png)\n    ![TwinVine GUI](https://github.com/vinefeeder/TwinVine/blob/main/images/hellyes.png)\n    ","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvinefeeder%2FTwinVine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvinefeeder%2FTwinVine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvinefeeder%2FTwinVine/lists"}