From e0b01549c4d2ffc0c0093a0b7f9fd1e507b08cce Mon Sep 17 00:00:00 2001 From: Essem Date: Fri, 22 Sep 2023 03:48:54 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E2=9C=A8=20Implement=20module=20player?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- COPYING | 4 + locales/en-US.yml | 1 + locales/pt-PT.yml | 1 + packages/backend/src/const.ts | 9 + packages/backend/src/server/web/index.ts | 2 +- packages/client/package.json | 1 + .../client/src/components/MkMediaList.vue | 46 +- .../client/src/components/MkModPlayer.vue | 516 ++++++++++++++++++ packages/client/src/components/form/range.vue | 32 +- packages/client/src/const.ts | 68 +++ packages/client/src/scripts/chiptune2.ts | 372 +++++++++++++ 11 files changed, 1038 insertions(+), 14 deletions(-) create mode 100644 packages/client/src/components/MkModPlayer.vue create mode 100644 packages/client/src/scripts/chiptune2.ts diff --git a/COPYING b/COPYING index 783b54c3a..f67a2cbcd 100644 --- a/COPYING +++ b/COPYING @@ -25,6 +25,10 @@ RsaSignature2017 implementation by Transmute Industries Inc License: MIT https://github.com/transmute-industries/RsaSignature2017/blob/master/LICENSE +Chiptune2.js by Simon Gündling +License: MIT +https://github.com/deskjet/chiptune2.js#license + Licenses for all softwares and software libraries installed via the Node Package Manager ("npm") can be found by running the following shell command in the root directory of this repository: `yarn -R info --manifest` diff --git a/locales/en-US.yml b/locales/en-US.yml index 02a2c6aef..cc443b3ee 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -135,6 +135,7 @@ rememberNoteVisibility: "Remember post visibility settings" attachCancel: "Remove attachment" markAsSensitive: "Mark as sensitive" unmarkAsSensitive: "Unmark as sensitive" +clickToShowPatterns: "Click to show module patterns" enterFileName: "Enter filename" mute: "Mute" unmute: "Unmute" diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml index 59f12b8b6..88e6f2c60 100644 --- a/locales/pt-PT.yml +++ b/locales/pt-PT.yml @@ -113,6 +113,7 @@ rememberNoteVisibility: "Lembrar das configurações de visibilidade de notas" attachCancel: "Remover anexo" markAsSensitive: "Marcar como sensível" unmarkAsSensitive: "Desmarcar como sensível" +clickToShowPatterns: "Clique para mostrar os padrões do módulo" enterFileName: "Digite o nome do ficheiro" mute: "Silenciar" unmute: "Dessilenciar" diff --git a/packages/backend/src/const.ts b/packages/backend/src/const.ts index 2a955ee52..6dddf1fff 100644 --- a/packages/backend/src/const.ts +++ b/packages/backend/src/const.ts @@ -68,6 +68,15 @@ export const FILE_TYPE_BROWSERSAFE = [ "audio/x-flac", "audio/flac", "audio/vnd.wave", + + "audio/mod", + "audio/x-mod", + "audio/s3m", + "audio/x-s3m", + "audio/xm", + "audio/x-xm", + "audio/it", + "audio/x-it", ]; /* https://github.com/sindresorhus/file-type/blob/main/supported.js diff --git a/packages/backend/src/server/web/index.ts b/packages/backend/src/server/web/index.ts index ab81400ab..3b7448da9 100644 --- a/packages/backend/src/server/web/index.ts +++ b/packages/backend/src/server/web/index.ts @@ -473,7 +473,7 @@ router.get("/notes/:note", async (ctx, next) => { ctx.set("Cache-Control", "public, max-age=15"); ctx.set( "Content-Security-Policy", - "default-src 'self' 'unsafe-inline'; img-src *; media-src *; frame-ancestors *", + "default-src 'self' 'unsafe-inline' 'unsafe-eval'; connect-src *; font-src 'self' data:; img-src *; media-src *; worker-src 'self'; frame-ancestors *", ); return; diff --git a/packages/client/package.json b/packages/client/package.json index c519f5cd8..54f8337b7 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -62,6 +62,7 @@ "json5": "2.2.3", "katex": "0.16.8", "matter-js": "0.18.0", + "libopenmpt-wasm": "github:TheEssem/libopenmpt-packaging#build", "mfm-js": "0.23.3", "paralint": "^1.2.1", "photoswipe": "5.3.8", diff --git a/packages/client/src/components/MkMediaList.vue b/packages/client/src/components/MkMediaList.vue index 0c9a7a321..83bc7c219 100644 --- a/packages/client/src/components/MkMediaList.vue +++ b/packages/client/src/components/MkMediaList.vue @@ -12,16 +12,28 @@ :class="{ dmWidth: inDm }" >
- + > + + +
@@ -35,8 +47,13 @@ import PhotoSwipe from "photoswipe"; import "photoswipe/style.css"; import XBanner from "@/components/MkMediaBanner.vue"; import XMedia from "@/components/MkMedia.vue"; +import XModPlayer from "@/components/MkModPlayer.vue"; import * as os from "@/os"; -import { FILE_TYPE_BROWSERSAFE } from "@/const"; +import { + FILE_TYPE_BROWSERSAFE, + FILE_TYPE_TRACKER_MODULES, + FILE_EXT_TRACKER_MODULES, +} from "@/const"; import { defaultStore } from "@/store"; const props = defineProps<{ @@ -171,11 +188,24 @@ onMounted(() => { const previewable = (file: misskey.entities.DriveFile): boolean => { if (file.type === "image/svg+xml") return true; // svgのwebpublic/thumbnailはpngなのでtrue // FILE_TYPE_BROWSERSAFEに適合しないものはブラウザで表示するのに不適切 + if (isModule(file)) return true; return ( (file.type.startsWith("video") || file.type.startsWith("image")) && FILE_TYPE_BROWSERSAFE.includes(file.type) ); }; + +const isModule = (file: misskey.entities.DriveFile): boolean => { + return ( + FILE_TYPE_TRACKER_MODULES.some((type) => { + return file.type === type; + }) || + FILE_EXT_TRACKER_MODULES.some((ext) => { + return file.name.toLowerCase().endsWith("." + ext); + }) + ); +}; + const previewableCount = props.mediaList.filter((media) => previewable(media), ).length; diff --git a/packages/client/src/components/MkModPlayer.vue b/packages/client/src/components/MkModPlayer.vue new file mode 100644 index 000000000..f7ad14095 --- /dev/null +++ b/packages/client/src/components/MkModPlayer.vue @@ -0,0 +1,516 @@ + + + + + diff --git a/packages/client/src/components/form/range.vue b/packages/client/src/components/form/range.vue index 38ebe29e3..95e2fafe4 100644 --- a/packages/client/src/components/form/range.vue +++ b/packages/client/src/components/form/range.vue @@ -1,7 +1,7 @@