{"id":13904911,"url":"https://github.com/simongoricar/euphony","last_synced_at":"2025-04-10T11:07:52.044Z","repository":{"id":49393781,"uuid":"510714750","full_name":"simongoricar/euphony","owner":"simongoricar","description":"A ♯ personal music library transcode manager.","archived":false,"fork":false,"pushed_at":"2024-04-22T11:06:44.000Z","size":1794,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-24T09:52:50.787Z","etag":null,"topics":["cli","library-manager","mp3","music","music-collection","music-manager","rust","transcode-audio","tui"],"latest_commit_sha":null,"homepage":"","language":"Rust","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/simongoricar.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,"roadmap":null,"authors":null,"dei":null}},"created_at":"2022-07-05T11:53:00.000Z","updated_at":"2025-03-05T16:49:21.000Z","dependencies_parsed_at":"2024-04-22T12:27:10.725Z","dependency_job_id":"bd8a794a-8b08-4864-b798-16a8e14280af","html_url":"https://github.com/simongoricar/euphony","commit_stats":null,"previous_names":["simongoricar/euphony","defaultsimon/euphony"],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simongoricar%2Feuphony","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simongoricar%2Feuphony/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simongoricar%2Feuphony/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simongoricar%2Feuphony/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/simongoricar","download_url":"https://codeload.github.com/simongoricar/euphony/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248208548,"owners_count":21065202,"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":["cli","library-manager","mp3","music","music-collection","music-manager","rust","transcode-audio","tui"],"created_at":"2024-08-06T23:01:06.230Z","updated_at":"2025-04-10T11:07:52.020Z","avatar_url":"https://github.com/simongoricar.png","language":"Rust","funding_links":[],"categories":["HarmonyOS"],"sub_categories":["Windows Manager"],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003ch1 align=\"center\"\u003eeuphony\u003c/h1\u003e\n  \u003ch6 align=\"center\"\u003ea ♯ personal music library transcode manager\u003c/h6\u003e\n\u003c/div\u003e\n\n---\n\n\n**Important: `euphony` *does not organise* your original audio files** - it transcodes your already-organised library/libraries into an aggregated (usually lossy) library, leaving your source libraries alone. If music library organisation is what you're after, you might want to look into tools like [MusicBrainz Picard](https://picard.musicbrainz.org/) or [Beets](https://beets.readthedocs.io/en/stable/).\n\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/DefaultSimon/euphony/master/assets/euphony-v2.0.0-demo.gif\" width=\"100%\" height=\"auto\"\u003e\n  \u003cdiv\u003e\u003ccode\u003etranscode\u003c/code\u003e command demo\u003c/div\u003e\n\u003c/div\u003e\n\n---\n\n## Table of contents\n- [1. Why and how](#1-why-and-how)\n  - [1.1 Diffing](#11-diffing)\n  - [1.2 MP3 V0](#12-mp3-v0)\n- [2. Library structure](#2-library-structure)\n- [3. Installation](#3-installation)\n- [4. Setup](#4-setup)\n- [5. Usage](#5-usage)\n    - [5.1 `transcode`](#51-transcode)\n    - [5.2 `validate`](#52-validate)\n- [6. Advanced topics](#6-advanced-topics)\n  - [6.1. `.album.override.euphony` (per-album overrides)](#61-albumoverrideeuphony-per-album-overrides)\n- [7. Implementation details](#7-implementation-details)\n      - [7.1 `.album.source-state.euphony` / `.album.transcode-state.euphony`](#71-albumsource-stateeuphony--albumtranscode-stateeuphony)\n\n## Other resources\n* [Changelog](https://github.com/DefaultSimon/euphony/blob/master/CHANGELOG.md)\n\n---\n\n# 1. Why and how\n\u003cdetails\u003e\n\n\u003csummary\u003e📇 Why I made euphony\u003c/summary\u003e\n\n\u003e Over the years, I've been collecting an offline music library that has been growing in size, but simultaneously getting harder to maintain.\n\u003e Considering you're here, you might have encountered the same :). Here's a quick outline of why and how.\n\u003e\n\u003e Let's say most of your music library is lossless, but a significant chunk of it is lossy.\n\u003e In this case, you could:\n\u003e - have both lossless and lossy files in the same folder (e.g., organized by artist, then by album, mixing the qualities), or,\n\u003e - separate lossless and lossy folders (each one again organized by artist, then by album, etc.).\n\u003e\n\u003e If you only listen on one device, such as your computer, neither of those approaches is likely to pose a problem. \n\u003e However, for multi-device users, large libraries quickly become both a storage and a deduplication nightmare.\n\u003e Ideally, you'd want to maintain the original library (or libraries) as they were,\n\u003e but still have a standalone *transcoded* (or *aggregated*, if you will) version of your entire collection containing files from all the\n\u003e libraries transcoded down to a more manageable size, ready for on-the-go listening.\n\u003e\n\u003e This is the problem `euphony` was written to solve.\n\n\u003c/details\u003e\n\n\nEuphony becomes a useful tool when your goal is taking your music library with you on the go \n(e.g. having a copy of your music library on a phone). \nIf your music library is large and of high audio quality, copying the entire library file for file\nwill likely not be a good option.\n\nThe obvious solution is to **transcode your library** down to something like MP3 V0 \nand copy those transcoded files to your other devices. \nDoing this manually or even with a simple script is a tedious process, prone to forgetfulness, \noccasional human errors and eventual drift from the source music library.\n\n---\n\nHere's how `euphony` solves this with an automated transcoding process:\n- *You specify the locations of your music library (or libraries)* in the configuration file.  \n  Note that euphony currently supports only the following library structure: `Artist Name/Album Title` (see example below).\n- You may then opt to *validate the library for any unusual files and collisions* (see the `validate` command for more information).  \n  This way, if you have multiple source libraries (e.g. one for lossy and one for lossless music), euphony will inform you of any potential collisions (e.g. same album by the same artist in both libraries). \n  This will prevent you from accidentally storing two copies of the same album in two places \n  (it would also be unclear as to which of those euphony should transcode).\n- **When you wish to transcode your entire music library into a smaller single-folder (\"aggregated\") transcoded copy, you run the `transcode` command.**   \n  This takes all of your source music libraries and transcodes everything into MP3 V0 by default (you may reconfigure this into any ffmpeg-supported format). It then puts the resulting files from all of your source libraries into a single (i.e. merged) *transcoded library* — this is the directory that you take with you on the go.  \n  Euphony will also copy album art and any other data files as configured (contains sensible defaults).\n\n\nAs mentioned, euphony supports multiple source music libraries: e.g. one for lossless and one for lossy audio, another one for a specific collection, etc.\n\n\n## 1.1 Diffing\nIf you run the `transcode` command two times without modifying any of your source libraries, you'll notice that euphony won't re-transcode anything. \nThis is because euphony tracks your source files' size and modification date in order to avoid processing albums that haven't changed.\n\nThis is done by storing three types of files:\n- Minimal metadata about each album's tracked files is stored in a file called `.album.source-state.euphony` (in the source album directory) \n  and `.album.transcode-state.euphony` (in the transcoded album directory).\n- To detect album and artist removal, euphony also stores the `.library.state.euphony` file at the root of each registered source music library.\n\nImplementation details of this change detection algorithm are available at the end.\n\n\n## 1.2 MP3 V0\nAudio files are transcoded to MP3 V0 by default, but you may \nreconfigure this to essentially any audio format ffmpeg supports. \n\nI've chosen MP3 V0 for now due to a good tradeoff between space on disk and audio quality.\nAs V0 is practically transparent that is, at least to me, more than enough for on-the-go listening.\nObviously, regardless of your transcoded audio format, your source libraries are untouched if you \nchange your mind later and decide to change the transcoding format and retranscode the entire collection.\n\n\n---\n\n\n# 2. Library structure\nHaving the library structure be configurable would quickly become very complicated.\nConsequently `euphony` currently expects the user to have the commonly-used `Artist Name/Album Title` library structure:\n\n```markdown\n\u003clibrary's base directory\u003e\n|\n|-- \u003cartist directory\u003e\n|   |\n|   |- [possibly some album-related text files, logs, etc.]\n|   |  (settings for \"other files\" apply here (see \"other files\" section below))\n|   |\n|   |-- \u003calbum directory\u003e\n|   |   |\n|   |   | ... [audio files]\n|   |   |     (any audio types you allow inside each library - see \n|   |   |     `allowed_audio_file_extensions` in the configuration file)\n|   |   |\n|   |   | ... [cover art]\n|   |   |\n|   |   | ... [some album-related text files, logs, etc.]\n|   |   |     (settings for \"other files\" apply here (see \"other files\" section below))\n|   |   |\n|   |   | ... \u003cpotentially other directories that you don't want transcoded or copied\u003e\n|   |   |     (album subdirectories are ignored by default, see `depth` in per-album configuration)\n|\n|-- \u003cany other ignored directory\u003e\n|   (it is sometimes useful to have additional directories inside your library that are\n|    not artist directories, but instead contain some other stuff (e.g. temporary files) \n|    you don't want to transcode - these directories can be ignored for each individual \n|    library using `ignored_directories_in_base_directory`)\n|\n| ... [other files]\n|     (of whatever type or name you allow in the configuration, see\n|      `allowed_other_file_extensions` and `allowed_other_files_by_name` - these settings\n|      also apply to artist and album directories above)\n```  \n\n\u003cdetails\u003e\n\n\u003csummary\u003e✍️ \u003cb\u003eExample of a library\u003c/b\u003e and its corresponding configuration\u003c/summary\u003e\n\nLook at the following directory structure:\n```markdown\n  LosslessLibrary\n  |\n  |- LOSSLESS_README.txt\n  |\n  |-- Aindulmedir\n  |   |-- The Lunar Lexicon\n  |   |   | 01 Aindulmedir - Wind-Bitten.flac\n  |   |   | 02 Aindulmedir - Book of Towers.flac\n  |   |   | 03 Aindulmedir - The Librarian.flac\n  |   |   | 04 Aindulmedir - Winter and Slumber.flac\n  |   |   | 05 Aindulmedir - The Lunar Lexicon.flac\n  |   |   | 06 Aindulmedir - Snow Above Blue Fire.flac\n  |   |   | 07 Aindulmedir - Sleep-Form.flac\n  |   |   | cover.jpg\n  |   |   | Aindulmedir - The Lunar Lexicon.log\n  |\n  |-- Dakota\n  |   |-- Leda\n  |   |   | 01 Dakota - Automatic.mp3\n  |   |   | 02 Dakota - Icon.mp3\n  |   |   | 03 Dakota - Easier.mp3\n  |   |   | 04 Dakota - Leave Me Out.mp3\n  |   |   | 05 Dakota - Bare Hands.mp3\n  |   |   | 06 Dakota - Tension.mp3\n  |   |   | cover.jpg\n  |\n  |\n  |-- _other\n  |   | some_other_metadata_or_something.db\n  |   | ... other files we don't want to validate or transcode\n```\n\nIn this example there exists a lossless library in a directory named `LosslessLibrary`. We'll call it `Lossless`. We want to transcode both `mp3` and `flac` files and include any `jpg` and `log` files in our transcoded library. We also don't want euphony to touch the `_other` directory.\n\nWe get the following:\n\n```toml\n[libraries.lossless]\nname = \"Losless\"\npath = \"/some/absolute/path/to/LosslessLibrary\"\nignored_directories_in_base_directory = [\"_other\"]\n\n[libraries.lossless.validation]\nallowed_audio_file_extensions = [\"mp3\", \"flac\"]\nallowed_other_file_extensions = [\"jpg\", \"log\"]\nallowed_other_files_by_name = []\n\n[libraries.lossless.transcoding]\naudio_file_extensions = [\"mp3\", \"flac\"]\nother_file_extensions = [\"jpg\", \"log\"]\n```\n\n\u003c/details\u003e\n\n\n---\n\n# 3. Installation\nPrerequisites for installation:\n- [Rust](https://www.rust-lang.org/) (minimal supported Rust version as of `euphony v2.0.0` is `1.70.0`!),\n- a [ffmpeg](https://ffmpeg.org/) binaries (Windows builds are available [here](https://www.gyan.dev/ffmpeg/builds/)).\n\nClone or download this repository, then move into the root directory of the project and:\n- On Windows: run the convenient `./install-euphony.ps1` PowerShell script to compile the project and copy the required files into the `bin` directory. You should add that `bin` directory to your `PATH` afterwards so you can use `euphony` from the command line.\n- On Linux/other: run `cargo build --release` to compile the project. You'll find the `euphony` binary in the `./target/release/` directory - copy it to a place of your choosing along with the configuration file template.\n\n\n# 4. Setup\nBefore running the binary you've built in the previous step, make sure you have the `configuration.TEMPLATE.toml` handy.\nIf you used the `install-euphony.ps1` script, it will already be prepared in the `bin` directory. \nIf you're on a different platform, copy one from the `data` directory.\n\nThe `configuration.toml` file must be in `./data/configuration.toml` (relative to the binary) or wherever else you prefer with the `--config` option.\nThe Windows PowerShell install script places this automatically, you just need to rename and fill out the file, but other platforms will require manually copying the file.\n\nMake sure the file name is named `configuration.toml`. *Carefully read* the explanations inside and fill out the contents.\nThe contents of the configuration file are mostly about specifying where ffmpeg is, which files to track, where your libraries reside and what files you want to allow or forbid inside them.\n\nIf you are unfamiliar with the format, see the [TOML](https://toml.io/en/) specification.\n\n\n\u003e As an example, let's say I have two separate libraries: a lossy and a lossless one. The lossless one has its \n\u003e `allowed_audio_file_extensions` value set to `[\"flac\"]`, as I don't want any other file types inside. The lossy one instead\n\u003e has the value `[\"mp3\"]`, because MP3 is my format of choice for lossy audio for now. If I were to place a non-FLAC file inside the\n\u003e lossless library, euphony would flag it for me as an error when I ran `euphony validate`.\n\n\nNext, **extract the portable copy of ffmpeg** that was mentioned above. Again, unless you're sure what you're doing,\nplace it just next to the binary in a subfolder called `tools`. Adapt the `tools.ffmpeg.binary` configuration field in the configuration file to point to the ffmpeg binary.\n\nChange any other configuration values you haven't yet, then save. **You're ready to go!**\n\n---\n\n\n# 5. Usage\nRun `euphony` with the `--help` option to get all available commands and their short explanations:\n```\nEuphony is a music library transcode manager that allows the user to retain \nhigh quality audio files in one or more libraries and helps with transcoding \nthe collection into a smaller size. That smaller version of the library can \nthen be used on portable devices or similar occasions where space has a larger \nimpact. For more info, see the README file in the repository.\n\nUsage: euphony [OPTIONS] \u003cCOMMAND\u003e\n\nCommands:\n  transcode\n          Transcode all libraries into the aggregated library. \n          [aliases: transcode-collection]\n  validate\n          Validate all the available libraries for inconsistencies, \n          such as forbidden files, any inter-library collisions that would \n          cause problems when transcoding, etc. \n          [aliases: validate-collection] \n  show-config\n          Loads, validates and prints the current configuration.\n  list-libraries\n          List all the registered libraries registered in the configuration.\n  help\n          Print this message or the help of the given subcommand(s)\n\nOptions:\n  -c, --config \u003cCONFIG\u003e\n          Optionally a path to your configuration file. Without this option, \n          euphony tries to load ./data/configuration.toml, but understandably \n          this might not always be the most convenient location.   \n\n  -v, --verbose\n          Increase the verbosity of output.\n\n  -h, --help\n          Print help (see a summary with '-h')\n\n  -V, --version\n          Print version\n```\n\nFor more info about each command, run `euphony \u003ccommand-name\u003e --help`.\n\n### 5.1 `transcode`\n\u003e Usage: `euphony transcode`\n\u003e Help: `euphony transcode --help`\n\nUsing the `transcode` command will scan your source libraries for changes and transcode the entire music collection into a single folder called the transcoded or aggregated library (see `aggregated_library.path` in the configuration file). This is the directory that will contain all transcoded files (and cover art).\n\nThe transcoded audio files will be MP3 V0 by default. Changing this should be reasonably easy - see `tools.ffmpeg.audio_transcoding_args` in the configuration file.\n\n### 5.2 `validate`\n\u003e Usage: `euphony validate`\n\u003e Help: `euphony validate --help`\n\nUsing the `validate` command will scan your source libraries and notify you of any unexpected files and any inter-library collisions.\n\nThis catches things like:\n- accidentally putting an album in an artist directory,\n- unwanted audio file formats (based on the configuration),\n- unwanted cover image formats (based on the configuration),\n- other unwanted files in the library root, artist and album directories.\n\n---\n\n# 6. Advanced topics\n\u003e What follows are advanced features - I'd recommend getting acquainted with the rest of the functionality first.\n\n## 6.1. `.album.override.euphony` (per-album overrides)\nYou can optionally create an `.album.override.euphony` file in the root of each source album directory (in the same directory as the `.album.source-state.euphony` file).\nIts purpose is to influence the scanning and transcoding process for the relevant album.\n\nAt the moment, this file can contain the following options:\n```toml\n# This file serves as a sample of what can be done using album overrides.\n\n[scan]\n# How deep the transcoding scan should look.\n# 0 means only the album directory and no subdirectories \n#   (most common, and is also the default).\n# 1 means only one directory level deeper, and so on.\ndepth = 0\n```\n\n\u003e In case this description falls behind, an up-to-date documented version of the `.album.override.euphony` file and its options is always available in the `data` directory.\n\nWhy is this useful? Well, let's say you have an album that has multiple discs, each of which is in a separate directory, like so:\n```markdown\n\u003calbum directory\u003e\n|- cover.jpg\n|\n|-- Disc 1\n|   |- \u003c... a lot of audio files ...\u003e\n|\n|-- Disc 2\n|   |- \u003c... a lot of audio files ...\u003e\n|\n|-- Disc 3\n|   |- \u003c... a lot of audio files ...\u003e\n|\n|-- Disc 4\n|   |- \u003c... a lot of audio files ...\u003e\n|\n|-- \u003c...\u003e\n```\n\nIn this case you may want to create an `.album.override.euphony` file inside the album directory and set the `depth` setting to `1`.\nThis will make euphony scan one directory deeper, catching and transcoding your per-disc audio files.\n\n---\n\n# 7. Implementation details\n\n#### 7.1 `.album.source-state.euphony` / `.album.transcode-state.euphony`\nTo make sure we don't have to transcode or copy all the files again when changing a single one,\neuphony stores a special file in the root directory of each **album** called `.album.source-state.euphony`.\n\nThe contents of the file are in JSON, similar to the example below:\n```json5\n{\n  \"schema_version\": 2,\n  // All tracked files in the directory are listed here. \n  // Which files are tracked is dictated by the configuration \n  // in the file_metadata table (audio_file_extensions and other_file_extensions).\n  \"tracked_files\": {\n    \"audio_files\": {\n      // Each file has several attributes - if any of them don't match, \n      // the file has likely changed and will be transcoded or copied again.\n      // Paths are relative to the base album directory.\n      \"01 Amos Roddy - Aeronaut.mp3\": {\n        \"size_bytes\": 3403902,\n        \"time_modified\": 1636881979.7336252,\n        \"time_created\": 1669553407.7848136,\n      }\n      // ...\n    },\n    \"data_files\": {\n      \"cover.png\": {\n        \"size_bytes\": 32955,\n        \"time_modified\": 1636881979.7336252,\n        \"time_created\": 1669553407.7848136,\n      }\n    },\n  }\n}\n```\n\nFields:\n- `size_bytes` is the file size in bytes,\n- `time_modified` is the file modification time (as reported by filesystem; compared with one decimal point of precision),\n- `time_created` is the file creation time (as reported by filesystem; compared with one decimal point of precision).\n\nIf any of these attributes don't match for a given file, we can be pretty much certain the file has changed.\nThe opposite is not entirely true, but enough for most purposes.\n\nA similar file named `.album.transcode-state.euphony` with almost the same structure is saved in the transcoded album directory.\n\n\u003e For more details about these files, see the `euphony_libary::state` module.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimongoricar%2Feuphony","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimongoricar%2Feuphony","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimongoricar%2Feuphony/lists"}