{"id":17169233,"url":"https://github.com/rnag/wystia","last_synced_at":"2025-10-16T11:27:16.920Z","repository":{"id":40573035,"uuid":"376981577","full_name":"rnag/wystia","owner":"rnag","description":"A Python wrapper library for the Wistia API","archived":false,"fork":false,"pushed_at":"2025-10-01T22:51:14.000Z","size":10418,"stargazers_count":5,"open_issues_count":8,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-10-02T00:23:15.794Z","etag":null,"topics":["dataclasses","python","social-media","social-media-website","video-cms","video-hosting-platform","wistia","wistia-api","wistia-data-api","wistia-video","youtube-alternative"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rnag.png","metadata":{"files":{"readme":"README.rst","changelog":"HISTORY.rst","contributing":"CONTRIBUTING.rst","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":"2021-06-14T23:31:49.000Z","updated_at":"2024-04-28T12:24:19.000Z","dependencies_parsed_at":"2024-06-01T20:40:10.051Z","dependency_job_id":"bf8404ba-5109-47b5-9604-325a2d291484","html_url":"https://github.com/rnag/wystia","commit_stats":{"total_commits":56,"total_committers":3,"mean_commits":"18.666666666666668","dds":0.5357142857142857,"last_synced_commit":"49444661b2054366f8971be35464243fa0e31a19"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/rnag/wystia","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rnag%2Fwystia","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rnag%2Fwystia/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rnag%2Fwystia/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rnag%2Fwystia/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rnag","download_url":"https://codeload.github.com/rnag/wystia/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rnag%2Fwystia/sbom","scorecard":{"id":779252,"data":{"date":"2025-08-11","repo":{"name":"github.com/rnag/wystia","commit":"0e2605b2ad8639b9f8440f1069b665d4428d435f"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.4,"checks":[{"name":"Code-Review","score":1,"reason":"Found 1/10 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/dev.yml:1","Warn: no topLevel permission defined: .github/workflows/release.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/dev.yml:31: update your workflow using https://app.stepsecurity.io/secureworkflow/rnag/wystia/dev.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/dev.yml:32: update your workflow using https://app.stepsecurity.io/secureworkflow/rnag/wystia/dev.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:30: update your workflow using https://app.stepsecurity.io/secureworkflow/rnag/wystia/release.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:38: update your workflow using https://app.stepsecurity.io/secureworkflow/rnag/wystia/release.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/release.yml:52: update your workflow using https://app.stepsecurity.io/secureworkflow/rnag/wystia/release.yml/main?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/dev.yml:38","Warn: pipCommand not pinned by hash: .github/workflows/dev.yml:39","Warn: pipCommand not pinned by hash: .github/workflows/release.yml:44","Warn: pipCommand not pinned by hash: .github/workflows/release.yml:45","Info:   0 out of   4 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned","Info:   0 out of   4 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":9,"reason":"1 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: PYSEC-2023-228 / GHSA-mq26-g339-26xf"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 26 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-23T04:30:00.720Z","repository_id":40573035,"created_at":"2025-08-23T04:30:00.720Z","updated_at":"2025-08-23T04:30:00.720Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279183407,"owners_count":26121397,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-16T02:00:06.019Z","response_time":53,"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":["dataclasses","python","social-media","social-media-website","video-cms","video-hosting-platform","wistia","wistia-api","wistia-data-api","wistia-video","youtube-alternative"],"created_at":"2024-10-14T23:25:28.309Z","updated_at":"2025-10-16T11:27:16.846Z","avatar_url":"https://github.com/rnag.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"==========================\nWystia - Wistia API Helper\n==========================\n\n\n.. image:: https://img.shields.io/pypi/v/wystia.svg\n        :target: https://pypi.org/project/wystia/\n\n.. image:: https://img.shields.io/pypi/l/wystia.svg\n        :target: https://pypi.org/project/wystia/\n\n.. image:: https://img.shields.io/pypi/pyversions/wystia.svg\n        :target: https://pypi.org/project/wystia\n\n.. image:: https://github.com/rnag/wystia/actions/workflows/dev.yml/badge.svg\n        :target: https://github.com/rnag/wystia/actions/workflows/dev.yml\n\n.. image:: https://readthedocs.org/projects/wystia/badge/?version=latest\n        :target: https://wystia.readthedocs.io/en/latest/?version=latest\n        :alt: Documentation Status\n\n\n.. image:: https://pyup.io/repos/github/rnag/wystia/shield.svg\n     :target: https://pyup.io/repos/github/rnag/wystia/\n     :alt: Updates\n\n\n\nA Python wrapper library for the Wistia API\n\n\n* Free software: MIT license\n* Documentation: https://wystia.readthedocs.io.\n* Wistia Developer Docs: https://wistia.com/support/developers.\n\nInstallation\n------------\n\nThe Wystia library is available `on PyPI`_, and can be installed with ``pip``:\n\n.. code-block:: shell\n\n    $ pip install wystia\n\nYou'll also need to create an access token as outlined `in the docs`_.\n\nUsage\n-----\n\nSample usage with the `Data API \u003chttps://wistia.com/support/developers/data-api\u003e`_:\n\n    Note: The following example makes use of ``WistiaApi``, which is an alias to\n    the class ``WistiaDataApi``.\n\n.. code-block:: python3\n\n    from wystia import WistiaApi\n    from wystia.models import SortBy, LanguageCode, Customizations, Private\n\n    # Setup the Wistia API token to use for requests. You can alternatively\n    # set this via the env variable 'WISTIA_API_TOKEN'.\n    WistiaApi.configure('MY-TOKEN')\n\n    # Retrieve a list of all projects in the Wistia account,\n    # sorted A-Z and in ascending order.\n    projects = WistiaApi.list_all_projects(SortBy.NAME)\n    project_ids = [p.hashed_id for p in projects]\n    # Print the project data as a prettified JSON string\n    print(projects.prettify())\n\n    # Retrieve a list of videos for a Wistia project.\n    # Note: If you don't require asset info (such as ADs) on each\n    #   video, I suggest calling `list_project` instead.\n    videos = WistiaApi.list_videos('project-id')\n\n    # Retrieve info on a particular video\n    vd = WistiaApi.get_video('video-id')\n    # If the video has captions, that won't be included in the `Medias#show`\n    # response by default, so we'll need a separate API call as below.\n    # vd.process_captions(\n    #     WistiaApi.list_captions(real_video_id))\n    print(vd)\n\n    # Update attributes on a media (video), or set a custom thumbnail on the video.\n    WistiaApi.update_video(\n        'video-id',\n        thumbnail_media_id='uploaded-thumbnail-id'\n    )\n\n    # Get aggregated stats for a video, such as view count\n    stats = WistiaApi.get_stats_for_video('video-id')\n\n    # Retrieve the customization data for a video\n    customizations = WistiaApi.get_customizations('video-id')\n\n    # Update only specific customizations for a video\n    # Note the embed options are documented here:\n    #   https://wistia.com/support/developers/embed-options\n    sample_embed_options = Customizations(\n        player_color='#e7fad1',\n        # Hide comments on the media page\n        private=Private(show_comments=False)\n    )\n    WistiaApi.update_customizations('video-id', sample_embed_options)\n\n    # Get the Spanish captions on a video\n    captions = WistiaApi.get_captions('video-id', LanguageCode.SPANISH)\n\n    # Add (or replace) the English captions on a video\n    WistiaApi.update_captions(\n        'video-id',\n        LanguageCode.ENGLISH,\n        srt_file='path/to/file.srt'\n    )\n\n\n... or to upload media via the `Upload API \u003chttps://wistia.com/support/developers/upload-api\u003e`_:\n\n.. code-block:: python3\n\n    from wystia import WistiaUploadApi\n\n    # Upload a file to a (default) project on Wistia\n    r = WistiaUploadApi.upload_file('path/to/my-file.mp4')\n    # Check if the video was successfully uploaded\n    # assert r.created\n    # assert r.name == 'my-file.mp4'\n\n    # Uploads with a public link to a video, such as\n    # an S3 pre-signed url.\n    r = WistiaUploadApi.upload_link('my-s3-link',\n                                    title='My Video Name',\n                                    description='My Description')\n\n... you can alternatively retrieve asset info via the public Media Embed link:\n\n.. code-block:: python3\n\n    from wystia import WistiaEmbedApi\n\n    # Get the media embed data\n    embed_data = WistiaEmbedApi.get_data('video-id')\n\n    # Retrieve the source URL of the original media\n    source_url = WistiaEmbedApi.asset_url(media_data=embed_data)\n\n... when using the *Data API*, the ``WistiaHelper`` can help to further simplify some calls:\n\n.. code-block:: python3\n\n    from wystia import WistiaHelper\n\n    # Check if the video exists in your Wistia account\n    assert WistiaHelper.video_exists('abc1234567')\n\n    # Check if a video's name indicates the video is an archived copy of an\n    # existing video, as discussed in the below article on replacing a media:\n    #   https://wistia.com/learn/product-updates/improved-library-management-tools\n    assert WistiaHelper.is_archived_video(\n        'My Title [Archived on August 13, 2015]')\n\n    # Update the player color on a video\n    WistiaHelper.customize_video_on_wistia('video-id', 'ffffcc')\n\n    # Individually enable captions / AD in the player for a video\n    WistiaHelper.enable_ad('video-id')\n    WistiaHelper.enable_captions('video-id', on_by_default=False)\n\n    # Disable captions / AD in the player for a video\n    if WistiaHelper.has_captions_enabled('video-id'):\n        print('Disabling captions and AD for the video')\n        WistiaHelper.disable_captions_and_ad('video-id')\n\nGetting Started\n---------------\n\nUsing the methods on the API classes assume your Wistia API token\nhas previously been configured, for example via the environment. The API token will\nthen be used globally by all the API classes when making requests to the Wistia API.\n\nYou can set the following environment variable with your API token:\n\n* ``WISTIA_API_TOKEN``\n\nAnother option is to use the global ``configure`` method as shown below:\n\n.. code-block:: python3\n\n    WistiaDataApi.configure('MY-API-TOKEN')\n\nThere is additionally a `Quickstart`_ section in the docs which walks\nthrough - in more detail - how to get up and running with the\nWystia library.\n\nData API\n--------\n\nThe wrapper class ``WistiaDataApi`` (aliased to ``WistiaApi``) interacts\nwith the Wistia Data API (docs below):\n\n- https://wistia.com/support/developers/data-api\n\n\nIt fully implements the following sections in the API documentation:\n\n    - Paging and Sorting Responses\n    - Projects\n    - Medias\n    - Customizations\n    - Captions\n\nThe following sections in the API have *not* been implemented (mainly as I haven't used them before):\n\n    - Project Sharings\n    - Account\n\n\nTips\n~~~~\n\nContainers\n==========\n\nIn general, the API methods that begin with *list* - such as ``list_project`` -\nwill return a `Container`_ object, which essentially acts as a thin wrapper\naround a collection of model classes. For all intents and purposes, this behaves\nexactly the same as a ``list`` object.\n\nOne of the main benefits is that it implements a ``__str__`` method, which leverages\nthe builtin ``pprint`` module in Python to pretty-print the Python object representation\nof each model or *dataclass* instance; this will format the output more nicely, for example\nwhenever ``print(obj)`` is called on the `Container` result.\n\nThe ``Container`` objects also implement the following convenience methods, which can\nbe used to easily display the JSON string representation of the list of dataclass instances:\n\n    * ``to_json`` - Convert the list of instances to a JSON string.\n\n    * ``prettify`` - Convert the list of instances to a *prettified* JSON string.\n\nList Medias in a Project\n========================\n\nIf you need to retrieve info on videos in a project and you\ndon't need complete info such as a list of assets for the video,\nI recommend using ``list_project`` instead of ``list_videos``. This is because\nthe `Projects#show \u003chttps://wistia.com/support/developers/data-api#projects_show\u003e`_\nAPI returns up to 500 results per request, whereas the ``Medias#list``\nonly returns the default 100 results per page.\n\nAssuming a project in your Wistia account has a total of about 250 media, here is the number of API\ncalls you might expect from each individual approach:\n\n.. code-block:: python3\n\n    from wystia import WistiaDataApi\n\n    videos = WistiaDataApi.list_videos('project-id')\n    assert WistiaDataApi.request_count() == 3\n\n    # Resets request count for the next call\n    WistiaDataApi.reset_request_count()\n\n    videos = WistiaDataApi.list_project('project-id')\n    assert WistiaDataApi.request_count() == 1\n\n\nThread Safety\n-------------\n\nThe Wistia API classes are completely thread safe, since ``requests.Session``\nobjects are not re-used between API calls.\n\nThis means that if you have two (un-related) API operations to perform,\nsuch as updating a video's title and adding captions on the video,\nthen you can certainly run those calls in parallel so that\nthey complete a bit faster.\n\n\nCredits\n-------\n\nThis package was created with Cookiecutter_ and the `audreyr/cookiecutter-pypackage`_ project template.\n\n.. _on PyPI: https://pypi.org/project/wystia/\n.. _in the docs: https://wistia.com/support/developers/making-api-requests#creating-and-managing-access-tokens\n.. _Container: https://dataclass-wizard.readthedocs.io/en/latest/dataclass_wizard.html?highlight=container#dataclass_wizard.Container\n.. _Cookiecutter: https://github.com/audreyr/cookiecutter\n.. _`audreyr/cookiecutter-pypackage`: https://github.com/audreyr/cookiecutter-pypackage\n.. _Quickstart: https://wystia.readthedocs.io/en/latest/quickstart.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frnag%2Fwystia","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frnag%2Fwystia","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frnag%2Fwystia/lists"}