{"id":24534559,"url":"https://github.com/bloomtom/volyar","last_synced_at":"2025-03-15T22:23:01.540Z","repository":{"id":38006465,"uuid":"165051898","full_name":"bloomtom/Volyar","owner":"bloomtom","description":"Automated media transcoding, indexing, and storage.","archived":false,"fork":false,"pushed_at":"2024-10-09T01:31:22.000Z","size":3620,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-08T20:09:20.085Z","etag":null,"topics":["encoder","media-library"],"latest_commit_sha":null,"homepage":"","language":"C#","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/bloomtom.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","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}},"created_at":"2019-01-10T11:57:42.000Z","updated_at":"2024-08-29T22:02:20.000Z","dependencies_parsed_at":"2024-08-29T22:22:54.854Z","dependency_job_id":null,"html_url":"https://github.com/bloomtom/Volyar","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bloomtom%2FVolyar","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bloomtom%2FVolyar/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bloomtom%2FVolyar/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bloomtom%2FVolyar/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bloomtom","download_url":"https://codeload.github.com/bloomtom/Volyar/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243797862,"owners_count":20349483,"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":["encoder","media-library"],"created_at":"2025-01-22T11:17:41.083Z","updated_at":"2025-03-15T22:23:01.488Z","avatar_url":"https://github.com/bloomtom.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Volyar\n\n\u003eAutomated media transcoding, indexing, and storage.\n\nGetting media files to playback on all platforms is a real chore. There are media library managers and transcoders, but none are simultaneously comprehensive, easy to use, and free.\nVolyar is a media library transcoder built to fill the gap.\n\n## Contents\n  - [Setup](#setup)\n    - [Systemd](#setup-systemd)\n  - [Configuration](#configuration)\n    - [Web Configuration](#web-configuration)\n    - [Database Configuration](#database-configuration)\n    - [Misc Configuration](#misc-configuration)\n    - [Library Configuration](#library-configuration)\n      - [Qualities](#library-quality-configuration)\n      - [Storage Backend](#library-storage-configuration)\n        - [Filesystem](#library-storage-filesystem-configuration) \n        - [AmazonS3](#library-storage-s3-configuration) \n        - [Azure Files](#library-storage-azure-configuration) \n        - [BunnyCDN](#library-storage-bunny-configuration) \n      - [WebHooks](#library-webhook-configuration)\n      - [API Integration](#library-api-integration-configuration)\n  - [Web UI](#web-ui)\n    - [Conversion Status](#web-ui-conversion)\n    - [Pending Deletions](#web-ui-deletion)\n  - [Web API](#web-api)\n  - [Differentials](#differentials)\n\n\u003ca name=\"setup\"\u003e\u003c/a\u003e\n## Setup\n\nThis is a dotnet core application, so you'll need the dotnet framework installed. Many platforms have dotnet core available via package management, but if you just want install binaries they're available [here](https://dotnet.microsoft.com/download/dotnet-core/2.1).\nYou'll also need [ffmpeg, ffprobe](https://ffmpeg.org/) and [mp4box](https://gpac.wp.imt.fr/downloads/) installed.\n\nWith the prerequisites handled, clone this repository:\n```\n\u003egit clone https://github.com/bloomtom/Volyar\n```\nEnter the repo directory:\n```\n\u003ecd Volyar\n```\nRun the build script. This will also update you to the latest commit:\n```\n\u003echmod +x build.sh\n\u003e./build.sh\n```\nNow run the run script:\n```\n\u003echmod +x run.sh\n\u003e./run.sh\nHosting environment: Production\nNow listening on: http://0.0.0.0:7014\nApplication started. Press Ctrl+C to shut down.\n```\nYou can now navigate to the web UI at `http:/localhost:7014/voly/external/ui`.\n\n\u003ca name=\"setup-systemd\"\u003e\u003c/a\u003e\n##### Systemd Service Configuration\nA service file is also included. Modify it to your needs, and install it the typical way:\n```\nsudo cp volyar.service /etc/systemd/system/\nsudo systemctl daemon-reload\nsudo systemctl enable volyar\nsudo systemctl start volyar\nsudo systemctl status volyar\n```\n\n\u003ca name=\"configuration\"\u003e\u003c/a\u003e\n## Configuration\n\nOn first start, a configuration file (`vsettings.json`) is created in the working directory. The default configuration works, but won't be terribly useful without modification.\n\n\u003ca name=\"web-configuration\"\u003e\u003c/a\u003e\n#### Web interface settings\n - `Listen`\n   - The IP address to listen on. Use 0.0.0.0 to listen on all interfaces.\n - `Port`\n   - The port to listen on for the web UI and APIs.\n - `BasePath`\n   - The web UI and API base path. \n\n\u003ca name=\"database-configuration\"\u003e\u003c/a\u003e\n#### Database settings\n - `DatabaseType`\n   - The type of database to use. Accepts: temp, sqlite, sqlserver, mysql.\n - `DatabaseConnection`\n   - The database connection to use. Example: `Data Source=mydata.sqlite;`\n\n\u003ca name=\"dependency-configuration\"\u003e\u003c/a\u003e\n#### External Dependency Settings\n - `FFmpegPath`\n   - An absolute or relative path to an ffmpeg executable.\n - `FFprobePath`\n   - An absolute or relative path to an ffprobe executable.\n - `Mp4BoxPath`\n   - An absolute or relative path to an mp4box executable.\n\n\u003ca name=\"misc-configuration\"\u003e\u003c/a\u003e\n#### Misc Settings\n - `TempPath`\n   - The global temp path to use. If none is given, the working directory is used.\n - `Parallelization`\n   - The number of media files to process at once.\n - `CompleteQueueLength`\n   - Specifies the number of items to keep track of after they've gone through conversion.\n\n\u003ca name=\"library-configuration\"\u003e\u003c/a\u003e\n#### Library Settings\nLibraries are given as a collection of the following properties.\n - `Name`\n   - The library name. Must be unique.\n - `Enable`\n   - True by default. Set to false to disable processing of this library.\n - `OriginPath`\n   - The path to source media items from.\n - `SourceHandling`\n   - None by default. Defines what should be done with source files after successful processing.\n     - none\n\t   - Nothing will be done to source files.\n\t - truncate\n\t   - Source files will be truncated to zero bytes.\n\t - delete\n\t   - Source files will be deleted.\n\t   - Avoid using this with DeleteWithSource: true.\n - `DeleteWithSource`\n   - True by default. Transcoded media objects are scheduled for deletion from the database and storage backend when the source file cannot be found. Avoid using this with SourceHandling: \"delete\" unless you don't care about your files.\n - `TempPath`\n   - The temporary path to store intermediate files when encoding.\n - `ForceFramerate`\n   - Zero by default. If not zero, the output framerate is forced to this value on encode.\n - `ValidExtensions`\n   - The file extensions allowed for uptake in a scan. Should start with a dot (.mp4, .mkv, etc.)\n - `DownmixAudio`\n   - True by default. Audio tracks with more than two channels are downmixed into stereo. Two channel audio is more compatible with web browsers.\n - [`Qualities`](#library-quality-configuration)\n   - A collection of qualities to encode into.\n - [`StorageBackend`](#library-storage-configuration)\n   - The storage backend setting to use for this library.\n - [`WebHooks`](#library-webhook-configuration)\n   - A collection of web hooks to call upon completion of a conversion.\n - [`ApiIntegration`](#library-api-integration-configuration)\n   - A backend API to query for metadata about a media file upon conversion.\n \n The qualities, storage backend and web hooks settings for a library are a bit more complex than the others, so they're broken out below.\n \n\u003ca name=\"library-quality-configuration\"\u003e\u003c/a\u003e\n##### Qualities\nQualities determine how media will be encoded. You can configure multiple qualities to take advantage of MPEG DASH adaptive bitrate streaming.\n```\n\"Qualities\": [\n  {\n    \"Width\": 1920,\n    \"Height\": 1080,\n    \"Bitrate\": 4000,\n    \"Preset\": \"medium\",\n    \"Profile\": \"High\",\n    \"Level\": \"4.1\",\n    \"PixelFormat\": \"yuv420p\"\n  },\n  {\n    \"Width\": 1280,\n    \"Height\": 720,\n    \"Bitrate\": 1800,\n    \"Preset\": \"medium\",\n    \"Profile\": \"High\",\n    \"Level\": \"4.1\",\n    \"PixelFormat\": \"yuv420p\"\n  }\n],\n```\n - `Width`\n   - Width of frame in pixels.\n - `Height`\n   - Height of frame in pixels.\n - `Bitrate`\n   - Bitrate of media in kb/s.\n - `Preset`\n   - ffmpeg preset (veryfast, fast, medium, slow, veryslow).\n - `Profile`\n   - ffmpeg h264 encoding profile (Baseline, Main, High).\n - `Level`\n   - ffmpeg h264 encoding profile level (3.0, 4.0, 4.1...)\n - `PixelFormat`\n   - ffmpeg pixel format or pix_fmt (yuv420p for best compatibility).\n\n\u003ca name=\"library-storage-configuration\"\u003e\u003c/a\u003e\n##### Storage Backend\nThe storage backend determines how media will be stored. You can store to a local disk, or upload media to a cloud provider.\nYou should only set one of the following, and leave the rest as `null`. Setting more than one will cause only one to be used.\n\u003ca name=\"library-storage-filesystem-configuration\"\u003e\u003c/a\u003e\n##### `Filesystem`\n```\n\"StorageBackend\": {\n  \"Filesystem\": {\n\t\"Directory\": \"C:\\\\Users\\\\SAMSA\\\\Documents\\\\volytest\\\\library\"\n  }\n},\n```\n  - `Directory`\n    - The filesystem path to store at\n\u003ca name=\"library-storage-s3-configuration\"\u003e\u003c/a\u003e\n##### `AmazonS3`\n```\n\"StorageBackend\": {\n  \"AmazonS3\": {\n    \"Filesystem\": {\n      \"AccessKey\": \"AAAABBBBCCCCDDDDEEEE\",\n      \"ApiKey\": \"abcdefghijklmnopqrstuvwxyz0123456789ABCD\",\n      \"Endpoint\": \"apigateway.us-east-1.amazonaws.com\",\n      \"Bucket\": \"mybucket\",\n      \"CannedACL\": \"public-read\",\n      \"SignatureVersion\": \"4\",\n    }\n  }\n},\n```\n  - `AccessKey`\n    - Your AWS access key.\n  - `ApiKey`\n    - The API key to use for authentication.\n  - `Endpoint`\n    - The [endpoint](https://docs.aws.amazon.com/general/latest/gr/rande.html) domain to connect to.\n  - `Bucket`\n    - The S3 bucket to store files in.\n  - `CannedACL`\n    - An optional [canned ACL](https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl) string.\n  - `SignatureVersion`\n    - Some S3 providers don't support v4 signatures (e.g. Digital Ocean Spaces). Set this to \"2\" when using such a provider.\n\u003ca name=\"library-storage-azure-configuration\"\u003e\u003c/a\u003e\n##### `Azure`\n```\n\"StorageBackend\": {\n  \"Azure\": {\n    \"Account\": \"myaccount\",\n    \"SasToken\": \"?sv=2019-01-01\u0026ss=b\u0026srt=co\u0026sp=abcdef\u0026se=2019-12-31T12:00:00Z\u0026st=2019-12-31T12:00:00Z\u0026spr=https\u0026sig=abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHI\",\n    \"Container\": \"mycontainer\"\n  }\n},\n```\n  - `Account`\n    - The Azure account to connect to.\n  - `SasToken`\n    - The Azure SAS token to authenticate with.\n  - `Container`\n    - The container to store files in.\n\u003ca name=\"library-storage-bunny-configuration\"\u003e\u003c/a\u003e\n##### `BunnyCDN`\n```\n\"StorageBackend\": {\n  \"BunnyCDN\": {\n    \"ApiKey\": \"abcdefgh-ijkl-mnop-qrstuvwxyz01-2345-6789\",\n    \"StorageZone\": \"myzone\"\n  }\n},\n```\n  - `ApiKey`\n    - Your BunnyCDN API key.\n  - `StorageZone`\n    - The storage zone to store files in.\n\n\u003ca name=\"library-webhook-configuration\"\u003e\u003c/a\u003e\n##### WebHooks\nWebHooks are called when a conversion is completed. They simply make a call to a remote resource with no payload.\n```\n\"WebHooks\": [\n  {\n    \"Url\": \"https://somesite.example\",\n    \"Method\": \"post\",\n    \"Username\": \"tom\",\n    \"Password\": \"mypass\"\n  },\n]\n```\n  - `Url`\n    - The endpoint to call.\n  - `Method`\n    - The HTTP method to use: Put or post.\n  - `Username`\n    - A username if needed for http authentication.\n  - `Password`\n    - A password if needed for http authentication.\n\n\u003ca name=\"library-api-integration-configuration\"\u003e\u003c/a\u003e\n##### API Integration\nA library's API integration is called whenever a media item is transcoded. It's used to fill in metadata about a media item, like the series and episode title, or the IMDb or TVDB IDs. This can be incredibly useful, so adding an integration hook is strongly encouraged if one is available.\n```\n\"ApiIntegration\": [\n  {\n    \"Url\": \"https://somesite.example/sonarr\",\n    \"Type\": \"sonarr\",\n    \"ApiKey\": \"6c2e7634f46959b20a51993205b5c29b\",\n    \"Username\": \"tom\",\n    \"Password\": \"mypass\"\n  },\n]\n```\n  - `Url`\n    - The API endpoint to call. Don't include /api, just the base URL.\n  - `Type`\n    - The type of API. Supports 'sonarr' and 'radarr'.\n  - `Username`\n    - A username if needed for http authentication.\n  - `Password`\n    - A password if needed for http authentication.\n\n\n\u003ca name=\"web-ui\"\u003e\u003c/a\u003e\n## Web UI\n\nThe Web UI can be accessed by default at [http://localhost:7014/voly/external/ui](http://localhost:7014/voly/external/ui). The port and \"/voly/\" (`BasePath`) component of the URL are configurable. See [Web Configuration](#web-configuration) section for more info.\n\nThe Web UI is split into tabs for organization of function. The following explains what each tab does.\n\n\u003ca name=\"web-ui-conversion\"\u003e\u003c/a\u003e\n#### Conversions\nThe conversions tab can be used to trigger a global library scan, check conversion in progress, and see the last few items which were converted. You can also pause and resume the queue, as well as cancel items. All operations are completely safe to do at any time, although obviously if you cancel a conversion in progress you'll lose that progress.\n\n##### A note on pause/resume\nPausing the conversion queue doesn't pause conversions, it pauses the queue. What's the difference? When you pause the queue, the conversion currently in `Processing` will continue on, but when they finish no items from the `Waiting` area will begin processing until the queue is resumed.\n\n\u003ca name=\"web-ui-deletion\"\u003e\u003c/a\u003e\n#### Deletions\nWhen items are deleted, by default they aren't -actually- deleted, they're put into the pending deletions queue. This is done to provide a trade-off between automation and safety. To actually delete things, you'll typically review the pending deletions tab on the web UI, and approve or cancel deletions manually.\n\n\u003ca name=\"web-api\"\u003e\u003c/a\u003e\n## Web API\n\n - `/external/api/version`\n   - GET \n     - Returns the program version.\n\n\n - `/external/api/conversion/status`\n   - GET\n     - Returns a collection of items currently being processed, and in queue for processing.\n\n\t \n - `/external/api/conversion/complete`\n   - GET\n     - Returns a collection of items which have either completed, or errored out (ErrorText != null).\n\n\t \n - `/external/api/media/allmedia`\n   - GET\n     - Returns a collection of all indexed media from all libraries.\n\n\n - `/external/api/media/diff/[library?]/[transactionId]`\n   - GET\n     - Returns a [differential](#differentials) between some point, given by the transaction ID, and now.\n\t - The library component of the path is optional. Simply omit it to get a diff across all libraries.\n\t - When a library is queried, the API will only return additions and deletions for that library, but will return all deletions. This is done because the library a deleted item belonged to is not stored.\n\n - `/internal/api/scan/full`\n   - POST\n     - Starts a scan across all libraries.\n\n\n - `/internal/api/scan/library/[library]`\n   - POST\n     - Starts a scan of a single library.\n\n - `/internal/api/scan/library/[library]/filtered`\n   - POST\n     - Starts a scan of a single library, and restricts the scan to a set of files.\n     - Takes a json array of strings as the HTTP body.\n\n - `/internal/api/scan/sonarr/[library]`\n   - POST\n     - Starts a scan of a single file, as specified by a Sonarr or Radarr [WebHook](https://github.com/Sonarr/Sonarr/wiki/Webhook) body.\n\n\u003ca name=\"differentials\"\u003e\u003c/a\u003e\n## Differentials\n\nYour media library might be quite large, and the queries to `allmedia` quite slow. A much faster way for an external service to determine what's in a library is to keep its local database synchronized with the Volyar database by using differentials. A differential is just a set of data showing what's been added, removed and changed since the last query. Each differential you get will include a `TransactionId`, which can be used for the next diff query to determine what's been changed in the meantime.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbloomtom%2Fvolyar","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbloomtom%2Fvolyar","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbloomtom%2Fvolyar/lists"}