{"id":24266108,"url":"https://github.com/stex/plex_symlinker","last_synced_at":"2025-10-07T21:11:58.075Z","repository":{"id":56888267,"uuid":"278605887","full_name":"stex/plex_symlinker","owner":"stex","description":"Create a plex music agent friendly symlink structure for your audiobook files. ","archived":false,"fork":false,"pushed_at":"2023-07-23T22:41:51.000Z","size":1260,"stargazers_count":3,"open_issues_count":3,"forks_count":1,"subscribers_count":1,"default_branch":"develop","last_synced_at":"2025-08-13T06:05:43.001Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","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/stex.png","metadata":{"files":{"readme":"README.dockerhub.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","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":"2020-07-10T10:27:56.000Z","updated_at":"2021-11-27T13:54:19.000Z","dependencies_parsed_at":"2025-01-15T10:59:44.548Z","dependency_job_id":"dccdec6e-5b42-4caf-b023-9b280cd4e0eb","html_url":"https://github.com/stex/plex_symlinker","commit_stats":{"total_commits":38,"total_committers":1,"mean_commits":38.0,"dds":0.0,"last_synced_commit":"310b4667f55461245407bd823570383d72bc37d5"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/stex/plex_symlinker","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stex%2Fplex_symlinker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stex%2Fplex_symlinker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stex%2Fplex_symlinker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stex%2Fplex_symlinker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stex","download_url":"https://codeload.github.com/stex/plex_symlinker/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stex%2Fplex_symlinker/sbom","scorecard":{"id":853016,"data":{"date":"2025-08-11","repo":{"name":"github.com/stex/plex_symlinker","commit":"310b4667f55461245407bd823570383d72bc37d5"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.6,"checks":[{"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":"Dangerous-Workflow","score":-1,"reason":"no workflows found","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":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"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":"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":"Code-Review","score":0,"reason":"Found 0/27 approved changesets -- score normalized to 0","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":"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":"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":"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.txt:0","Info: FSF or OSI recognized license: MIT License: LICENSE.txt: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":0,"reason":"Project has not signed or included provenance with any releases.","details":["Warn: release artifact v0.1.1 not signed: https://api.github.com/repos/stex/plex_symlinker/releases/29033214","Warn: release artifact v0.1.0 not signed: https://api.github.com/repos/stex/plex_symlinker/releases/29031408","Warn: release artifact v0.1.1 does not have provenance: https://api.github.com/repos/stex/plex_symlinker/releases/29033214","Warn: release artifact v0.1.0 does not have provenance: https://api.github.com/repos/stex/plex_symlinker/releases/29031408"],"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":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"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":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: containerImage not pinned by hash: Dockerfile:1: pin your Docker image by updating ruby:slim to ruby:slim@sha256:0d2adfa1930d67ee79e5d16c3610f4fbed43c98e98dbda14c2811b8197211c74","Warn: containerImage not pinned by hash: Dockerfile.dev:1: pin your Docker image by updating ruby:slim to ruby:slim@sha256:0d2adfa1930d67ee79e5d16c3610f4fbed43c98e98dbda14c2811b8197211c74","Info:   0 out of   2 containerImage 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":"Vulnerabilities","score":0,"reason":"11 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-cr5q-6q9f-rq6q","Warn: Project is vulnerable to: GHSA-j6gc-792m-qgm2","Warn: Project is vulnerable to: GHSA-pj73-v5mw-pm9j","Warn: Project is vulnerable to: GHSA-2rxp-v6pw-ch6m","Warn: Project is vulnerable to: GHSA-4xqq-m2hx-25v8","Warn: Project is vulnerable to: GHSA-5866-49gr-22v4","Warn: Project is vulnerable to: GHSA-8cr8-4vfw-mr7h","Warn: Project is vulnerable to: GHSA-r55c-59qm-vjw6","Warn: Project is vulnerable to: GHSA-vg3r-rm7w-2xgh","Warn: Project is vulnerable to: GHSA-vmwr-mc7x-5vc3","Warn: Project is vulnerable to: GHSA-5cm2-9h8c-rvfx"],"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 4 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-23T23:02:34.903Z","repository_id":56888267,"created_at":"2025-08-23T23:02:34.903Z","updated_at":"2025-08-23T23:02:34.903Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275411263,"owners_count":25460141,"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-09-16T02:00:10.229Z","response_time":65,"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":"2025-01-15T10:59:20.389Z","updated_at":"2025-09-24T02:32:05.885Z","avatar_url":"https://github.com/stex.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PlexSymlinker\n\nThis gem allows creating a Plex-friendly folder structure with symlinks for your audio books.\nAll you need are audio files with correct tagging, the gem takes care of making Plex understand them without you having to change the way you're organising your files.\n\n**The Problem**\n\nMost of my audio files are in apple's m4b format, leaving me with one file per book most of the times:\n\n    📁 audiobooks\n     ∟ 📁 author name\n        ∟ book1.m4b\n        ∟ book2.m4b\n\nThe problem with this structure is that Plex' music agent doesn't quite understand it.\nEven though the files are properly tagged with author, album, etc., Plex tends to create a giant album out of all the files inside - often with the first audio book as album name.\n\nWhat the Plex music agent expects is a structure like this:\n\n    📁 audiobooks\n     ∟ 📁 author name\n        ∟ 📁 Book 1\n           ∟ book1.m4b\n        ∟ 📁 Book 2\n           ∟ book2.m4b\n\nThis would mean that I'd have to introduce single-file-directories into my structure which didn't really make sense for me - especially as I have a lot of \"Hörspiele\" (mostly german format of short audio books with multiple actors + music).\n\n**This gem's solution**\n\nPlexSymlinker creates symlinks pointing to your actual audio files in exactly the \nstructure Plex' music agent expects to find. It uses the embedded tags in your files to build it, \nso even one big directory with all your audio files in it would work as expected.\n\nJust point plex to the symlink directory instead of your actual files and you're good to go.\n\n## Usage\n\nSince the whole purpose of PlexSymlinker is to read a directory full of audio files and \nfill another directory with symlinks, we have to make sure the docker container has access to both of them.\n\nWhen using the docker image, the audio files directory is expected to be mounted\nas `/app/source` and the directory the symlinks should be placed in as `/app/target`.\n\n💡 You also have to pass in the `SYMLINK_TARGET_DIR` environment variable. \nSince the gem only sees `/app/source` inside the docker container, \nit would point all symlinks there instead of the actual directory on your host machine. \nJust set it to the same directory that you mounted as `/app/source`.\n\n### 0. Make sure your files are properly tagged!\n\nAt least the album and album artist fields have to set accordingly.\n\nBut since you are planning to import those files into Plex which needs a lot more information, \nit would make sense to fill out all details you can provide.\n\n### 1. Create the directory the symlinks should be placed in (if it doesn't exist yet)\n\nIt's important that the directory exists (owned by your current user) before you mount it.\n\n    mkdir -p /path/to/symlink/dir\n\nIf you ran PlexSymlinker before, you can just re-run it on the same directory again. \nIt will automatically sync the symlinks against the actual files.\n\n### 2. Run the docker image\n\n#### MacOS\n\n    docker run -it --rm -v /path/to/audiobooks:/app/source:ro -v /path/to/symlink/dir:/app/target --env SYMLINK_TARGET_DIR=/path/to/audiobooks sterexx/plex_symlinker\n\n#### Linux\n\n💡 Under Linux, you have to make sure to set your current user/group id \nwith `--user \"$(id -u):$(id -g)\"` - otherwise, all the generated symlinks/directories \nwill be owned by the root user. Docker for mac/windows takes care of that for you automatically.\n\n    docker run -it --user \"$(id -u):$(id -g)\" --rm -v /path/to/audiobooks:/app/source:ro -v /path/to/symlink/dir:/app/target --env SYMLINK_TARGET_DIR=/path/to/audiobooks sterexx/plex_symlinker\n\n### 3. Create a Plex library pointing to the symlink directory\n\n\nThere might be multiple ways to achieve a good audiobook setup, but for me (with properly tagged files and embedded artwork), the following one worked great:\n\n✅ Prefer local metadata  \n✅ Store track progress  \n✅ Artist Bios  \n- Genres -\u003e Embedded Tags\n- Album Art -\u003e Both Plex Music and Local Files\n- Scanner -\u003e Plex Music\n- Agent -\u003e Plex Music\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstex%2Fplex_symlinker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstex%2Fplex_symlinker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstex%2Fplex_symlinker/lists"}