From 301690b2c2473574019a291c72f4183f5b867b27 Mon Sep 17 00:00:00 2001 From: Wang Jiefan Date: Sun, 16 Apr 2023 16:12:30 +0800 Subject: [PATCH 01/21] done --- playground/src/json_view.tsx | 8 +++- playground/src/lrc.ts | 17 +++++++- src/constants.ts | 14 +++++++ src/index.ts | 19 ++++++++- src/parse.ts | 78 ++++++++++++++++++++++++++++-------- tsconfig.json | 5 ++- 6 files changed, 116 insertions(+), 25 deletions(-) diff --git a/playground/src/json_view.tsx b/playground/src/json_view.tsx index 1ecf3a7..0aa781d 100644 --- a/playground/src/json_view.tsx +++ b/playground/src/json_view.tsx @@ -1,7 +1,7 @@ import { useMemo, useDeferredValue } from 'react'; import styled from 'styled-components'; import JsonView from 'react-json-view'; -import { parse } from 'clrc'; +import { LineType, LyricLine, parse } from 'clrc'; const Style = styled.div` padding: 10px; @@ -15,7 +15,11 @@ const Style = styled.div` const Wrapper = ({ lrc }: { lrc: string }) => { const deferedLrc = useDeferredValue(lrc); - const parsed = useMemo(() => parse(deferedLrc), [deferedLrc]); + const parsed = useMemo(() => parse(deferedLrc), [deferedLrc]).sort((a,b)=>{ + const keyA = a.type === LineType.LYRIC || a.type === LineType.LYRIC_EXT ? (a as LyricLine).startMillisecond : 0; + const keyB = b.type === LineType.LYRIC || b.type === LineType.LYRIC_EXT ? (b as LyricLine).startMillisecond : 0; + return keyA - keyB; + }); return ( diff --git a/playground/src/json_view.tsx b/playground/src/json_view.tsx index 1ecf3a7..19afb24 100644 --- a/playground/src/json_view.tsx +++ b/playground/src/json_view.tsx @@ -13,9 +13,12 @@ const Style = styled.div` background-color: rgb(222 222 222 / 0.2); `; -const Wrapper = ({ lrc }: { lrc: string }) => { +const Wrapper = ({ lrc, enhanced }: { lrc: string; enhanced: boolean }) => { const deferedLrc = useDeferredValue(lrc); - const parsed = useMemo(() => parse(deferedLrc), [deferedLrc]); + const parsed = useMemo( + () => parse(deferedLrc, { enhanced }), + [deferedLrc, enhanced] + ); return ( + ); +} + +export default Option; diff --git a/src/constants.ts b/src/constants.ts index 7c3a298..523e6cc 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,9 +1,9 @@ // eslint-disable-next-line no-shadow export enum LineType { INVALID = 'invalid', - LYRIC = 'lyric', - LYRIC_ENH = 'lyric_enhanced', METADATA = 'metadata', + LYRIC = 'lyric', + LYRIC_ENHANCED = 'lyric_enhanced', } export interface Line { @@ -36,7 +36,7 @@ export interface Syllable { } export interface LyricExtLine extends Line { - type: LineType.LYRIC_ENH; + type: LineType.LYRIC_ENHANCED; startMillisecond: number; content: string; syllables: Syllable[]; diff --git a/src/parse.ts b/src/parse.ts index 73cb5fe..f3cd0f7 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -91,7 +91,7 @@ function parse( ) ); parsedLines.push({ - type: LineType.LYRIC_ENH, + type: LineType.LYRIC_ENHANCED, lineNumber: parsedLines.length, raw, content: strippedExt, From f6085e735b43bc2767cb3d9b0e96be58c1562c78 Mon Sep 17 00:00:00 2001 From: Wang Jiefan Date: Tue, 25 Apr 2023 21:51:45 +0800 Subject: [PATCH 13/21] add tostring & playground --- playground/src/app.tsx | 39 ++++++++++++++++++++++++++++++++++++--- src/index.ts | 2 ++ src/toString.ts | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 src/toString.ts diff --git a/playground/src/app.tsx b/playground/src/app.tsx index 802d469..1165b23 100644 --- a/playground/src/app.tsx +++ b/playground/src/app.tsx @@ -1,9 +1,10 @@ import React, { useState } from 'react'; import styled from 'styled-components'; -import demoLrc from './lrc'; +import Github from './github'; import GlobalStyle from './global_style'; import JsonView from './json_view'; -import Github from './github'; +import demoLrc from './lrc'; +import { parse, toString } from 'clrc'; const Style = styled.div` position: absolute; @@ -28,16 +29,48 @@ const Textarea = styled.textarea` border: 1px solid rgb(222, 222, 222); `; +const Button = styled.button` + padding: 5px 10px; + border: 1px solid #333; + border-radius: 5px; + background-color: #fff; + cursor: pointer; + font-size: 20px; + max-height: 50px; +`; + +const Box = styled.div` + display: flex; + flex: 1; + flex-direction: column; + gap: 10px; + padding: 10px; +`; + const App = () => { const [lrc, setLrc] = useState(demoLrc); const onLrcChange = (event: React.ChangeEvent) => setLrc(event.target.value); + const reset = () => setLrc(demoLrc); + + const regenerate = () => { + setLrc(toString(parse(lrc))); + }; + return ( <> diff --git a/src/index.ts b/src/index.ts index efd5c63..5a6e7e0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,6 +8,7 @@ import { Options, } from './constants'; import parse from './parse'; +import toString from './toString'; export { parse, @@ -18,4 +19,5 @@ export { Syllable, LyricExtLine, Options, + toString, }; diff --git a/src/toString.ts b/src/toString.ts new file mode 100644 index 0000000..4b84779 --- /dev/null +++ b/src/toString.ts @@ -0,0 +1,34 @@ +import { Line, LineType, LyricExtLine, LyricLine } from './constants'; + +function toMMSSmmm(millisecond: number) { + let minutes: String | number = Math.floor(millisecond / 60000); + let seconds: String | number = Math.floor((millisecond % 60000) / 1000); + let ms: String | number = Math.round(millisecond % 1000); + + minutes = `0${minutes}`.slice(-2); + seconds = `0${seconds}`.slice(-2); + ms = `000${ms}`.slice(-3); + return `${minutes}:${seconds}.${ms}`; +} + +export default function toString(lrc: Line[]) { + const strings = lrc.map((line) => { + if (line.type === LineType.LYRIC_ENH) { + const lineExt = line as LyricExtLine; + const content = []; + content.push(`[${toMMSSmmm(lineExt.startMillisecond)}]`); + content.push(lineExt.syllables[0].content); + lineExt.syllables.forEach((syl, i) => { + if (i === 0) return; + content.push(`<${toMMSSmmm(syl.startMillisecond)}>${syl.content}`); + }); + return content.join(''); + } + if (line.type === LineType.LYRIC) { + const lineN = line as LyricLine; + return `[${toMMSSmmm(lineN.startMillisecond)}]${lineN.content}`; + } + return line.raw; + }); + return strings.join('\n'); +} From d35862f3f2123ac6d944c3329ad192c30c6b17aa Mon Sep 17 00:00:00 2001 From: Wang Jiefan Date: Tue, 25 Apr 2023 22:16:20 +0800 Subject: [PATCH 14/21] add functions toString and expand --- playground/src/app.tsx | 34 +++++++++++++++------------------- playground/src/lrc.ts | 33 +++++++++++++++++++++------------ playground/src/option.tsx | 2 +- src/expand.ts | 6 ++++++ src/index.ts | 2 ++ src/toString.ts | 16 ++++++++++++++-- 6 files changed, 59 insertions(+), 34 deletions(-) create mode 100644 src/expand.ts diff --git a/playground/src/app.tsx b/playground/src/app.tsx index 8f8b99e..dcd89b4 100644 --- a/playground/src/app.tsx +++ b/playground/src/app.tsx @@ -1,10 +1,10 @@ +import { expand } from 'clrc'; import React, { useState } from 'react'; import styled from 'styled-components'; import Github from './github'; import GlobalStyle from './global_style'; import JsonView from './json_view'; import demoLrc from './lrc'; -import { parse, toString } from 'clrc'; import Option from './option'; const Style = styled.div` @@ -22,6 +22,12 @@ const Style = styled.div` display: flex; flex-direction: column; + + > .toolbar { + display: flex; + align-items: center; + gap: 10px; + } } `; const Textarea = styled.textarea` @@ -44,29 +50,21 @@ const Button = styled.button` border-radius: 5px; background-color: #fff; cursor: pointer; - font-size: 20px; max-height: 50px; `; -const Box = styled.div` - display: flex; - flex: 1; - flex-direction: column; - gap: 10px; - padding: 10px; -`; - const App = () => { const [enhanced, setEnhanced] = useState(false); const [lrc, setLrc] = useState(demoLrc); + const onLrcChange = (event: React.ChangeEvent) => setLrc(event.target.value); const reset = () => setLrc(demoLrc); - const regenerate = () => { - setLrc(toString(parse(lrc))); + const handleExpand = () => { + setLrc(expand(lrc)); }; return ( @@ -74,14 +72,12 @@ const App = () => { diff --git a/playground/src/lrc.ts b/playground/src/lrc.ts index 9093991..319838e 100644 --- a/playground/src/lrc.ts +++ b/playground/src/lrc.ts @@ -30,18 +30,27 @@ const lrc_old = `something wrong [03:59.39]Come away with me, it's gonna be all right you'll see, you'll see [04:24.46]Come away with me`; -const lrc = `basking in the glow +const lrc = `Gold [by:nafeij] -[ar:Oso Oso] -[00:32.79][00:56.00]Little <00:33.58>jagged <00:34.14>edge<00:35.25> -[00:35.50]I'm <00:35.89>leaning <00:36.80>in <00:37.59>again<00:38.46> -[00:38.71]I <00:39.19>felt <00:39.53>it <00:39.85>when <00:40.37>you <00:40.81>said<00:41.58> -[00:41.83]That I am just a mess -[00:44.89]There was nothing I could do -[00:48.40]But <00:48.70>see <00:49.04>your <00:49.40>point <00:49.82>of <00:50.10>view<00:51.00> -[00:44.89] -[00:51.25]<00:52.03> -[00:52.28]And that's <00:52.98>the <00:53.19>truth<00:54.08> -[00:54.33]<00:55.67>`; +[ar:Chet Faker] +[00:19.51][00:33.09][00:39.72][00:46.53][01:55.07][02:09.05][03:17.35][03:31.19][03:44.95]You <00:19.73>gotta <00:20.18>know, <00:21.29>I'm <00:21.55>feeling <00:22.19>love <00:22.66> +[00:23.13][00:50.18][01:58.75][02:12.41][03:20.93][03:34.72][03:48.43]Made <00:23.43>of <00:23.59>gold, <00:24.71>I <00:24.90>never <00:25.29>loved <00:25.61>a <00:25.89> +[00:26.59][00:53.45][02:02.14][02:15.78][03:24.38][03:37.94][03:51.83]Another <00:27.04>one, <00:28.18>another <00:28.87>you <00:29.45> +[00:29.88][00:56.94][02:05.51][02:19.26][03:27.74][03:41.58][03:55.38]It's <00:30.07>gotta <00:30.56>be <00:31.70>love <00:32.00>I <00:32.17>said <00:32.55>it<00:33.07> +[01:00.68]I <01:00.91>might <01:01.15>as <01:01.39>well <01:01.60>be <01:01.81>in <01:02.01>a <01:02.25>garden <01:06.26> +[01:06.26]I <01:06.51>said, <01:06.71>ah <01:07.17> +[01:07.53]A <01:07.62>smell <01:08.00>in <01:08.18>the <01:08.44>air <01:09.17>is <01:09.33>a <01:09.46>dripping <01:09.95>rose<01:10.41> +[01:10.41](You <01:10.92>could <01:11.23>be <01:11.70>the <01:12.07>one <01:12.72>for <01:12.94>me) <01:13.73> +[01:14.41]Another <01:14.96>soul <01:15.27>to <01:15.45>meet <01:15.72>my <01:16.01>void <01:16.60>then <01:17.82> +[01:18.14][03:58.09]♪ <01:20.62> +[01:21.33]Of <01:21.49>anything <01:22.14>bare <01:22.83>that's <01:23.08>made <01:23.71>of <01:23.93>gold <01:26.12> +[01:28.10][02:50.05]A <01:28.39>physical <01:29.16>kiss <01:29.91>is <01:30.07>nothing <01:30.68>without <01:31.38>it <01:32.29> +[01:35.01][02:56.76]And <01:35.25>you <01:35.44>close <01:35.76>your <01:36.03>eyes <01:36.77>to <01:36.94>see <01:37.19>what <01:37.51>it's <01:37.79>done <01:38.72> +[01:41.84][03:03.51]The <01:42.06>body <01:42.66>that <01:42.84>lies <01:43.51>is <01:43.81>built <01:44.06>up <01:44.29>on <01:44.66>looking <01:45.64> +[01:48.71][03:10.55]'Cause <01:48.96>all <01:49.23>that <01:49.46>remains <01:50.44>before <01:50.94>it's <01:51.25>begun <01:52.79> +[02:23.02]A <02:23.21>heart <02:23.47>will <02:23.64>swell <02:24.12>before <02:24.75>it's <02:25.14>hardened <02:26.57> +[02:29.65]With <02:29.86>the <02:29.97>flick <02:30.28>of <02:30.46>the <02:30.78>hair, <02:31.24>it <02:31.46>can <02:31.86>make <02:32.14>you <02:32.53>old <02:33.26> +[02:37.19]Another <02:37.72>hole <02:38.04>to <02:38.24>dig <02:38.49>my <02:38.69>soul <02:39.32>in <02:39.85> +[02:43.31]I'll <02:43.59>leave <02:43.82>anything <02:44.47>bare <02:45.04>that <02:45.28>keeps <02:45.80>me <02:46.25>soul <02:47.81>`; export default lrc; diff --git a/playground/src/option.tsx b/playground/src/option.tsx index cc7cb97..d07389c 100644 --- a/playground/src/option.tsx +++ b/playground/src/option.tsx @@ -2,7 +2,7 @@ import React from 'react'; import styled from 'styled-components'; const Style = styled.div` - padding: 10px 20px; + padding: 10px 10px 10px 20px; `; function Option({ diff --git a/src/expand.ts b/src/expand.ts new file mode 100644 index 0000000..43f7ba3 --- /dev/null +++ b/src/expand.ts @@ -0,0 +1,6 @@ +import parse from './parse'; +import toString from './toString'; + +export default function expand(lrc: string) { + return toString(parse(lrc)); +} diff --git a/src/index.ts b/src/index.ts index 5a6e7e0..7865211 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,6 +9,7 @@ import { } from './constants'; import parse from './parse'; import toString from './toString'; +import expand from './expand'; export { parse, @@ -20,4 +21,5 @@ export { LyricExtLine, Options, toString, + expand, }; diff --git a/src/toString.ts b/src/toString.ts index 4b84779..8b3710c 100644 --- a/src/toString.ts +++ b/src/toString.ts @@ -11,9 +11,21 @@ function toMMSSmmm(millisecond: number) { return `${minutes}:${seconds}.${ms}`; } +type Timed = LyricLine | LyricExtLine; +function isTimed(line: Line): line is Timed { + return line.type === LineType.LYRIC || line.type === LineType.LYRIC_ENHANCED; +} + +function selectiveSort(a: Line, b: Line) { + if (isTimed(a) && isTimed(b)) { + return a.startMillisecond - b.startMillisecond; + } + return 0; +} + export default function toString(lrc: Line[]) { - const strings = lrc.map((line) => { - if (line.type === LineType.LYRIC_ENH) { + const strings = lrc.sort(selectiveSort).map((line) => { + if (line.type === LineType.LYRIC_ENHANCED) { const lineExt = line as LyricExtLine; const content = []; content.push(`[${toMMSSmmm(lineExt.startMillisecond)}]`); From 2a2d721470e6efc4c616bf42abc8be3c3e855df8 Mon Sep 17 00:00:00 2001 From: Wang Jiefan Date: Tue, 25 Apr 2023 22:30:00 +0800 Subject: [PATCH 15/21] Update README.md --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 902fe8f..7e4fdfc 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,15 @@ The output is: ### parse(lrcString) -parse lrc string to array. +parse lrc string to Line array. + +### toString(Line[]) + +parse Line array back into valid lrc string. + +### expand(lrcString) + +returns new lrc string with all repeating lyrics inserted into separate lines. ### LineType @@ -119,7 +127,7 @@ types of line: - `LineType.INVALID` means it's invalid line - `LineType.LYRIC` means it's lyric line - `LineType.METADATA` means it's metadata line -- `LineType.LYRIC_ENH` means it's lyric with inline enhanced lrc tags. +- `LineType.LYRIC_ENHANCED` means it's lyric with inline enhanced lrc tags. ## License From c955ce2227429b19345bbf6acfe76db320bddc0b Mon Sep 17 00:00:00 2001 From: Wang Jiefan Date: Tue, 25 Apr 2023 22:31:40 +0800 Subject: [PATCH 16/21] Update README.md --- README.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7e4fdfc..4957617 100644 --- a/README.md +++ b/README.md @@ -44,11 +44,11 @@ The output is: "value": "Oso Oso" }, { - "type": "lyric", + "type": "lyric_enhanced", "lineNumber": 2, "raw": "[00:32.79][00:56.00]Little <00:33.58>jagged <00:34.14>edge<00:35.25>", + "content": "Little jagged edge", "startMillisecond": 32790, - "content": "Little <00:33.58>jagged <00:34.14>edge<00:35.25>", "syllables": [ { "sylNumber": 0, @@ -67,15 +67,54 @@ The output is: "raw": "<00:34.14>edge", "startMillisecond": 34140, "content": "edge" + }, + { + "sylNumber": 3, + "raw": "<00:35.25>", + "startMillisecond": 35250, + "content": "" } ] }, { - "type": "lyric", + "type": "lyric_enhanced", "lineNumber": 3, + "raw": "[00:32.79][00:56.00]Little <00:33.58>jagged <00:34.14>edge<00:35.25>", + "content": "Little jagged edge", + "startMillisecond": 56000, + "syllables": [ + { + "sylNumber": 0, + "raw": "Little ", + "startMillisecond": 56000, + "content": "Little " + }, + { + "sylNumber": 1, + "raw": "<00:33.58>jagged ", + "startMillisecond": 56790, + "content": "jagged " + }, + { + "sylNumber": 2, + "raw": "<00:34.14>edge", + "startMillisecond": 57350, + "content": "edge" + }, + { + "sylNumber": 3, + "raw": "<00:35.25>", + "startMillisecond": 58460, + "content": "" + } + ] + }, + { + "type": "lyric_enhanced", + "lineNumber": 4, "raw": "[00:35.50]I'm <00:35.89>leaning <00:36.80>in <00:37.59>again<00:38.46>", + "content": "I'm leaning in again", "startMillisecond": 35500, - "content": "I'm <00:35.89>leaning <00:36.80>in <00:37.59>again<00:38.46>", "syllables": [ { "sylNumber": 0, @@ -100,6 +139,12 @@ The output is: "raw": "<00:37.59>again", "startMillisecond": 37590, "content": "again" + }, + { + "sylNumber": 4, + "raw": "<00:38.46>", + "startMillisecond": 38460, + "content": "" } ] } From e5fe2ac83e0d0213439599f8bac3fce429866571 Mon Sep 17 00:00:00 2001 From: mebtte Date: Wed, 26 Apr 2023 18:23:25 +0800 Subject: [PATCH 17/21] remove expand --- playground/src/app.tsx | 28 +--------------------------- src/expand.ts | 6 ------ src/index.ts | 2 -- 3 files changed, 1 insertion(+), 35 deletions(-) delete mode 100644 src/expand.ts diff --git a/playground/src/app.tsx b/playground/src/app.tsx index dcd89b4..ee771d6 100644 --- a/playground/src/app.tsx +++ b/playground/src/app.tsx @@ -1,4 +1,3 @@ -import { expand } from 'clrc'; import React, { useState } from 'react'; import styled from 'styled-components'; import Github from './github'; @@ -22,12 +21,6 @@ const Style = styled.div` display: flex; flex-direction: column; - - > .toolbar { - display: flex; - align-items: center; - gap: 10px; - } } `; const Textarea = styled.textarea` @@ -44,15 +37,6 @@ const Textarea = styled.textarea` border: 1px solid rgb(222, 222, 222); `; -const Button = styled.button` - padding: 5px 10px; - border: 1px solid #333; - border-radius: 5px; - background-color: #fff; - cursor: pointer; - max-height: 50px; -`; - const App = () => { const [enhanced, setEnhanced] = useState(false); @@ -61,22 +45,12 @@ const App = () => { const onLrcChange = (event: React.ChangeEvent) => setLrc(event.target.value); - const reset = () => setLrc(demoLrc); - - const handleExpand = () => { - setLrc(expand(lrc)); - }; - return ( <> diff --git a/playground/src/json_view.tsx b/playground/src/json_view.tsx index 2db1b18..2fe470a 100644 --- a/playground/src/json_view.tsx +++ b/playground/src/json_view.tsx @@ -1,7 +1,7 @@ -import { useMemo, useDeferredValue } from 'react'; +import { useMemo, useEffect } from 'react'; import styled from 'styled-components'; import JsonView from 'react-json-view'; -import { parse, parseEnhanced } from 'clrc'; +import { parse, parseEnhanced, stringify } from 'clrc'; const Style = styled.div` padding: 10px; @@ -14,12 +14,17 @@ const Style = styled.div` `; const Wrapper = ({ lrc, enhanced }: { lrc: string; enhanced: boolean }) => { - const deferedLrc = useDeferredValue(lrc); const parsed = useMemo( - () => (enhanced ? parseEnhanced(deferedLrc) : parse(deferedLrc)), - [deferedLrc, enhanced] + () => (enhanced ? parseEnhanced(lrc) : parse(lrc)), + [lrc, enhanced] ); + useEffect(() => { + console.group('stringify from parsed result'); + console.log(stringify(parsed)); + console.groupEnd(); + }, [parsed]); + return (