diff --git a/bun.lock b/bun.lock index 5de4f057..00773a28 100644 --- a/bun.lock +++ b/bun.lock @@ -21,6 +21,7 @@ "overlayscrollbars-svelte": "^0.5.5", "p-queue": "^8.1.1", "riff-file": "^1.0.3", + "sanitize-html": "^2.17.0", "svelte-stripe": "^1.4.0", "vert-wasm": "^0.0.2", "vite-plugin-wasm": "^3.5.0", @@ -32,6 +33,7 @@ "@sveltejs/kit": "^2.42.2", "@sveltejs/vite-plugin-svelte": "^4.0.4", "@types/eslint": "^9.6.1", + "@types/sanitize-html": "^2.16.0", "autoprefixer": "^10.4.21", "css-select": "5.1.0", "eslint": "^9.36.0", @@ -317,6 +319,10 @@ "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + "@types/node": ["@types/node@24.8.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-alv65KGRadQVfVcG69MuB4IzdYVpRwMG/mq8KWOaoOdyY617P5ivaDiMCGOFDWD2sAn5Q0mR3mRtUOgm99hL9Q=="], + + "@types/sanitize-html": ["@types/sanitize-html@2.16.0", "", { "dependencies": { "htmlparser2": "^8.0.0" } }, "sha512-l6rX1MUXje5ztPT0cAFtUayXF06DqPhRyfVXareEN5gGCFaP/iwsxIyKODr9XDhfxPpN6vXUFNfo5kZMXCxBtw=="], + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.44.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.44.0", "@typescript-eslint/type-utils": "8.44.0", "@typescript-eslint/utils": "8.44.0", "@typescript-eslint/visitor-keys": "8.44.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.44.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-EGDAOGX+uwwekcS0iyxVDmRV9HX6FLSM5kzrAToLTsr9OWCIKG/y3lQheCq18yZ5Xh78rRKJiEpP0ZaCs4ryOQ=="], "@typescript-eslint/parser": ["@typescript-eslint/parser@8.44.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.44.0", "@typescript-eslint/types": "8.44.0", "@typescript-eslint/typescript-estree": "8.44.0", "@typescript-eslint/visitor-keys": "8.44.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-VGMpFQGUQWYT9LfnPcX8ouFojyrZ/2w3K5BucvxL/spdNehccKhB4jUyB1yBCXpr2XFm0jkECxgrpXBW2ipoAw=="], @@ -541,6 +547,8 @@ "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + "htmlparser2": ["htmlparser2@8.0.2", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1", "entities": "^4.4.0" } }, "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA=="], + "human-id": ["human-id@4.1.1", "", { "bin": { "human-id": "dist/cli.js" } }, "sha512-3gKm/gCSUipeLsRYZbbdA1BD83lBoWUkZ7G9VFrhWPAU76KwYo5KR8V28bpoPm/ygy0x5/GCbpRQdY7VLYCoIg=="], "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], @@ -567,6 +575,8 @@ "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + "is-plain-object": ["is-plain-object@5.0.0", "", {}, "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="], + "is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="], "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], @@ -671,6 +681,8 @@ "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + "parse-srcset": ["parse-srcset@1.0.2", "", {}, "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="], + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], @@ -737,6 +749,8 @@ "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], + "sanitize-html": ["sanitize-html@2.17.0", "", { "dependencies": { "deepmerge": "^4.2.2", "escape-string-regexp": "^4.0.0", "htmlparser2": "^8.0.0", "is-plain-object": "^5.0.0", "parse-srcset": "^1.0.2", "postcss": "^8.3.11" } }, "sha512-dLAADUSS8rBwhaevT12yCezvioCA+bmUTPH/u57xKPT8d++voeYE6HeluA/bPbQ15TwDBG2ii+QZIEmYx8VdxA=="], + "sass": ["sass@1.93.0", "", { "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-CQi5/AzCwiubU3dSqRDJ93RfOfg/hhpW1l6wCIvolmehfwgCI35R/0QDs1+R+Ygrl8jFawwwIojE2w47/mf94A=="], "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], @@ -807,6 +821,8 @@ "uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="], + "undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="], + "unplugin": ["unplugin@2.3.10", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw=="], "update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="], diff --git a/messages/de.json b/messages/de.json index 90e7de1b..bc484355 100644 --- a/messages/de.json +++ b/messages/de.json @@ -17,8 +17,7 @@ "subtitle": "Die Verarbeitung aller Bild-, Audio- und Dokumentdateien findet auf deinem Gerät statt. Videos werden auf unseren blitzschnellen Servern konvertiert. Kein Dateigrößenlimit, keine Werbung und vollständig Open-Source.", "uploader": { "text": "Dateien hier ablegen oder klicken zum {action}", - "convert": "Konvertieren", - "jpegify": "Jpegifizieren" + "convert": "Konvertieren" }, "cards": { "title": "VERT unterstützt...", @@ -190,7 +189,7 @@ "notable_contributors": "Besonders erwähnenswerte Mitwirkende", "notable_description": "Wir möchten diesen Personen für ihre wichtigen Beiträge zu VERT danken.", "github_contributors": "GitHub-Mitwirkende", - "github_description": "Ein großes [jpegify_link]Dankeschön[/jpegify_link] an all diese Leute für ihre Hilfe! [github_link]Möchtest du auch helfen?[/github_link]", + "github_description": "Ein großes Dankeschön an all diese Leute für ihre Hilfe! [github_link]Möchtest du auch helfen?[/github_link]", "no_contributors": "Scheint, als hätte noch niemand beigetragen... [contribute_link]sei der Erste, der beiträgt![/contribute_link]", "libraries": "Bibliotheken", "libraries_description": "Ein großes Dankeschön an FFmpeg (Audio, Video), ImageMagick (Bilder) und Pandoc (Dokumente) für die Pflege solch exzellenter Bibliotheken über so viele Jahre. VERT verlässt sich auf sie, um dir deine Konvertierungen zu ermöglichen.", @@ -215,12 +214,5 @@ "no_audio": "Kein Audiostream gefunden.", "invalid_rate": "Ungültige Abtastrate angegeben: {rate}Hz" } - }, - "jpegify": { - "title": "GEHEIMES JPEGIFIZIEREN!!!", - "subtitle": "(psst... sag es niemandem!)", - "button": "JPEGIFIZIEREN {compression}%!!!", - "download": "Herunterladen", - "delete": "Löschen" } } \ No newline at end of file diff --git a/messages/el.json b/messages/el.json index 5bfdc82f..49cc484f 100644 --- a/messages/el.json +++ b/messages/el.json @@ -17,8 +17,7 @@ "subtitle": "Όλη η επεξεργασία εικόνων, ήχου και εγγράφων γίνεται στη συσκευή σας. Τα βίντεο μετατρέπονται στους κεραυνοβόλα γρήγορους διακομιστές μας. Χωρίς όριο μεγέθους αρχείου, χωρίς διαφημίσεις και εντελώς ανοιχτού κώδικα.", "uploader": { "text": "Σύρετε ή κάντε κλικ για {action}", - "convert": "μετατροπή", - "jpegify": "μετατροπή σε jpeg" + "convert": "μετατροπή" }, "cards": { "title": "Το VERT υποστηρίζει...", @@ -216,7 +215,7 @@ "notable_contributors": "Αξιόλογοι συνεισφέροντες", "notable_description": "Θα θέλαμε να ευχαριστήσουμε αυτά τα άτομα για τις σημαντικές συνεισφορές τους στο VERT.", "github_contributors": "Συνεισφέροντες στο GitHub", - "github_description": "Μεγάλες [jpegify_link]ευχαριστίες[/jpegify_link] σε όλα αυτά τα άτομα που βοήθησαν! [github_link]Θέλετε να βοηθήσετε κι εσείς;[/github_link]", + "github_description": "Μεγάλες ευχαριστίες σε όλα αυτά τα άτομα που βοήθησαν! [github_link]Θέλετε να βοηθήσετε κι εσείς;[/github_link]", "no_contributors": "Φαίνεται ότι κανείς δεν έχει συνεισφέρει ακόμα... [contribute_link]γίνετε ο πρώτος που θα συνεισφέρει![/contribute_link]", "libraries": "Βιβλιοθήκες", "libraries_description": "Μεγάλες ευχαριστίες στα FFmpeg (ήχος, βίντεο), ImageMagick (εικόνες) και Pandoc (έγγραφα) που διατηρούν τέτοιες εξαιρετικές βιβλιοθήκες για τόσα χρόνια. Το VERT βασίζεται σε αυτές για να σας παρέχει τις μετατροπές σας.", @@ -241,12 +240,5 @@ "no_audio": "Δεν βρέθηκε ροή ήχου.", "invalid_rate": "Καθορίστηκε μη έγκυρος ρυθμός δειγματοληψίας: {rate}Hz" } - }, - "jpegify": { - "title": "ΜΥΣΤΙΚΟ JPEGIFY!!!", - "subtitle": "(σσσ... μην το πεις σε κανέναν!)", - "button": "JPEGIFY {compression}%!!!", - "download": "Λήψη", - "delete": "Διαγραφή" } } \ No newline at end of file diff --git a/messages/en.json b/messages/en.json index 59f725d6..620a1e46 100644 --- a/messages/en.json +++ b/messages/en.json @@ -10,15 +10,15 @@ "footer": { "copyright": "© {year} VERT.", "source_code": "Source code", - "discord_server": "Discord server" + "discord_server": "Discord server", + "privacy_policy": "Privacy policy" }, "upload": { "title": "The file converter you'll love.", "subtitle": "All image, audio, and document processing is done on your device. Videos are converted on our lightning-fast servers. No file size limit, no ads, and completely open source.", "uploader": { "text": "Drop or click to {action}", - "convert": "convert", - "jpegify": "jpegify" + "convert": "convert" }, "cards": { "title": "VERT supports...", @@ -79,11 +79,20 @@ "errors": { "cant_convert": "We can't convert this file.", "vertd_server": "what are you doing..? you're supposed to run the vertd server!", + "vertd_generic_view": "View error details", "vertd_generic_body": "An error occurred whilst whilst trying convert your video. Would you like to submit this video to the developers to help fix this bug? Only your video file will be sent. No identifiers will be uploaded.", "vertd_generic_title": "Video conversion error", "vertd_generic_yes": "Submit video", "vertd_generic_no": "Don't submit", "vertd_failed_to_keep": "Failed to keep the video on the server: {error}", + "vertd_details": "View error details", + "vertd_details_body": "If you press submit, your video will also be attached alongside the error log which is always reported to us for review. The following information is the log that we automatically receive:", + "vertd_details_footer": "This information will only be used for troubleshooting purposes and will never be shared. View our [privacy_link]privacy policy[/privacy_link] for more details.", + "vertd_details_job_id": "Job ID: {jobId}", + "vertd_details_from": "From format: {from}", + "vertd_details_to": "To format: {to}", + "vertd_details_error_message": "Error message: [view_link]View error logs[/view_link]", + "vertd_details_close": "Close", "unsupported_format": "Only image, video, audio, and document files are supported", "vertd_not_found": "Could not find the vertd instance to start video conversion. Are you sure the instance URL is set correctly?", "worker_downloading": "The {type} converter is currently being initialized, please wait a few moments.", @@ -216,7 +225,7 @@ "notable_contributors": "Notable contributors", "notable_description": "We'd like to thank these people for their major contributions to VERT.", "github_contributors": "GitHub contributors", - "github_description": "Big [jpegify_link]thanks[/jpegify_link] to all these people for helping out! [github_link]Want to help too?[/github_link]", + "github_description": "Big thanks to all these people for helping out! [github_link]Want to help too?[/github_link]", "no_contributors": "Seems like no one has contributed yet... [contribute_link]be the first to contribute![/contribute_link]", "libraries": "Libraries", "libraries_description": "A big thanks to FFmpeg (audio, video), ImageMagick (images) and Pandoc (documents) for maintaining such excellent libraries for so many years. VERT relies on them to provide you with your conversions.", @@ -242,11 +251,38 @@ "invalid_rate": "Invalid sample rate specified: {rate}Hz" } }, - "jpegify": { - "title": "SECRET JPEGIFY!!!", - "subtitle": "(shh... don't tell anyone!)", - "button": "JPEGIFY {compression}%!!!", - "download": "Download", - "delete": "Delete" + "privacy": { + "title": "Privacy Policy", + "summary": { + "title": "Summary", + "description": "VERT's privacy policy is very simple: we do not collect or store any data on you at all. We don't use cookies or trackers, analytics are completely private, and all conversions (except videos) happen locally on your browser. Videos are deleted after being downloaded, or an hour, unless explicitly given permission by you to be stored; it will only be used for the purpose of troubleshooting. VERT self-hosts a Coolify instance for hosting the website and vertd (for video conversion), and a Plausible instance for completely anonymous and aggregated analytics.

Note this may only apply to the official VERT instance at [vert_link]vert.sh[/vert_link]; third-party instances may handle your data differently." + }, + "conversions": { + "title": "Conversions", + "description": "Most conversions (images, documents, audio) happen entirely locally on your device using WebAssembly versions of the relevant tools (e.g. ImageMagick, Pandoc, FFmpeg). This means your files never leave your device and we will never have access to them.

Video conversions are performed on our servers because they require more processing power and cannot be done very quickly on the browser yet. Videos you convert with VERT are deleted after being downloaded, or after one hour, unless you explicitly give permission for us to store them longer purely for troubleshooting purposes." + }, + "conversion_errors": { + "title": "Conversion Errors", + "description": "When a video conversion fails, we may collect some anonymous data to help us diagnose the issue. This data may include:", + "list_job_id": "The job ID, which is the anonymized file name", + "list_format_from": "The format you converted from", + "list_format_to": "The format you converted to", + "list_stderr": "The FFmpeg stderr output of your job (error message)", + "list_video": "The actual video file (if given explicit permission)", + "footer": "This information is used solely for the purpose of diagnosing conversion issues. The actual video file will only ever be collected if you give us permission to do so, where it will only be used for troubleshooting." + }, + "analytics": { + "title": "Analytics", + "description": "We self-host a Plausible instance for completely anonymous and aggregated analytics. Plausible does not use cookies and complies with all major privacy regulations (GDPR/CCPA/PECR). You can opt out of analytics in the \"Privacy & data\" section in [settings_link]settings[/settings_link] and read more about Plausible's privacy practices [plausible_link]here[/plausible_link]." + }, + "local_storage": { + "title": "Local Storage", + "description": "We use your browser's local storage to save your settings, and your browser's session storage to temporarily store the GitHub contributors list for the \"About\" section to reduce repeated GitHub API requests. No personal data is stored or transmitted.

The WebAssembly versions of the conversion tools we use (FFmpeg, ImageMagick, Pandoc) are also stored locally on your browser when you first visit the website, so you don't need to redownload them each visit. No personal data is stored or transmitted. You may view or delete this data at any time in the \"Privacy & data\" section in [settings_link]settings[/settings_link]." + }, + "contact": { + "title": "Contact", + "description": "For questions, email us at: [email_link]hello@vert.sh[/email_link]. If you are using a third-party instance of VERT, please contact the hoster of that instance instead." + }, + "last_updated": "Last updated: 2025-10-19" } } diff --git a/messages/es.json b/messages/es.json index 2256a16c..9882f7fc 100644 --- a/messages/es.json +++ b/messages/es.json @@ -17,8 +17,7 @@ "subtitle": "Todo el procesamiento de imágenes, audio y documentos es hecho en tu dispositivo. Los vídeos son convertidos en nuestros servidores ultra rápidos. Sin límite de tamaño de archivo, sin anuncios y de código abierto.", "uploader": { "text": "Arrastra o haz clic para {action}", - "convert": "convertir", - "jpegify": "jpegificar" + "convert": "convertir" }, "cards": { "title": "VERT soporta...", @@ -210,7 +209,7 @@ "notable_contributors": "Colaboradores destacados", "notable_description": "Queremos dar las gracias a las siguientes personas por sus importantes contribuciones a VERT.", "github_contributors": "Contribuidores de GitHub", - "github_description": "¡Muchas [jpegify_link]gracias[/jpegify_link] a todos los que han contribuido! [github_link]¿Quieres contribuir también?[/github_link]", + "github_description": "¡Muchas gracias a todos los que han contribuido! [github_link]¿Quieres contribuir también?[/github_link]", "no_contributors": "Parece que nadie ha contribuido todavía... [contribute_link]¡Sé el primero en hacerlo![/contribute_link]", "libraries": "Librerías", "libraries_description": "Muchas gracias a FFmpeg (audio, vídeo), ImageMagick (imágenes) y Pandoc (documentos) por mantener librerías excelentes por tantos años. VERT depende de ellas para proporcionar tus conversiones.", @@ -235,12 +234,5 @@ "no_audio": "No se encontró una pista de audio.", "invalid_rate": "La tasa de muestreo especificada no es válida: {rate}Hz" } - }, - "jpegify": { - "title": "¡¡¡JPEGIFICADOR SECRETO!!!", - "subtitle": "(shh... ¡no se lo digas a nadie!)", - "button": "¡¡¡JPEGIFICAR {compression}%!!!", - "download": "Descargar", - "delete": "Eliminar" } } diff --git a/messages/fr.json b/messages/fr.json index 48f2c005..d7d30ac0 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -17,8 +17,7 @@ "subtitle": "Tout le traitement des images, des fichiers audio et des documents s'effectue sur votre appareil. Les vidéos sont converties sur nos serveurs ultra-rapides. Aucune limite de taille de fichier, aucune publicité et entièrement open source.", "uploader": { "text": "Déposer ou cliquer pour {action}", - "convert": "convertir", - "jpegify": "jpegify" + "convert": "convertir" }, "cards": { "title": "VERT supports...", @@ -190,7 +189,7 @@ "notable_contributors": "Contributeurs notables", "notable_description": "Nous tenons à remercier ces personnes pour leurs contributions majeures à VERT.", "github_contributors": "Les contributeurs de GitHub", - "github_description": "Un grand [jpegify_link]merci[/jpegify_link] à toutes ces personnes pour leur aide ! [github_link]Vous voulez aussi aider ?[/github_link]", + "github_description": "Un grand merci à toutes ces personnes pour leur aide ! [github_link]Vous voulez aussi aider ?[/github_link]", "no_contributors": "Il semble que personne n'ait encore contribué... [contribute_link]soyez le premier à contribuer ![/contribute_link]", "libraries": "Bibliothèques", "libraries_description": "un grand merci à FFmpeg (audio, video), ImageMagick (images) et Pandoc (documents) pour avoir maintenu d'aussi excellentes bibliothèques pendant tant d'années, VERT compte sur eux pour vous fournir vos conversions.", @@ -215,12 +214,5 @@ "no_audio": "Aucun flux audio détécté.", "invalid_rate": "Taux d'échantillonnage spécifié non valide: {rate}Hz" } - }, - "jpegify": { - "title": "SECRET JPEGIFY!!!", - "subtitle": "(chut... ne le dis à personne !)", - "button": "JPEGIFY {compression}%!!!", - "download": "Télécharger", - "delete": "Supprimer" } } \ No newline at end of file diff --git a/messages/hr.json b/messages/hr.json index 3d3113c5..94d02143 100644 --- a/messages/hr.json +++ b/messages/hr.json @@ -17,8 +17,7 @@ "subtitle": "Cijelokupna obrada slika, zvuka i dokumenata se odvija na vašem uređaju. Videozapisi se pretvaraju na našim izrazito brzim serverima. Nema nikakvih ograničenja veličine niti reklama i potpuno je open source.", "uploader": { "text": "Ubaci ili klikni da {action}", - "convert": "pretvori", - "jpegify": "jpegiraj" + "convert": "pretvori" }, "cards": { "title": "VERT podržava...", @@ -190,7 +189,7 @@ "notable_contributors": "Značajni suradnici", "notable_description": "Želimo zahvaliti ovim ljudima za njihove ogromne doprinose VERTu.", "github_contributors": "GitHub suradnici", - "github_description": "Velike [jpegify_link]zahvale[/jpegify_link] svim ovim ljudima koji su nam pomogli! [github_link]Želiš nam i ti pomoći?[/github_link]", + "github_description": "Velike zahvale svim ovim ljudima koji su nam pomogli! [github_link]Želiš nam i ti pomoći?[/github_link]", "no_contributors": "Čini se kako nitko nije još doprinio... [contribute_link]budite prvi koji će doprinjeti![/contribute_link]", "libraries": "Biblioteke", "libraries_description": "Velike zahvale prema FFmpeg (audio, video), ImageMagick (slike) i Pandoc (dokumenti) što su održavali tako odlične biblioteke svih ovih godina. VERT se oslanja na njih da bi Vam pružili pretvorbu.", @@ -214,12 +213,5 @@ "no_audio": "Nije pronađen audio.", "invalid_rate": "Upisan nevažeći sample rate: {rate}Hz!" } - }, - "jpegify": { - "title": "TAJNI JPEGIFY!!!", - "subtitle": "(shhhh... nemoj reći nikome!)", - "button": "JPEGIRAJ {compression}%!!!", - "download": "Preuzmi", - "delete": "Obriši" } } diff --git a/messages/it.json b/messages/it.json index 6e04e0cf..11b02ead 100644 --- a/messages/it.json +++ b/messages/it.json @@ -17,8 +17,7 @@ "subtitle": "Tutta l'elaborazione di immagini, audio e documenti avviene sul tuo dispositivo. I video sono convertiti sui nostri server velocissimi. Nessun limite di dimensione, nessuna pubblicità e completamente open source.", "uploader": { "text": "Trascina o clicca per {action}", - "convert": "convertire", - "jpegify": "jpegificare" + "convert": "convertire" }, "cards": { "title": "VERT supporta...", @@ -216,7 +215,7 @@ "notable_contributors": "Contributori di rilievo", "notable_description": "Vorremmo ringraziare queste persone per i loro importanti contributi a VERT.", "github_contributors": "Contributori GitHub", - "github_description": "Un grande [jpegify_link]grazie[/jpegify_link] a tutte queste persone per aver dato una mano! [github_link]Vuoi aiutare anche tu?[/github_link]", + "github_description": "Un grande grazie a tutte queste persone per aver dato una mano! [github_link]Vuoi aiutare anche tu?[/github_link]", "no_contributors": "Sembra che nessuno abbia ancora contribuito... [contribute_link]sii il primo a contribuire![/contribute_link]", "libraries": "Librerie", "libraries_description": "Un grande ringraziamento a FFmpeg (audio, video), ImageMagick (immagini) e Pandoc (documenti) per aver mantenuto librerie così eccellenti per così tanti anni. VERT si affida a loro per fornirti le tue conversioni.", @@ -241,12 +240,5 @@ "no_audio": "Nessuno *stream* audio trovato.", "invalid_rate": "Frequenza di campionamento specificata non valida: {rate}Hz" } - }, - "jpegify": { - "title": "JPEGIFY SEGRETO!!!", - "subtitle": "(shh... non dirlo a nessuno!)", - "button": "JPEGIFY {compression}%!!!", - "download": "Scarica", - "delete": "Elimina" } } diff --git a/messages/ja.json b/messages/ja.json index 04019378..77f5b2f0 100644 --- a/messages/ja.json +++ b/messages/ja.json @@ -17,8 +17,7 @@ "subtitle": "すべての画像・音声・ドキュメント処理はデバイス上で行われます。動画は超高速サーバーで変換されます。ファイルサイズ制限なし、広告なし、完全オープンソース。", "uploader": { "text": "ドロップまたはクリックして{action}", - "convert": "変換", - "jpegify": "JPEG化" + "convert": "変換" }, "cards": { "title": "VERTがサポートしている形式", @@ -210,7 +209,7 @@ "notable_contributors": "特筆すべき貢献者", "notable_description": "VERTに大きく貢献してくださった方々に感謝します。", "github_contributors": "GitHubの貢献者", - "github_description": "多くの方々に[jpegify_link]感謝[/jpegify_link]します![github_link]あなたも参加してみませんか?[/github_link]", + "github_description": "多くの方々に感謝します![github_link]あなたも参加してみませんか?[/github_link]", "no_contributors": "まだ誰も貢献していないようです… [contribute_link]最初の貢献者になりましょう![/contribute_link]", "libraries": "ライブラリ", "libraries_description": "長年にわたり優れたライブラリを提供してくれているFFmpeg(音声・動画)、ImageMagick(画像)、Pandoc(ドキュメント)に感謝します。VERTはこれらに依存して動作しています。", @@ -235,12 +234,5 @@ "no_audio": "音声ストリームが見つかりません。", "invalid_rate": "無効なサンプリングレートが指定されました: {rate}Hz" } - }, - "jpegify": { - "title": "秘密のJPEGIFY!!!", - "subtitle": "(しっ…誰にも言わないで!)", - "button": "JPEGIFY {compression}%!!!", - "download": "ダウンロード", - "delete": "削除" } } diff --git a/messages/tr.json b/messages/tr.json index 696727ba..4b1fa076 100644 --- a/messages/tr.json +++ b/messages/tr.json @@ -17,8 +17,7 @@ "subtitle": "Tüm görüntü, ses ve belge işlemleri cihazınızda gerçekleştirilir. Videolar, ışık hızındaki sunucularımızda dönüştürülür. Dosya boyutu sınırı ve reklam yoktur. Tamamen açık kaynaklıdır.", "uploader": { "text": "{action} için sürükleyip bırakın veya dosya seçin", - "convert": "dönüştürmek", - "jpegify": "jpegify" + "convert": "dönüştürmek" }, "cards": { "title": "VERT'in desteklediği formatlar...", @@ -211,7 +210,7 @@ "notable_contributors": "Önemli katılımcılar", "notable_description": "VERT'e sağladıkları büyük katkılardan dolayı bu kişilere teşekkür ederiz.", "github_contributors": "GitHub katılımcıları", - "github_description": "Yardımcı olan herkese çok [jpegify_link]teşekkürler[/jpegify_link]! [github_link]Sen de yardım etmek ister misin?[/github_link]", + "github_description": "Yardımcı olan herkese çok teşekkürler! [github_link]Sen de yardım etmek ister misin?[/github_link]", "no_contributors": "Henüz kimse katkıda bulunmamış gibi görünüyor... [contribute_link]ilk katkıda bulunan sen ol![/contribute_link]", "libraries": "Kütüphaneler", "libraries_description": "Bu mükemmel kütüphaneleri yıllardır geliştirdikleri için FFmpeg (ses, video), ImageMagick (görseller) ve Pandoc (belgeler)'a çok teşekkür ederiz. VERT, dönüştürme işlemleri için bu kütüphaneleri kullanmaktadır.", @@ -236,12 +235,5 @@ "no_audio": "Ses akışı bulunamadı.", "invalid_rate": "Geçersiz örnekleme hızı: {hız}Hz" } - }, - "jpegify": { - "title": "GİZLİ JPEGIFY!!!", - "subtitle": "(şşş... kimseye söyleme!)", - "button": "JPEGIFY {compression}%!!!", - "download": "İndir", - "delete": "Sil" } } diff --git a/package.json b/package.json index fe5e7d49..fc11b0fa 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@sveltejs/kit": "^2.42.2", "@sveltejs/vite-plugin-svelte": "^4.0.4", "@types/eslint": "^9.6.1", + "@types/sanitize-html": "^2.16.0", "autoprefixer": "^10.4.21", "css-select": "5.1.0", "eslint": "^9.36.0", @@ -54,6 +55,7 @@ "overlayscrollbars-svelte": "^0.5.5", "p-queue": "^8.1.1", "riff-file": "^1.0.3", + "sanitize-html": "^2.17.0", "svelte-stripe": "^1.4.0", "vert-wasm": "^0.0.2", "vite-plugin-wasm": "^3.5.0" diff --git a/src/lib/components/functional/Dialog.svelte b/src/lib/components/functional/Dialog.svelte index a0931088..1bd5f970 100644 --- a/src/lib/components/functional/Dialog.svelte +++ b/src/lib/components/functional/Dialog.svelte @@ -3,19 +3,13 @@ import { removeDialog } from "$lib/store/DialogProvider"; import { BanIcon, CheckIcon, InfoIcon, TriangleAlert } from "lucide-svelte"; import { quintOut } from "svelte/easing"; + import type { Dialog as DialogType } from "$lib/store/DialogProvider"; - type Props = { - id: number; - title: string; - message: string; - buttons: { - text: string; - action: () => void; - }[]; - type: "success" | "error" | "info" | "warning"; - }; + type Props = DialogType; - let { id, title, message, buttons, type }: Props = $props(); + let props: Props = $props(); + const { id, title, message, buttons, type } = props; + const additional = "additional" in props ? props.additional : undefined; const colors = { success: "purple", @@ -59,7 +53,14 @@
-

{message}

+ {#if typeof message === "string"} +

{message}

+ {:else} + {@const MessageComponent = message} +
+ +
+ {/if}
{#each buttons as { text, action }, i} diff --git a/src/lib/components/functional/Uploader.svelte b/src/lib/components/functional/Uploader.svelte index 7feb8dfc..a12929b8 100644 --- a/src/lib/components/functional/Uploader.svelte +++ b/src/lib/components/functional/Uploader.svelte @@ -11,10 +11,9 @@ type Props = { class?: string; - jpegify?: boolean; }; - const { class: classList, jpegify }: Props = $props(); + const { class: classList }: Props = $props(); let uploaderButton = $state(); let fileInput = $state(); @@ -43,13 +42,9 @@ const handleFileChange = (e: Event) => { if (!fileInput) return; - if (page.url.pathname !== "/jpegify/") { - const oldLength = files.files.length; - files.add(fileInput.files); - if (oldLength !== files.files.length) goto("/convert"); - } else { - files.add(fileInput.files); - } + const oldLength = files.files.length; + files.add(fileInput.files); + if (oldLength !== files.files.length) goto("/convert"); }; onMount(() => { @@ -100,9 +95,7 @@

{m["upload.uploader.text"]({ - action: jpegify - ? m["upload.uploader.jpegify"]() - : m["upload.uploader.convert"](), + action: m["upload.uploader.convert"]() })}

diff --git a/src/lib/components/functional/VertdError.svelte b/src/lib/components/functional/VertdError.svelte index 827e09d9..a603902a 100644 --- a/src/lib/components/functional/VertdError.svelte +++ b/src/lib/components/functional/VertdError.svelte @@ -2,6 +2,10 @@ export interface VertdErrorProps { jobId: string; auth: string; + from?: string; + to?: string; + errorMessage?: string; + fileName?: string; } @@ -10,6 +14,8 @@ import { m } from "$lib/paraglide/messages"; import { ToastManager, type ToastProps } from "$lib/toast/index.svelte"; + import { addDialog } from "$lib/store/DialogProvider"; + import VertdErrorDetails from "./VertdErrorDetails.svelte"; const toast: ToastProps = $props(); @@ -52,22 +58,50 @@ ToastManager.remove(toast.id); }; + + const showDetails = () => { + addDialog( + m["convert.errors.vertd_details"](), + VertdErrorDetails as any, + [ + { + text: "Close", + action: () => {}, + }, + ], + "info", + { + jobId: toast.additional.jobId || "Unknown", + from: toast.additional.from || "Unknown", + to: toast.additional.to || "Unknown", + errorMessage: toast.additional.errorMessage || "Unknown error", + }, + ); + };

{m["convert.errors.vertd_generic_body"]()}

-
- +
{m["convert.errors.vertd_generic_view"]()} +
+ + +
diff --git a/src/lib/components/functional/VertdErrorDetails.svelte b/src/lib/components/functional/VertdErrorDetails.svelte new file mode 100644 index 00000000..52513586 --- /dev/null +++ b/src/lib/components/functional/VertdErrorDetails.svelte @@ -0,0 +1,64 @@ + + +
+

{@html sanitize(m["convert.errors.vertd_details_body"]())}

+

+ + {@html sanitize(m["convert.errors.vertd_details_job_id"]({ + jobId: additional.jobId, + }))} + +

+

+ + {@html sanitize(m["convert.errors.vertd_details_from"]({ + from: additional.from, + }))} + +

+

+ + {@html sanitize(m["convert.errors.vertd_details_to"]({ to: additional.to }))} + +

+

+ + {@html sanitize(link( + ["view_link"], + m["convert.errors.vertd_details_error_message"](), + [ + URL.createObjectURL( + new Blob([additional.errorMessage], { + type: "text/plain", + }), + ), + ], + [true], + ["text-blue-500 font-normal"], + ))} + +

+

+ {@html sanitize(link( + ["privacy_link"], + m["convert.errors.vertd_details_footer"](), + "/privacy", + [true], + ))} +

+
diff --git a/src/lib/components/layout/Dialogs.svelte b/src/lib/components/layout/Dialogs.svelte index d8c4fa20..7914117e 100644 --- a/src/lib/components/layout/Dialogs.svelte +++ b/src/lib/components/layout/Dialogs.svelte @@ -26,9 +26,9 @@ easing: quintOut, }} > - {#each dialogList as { id, title, message, buttons, type }, i} + {#each dialogList as dialog, i} {#if i === 0} - + {/if} {/each}
diff --git a/src/lib/components/layout/Footer.svelte b/src/lib/components/layout/Footer.svelte index cb0226a0..0e4867fb 100644 --- a/src/lib/components/layout/Footer.svelte +++ b/src/lib/components/layout/Footer.svelte @@ -5,6 +5,7 @@ const items = $derived([ [m["footer.source_code"](), GITHUB_URL_VERT], [m["footer.discord_server"](), DISCORD_URL], + [m["footer.privacy_policy"](), "/privacy"], ]); const year = new Date().getFullYear(); diff --git a/src/lib/components/layout/Gradients.svelte b/src/lib/components/layout/Gradients.svelte index 7a7d85b5..3e3cd8f3 100644 --- a/src/lib/components/layout/Gradients.svelte +++ b/src/lib/components/layout/Gradients.svelte @@ -37,7 +37,7 @@ at: 25, }, { - matcher: (path) => path === "/jpegify/", + matcher: (path) => path === "/privacy/", color: "var(--bg-gradient-red-from)", at: 100, }, diff --git a/src/lib/converters/vertd.svelte.ts b/src/lib/converters/vertd.svelte.ts index 52dd596b..2db5c724 100644 --- a/src/lib/converters/vertd.svelte.ts +++ b/src/lib/converters/vertd.svelte.ts @@ -377,6 +377,9 @@ export class VertdConverter extends Converter { additional: { jobId: uploadRes.id, auth: uploadRes.auth, + from: input.from, + to: to, + errorMessage: msg.data.message, }, }); } diff --git a/src/lib/sections/about/Credits.svelte b/src/lib/sections/about/Credits.svelte index 7b1110aa..29e90212 100644 --- a/src/lib/sections/about/Credits.svelte +++ b/src/lib/sections/about/Credits.svelte @@ -6,7 +6,7 @@ GITHUB_URL_VERT, } from "$lib/consts"; import { m } from "$lib/paraglide/messages"; - import { link } from "$lib/store/index.svelte"; + import { link, sanitize } from "$lib/store/index.svelte"; let { mainContribs, notableContribs, ghContribs } = $props(); @@ -101,23 +101,24 @@ {#if ghContribs && ghContribs.length > 0}

- {@html link( - ["jpegify_link", "github_link"], - m["about.credits.github_description"](), - ["/jpegify", GITHUB_URL_VERT], - [false, true], - [ - "text-black dynadark:text-white", - "text-blue-500 font-normal hover:underline", - ], + {@html sanitize( + link( + "github_link", + m["about.credits.github_description"](), + GITHUB_URL_VERT, + true, + ), )}

{:else}

- {@html link( - "contribute_link", - m["about.credits.no_contributors"](), - GITHUB_URL_VERT, + {@html sanitize( + link( + "contribute_link", + m["about.credits.no_contributors"](), + GITHUB_URL_VERT, + true, + ), )}

{/if} @@ -131,12 +132,12 @@ {/each} {/if} - -

{m["about.credits.libraries"]()}

-

- {m["about.credits.libraries_description"]()} -

+ +

{m["about.credits.libraries"]()}

+

+ {m["about.credits.libraries_description"]()} +

{/if} - + + diff --git a/src/lib/sections/about/Sponsors.svelte b/src/lib/sections/about/Sponsors.svelte index 71dce299..3be91597 100644 --- a/src/lib/sections/about/Sponsors.svelte +++ b/src/lib/sections/about/Sponsors.svelte @@ -5,7 +5,7 @@ import { DISCORD_URL } from "$lib/consts"; import { error } from "$lib/logger"; import { m } from "$lib/paraglide/messages"; - import { link } from "$lib/store/index.svelte"; + import { link, sanitize } from "$lib/store/index.svelte"; import { ToastManager } from "$lib/toast/index.svelte"; let copied = false; @@ -48,11 +48,12 @@

- {@html link( + {@html sanitize(link( "discord_link", m["about.sponsors.description"](), DISCORD_URL, - )} + true + ))} -

- {#each images as file, i (file.id)} -
- -
- {file.name} - {file.name} -
-
- - -
-
-
- {/each} -
- diff --git a/src/routes/privacy/+page.svelte b/src/routes/privacy/+page.svelte new file mode 100644 index 00000000..4900d566 --- /dev/null +++ b/src/routes/privacy/+page.svelte @@ -0,0 +1,93 @@ + + +
+

+ + {m["privacy.title"]()} +

+ +
+
+

{m["privacy.summary.title"]()}

+

+ {@html sanitize( + link( + ["vert_link"], + m["privacy.summary.description"](), + ["https://vert.sh"], + [true], + ), + )} +

+ +

{m["privacy.conversions.title"]()}

+

+ {@html sanitize(m["privacy.conversions.description"]())} +

+ +

+ {m["privacy.conversion_errors.title"]()} +

+
+ {m["privacy.conversion_errors.description"]()} +
    +
  • {m["privacy.conversion_errors.list_job_id"]()}
  • +
  • {m["privacy.conversion_errors.list_format_from"]()}
  • +
  • {m["privacy.conversion_errors.list_format_to"]()}
  • +
  • {m["privacy.conversion_errors.list_stderr"]()}
  • +
  • {m["privacy.conversion_errors.list_video"]()}
  • +
+ {m["privacy.conversion_errors.footer"]()} +
+ +

{m["privacy.analytics.title"]()}

+

+ {@html sanitize( + link( + ["settings_link", "plausible_link"], + m["privacy.analytics.description"](), + [ + "/settings", + "https://plausible.io/privacy-focused-web-analytics", + ], + [false, true], + ), + )} +

+ +

+ {m["privacy.local_storage.title"]()} +

+

+ {@html sanitize( + link( + ["settings_link"], + m["privacy.local_storage.description"](), + ["/settings"], + [false], + ), + )} +

+ +

{m["privacy.contact.title"]()}

+

+ {@html sanitize( + link( + ["email_link"], + m["privacy.contact.description"](), + ["mailto:hello@vert.sh"], + [false], + ), + )} +

+ +

{m["privacy.last_updated"]()}

+
+
+
diff --git a/static/robots.txt b/static/robots.txt new file mode 100644 index 00000000..42819c6c --- /dev/null +++ b/static/robots.txt @@ -0,0 +1,15 @@ +User-agent: * +Allow: / + +# language urls (doesn't actually change language when visiting) +# should prob be auto-generated? +Disallow: /es/ +Disallow: /fr/ +Disallow: /de/ +Disallow: /it/ +Disallow: /hr/ +Disallow: /tr/ +Disallow: /ja/ +Disallow: /el/ + +Sitemap: https://vert.sh/sitemap.xml diff --git a/static/sitemap.xml b/static/sitemap.xml new file mode 100644 index 00000000..9d9ddd79 --- /dev/null +++ b/static/sitemap.xml @@ -0,0 +1,32 @@ + + + + + https://vert.sh/ + 2025-10-17T19:23:05+00:00 + 1.00 + + + https://vert.sh/convert/ + 2025-10-17T19:23:05+00:00 + 0.80 + + + https://vert.sh/settings/ + 2025-10-17T19:23:05+00:00 + 0.80 + + + https://vert.sh/about/ + 2025-10-17T19:23:05+00:00 + 0.80 + + + https://vert.sh/privacy/ + 2025-10-17T19:23:05+00:00 + 0.80 + + \ No newline at end of file