[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-73738":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":17,"stars7d":18,"stars30d":19,"stars90d":16,"forks30d":16,"starsTrendScore":20,"compositeScore":21,"rankGlobal":10,"rankLanguage":10,"license":22,"archived":23,"fork":23,"defaultBranch":24,"hasWiki":23,"hasPages":25,"topics":26,"createdAt":10,"pushedAt":10,"updatedAt":27,"readmeContent":28,"aiSummary":29,"trendingCount":16,"starSnapshotCount":16,"syncStatus":30,"lastSyncTime":31,"discoverSource":32},73738,"convert","p2r3\u002Fconvert","p2r3","Truly universal online file converter","https:\u002F\u002Fconvert.to.it",null,"TypeScript",3649,338,14,141,0,25,87,296,75,109.59,"GNU General Public License v2.0",false,"master",true,[],"2026-06-12 04:01:10","# [Convert to it!](https:\u002F\u002Fconvert.to.it\u002F)\n**Truly universal online file converter.**\n\nMany online file conversion tools are **boring** and **insecure**. They only allow conversion between two formats in the same medium (images to images, videos to videos, etc.), and they require that you _upload your files to some server_.\n\nThis is not just terrible for privacy, it's also incredibly lame. What if you _really_ need to convert an AVI video to a PDF document? Try to find an online tool for that, I dare you.\n\n[Convert.to.it](https:\u002F\u002Fconvert.to.it\u002F) aims to be a tool that \"just works\". You're almost _guaranteed_ to get an output - perhaps not always the one you expected, but it'll try its best to not leave you hanging.\n\nFor a semi-technical overview of this tool, check out the video: https:\u002F\u002Fyoutu.be\u002FbtUbcsTbVA8\n\n## Usage\n\n1. Go to [convert.to.it](https:\u002F\u002Fconvert.to.it\u002F)\n2. Click the big blue box to add your file (or just drag it on to the window).\n3. An input format should have been automatically selected. If it wasn't, yikes! Try searching for it, or if it's really not there, see the \"Issues\" section below.\n4. Select an output format from the second list. If you're on desktop, that's the one on the right side. If you're on mobile, it'll be somewhere lower down.\n5. Click **Convert**!\n6. Hopefully, after a bit (or a lot) of thinking, the program will spit out the file you wanted. If not, see the \"Issues\" section below.\n\n## Issues\n\nEver since the YouTube video released, we've been getting spammed with issues suggesting the addition of all kinds of niche file formats. To keep things organized, I've decided to specify what counts as a valid issue and what doesn't.\n\n> [!IMPORTANT]\n> **SIMPLY ASKING FOR A FILE FORMAT TO BE ADDED IS NOT A MEANINGFUL ISSUE!**\n\nThere are thousands of file formats out there. It can take hours to add support for just one. The math is simple - we can't possibly support every single file. As such, simply listing your favorite file formats is not helpful. We already know that there are formats we don't support, we don't need tickets to tell us that.\n\nWhen suggesting a file format, you must _at minimum_:\n- Make sure that there isn't already an issue about the same thing, and that we don't already support the format.\n- Explain what you expect the conversion to be like (what medium is it converting to\u002Ffrom). It's important to note here that simply parsing the underlying data is _not sufficient_. Imagine if we only treated SVG images as raw XML data and didn't support converting them to raster images - that would defeat the point. In other words, try to avoid crude \"binary waterfalls\".\n- Provide links to existing browser-based solutions if possible, or at the very least a reference for implementing the format, and make sure the license is compatible with GPL-2.0.\n\nIf this seems like a lot, please remember - a developer will have to do 100x more work to actually implement the format. Doing a bit of research not only saves them precious time, it also weeds out \"unserious\" proposals that would only bloat our to-do list.\n\n**If you're submitting a bug report,** you only need to do step 1 - check if the problem isn't already reported by someone else. Bug reports are generally quite important otherwise.\n\nThough please note, \"converting X to Y doesn't work\" is **not** a bug report.  However, \"converting X to Y works but not how I expected\" likely **is** a bug report.\n\n## Deployment\n\n### Local development (Bun + Vite)\n\n1. Clone this repository ***WITH SUBMODULES***. You can use `git clone --recursive https:\u002F\u002Fgithub.com\u002Fp2r3\u002Fconvert` for that. Omitting submodules will leave you missing a few dependencies.\n2. Install [Bun](https:\u002F\u002Fbun.sh\u002F).\n3. Run `bun install` to install dependencies.\n4. Run `bunx vite` to start the development server.\n\n_The following steps are optional, but recommended for performance:_\n\nWhen you first open the page, it'll take a while to generate the list of supported formats for each tool. If you open the console, you'll see it complaining a bunch about missing caches.\n\nAfter this is done (indicated by a `Built initial format list` message in the console), use `printSupportedFormatCache()` to get a JSON string with the cache data. You can then save this string to `cache.json` to skip that loading screen on startup.\n\nIf you run into issues where your changes seem to not be applying, try disabling this cache.\n\n### Docker (prebuilt image)\n\nDocker compose files live in the `docker\u002F` directory, so run compose with `-f` from the repository root:\n\n```bash\ndocker compose -f docker\u002Fdocker-compose.yml up -d\n```\n\nAlternatively download the `docker-compose.yml` separately and start it by executing `docker compose up -d` in the same directory.\n\nThis runs the container on `http:\u002F\u002Flocalhost:8080\u002Fconvert\u002F`.\n\n### Docker (local build for development)\n\nUse the override file to build the image locally:\n\n```bash\ndocker compose -f docker\u002Fdocker-compose.yml -f docker\u002Fdocker-compose.override.yml up --build -d\n```\n\nThe first Docker build is expected to be slow because Chromium and related system packages are installed in the build stage (needed for puppeteer in `buildCache.js`). Later builds are usually much faster due to Docker layer caching.\n\n## Contributing\n\nThe best way to contribute is by adding support for new file formats (duh). If you don't have a format to add but are eager to help, take a look at our issues. There are plenty of suggestions there.\n\nHere's how adding a format works works:\n\n### Creating a handler\n\nEach \"tool\" used for conversion has to be normalized to a standard form - effectively a \"wrapper\" that abstracts away the internal processes. These wrappers are available in [src\u002Fhandlers](src\u002Fhandlers\u002F).\n\nBelow is a super barebones handler that does absolutely nothing. You can use this as a starting point for adding a new format:\n\n```ts\n\u002F\u002F file: dummy.ts\n\nimport type { FileData, FileFormat, FormatHandler } from \"..\u002FFormatHandler.ts\";\nimport CommonFormats from \"src\u002FCommonFormats.ts\";\n\nclass dummyHandler implements FormatHandler {\n\n  public name: string = \"dummy\";\n  public supportedFormats?: FileFormat[];\n  public ready: boolean = false;\n\n  async init () {\n    this.supportedFormats = [\n      \u002F\u002F Example PNG format, with both input and output disabled\n      CommonFormats.PNG.builder(\"png\")\n        .markLossless()\n        .allowFrom(false)\n        .allowTo(false),\n\n      \u002F\u002F Alternatively, if you need a custom format, define it like so:\n      {\n        name: \"CompuServe Graphics Interchange Format (GIF)\",\n        format: \"gif\",\n        extension: \"gif\",\n        mime: \"image\u002Fgif\",\n        from: false,\n        to: false,\n        internal: \"gif\",\n        category: [\"image\", \"video\"],\n        lossless: false\n      },\n    ];\n    this.ready = true;\n  }\n\n  async doConvert (\n    inputFiles: FileData[],\n    inputFormat: FileFormat,\n    outputFormat: FileFormat\n  ): Promise\u003CFileData[]> {\n    const outputFiles: FileData[] = [];\n    return outputFiles;\n  }\n\n}\n\nexport default dummyHandler;\n```\n\nFor more details on how all of these components work, refer to the doc comments in [src\u002FFormatHandler.ts](src\u002FFormatHandler.ts). You can also take a look at existing handlers to get a more practical example.\n\nThere are a few additional things that I want to point out in particular:\n\n- Pay attention to the naming system. If your tool is called `dummy`, then the class should be called `dummyHandler`, and the file should be called `dummy.ts`.\n- The handler is responsible for setting the output file's name. This is done to allow for flexibility in rare cases where the _full_ file name matters. Of course, in most cases, you'll only have to swap the file extension.\n- The handler is also responsible for ensuring that any byte buffers that enter or exit the handler _do not get mutated_. If necessary, clone the buffer by wrapping it in `new Uint8Array()`.\n- When handling MIME types, run them through [normalizeMimeType](src\u002FnormalizeMimeType.ts) first. One file can have multiple valid MIME types, which isn't great when you're trying to match them algorithmically.\n- When implementing\u002Fsuggesting a new file format, please treat the file as the media that it represents, not the data that it contains. For example, if you were making an SVG handler, you should treat the file as an _image_, not as XML. In other words, avoid simple \"binary waterfalls\", as they're not semantically meaningful.\n\n### Testing\n\nThis project currently uses two levels of tests:\n\n- Broad project-level tests live directly in `test\u002F` (for example graph traversal and end-to-end conversion smoke tests).\n- Optional handler-specific unit tests live in `test\u002Fhandlers\u002F`, using the file name pattern `\u003ChandlerName>.test.ts`. These are a good fit for handlers with meaningful parsing, serialization, or file-naming logic that is hard to exercise reliably through traversal alone.\n\nNot every handler needs a dedicated unit test, but handlers with non-trivial custom internal logic may benefit from having one.\n\n### Adding dependencies\n\nIf your tool requires an external dependency (which it likely does), there are currently two well-established ways of going about this:\n\n- If it's an `npm` package, just install it to the project like you normally would.\n- If it's a Git repository, add it as a submodule to [src\u002Fhandlers](src\u002Fhandlers).\n- If neither of the above are available, then **as a last resort**, you may create a folder with the required assets under `src\u002Fhandlers\u002FhandlerName`.\n\n**Please try to avoid CDNs (Content Delivery Networks).** They're really cool on paper, but they don't work well with TypeScript, and each one introduces a tiny bit of instability. For a project that leans heavily on external dependencies, those bits of instability can add up fast.\n\n- If you need to load a WebAssembly binary (or similar), add its path to [vite.config.js](vite.config.js) and target it under `\u002Fconvert\u002Fwasm\u002F`. **Do not link to node_modules**.\n\n### AI Usage Policy\n\nIf you intend to use an LLM, agent-enabled IDE, or other AI-driven tool for your contribution, please follow these guidelines:\n\n- Clearly state that you've used an LLM, ideally in your pull request's description. Do not attempt to pass off an AI's work as your own. I'm far more likely to accept a pull request that openly admits to using AI than one that does but pretends it doesn't. Transparency helps the maintainer (me) know what to keep an eye out for (e.g. hallucinations), and helps you keep yourself in check.\n- Do not overindulge. If your contribution is trivial or simple enough to be written by hand, please opt to write it by hand. This is especially true if it's your first contribution. You're much more likely to retain knowledge and understanding about architectural details if you've familiarized yourself with the process hands-on first.\n- Keep the scope to things you _could_ do by hand. LLMs are tools, and this is a community-driven project. Orchestrating an AI to write logic that you don't fully comprehend is not only reckless for a community project, it's also disrespectful towards human contributors who took the time to research their additions. In other words, there should _never_ be a scenario where you _need_ an LLM.\n- Explain what you (and the LLM) are doing, in a way that makes it clear that you understand the changes you're making.\n\nNot adhering to these rules will likely get your pull request closed.\n\nI figure that there are people who'd prefer if I merged _zero_ AI-written code, but I believe that's simply not feasible. Just from a code integrity perspective, it's much safer to be transparent about AI usage and define clear guidelines than to make it a taboo and risk people \"sneaking in\" unvetted AI code. Making things illegal doesn't stop everyone from doing those things - some will still do them, just in secret and with less oversight.\n","p2r3\u002Fconvert 是一个真正意义上的通用在线文件转换工具。其核心功能在于支持跨媒介的文件格式转换，如将视频转换为文档等，并且整个转换过程在用户本地完成，无需上传文件至服务器，从而极大提升了隐私安全性。项目采用 TypeScript 开发，确保了代码的可维护性和类型安全。适用于需要灵活处理多种文件格式转换的场景，尤其是当传统工具无法满足特定需求时，例如从 AVI 视频转换到 PDF 文档。通过简洁直观的操作界面，用户可以轻松选择输入输出格式并快速获得转换结果。",2,"2026-06-11 03:47:10","high_star"]