{"id":14985069,"url":"https://github.com/kyleross/node-video-hash","last_synced_at":"2025-04-10T23:24:40.260Z","repository":{"id":34592701,"uuid":"179113845","full_name":"KyleRoss/node-video-hash","owner":"KyleRoss","description":"Generates unique fingerprints for video files","archived":false,"fork":false,"pushed_at":"2023-11-28T14:07:22.000Z","size":9100,"stargazers_count":24,"open_issues_count":7,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-10-30T00:46:27.912Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/KyleRoss.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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}},"created_at":"2019-04-02T16:06:32.000Z","updated_at":"2024-02-27T14:44:37.000Z","dependencies_parsed_at":"2023-11-30T23:12:07.686Z","dependency_job_id":null,"html_url":"https://github.com/KyleRoss/node-video-hash","commit_stats":{"total_commits":9,"total_committers":2,"mean_commits":4.5,"dds":"0.11111111111111116","last_synced_commit":"5e00c798383f007e35387405f23e1aa40fcb5f0f"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KyleRoss%2Fnode-video-hash","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KyleRoss%2Fnode-video-hash/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KyleRoss%2Fnode-video-hash/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KyleRoss%2Fnode-video-hash/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/KyleRoss","download_url":"https://codeload.github.com/KyleRoss/node-video-hash/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248313548,"owners_count":21082876,"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","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":"2024-09-24T14:10:14.520Z","updated_at":"2025-04-10T23:24:40.240Z","avatar_url":"https://github.com/KyleRoss.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# video-hash\n[![npm](https://img.shields.io/npm/v/video-hash.svg?style=for-the-badge)](https://www.npmjs.com/package/video-hash)\n[![npm](https://img.shields.io/npm/dt/video-hash.svg?style=for-the-badge)](https://www.npmjs.com/package/video-hash)\n[![David](https://img.shields.io/david/KyleRoss/node-video-hash.svg?style=for-the-badge)](https://david-dm.org/KyleRoss/node-video-hash)\n[![Travis](https://img.shields.io/travis/KyleRoss/node-video-hash/master.svg?style=for-the-badge)](https://travis-ci.org/KyleRoss/node-video-hash)\n[![Coveralls](https://img.shields.io/coveralls/github/KyleRoss/node-video-hash.svg?style=for-the-badge)](https://coveralls.io/github/KyleRoss/node-video-hash)\n[![license](https://img.shields.io/github/license/KyleRoss/node-video-hash.svg?style=for-the-badge)](https://github.com/KyleRoss/node-video-hash/blob/master/LICENSE)\n[![Beerpay](https://img.shields.io/beerpay/KyleRoss/node-video-hash.svg?style=for-the-badge)](https://beerpay.io/KyleRoss/node-video-hash)\n\nAlgorithm to fingerprint videos with relatively high accuracy and returns a hash. Achieved by capturing screenshots at particular intervals, hashing the pixels\nof each screenshot and creating a single hash from all of the generated pixel hashes. This package uses [FFmpeg](https://ffmpeg.org/) to take screenshots and \n[FFprobe](https://ffmpeg.org/ffprobe.html) to retrieve metadata. Please note that this package is going to be slow depending on the size of video you are \nfingerprinting.\n\n# Usage\n## Requirements\nIn order to use this package, you must have the following requirements met:\n\n1. [FFmpeg](https://ffmpeg.org/) installed and in your PATH or a local binary.\n    - You can use a package like [ffmpeg-installer](https://www.npmjs.com/package/@ffmpeg-installer/ffmpeg) to provide precompiled binaries for your OS.\n2. [FFprobe](https://ffmpeg.org/ffprobe.html) installed and in your PATH or a local binary.\n    - You can use a package like [ffprobe-installer](https://www.npmjs.com/package/@ffprobe-installer/ffprobe) to provide precompiled binaries for your OS.\n\n## Install\n```\nnpm install --save video-hash\n```\n\n## Quick Start\nThis example shows how to get started using precompiled binaries:\n\n```bash\nnpm install --save video-hash @ffmpeg-installer/ffmpeg @ffprobe-installer/ffprobe\n```\n\n```js\nconst ffmpeg = require('@ffmpeg-installer/ffmpeg');\nconst ffprobe = require('@ffprobe-installer/ffprobe');\n\nconst vHash = require('video-hash')({\n    ffmpegPath: ffmpeg.path,\n    ffprobePath: ffprobe.path\n});\n\nasync createFingerprint(videoPath) {\n    const video = vHash.video(videoPath);\n    \n    try {\n        let hash = await video.hash();\n        return hash;\n    } catch(err) {\n        throw err;\n    }\n}\n```\n\n# API Documentation\n\u003ca name=\"module_video-hash\"\u003e\u003c/a\u003e\n\n## video-hash\n\n* [video-hash](#module_video-hash)\n    * [VideoHash([options])](#exp_module_video-hash--VideoHash) ⇒ \u003ccode\u003ethis\u003c/code\u003e ⏏\n        * [.video(videoPath)](#module_video-hash--VideoHash+video) ⇒ [\u003ccode\u003eVideo\u003c/code\u003e](#Video)\n        * [.options](#module_video-hash--VideoHash.options) : \u003ccode\u003eObject\u003c/code\u003e\n\n\u003ca name=\"exp_module_video-hash--VideoHash\"\u003e\u003c/a\u003e\n\n### VideoHash([options]) ⇒ \u003ccode\u003ethis\u003c/code\u003e ⏏\nFunction exported from this module. Call with `options` object. May be called with or without `new`.\n\n**Kind**: Exported function  \n\n| Param | Type | Default | Description |\n| --- | --- | --- | --- |\n| [options] | \u003ccode\u003eObject\u003c/code\u003e | \u003ccode\u003e{}\u003c/code\u003e | Optional options to pass into the module. |\n| [options.ffmpegPath] | \u003ccode\u003eString\u003c/code\u003e | \u003ccode\u003e\u003c/code\u003e | Overrides the path to the `ffmpeg` binary. If not provided, it will attempt to locate on the system. |\n| [options.ffprobePath] | \u003ccode\u003eString\u003c/code\u003e | \u003ccode\u003e\u003c/code\u003e | Overrides the path to the `ffprobe` binary. If not provided, it will attempt to locate on the system. |\n| [options.hashAlgorithm] | \u003ccode\u003eString\u003c/code\u003e | \u003ccode\u003e\u0026#x27;sha256\u0026#x27;\u003c/code\u003e | The hashing algorithm to use when generating the hash. Must be one of the                                                   available from [crypto.createHash](https://nodejs.org/api/crypto.html#crypto_crypto_createhash_algorithm_options). |\n| [options.tempDir] | \u003ccode\u003eString\u003c/code\u003e | \u003ccode\u003eos.tmpdir()\u003c/code\u003e | Overrides the temp directory where various metadata during hashing is stored. |\n| [options.hashBits] | \u003ccode\u003eNumber\u003c/code\u003e | \u003ccode\u003e12\u003c/code\u003e | Hash length when generating hashes of screenshots. The longer the value, the more unique.                                        See [imghash.hash](https://github.com/pwlmaciejewski/imghash#hashfilepath-bits-format). |\n| [options.strength] | \u003ccode\u003eNumber\u003c/code\u003e | \u003ccode\u003e2\u003c/code\u003e | The strength of the generated video hash. Must be a number between 0.1 and 10. Determines the percentage in which                                       each screenshot is taken based on the video duration. Setting this option to a higher value will provide a stronger                                      fingerprint, but will take longer to generate the hash. |\n\n**Example**  \n```js\n// Using default options:\nconst vHash = require('video-hash')();\n\n// With options:\nconst vHash = require('video-hash')({\n    // options...\n});\n```\n\u003ca name=\"module_video-hash--VideoHash+video\"\u003e\u003c/a\u003e\n\n#### vHash.video(videoPath) ⇒ [\u003ccode\u003eVideo\u003c/code\u003e](#Video)\nPrepares a video for hashing at the provided `videoPath`. Returns a new instance of `Video`.\n\n**Kind**: instance method of [\u003ccode\u003eVideoHash\u003c/code\u003e](#exp_module_video-hash--VideoHash)  \n**Returns**: [\u003ccode\u003eVideo\u003c/code\u003e](#Video) - New instance of `Video`.  \n**Throws**:\n\n- \u003ccode\u003eError\u003c/code\u003e If `videoPath` is not provided.\n\n\n| Param | Type | Description |\n| --- | --- | --- |\n| videoPath | \u003ccode\u003eString\u003c/code\u003e | Path to video file to load for hashing. |\n\n**Example**  \n```js\nconst video = vHash.video('/path/to/some-video.mp4');\n// =\u003e Video { ... }\n```\n\u003ca name=\"module_video-hash--VideoHash.options\"\u003e\u003c/a\u003e\n\n#### vHash.options : \u003ccode\u003eObject\u003c/code\u003e\nCompiled options object. All options except `ffmpegPath` and `ffprobePath` can be changed at any time.\n\n**Kind**: static property of [\u003ccode\u003eVideoHash\u003c/code\u003e](#exp_module_video-hash--VideoHash)  \n\u003ca name=\"Video\"\u003e\u003c/a\u003e\n\n## Video\n**Kind**: global class  \n\n* [Video](#Video)\n    * [new Video(options, video)](#new_Video_new)\n    * [video.hash()](#Video+hash) ⇒ \u003ccode\u003ePromise.\u0026lt;String\u0026gt;\u003c/code\u003e\n    * [video.metadata()](#Video+metadata) ⇒ \u003ccode\u003ePromise.\u0026lt;Object\u0026gt;\u003c/code\u003e\n\n\u003ca name=\"new_Video_new\"\u003e\u003c/a\u003e\n\n### new Video(options, video)\nCreates an instance of Video. This constructor cannot be called directly.\n\n\n| Param | Type | Description |\n| --- | --- | --- |\n| options | \u003ccode\u003eObject\u003c/code\u003e | Options passed from VideoHash. |\n| video | \u003ccode\u003eFfmpegCommand\u003c/code\u003e | Instance of `FfmpegCommand` from `fluent-ffmpeg` for the video. |\n\n\u003ca name=\"Video+hash\"\u003e\u003c/a\u003e\n\n### video.hash() ⇒ \u003ccode\u003ePromise.\u0026lt;String\u0026gt;\u003c/code\u003e\nGenerates the hash/fingerprint for a single video.\n\n**Kind**: instance method of [\u003ccode\u003eVideo\u003c/code\u003e](#Video)  \n**Returns**: \u003ccode\u003ePromise.\u0026lt;String\u0026gt;\u003c/code\u003e - The generated hash/fingerprint for the video.  \n**Example**  \n```js\nconst vHash = require('video-hash')({\n    // options...\n});\n\nasync function hashVideo(videoPath) {\n    const video = vHash.video(videoPath);\n\n    try {\n        let hash = await video.hash();\n        return hash;\n    } catch(err) {\n        throw err;\n    }\n```\n\u003ca name=\"Video+metadata\"\u003e\u003c/a\u003e\n\n### video.metadata() ⇒ \u003ccode\u003ePromise.\u0026lt;Object\u0026gt;\u003c/code\u003e\nReturns basic metadata for a single video from `ffprobe`.\n\n**Kind**: instance method of [\u003ccode\u003eVideo\u003c/code\u003e](#Video)  \n**Returns**: \u003ccode\u003ePromise.\u0026lt;Object\u0026gt;\u003c/code\u003e - The `format` metadata object for the video. See [metadata](https://github.com/fluent-ffmpeg/node-fluent-ffmpeg#reading-video-metadata).  \n**Example**  \n```js\nasync function getMetadata(videoPath) {\n    const video = vHash.video(videoPath);\n    let metadata = await video.metadata();\n}\n```\n---\n\n## Algorithm\nVideo Hash uses a basic algorithm to generate a hash for a video that will provide consistent results each time for the same provided options. This works \nby taking captures at percentage-based intervals based off of the duration of the video. The module generates a number of captures to take from the video \nusing an equation like:\n\n```\n⌈(⌈duration⌉ * strength)⌉\n```\n\nTo simplify, the video duration is rounded up to the nearest whole number and multiplied by the provided `strength` option. The resulting number is then rounded \nup to the nearest whole number. From there, the dependency `fluent-ffmpeg` will generate an array of percentages based on the duration of the video for when \nFFmpeg shoud capture a screenshot. If the duration of the video does not change, neither will the hash.\n\nAfter all of the captures are taken by FFmpeg, each one is hashed using [perceptual hashing](https://en.wikipedia.org/wiki/Perceptual_hashing) and stored. Once \nthe hashes are generated, all are combined and a final hash (SHA256 by default) is generated and returned.\n\n## Tests\nTo run tests locally, ensure you have all of the requirements installed and run:\n\n```\nnpm test\n```\n\n## Contributing\nWhen contributing to this package, please follow the guidelines below:\n\n1. Ensure the there are no ESLint errors in the code, following the `.eslintrc.yml` file in the repo.\n2. Write or update tests for any changes. I strive for 100% coverage, but 95% or higher is acceptable.\n3. Run the tests locally and ensure they pass.\n4. Commits must use the [conventional commits spec](https://www.conventionalcommits.org/en/v1.0.0-beta.3/#specification). If you do not, your commit will fail.\n5. Submit a pull request.\n\n## License\nMIT License\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkyleross%2Fnode-video-hash","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkyleross%2Fnode-video-hash","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkyleross%2Fnode-video-hash/lists"}