forked from FoundKeyGang/FoundKey
resolve merge conflicts
This commit is contained in:
commit
98a845eda4
15 changed files with 419 additions and 157 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -13,6 +13,9 @@
|
|||
# vim
|
||||
/.vimlocal
|
||||
|
||||
# vimlocal
|
||||
.vimlocal
|
||||
|
||||
# Node.js
|
||||
node_modules
|
||||
report.*.json
|
||||
|
|
8
COPYING
8
COPYING
|
@ -13,3 +13,11 @@ https://github.com/muan/emojilib/blob/master/LICENSE
|
|||
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
|
||||
|
||||
libopenmpt (as part of openmpt) by OpenMPT
|
||||
License: BSD 3-Clause
|
||||
https://github.com/OpenMPT/openmpt/blob/master/LICENSE
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
"lodash": "^4.17.21"
|
||||
},
|
||||
"dependencies": {
|
||||
"argon2": "^0.30.2",
|
||||
"execa": "5.1.1",
|
||||
"gulp": "4.0.2",
|
||||
"gulp-cssnano": "2.1.3",
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
import bcrypt from 'bcryptjs';
|
||||
import * as argon2 from 'argon2';
|
||||
|
||||
export async function hashPassword(password: string): Promise<string> {
|
||||
const salt = await bcrypt.genSalt(8);
|
||||
return await bcrypt.hash(password, salt);
|
||||
return argon2.hash(password);
|
||||
}
|
||||
|
||||
export async function comparePassword(password: string, hash: string): Promise<boolean> {
|
||||
return await bcrypt.compare(password, hash);
|
||||
if (isOldAlgorithm(hash)) return bcrypt.compare(password, hash);
|
||||
|
||||
return argon2.verify(hash, password);
|
||||
}
|
||||
|
||||
export function isOldAlgorithm(hash: string): boolean {
|
||||
// bcrypt hashes start with $2[ab]$
|
||||
return hash.startsWith('$2');
|
||||
}
|
||||
|
|
|
@ -307,7 +307,6 @@ export const UserRepository = db.getRepository(User).extend({
|
|||
host: user.host,
|
||||
avatarUrl: this.getAvatarUrlSync(user),
|
||||
avatarBlurhash: user.avatar?.blurhash || null,
|
||||
avatarColor: null, // 後方互換性のため
|
||||
isAdmin: user.isAdmin || falsy,
|
||||
isModerator: user.isModerator || falsy,
|
||||
isBot: user.isBot || falsy,
|
||||
|
@ -332,7 +331,6 @@ export const UserRepository = db.getRepository(User).extend({
|
|||
lastFetchedAt: user.lastFetchedAt ? user.lastFetchedAt.toISOString() : null,
|
||||
bannerUrl: user.banner ? DriveFiles.getPublicUrl(user.banner, false) : null,
|
||||
bannerBlurhash: user.banner?.blurhash || null,
|
||||
bannerColor: null, // 後方互換性のため
|
||||
isLocked: user.isLocked,
|
||||
isSilenced: user.isSilenced || falsy,
|
||||
isSuspended: user.isSuspended || falsy,
|
||||
|
|
|
@ -8,7 +8,7 @@ import { Users, Signins, UserProfiles, UserSecurityKeys, AttestationChallenges }
|
|||
import { ILocalUser } from '@/models/entities/user.js';
|
||||
import { genId } from '@/misc/gen-id.js';
|
||||
import { getIpHash } from '@/misc/get-ip-hash.js';
|
||||
import { comparePassword } from '@/misc/password.js';
|
||||
import { comparePassword, hashPassword, isOldAlgorithm } from '@/misc/password.js';
|
||||
import signin from '../common/signin.js';
|
||||
import { verifyLogin, hash } from '../2fa.js';
|
||||
import { limiter } from '../limiter.js';
|
||||
|
@ -69,6 +69,11 @@ export default async (ctx: Koa.Context) => {
|
|||
// Compare password
|
||||
const same = await comparePassword(password, profile.password!);
|
||||
|
||||
if (same && isOldAlgorithm(profile.password!)) {
|
||||
profile.password = await hashPassword(password);
|
||||
await UserProfiles.save(profile);
|
||||
}
|
||||
|
||||
async function fail(): void {
|
||||
// Append signin history
|
||||
await Signins.insert({
|
||||
|
|
|
@ -36,6 +36,7 @@ html
|
|||
link(rel='prefetch' href=config.images.error)
|
||||
link(rel='stylesheet' href='/assets/fontawesome/css/all.css')
|
||||
link(rel='modulepreload' href=`/assets/${clientEntry.file}`)
|
||||
script(src='/client-assets/libopenmpt.js')
|
||||
|
||||
each href in clientEntry.css
|
||||
link(rel='preload' href=`/assets/${href}` as='style')
|
||||
|
@ -63,6 +64,7 @@ html
|
|||
script.
|
||||
var VERSION = "#{version}";
|
||||
var CLIENT_ENTRY = "#{clientEntry.file}";
|
||||
window.libopenmpt = window.Module;
|
||||
|
||||
body
|
||||
noscript: p
|
||||
|
|
1
packages/client/assets/libopenmpt.js
Normal file
1
packages/client/assets/libopenmpt.js
Normal file
File diff suppressed because one or more lines are too long
BIN
packages/client/assets/libopenmpt.wasm
Executable file
BIN
packages/client/assets/libopenmpt.wasm
Executable file
Binary file not shown.
|
@ -5,9 +5,9 @@
|
|||
<div ref="gallery" :data-count="mediaList.filter(media => previewable(media)).length">
|
||||
<template v-for="media in mediaList.filter(media => previewable(media))">
|
||||
<XVideo v-if="media.type.startsWith('video')" :key="media.id" :video="media"/>
|
||||
<XModPlayer :module="media" :key="media.id" v-else-if="media.name.endsWith('.mod') || media.name.endsWith('.xm') || media.name.endsWith('.s3m') || media.name.endsWith('.it')"/>
|
||||
<XAnsi :ansi-file="media" :key="media.id" v-else-if="media.name.endsWith('.ans')"/>
|
||||
<XAnsi :ansi-file="media" :key="media.id" v-else-if="media.name.endsWith('.ans')"/>
|
||||
<XImage v-else-if="media.type.startsWith('image')" :key="media.id" class="image" :data-id="media.id" :image="media" :raw="raw"/>
|
||||
<XModPlayer v-else-if="isModule(media)" :key="media.id" :module="media"/>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -25,8 +25,7 @@ import XImage from './media-image.vue';
|
|||
import XVideo from './media-video.vue';
|
||||
import XModPlayer from './mod-player.vue';
|
||||
import XAnsi from './media-ansi.vue';
|
||||
import { FILE_TYPE_BROWSERSAFE } from '@/const';
|
||||
|
||||
import { FILE_TYPE_BROWSERSAFE, FILE_EXT_TRACKER_MODULES } from '@/const';
|
||||
|
||||
const props = defineProps<{
|
||||
mediaList: foundkey.entities.DriveFile[];
|
||||
|
@ -127,11 +126,18 @@ onMounted(() => {
|
|||
|
||||
const previewable = (file: foundkey.entities.DriveFile): boolean => {
|
||||
if (file.type === 'image/svg+xml') return true; // svgのwebpublic/thumbnailはpngなのでtrue
|
||||
if (file.name.endsWith(".mod") || file.name.endsWith(".xm") || file.name.endsWith(".s3m") || file.name.endsWith(".it")) return true;
|
||||
if (file.name.endsWith(".ans")) return true;
|
||||
if (file.name.endsWith(".ans")) return 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: foundkey.entities.DriveFile): boolean => {
|
||||
return FILE_EXT_TRACKER_MODULES.some((ext) => {
|
||||
return file.name.toLowerCase().endsWith("." + ext);
|
||||
});
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
@ -1,127 +1,230 @@
|
|||
<template>
|
||||
<div class="mod-player-disabled" v-if="hide" @click="hide = false">
|
||||
<div class="mod-player-disabled" v-if="hide" @click="toggleVisible()">
|
||||
<div>
|
||||
<b><i class="fas fa-exclamation-triangle"></i> {{ $ts.sensitive }}</b>
|
||||
<span>{{ $ts.clickToShow }}</span>
|
||||
<b><i class="fas fa-exlamation-triangle"></i> {{ i18n.ts.sensitive }}</b>
|
||||
<span>{{ i18n.ts.clickToShow }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mod-player-enabled" v-else>
|
||||
<div class="pattern-display">
|
||||
<canvas class="pattern-canvas" ref="display"></canvas>
|
||||
<canvas class="pattern-canvas" ref="displayCanvas"></canvas>
|
||||
</div>
|
||||
<div class="controls">
|
||||
<button class="play" title="play" accesskey="P" @click="playPause()">
|
||||
<i class="fas fa-play" v-if="!playing"></i>
|
||||
<button class="play" @click="playPause()">
|
||||
<i class="fas fa-pause" v-if="playing"></i>
|
||||
<i class="fas fa-play" v-else></i>
|
||||
</button>
|
||||
<button class="stop" title="stop" accesskey="X" @click="stop()">
|
||||
<button class="stop" @click="stop()">
|
||||
<i class="fas fa-stop"></i>
|
||||
</button>
|
||||
<progress min="0" max="100" value="0" ref="progress"></progress>
|
||||
<input type="range" min="0" max="1" v-model="player.context.gain.value" step="0.1" ref="volume" title="volume"/>
|
||||
<a class="download" title="download" :href="module.url">
|
||||
<input class="progress" type="range" min="0" max="1" v-model="position" step="0.1" ref="progress" @mousedown="initSeek()" @mouseup="performSeek()"/>
|
||||
<input type="range" min="0" max="1" v-model="player.context.gain.value" step="0.1"/>
|
||||
<a class="download" :title="i18n.ts.download" :href="module.url" target="_blank">
|
||||
<i class="fas fa-download"></i>
|
||||
</a>
|
||||
</div>
|
||||
<i class="fas fa-eye-slash" @click="hide = true"></i>
|
||||
<i class="fas fa-eye-slash" @click="toggleVisible()"></i>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import * as os from '@client/os';
|
||||
import { ChiptuneJsPlayer, ChiptuneJsConfig } from '../scripts/chiptune2.ts';
|
||||
<script lang="ts" setup>
|
||||
import { ref, nextTick } from 'vue';
|
||||
import * as foundkey from 'foundkey-js';
|
||||
import { i18n } from '@/i18n';
|
||||
import { defaultStore } from '@/store';
|
||||
import { ChiptuneJsPlayer, ChiptuneJsConfig } from '@/scripts/chiptune2';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
module: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
const CHAR_WIDTH = 6;
|
||||
const CHAR_HEIGHT = 12;
|
||||
const ROW_OFFSET_Y = 10;
|
||||
|
||||
const colours = {
|
||||
background: '#000000',
|
||||
default: {
|
||||
active: '#ffffff',
|
||||
inactive: '#808080'
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
hide: true,
|
||||
playing: false
|
||||
};
|
||||
quarter: {
|
||||
active: '#ffff00',
|
||||
inactive: '#ffe135'
|
||||
},
|
||||
created() {
|
||||
this.rowBuffer = 24;
|
||||
this.hide = (this.$store.state.nsfw === 'force') ? true : this.module.isSensitive && (this.$store.state.nsfw !== 'ignore');
|
||||
this.player = new ChiptuneJsPlayer(new ChiptuneJsConfig());
|
||||
this.buffer = null;
|
||||
this.player.load(this.module.url).then((result) => {
|
||||
this.buffer = result;
|
||||
this.player.play(this.buffer);
|
||||
this.display();
|
||||
this.player.stop();
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
instr: {
|
||||
active: '#80e0ff',
|
||||
inactive: '#0099cc'
|
||||
},
|
||||
methods: {
|
||||
display () {
|
||||
if (this.$refs.display === null) {
|
||||
this.stop();
|
||||
return;
|
||||
}
|
||||
const pattern = this.player.getPattern() || 0;
|
||||
const row = this.player.getRow() || 0;
|
||||
const nbChannels = this.player.currentPlayingNode.nbChannels;
|
||||
if (this.$refs.display.width !== 12 + 84 * nbChannels + 2) {
|
||||
this.$refs.display.width = 12 + 84 * nbChannels + 2;
|
||||
this.$refs.display.height = 12 * this.rowBuffer;
|
||||
}
|
||||
const nbRows = this.player.getPatternNumRows(pattern);
|
||||
const ctx = this.$refs.display.getContext('2d');
|
||||
ctx.font = '10px monospace';
|
||||
ctx.fillStyle = 'black';
|
||||
ctx.fillRect(0, 0, this.$refs.display.width, this.$refs.display.height);
|
||||
ctx.fillStyle = 'gray';
|
||||
for (let rowOffset = 0; rowOffset < this.rowBuffer; rowOffset++) {
|
||||
const rowToDraw = row - this.rowBuffer / 2 + rowOffset;
|
||||
if (rowToDraw >= 0 && rowToDraw < nbRows) {
|
||||
let rowText = parseInt(rowToDraw).toString(16);
|
||||
if (rowText.length === 1) {
|
||||
rowText = '0' + rowText;
|
||||
}
|
||||
ctx.fillStyle = 'gray';
|
||||
if (rowToDraw === row) {
|
||||
ctx.fillStyle = 'white';
|
||||
}
|
||||
ctx.fillText(rowText, 0, 10 + rowOffset * 12);
|
||||
for (let channel = 0; channel < nbChannels; channel++) {
|
||||
const part = this.player.getPatternRowChannel(pattern, rowToDraw, channel);
|
||||
ctx.fillText("|" + part, 12 + 84 * channel, 10 + rowOffset * 12);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
playPause() {
|
||||
this.player.addHandler('onRowChange', () => {
|
||||
this.$refs.progress.max = this.player.duration();
|
||||
this.$refs.progress.value = this.player.position() % this.player.duration();
|
||||
this.display(this.player);
|
||||
});
|
||||
if (this.player.currentPlayingNode === null) {
|
||||
this.player.play(this.buffer);
|
||||
this.playing = true;
|
||||
} else {
|
||||
this.player.togglePause();
|
||||
this.playing = !this.player.currentPlayingNode.paused;
|
||||
}
|
||||
},
|
||||
stop() {
|
||||
this.player.stop();
|
||||
this.playing = false;
|
||||
this.player.play(this.buffer);
|
||||
this.display();
|
||||
this.player.stop();
|
||||
this.$refs.progress.value = 0;
|
||||
this.player.handlers = [];
|
||||
}
|
||||
},
|
||||
volume: {
|
||||
active: '#80ff80',
|
||||
inactive: '#008000'
|
||||
},
|
||||
fx: {
|
||||
active: '#ff80e0',
|
||||
inactive: '#800060'
|
||||
},
|
||||
operant: {
|
||||
active: '#ffe080',
|
||||
inactive: '#806000'
|
||||
}
|
||||
};
|
||||
|
||||
const props = defineProps<{
|
||||
module: foundkey.entities.DriveFile
|
||||
}>();
|
||||
|
||||
let hide = ref((defaultStore.state.nsfw === 'force') ? true : props.module.isSensitive && (defaultStore.state.nsfw !== 'ignore'));
|
||||
let playing = ref(false);
|
||||
let displayCanvas = ref<HTMLCanvasElement>(null);
|
||||
let progress = ref<HTMLProgressElement>(null);
|
||||
let position = ref(0);
|
||||
const player = ref(new ChiptuneJsPlayer(new ChiptuneJsConfig()));
|
||||
|
||||
const rowBuffer = 24;
|
||||
let buffer = null;
|
||||
let isSeeking = false;
|
||||
player.value.load(props.module.url).then((result) => {
|
||||
buffer = result;
|
||||
try {
|
||||
player.value.play(buffer);
|
||||
progress.value.max = player.value.duration();
|
||||
display();
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
player.value.stop();
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
function playPause() {
|
||||
player.value.addHandler('onRowChange', () => {
|
||||
progress.value.max = player.value.duration();
|
||||
if (!isSeeking) {
|
||||
position.value = player.value.position() % player.value.duration();
|
||||
}
|
||||
display();
|
||||
});
|
||||
|
||||
player.value.addHandler('onEnded', () => {
|
||||
stop();
|
||||
});
|
||||
|
||||
if (player.value.currentPlayingNode === null) {
|
||||
player.value.play(buffer);
|
||||
player.value.seek(position.value);
|
||||
playing.value = true;
|
||||
} else {
|
||||
player.value.togglePause();
|
||||
playing.value = !player.value.currentPlayingNode.paused;
|
||||
}
|
||||
}
|
||||
|
||||
function stop(noDisplayUpdate = false) {
|
||||
player.value.stop();
|
||||
playing.value = false;
|
||||
if (!noDisplayUpdate) {
|
||||
try {
|
||||
player.value.play(buffer);
|
||||
display();
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
}
|
||||
player.value.stop();
|
||||
position.value = 0;
|
||||
player.value.handlers = [];
|
||||
}
|
||||
|
||||
function initSeek() {
|
||||
isSeeking = true;
|
||||
}
|
||||
|
||||
function performSeek() {
|
||||
const noNode = !player.value.currentPlayingNode;
|
||||
if (noNode) {
|
||||
player.value.play(buffer);
|
||||
}
|
||||
player.value.seek(position.value);
|
||||
display();
|
||||
if (noNode) {
|
||||
player.value.stop();
|
||||
}
|
||||
isSeeking = false;
|
||||
}
|
||||
|
||||
function toggleVisible() {
|
||||
hide.value = !hide.value;
|
||||
nextTick(() => { stop(hide.value); });
|
||||
}
|
||||
|
||||
function display() {
|
||||
if (!displayCanvas.value) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
const canvas = displayCanvas.value;
|
||||
|
||||
const pattern = player.value.getPattern();
|
||||
const row = player.value.getRow();
|
||||
let nbChannels = 0;
|
||||
if (player.value.currentPlayingNode) {
|
||||
nbChannels = player.value.currentPlayingNode.nbChannels;
|
||||
}
|
||||
if (canvas.width !== 12 + 84 * nbChannels + 2) {
|
||||
canvas.width = 12 + 84 * nbChannels + 2;
|
||||
canvas.height = 12 * rowBuffer;
|
||||
}
|
||||
const nbRows = player.value.getPatternNumRows(pattern);
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.font = '10px monospace';
|
||||
ctx.fillStyle = colours.background;
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.fillStyle = colours.default.inactive;
|
||||
for (let rowOffset = 0; rowOffset < rowBuffer; rowOffset++) {
|
||||
const rowToDraw = row - rowBuffer / 2 + rowOffset;
|
||||
if (rowToDraw >= 0 && rowToDraw < nbRows) {
|
||||
const active = (rowToDraw === row) ? 'active' : 'inactive';
|
||||
let rowText = parseInt(rowToDraw).toString(16);
|
||||
if (rowText.length === 1) {
|
||||
rowText = '0' + rowText;
|
||||
}
|
||||
ctx.fillStyle = colours.default[active];
|
||||
if (rowToDraw % 4 === 0) {
|
||||
ctx.fillStyle = colours.quarter[active];
|
||||
}
|
||||
ctx.fillText(rowText, 0, 10 + rowOffset * 12);
|
||||
for (let channel = 0; channel < nbChannels; channel++) {
|
||||
const part = player.value.getPatternRowChannel(pattern, rowToDraw, channel);
|
||||
const baseOffset = (2 + (part.length + 1) * channel) * CHAR_WIDTH;
|
||||
const baseRowOffset = ROW_OFFSET_Y + rowOffset * CHAR_HEIGHT;
|
||||
|
||||
ctx.fillStyle = colours.default[active];
|
||||
ctx.fillText("|", baseOffset, baseRowOffset);
|
||||
|
||||
const note = part.substring(0, 3);
|
||||
ctx.fillStyle = colours.default[active];
|
||||
ctx.fillText(note, baseOffset + CHAR_WIDTH, baseRowOffset);
|
||||
|
||||
const instr = part.substring(4, 6);
|
||||
ctx.fillStyle = colours.instr[active];
|
||||
ctx.fillText(instr, baseOffset + CHAR_WIDTH * 5, baseRowOffset);
|
||||
|
||||
const volume = part.substring(6, 9);
|
||||
ctx.fillStyle = colours.volume[active];
|
||||
ctx.fillText(volume, baseOffset + CHAR_WIDTH * 7, baseRowOffset);
|
||||
|
||||
const fx = part.substring(10, 11);
|
||||
ctx.fillStyle = colours.fx[active];
|
||||
ctx.fillText(fx, baseOffset + CHAR_WIDTH * 11, baseRowOffset);
|
||||
|
||||
const op = part.substring(11, 13);
|
||||
ctx.fillStyle = colours.operant[active];
|
||||
ctx.fillText(op, baseOffset + CHAR_WIDTH * 12, baseRowOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -146,17 +249,17 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
> .pattern-display {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
background-color: black;
|
||||
background-color: black;
|
||||
text-align: center;
|
||||
.pattern-canvas {
|
||||
background-color: black;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .controls {
|
||||
display: flex;
|
||||
|
@ -171,7 +274,8 @@ export default defineComponent({
|
|||
border: none;
|
||||
background-color: transparent;
|
||||
color: var(--accent);
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--fg);
|
||||
}
|
||||
|
@ -183,10 +287,11 @@ export default defineComponent({
|
|||
width: 90px;
|
||||
padding: 0;
|
||||
margin: 4px 8px;
|
||||
overflow-x: hidden;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
|
||||
|
||||
&::-webkit-slider-runnable-track {
|
||||
background: var(--bg);
|
||||
}
|
||||
|
@ -204,6 +309,7 @@ export default defineComponent({
|
|||
animate: 0.2s;
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--fg);
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
&::-webkit-slider-thumb {
|
||||
|
@ -211,10 +317,12 @@ export default defineComponent({
|
|||
height: 100%;
|
||||
width: 14px;
|
||||
border-radius: 0;
|
||||
background: var(--accent);
|
||||
background: var(--accentLighten);
|
||||
cursor: pointer;
|
||||
-webkit-appearance: none;
|
||||
margin-top: -0.5px;
|
||||
box-shadow: calc(-100vw - 14px) 0 0 100vw var(--accent);
|
||||
clip-path: polygon(1px 0, 100% 0, 100% 100%, 1px 100%, 1px calc(50% + 10.5px), -100vw calc(50% + 10.5px), -100vw calc(50% - 10.5px), 0 calc(50% - 10.5px));
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&::-moz-range-track {
|
||||
|
@ -227,13 +335,19 @@ export default defineComponent({
|
|||
border: 1px solid var(--fg);
|
||||
}
|
||||
|
||||
&::-moz-range-progress {
|
||||
cursor: pointer;
|
||||
height: 100%;
|
||||
background: var(--accent);
|
||||
}
|
||||
|
||||
&::-moz-range-thumb {
|
||||
border: none;
|
||||
height: 100%;
|
||||
border-radius: 0;
|
||||
width: 14px;
|
||||
background: var(--accent);
|
||||
cursoer: pointer;
|
||||
background: var(--accentLighten);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&::-ms-track {
|
||||
|
@ -247,7 +361,13 @@ export default defineComponent({
|
|||
color: transparent;
|
||||
}
|
||||
|
||||
&::-ms-fill-lower, &::-ms-fill-upper {
|
||||
&::-ms-fill-lower {
|
||||
background: var(--accent);
|
||||
border: 1px solid var(--fg);
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
&::-ms-fill-upper {
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--fg);
|
||||
border-radius: 0;
|
||||
|
@ -259,19 +379,13 @@ export default defineComponent({
|
|||
height: 100%;
|
||||
width: 14px;
|
||||
border-radius: 0;
|
||||
background: var(--accent);
|
||||
background: var(--accentLighten);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
> progress {
|
||||
padding: unset;
|
||||
margin: 4px 8px;
|
||||
flex-grow: 1;
|
||||
background-color: var(--bg);
|
||||
|
||||
&::-moz-progress-bar, &::-webkit-progress-value {
|
||||
background-color: var(--accent);
|
||||
&.progress {
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,44 @@ export const FILE_TYPE_BROWSERSAFE = [
|
|||
'audio/x-flac',
|
||||
'audio/vnd.wave',
|
||||
];
|
||||
|
||||
export const FILE_EXT_TRACKER_MODULES = [
|
||||
'mod',
|
||||
's3m',
|
||||
'xm',
|
||||
'it',
|
||||
'mptm',
|
||||
'stm',
|
||||
'nst',
|
||||
'm15',
|
||||
'stk',
|
||||
'wow',
|
||||
'ult',
|
||||
'669',
|
||||
'mtm',
|
||||
'med',
|
||||
'far',
|
||||
'mdl',
|
||||
'ams',
|
||||
'dsm',
|
||||
'amf',
|
||||
'okt',
|
||||
'dmf',
|
||||
'ptm',
|
||||
'psm',
|
||||
'mt2',
|
||||
'dbm',
|
||||
'digi',
|
||||
'imf',
|
||||
'j2b',
|
||||
'gdm',
|
||||
'umx',
|
||||
'plm',
|
||||
'mo3',
|
||||
'xpk',
|
||||
'ppm',
|
||||
'mmcmp'
|
||||
];
|
||||
/*
|
||||
https://github.com/sindresorhus/file-type/blob/main/supported.js
|
||||
https://github.com/sindresorhus/file-type/blob/main/core.js
|
||||
|
|
|
@ -20,23 +20,24 @@ class I18n<T extends Record<string, any>> {
|
|||
// If a pattern is present in the string but not provided in args, it will not be replaced.
|
||||
// If `args` is not provided, no interpolation is performed.
|
||||
public t(key: string, args?: Record<string, string | number>): string {
|
||||
let str;
|
||||
try {
|
||||
// Resolve dot-delimited names as properties of objects.
|
||||
let str = key.split('.').reduce((o, i) => o[i], this.ts) as unknown as string;
|
||||
|
||||
// Perform string interpolation.
|
||||
if (args) {
|
||||
for (const [k, v] of Object.entries(args)) {
|
||||
str = str.replace(`{${k}}`, v.toString());
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
str = key.split('.').reduce((o, i) => o[i], this.ts) as unknown as string;
|
||||
} catch (err) {
|
||||
// This should normally not happen because of the English language fallback strings, see comment for ts member.
|
||||
console.warn(`missing localization '${key}'`);
|
||||
return key;
|
||||
}
|
||||
|
||||
// Perform string interpolation.
|
||||
if (args) {
|
||||
for (const [k, v] of Object.entries(args)) {
|
||||
str = str.replace(`{${k}}`, v.toString());
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -109,12 +109,13 @@ ChiptuneJsPlayer.prototype.load = function (input) {
|
|||
};
|
||||
|
||||
ChiptuneJsPlayer.prototype.play = function (buffer: ArrayBuffer) {
|
||||
this.unlock();
|
||||
this.stop();
|
||||
const processNode = this.createLibopenmptNode(buffer, this.buffer);
|
||||
if (processNode === null) {
|
||||
return;
|
||||
}
|
||||
libopenmpt._openmpt_module_set_repeat_count(processNode.modulePtr, this.config.repeatCount || 1);
|
||||
libopenmpt._openmpt_module_set_repeat_count(processNode.modulePtr, this.config.repeatCount || 0);
|
||||
this.currentPlayingNode = processNode;
|
||||
processNode.connect(this.context);
|
||||
this.context.connect(this.audioContext.destination);
|
||||
|
|
101
yarn.lock
101
yarn.lock
|
@ -1051,6 +1051,25 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@mapbox/node-pre-gyp@npm:^1.0.10":
|
||||
version: 1.0.10
|
||||
resolution: "@mapbox/node-pre-gyp@npm:1.0.10"
|
||||
dependencies:
|
||||
detect-libc: ^2.0.0
|
||||
https-proxy-agent: ^5.0.0
|
||||
make-dir: ^3.1.0
|
||||
node-fetch: ^2.6.7
|
||||
nopt: ^5.0.0
|
||||
npmlog: ^5.0.1
|
||||
rimraf: ^3.0.2
|
||||
semver: ^7.3.5
|
||||
tar: ^6.1.11
|
||||
bin:
|
||||
node-pre-gyp: bin/node-pre-gyp
|
||||
checksum: 1a98db05d955b74dad3814679593df293b9194853698f3f5f1ed00ecd93128cdd4b14fb8767fe44ac6981ef05c23effcfdc88710e7c1de99ccb6f647890597c8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@microsoft/api-extractor-model@npm:7.23.3":
|
||||
version: 7.23.3
|
||||
resolution: "@microsoft/api-extractor-model@npm:7.23.3"
|
||||
|
@ -1203,6 +1222,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@phc/format@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "@phc/format@npm:1.0.0"
|
||||
checksum: 15ee02504fbc16590923d89b1f1c2f5892df27cf2bf19180e5678511413e87b6e5355815a092749cd01698855ee5a0fc5d2393951c727acd650934eed290e26e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@redis/bloom@npm:1.0.2":
|
||||
version: 1.0.2
|
||||
resolution: "@redis/bloom@npm:1.0.2"
|
||||
|
@ -3177,6 +3203,16 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"are-we-there-yet@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "are-we-there-yet@npm:2.0.0"
|
||||
dependencies:
|
||||
delegates: ^1.0.0
|
||||
readable-stream: ^3.6.0
|
||||
checksum: 6c80b4fd04ecee6ba6e737e0b72a4b41bdc64b7d279edfc998678567ff583c8df27e27523bc789f2c99be603ffa9eaa612803da1d886962d2086e7ff6fa90c7c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"are-we-there-yet@npm:^3.0.0":
|
||||
version: 3.0.1
|
||||
resolution: "are-we-there-yet@npm:3.0.1"
|
||||
|
@ -3194,6 +3230,17 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"argon2@npm:^0.30.2":
|
||||
version: 0.30.2
|
||||
resolution: "argon2@npm:0.30.2"
|
||||
dependencies:
|
||||
"@mapbox/node-pre-gyp": ^1.0.10
|
||||
"@phc/format": ^1.0.0
|
||||
node-addon-api: ^5.0.0
|
||||
checksum: 5b0d680d2bb482ed5f46ae2933ff2dc5c1d5d2a984a5c81c63cb311b55a5f67393e2b6da1adf4a1342e146580dd3f888a695d1c56834df710a141c62e9f73ef7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"argparse@npm:^1.0.7, argparse@npm:~1.0.9":
|
||||
version: 1.0.10
|
||||
resolution: "argparse@npm:1.0.10"
|
||||
|
@ -4995,7 +5042,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"color-support@npm:^1.1.3":
|
||||
"color-support@npm:^1.1.2, color-support@npm:^1.1.3":
|
||||
version: 1.1.3
|
||||
resolution: "color-support@npm:1.1.3"
|
||||
bin:
|
||||
|
@ -5191,7 +5238,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"console-control-strings@npm:^1.1.0":
|
||||
"console-control-strings@npm:^1.0.0, console-control-strings@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "console-control-strings@npm:1.1.0"
|
||||
checksum: 8755d76787f94e6cf79ce4666f0c5519906d7f5b02d4b884cf41e11dcd759ed69c57da0670afd9236d229a46e0f9cf519db0cd829c6dca820bb5a5c3def584ed
|
||||
|
@ -7842,6 +7889,7 @@ __metadata:
|
|||
"@types/gulp": 4.0.9
|
||||
"@types/gulp-rename": 2.0.1
|
||||
"@typescript-eslint/parser": ^5.46.1
|
||||
argon2: ^0.30.2
|
||||
cross-env: 7.0.3
|
||||
cypress: 10.3.0
|
||||
execa: 5.1.1
|
||||
|
@ -8022,6 +8070,23 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"gauge@npm:^3.0.0":
|
||||
version: 3.0.2
|
||||
resolution: "gauge@npm:3.0.2"
|
||||
dependencies:
|
||||
aproba: ^1.0.3 || ^2.0.0
|
||||
color-support: ^1.1.2
|
||||
console-control-strings: ^1.0.0
|
||||
has-unicode: ^2.0.1
|
||||
object-assign: ^4.1.1
|
||||
signal-exit: ^3.0.0
|
||||
string-width: ^4.2.3
|
||||
strip-ansi: ^6.0.1
|
||||
wide-align: ^1.1.2
|
||||
checksum: 81296c00c7410cdd48f997800155fbead4f32e4f82109be0719c63edc8560e6579946cc8abd04205297640691ec26d21b578837fd13a4e96288ab4b40b1dc3e9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"gauge@npm:^4.0.3":
|
||||
version: 4.0.4
|
||||
resolution: "gauge@npm:4.0.4"
|
||||
|
@ -11515,7 +11580,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"make-dir@npm:^3.0.0":
|
||||
"make-dir@npm:^3.0.0, make-dir@npm:^3.1.0":
|
||||
version: 3.1.0
|
||||
resolution: "make-dir@npm:3.1.0"
|
||||
dependencies:
|
||||
|
@ -12326,7 +12391,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-fetch@npm:2.6.7, node-fetch@npm:^2.6.1":
|
||||
"node-fetch@npm:2.6.7, node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.7":
|
||||
version: 2.6.7
|
||||
resolution: "node-fetch@npm:2.6.7"
|
||||
dependencies:
|
||||
|
@ -12567,6 +12632,18 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"npmlog@npm:^5.0.1":
|
||||
version: 5.0.1
|
||||
resolution: "npmlog@npm:5.0.1"
|
||||
dependencies:
|
||||
are-we-there-yet: ^2.0.0
|
||||
console-control-strings: ^1.1.0
|
||||
gauge: ^3.0.0
|
||||
set-blocking: ^2.0.0
|
||||
checksum: 516b2663028761f062d13e8beb3f00069c5664925871a9b57989642ebe09f23ab02145bf3ab88da7866c4e112cafff72401f61a672c7c8a20edc585a7016ef5f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"npmlog@npm:^6.0.0":
|
||||
version: 6.0.2
|
||||
resolution: "npmlog@npm:6.0.2"
|
||||
|
@ -15329,6 +15406,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.7":
|
||||
version: 3.0.7
|
||||
resolution: "signal-exit@npm:3.0.7"
|
||||
checksum: a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3":
|
||||
version: 3.0.3
|
||||
resolution: "signal-exit@npm:3.0.3"
|
||||
|
@ -15336,13 +15420,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"signal-exit@npm:^3.0.7":
|
||||
version: 3.0.7
|
||||
resolution: "signal-exit@npm:3.0.7"
|
||||
checksum: a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"simple-concat@npm:^1.0.0":
|
||||
version: 1.0.1
|
||||
resolution: "simple-concat@npm:1.0.1"
|
||||
|
@ -17771,7 +17848,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"wide-align@npm:^1.1.5":
|
||||
"wide-align@npm:^1.1.2, wide-align@npm:^1.1.5":
|
||||
version: 1.1.5
|
||||
resolution: "wide-align@npm:1.1.5"
|
||||
dependencies:
|
||||
|
|
Loading…
Reference in a new issue