Compare commits
671 Commits
v0.0.4
...
7155fef677
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7155fef677 | ||
|
|
0566ae78e7 | ||
|
|
cf439d60c4 | ||
|
|
6127c67cd3 | ||
|
|
183eb063bb | ||
|
|
532609c57d | ||
|
|
83c6979973 | ||
|
|
8bea9336ae | ||
|
|
617a6a0b8e | ||
|
|
140ab772d0 | ||
|
|
e7d8c8814d | ||
|
|
588559c645 | ||
|
|
e7411d25b4 | ||
|
|
ed0bd88e3b | ||
|
|
b4fb2d058a | ||
|
|
4058b425c8 | ||
|
|
55d872a38c | ||
|
|
1f666d402d | ||
|
|
64dec36773 | ||
|
|
0b3df59015 | ||
|
|
e1b4f8ede9 | ||
|
|
b8c3f0a464 | ||
|
|
ee714c855b | ||
|
|
c5165c752d | ||
|
|
8c275c2edb | ||
|
|
2fc6f673d3 | ||
|
|
b7c9d12e41 | ||
|
|
8ca2934e8d | ||
|
|
868a2a1940 | ||
|
|
a1e57c5b9c | ||
|
|
b7a76e8f10 | ||
|
|
e82159b9a2 | ||
|
|
35d068e109 | ||
|
|
136e1a3774 | ||
|
|
fc6c6adfce | ||
|
|
5981d97d5f | ||
|
|
42c12b3bf9 | ||
|
|
39a8d0d741 | ||
|
|
1db0609961 | ||
|
|
26d60cecbf | ||
|
|
a97ec33c07 | ||
|
|
9a1bc0f449 | ||
|
|
e4d1024082 | ||
|
|
3d5f97f635 | ||
|
|
7c82766549 | ||
|
|
cfe91e0782 | ||
|
|
9a26683a76 | ||
|
|
8a9344e3ee | ||
|
|
d200c7cf09 | ||
|
|
eb01d62e53 | ||
|
|
f65f375283 | ||
|
|
08979d2079 | ||
|
|
c6efe07303 | ||
|
|
7294f0ca6d | ||
|
|
eac1c09149 | ||
|
|
1e9cd61eba | ||
|
|
7b7f341fa0 | ||
|
|
ac806b49b2 | ||
|
|
f20636a107 | ||
|
|
787a30e6f7 | ||
|
|
d1d217be18 | ||
|
|
944d0a371a | ||
|
|
0df03e0c9c | ||
|
|
7ffdf65705 | ||
|
|
89cdf91a48 | ||
|
|
43ebc27044 | ||
|
|
e6159555f3 | ||
|
|
1f2508aae9 | ||
|
|
ad13f58fa7 | ||
|
|
de4959d49f | ||
|
|
b5b75129e7 | ||
|
|
84346a486f | ||
|
|
3bdcddf5a2 | ||
|
|
98f68a5e14 | ||
|
|
2249b86af3 | ||
|
|
fd889922d8 | ||
|
|
8db7c6e320 | ||
|
|
5bc4ed6dfd | ||
|
|
22ad5f7fea | ||
|
|
c0369c1a14 | ||
|
|
322f4a3ca5 | ||
|
|
4e32453441 | ||
|
|
66725b8a64 | ||
|
|
f7bcbbca83 | ||
|
|
07a3b33040 | ||
|
|
2f9b4582f8 | ||
|
|
c3f63c58cf | ||
|
|
4a3529bc2e | ||
|
|
b0355a919f | ||
|
|
cfe1a0b4b9 | ||
|
|
b655e98f35 | ||
|
|
2d1021bc42 | ||
|
|
33d74999b9 | ||
|
|
84b7dd7a3c | ||
|
|
0c678fbda3 | ||
|
|
3486f7d050 | ||
|
|
d42a1010b8 | ||
|
|
ece6ceea45 | ||
|
|
b22ebb399e | ||
|
|
4431b10cb7 | ||
|
|
01a0c929e8 | ||
|
|
b06f6e8d09 | ||
|
|
753227acbb | ||
|
|
c7dd9091d0 | ||
|
|
bae20ce011 | ||
|
|
8da4759668 | ||
|
|
eb7c6d91e9 | ||
|
|
3c24dfe8a6 | ||
|
|
bb916daaaf | ||
|
|
3931e484c2 | ||
|
|
b67e258c31 | ||
|
|
1a7e6f5a43 | ||
|
|
437204dbe6 | ||
|
|
af105277d9 | ||
|
|
7efd327a36 | ||
|
|
0141586fa9 | ||
|
|
df1d8ccac6 | ||
|
|
10b6b95e4d | ||
|
|
a58e6f77bd | ||
|
|
fe2bd80ac6 | ||
|
|
870b44a973 | ||
|
|
48fd9ca7b2 | ||
|
|
14d03b7eb9 | ||
|
|
6f1db6c038 | ||
|
|
cd2d208e5c | ||
|
|
7d6ec72002 | ||
|
|
837cb6a978 | ||
|
|
aeeb0c08d7 | ||
|
|
72d8a7f485 | ||
|
|
5d3692c7a0 | ||
|
|
7e54231bef | ||
|
|
80a885dbf3 | ||
|
|
134c6bbb5f | ||
|
|
49a153adf7 | ||
|
|
99e15b0bda | ||
|
|
4de8a73af2 | ||
|
|
d104ba3180 | ||
|
|
abf0d4748f | ||
|
|
d2a9c44601 | ||
|
|
c269558bae | ||
|
|
cc22453a40 | ||
|
|
d525d92de4 | ||
|
|
2197dfe65c | ||
|
|
38ee00f474 | ||
|
|
8fdad41c71 | ||
|
|
f269995bb7 | ||
|
|
03a2db8c44 | ||
|
|
6d9cd3c6a8 | ||
|
|
303b2f7036 | ||
|
|
ec25c2ffd9 | ||
|
|
50ab608ddb | ||
|
|
3c76be9b81 | ||
|
|
ab7f0cf0b4 | ||
|
|
f9f590c4dc | ||
|
|
8d38fe582a | ||
|
|
dc4a26561d | ||
|
|
10c1d1f3a8 | ||
|
|
66bcf53d01 | ||
|
|
8ab4b7d693 | ||
|
|
ce2f097d32 | ||
|
|
f7575cd327 | ||
|
|
8634c6a211 | ||
|
|
b070013efc | ||
|
|
d2d9112f6c | ||
|
|
9fea18f2de | ||
|
|
74480f91ce | ||
|
|
b2e13b631f | ||
|
|
001d995c8f | ||
|
|
8cb2acea88 | ||
|
|
7c0d57d84e | ||
|
|
8cb875f449 | ||
|
|
e6bbe65723 | ||
|
|
f4a71a2476 | ||
|
|
47b9362b0a | ||
|
|
c1aad0806e | ||
|
|
4ccc90f9fb | ||
|
|
7dc63440e6 | ||
|
|
4094e8b80d | ||
|
|
e27cbaf715 | ||
|
|
1f39b27d79 | ||
|
|
f45891fd95 | ||
|
|
18fe644715 | ||
|
|
40cde8c69a | ||
|
|
4b0af47906 | ||
|
|
9365b3c8cd | ||
|
|
4b9f015ea7 | ||
|
|
c42d4a084e | ||
|
|
5bb3feb05b | ||
|
|
05f776ed8b | ||
|
|
9cec809485 | ||
|
|
429f909152 | ||
|
|
084dd23df1 | ||
|
|
e55afdd739 | ||
|
|
72128a132b | ||
|
|
92ca2cddad | ||
|
|
3db0d1dfe5 | ||
|
|
57907323e6 | ||
|
|
dbdca44c5f | ||
|
|
fe1dd2201f | ||
|
|
e0ae194cc3 | ||
|
|
6fc5700457 | ||
|
|
c4fdcf86d4 | ||
|
|
3088500c8d | ||
|
|
861f3a3624 | ||
|
|
c55783e4d9 | ||
|
|
955e284d41 | ||
|
|
fc4c47427e | ||
|
|
e2d7563faa | ||
|
|
27d69f7f8d | ||
|
|
a77bb5af44 | ||
|
|
00286261a4 | ||
|
|
0b898dccaa | ||
|
|
a1d9ac4e68 | ||
|
|
4150939e23 | ||
|
|
8f84b7f063 | ||
|
|
04b245ac64 | ||
|
|
12f7e62957 | ||
|
|
9600d310c7 | ||
|
|
dec5a2472a | ||
|
|
13eb7c6ea2 | ||
|
|
2356cfa10a | ||
|
|
3bfaefb3b0 | ||
|
|
78b8c25d96 | ||
|
|
c1d2ff2b96 | ||
|
|
24aee9446a | ||
|
|
2fb094ec31 | ||
|
|
53897c66ee | ||
|
|
ca4e266ae6 | ||
|
|
6612a1e16f | ||
|
|
55ceb65dfb | ||
|
|
6cad3d6afb | ||
|
|
151e1bdb8a | ||
|
|
44a3cfd1ff | ||
|
|
9cbc3028a7 | ||
|
|
8c30730d7b | ||
|
|
acfb870f9d | ||
|
|
3813528f50 | ||
|
|
e3bb014644 | ||
|
|
76a7afde76 | ||
|
|
1184f9f3f5 | ||
|
|
b754f8938f | ||
|
|
6b30ff04b7 | ||
|
|
1c40acca63 | ||
|
|
a5a7a8afaf | ||
|
|
583ac13a37 | ||
|
|
3e58972072 | ||
|
|
f15aa27727 | ||
|
|
2581014dbd | ||
|
|
baaaa1b57e | ||
|
|
160fbb3590 | ||
|
|
6f3253678c | ||
|
|
563ad66243 | ||
|
|
a8d002cc53 | ||
|
|
0615410fa4 | ||
|
|
fc98e065f8 | ||
|
|
66f671ffa0 | ||
|
|
69a35af456 | ||
|
|
e462bd0b4c | ||
|
|
ae6483427f | ||
|
|
ad97677104 | ||
|
|
996d15ef25 | ||
|
|
06de32ffe7 | ||
|
|
dd43074e46 | ||
|
|
93495e13db | ||
|
|
16950edae4 | ||
|
|
4af1203360 | ||
|
|
55b5bd1fd2 | ||
|
|
f0a7cf4ed0 | ||
|
|
62e7412abf | ||
|
|
275bf647d2 | ||
|
|
00af723be9 | ||
|
|
19da577836 | ||
|
|
bf3a2b469b | ||
|
|
bf31bfd099 | ||
|
|
d02fea99f2 | ||
|
|
2404bacb4e | ||
|
|
b6c274c181 | ||
|
|
f9b472aee7 | ||
|
|
45f277741b | ||
|
|
94179f59cd | ||
|
|
c7b550a3e3 | ||
|
|
fd51fd2387 | ||
|
|
23d1798ab6 | ||
|
|
90e81d0d4d | ||
|
|
6a7a19547d | ||
|
|
1550849ee2 | ||
|
|
15116e2197 | ||
|
|
63eda5179b | ||
|
|
d7b1277363 | ||
|
|
337c933b92 | ||
|
|
b01b2cc9c0 | ||
|
|
30069b2f33 | ||
|
|
c5bd57468c | ||
|
|
c050c65675 | ||
|
|
e1bd7e7563 | ||
|
|
cc129f6384 | ||
|
|
e7ea0c0ff0 | ||
|
|
9630d51c4c | ||
|
|
ceb140a4c2 | ||
|
|
fe8410ab98 | ||
|
|
00731cda93 | ||
|
|
c05979cb11 | ||
|
|
6e1a10e45c | ||
|
|
bd74dfdb26 | ||
|
|
b7c2fd3387 | ||
|
|
b65e41ca23 | ||
|
|
ec70eded14 | ||
|
|
dcf9047d82 | ||
|
|
cd85e9f65a | ||
|
|
066fd4fb77 | ||
|
|
9a6bb30e73 | ||
|
|
99d9f27618 | ||
|
|
02ddac6b17 | ||
|
|
017438ee50 | ||
|
|
d938982107 | ||
|
|
bdde1969f7 | ||
|
|
c8eb038190 | ||
|
|
2d90b79f73 | ||
|
|
f39d3baff5 | ||
|
|
84664ee272 | ||
|
|
d603216baf | ||
|
|
522873c7fb | ||
|
|
a6548f9941 | ||
|
|
3843dd88b2 | ||
|
|
baddb4e9d4 | ||
|
|
4aa51b51bd | ||
|
|
725494db7d | ||
|
|
292caa4158 | ||
|
|
29e9656919 | ||
|
|
78f4682efb | ||
|
|
fa090b0b66 | ||
|
|
32b7e9c3c2 | ||
|
|
4d3e069a81 | ||
|
|
3ed658a31c | ||
|
|
efb24798c8 | ||
|
|
e72e9027ef | ||
|
|
17c93fb716 | ||
|
|
a826666ad6 | ||
|
|
c8282cb66f | ||
|
|
592fd3940e | ||
|
|
7e9980b098 | ||
|
|
283ee06034 | ||
|
|
9a00693bb3 | ||
|
|
16906a46cd | ||
|
|
bdf017024a | ||
|
|
58ae1ef426 | ||
|
|
98e6544c25 | ||
|
|
1b57beeea6 | ||
|
|
1625a5f889 | ||
|
|
ae20e7fad7 | ||
|
|
fc594b12e0 | ||
|
|
0d25f32101 | ||
|
|
cfd4522036 | ||
|
|
f638d4aee0 | ||
|
|
b237b78300 | ||
|
|
ed2983c073 | ||
|
|
730227ac45 | ||
|
|
7fb4f41f01 | ||
|
|
d92e013413 | ||
|
|
980fd145d0 | ||
|
|
693734e12a | ||
|
|
cbeae9b40d | ||
|
|
4d0cc2c3b6 | ||
|
|
5dac578389 | ||
|
|
710718bcd6 | ||
|
|
21b4914817 | ||
|
|
77dde29f8d | ||
|
|
20e11c48ff | ||
|
|
dbe47aa223 | ||
|
|
b916553619 | ||
|
|
d752a0260b | ||
|
|
97cf7b1420 | ||
|
|
50e2377efb | ||
|
|
940cfcd9ed | ||
|
|
cfd36cdaf3 | ||
|
|
d3133388d9 | ||
|
|
05bfe68e92 | ||
|
|
d31aa51cf0 | ||
|
|
6d8918f431 | ||
|
|
a5b6856b81 | ||
|
|
68d9d7801a | ||
|
|
f51934d02a | ||
|
|
21a702e716 | ||
|
|
418f799b64 | ||
|
|
185ef743b5 | ||
|
|
54da9b3cf4 | ||
|
|
b5fed447dd | ||
|
|
9b88a21338 | ||
|
|
0c82cd4285 | ||
|
|
22e6c5338a | ||
|
|
8e4e3b5aa9 | ||
|
|
4b57b56189 | ||
|
|
77eab5e80c | ||
|
|
15dac35d31 | ||
|
|
d84c03c93d | ||
|
|
7e417c5bbe | ||
|
|
fe2471553d | ||
|
|
3e2a3cc4fd | ||
|
|
b9830f336e | ||
|
|
782c339c33 | ||
|
|
2c60a7d987 | ||
|
|
0e6f53a343 | ||
|
|
b0a85654ab | ||
|
|
8b6b5d2695 | ||
|
|
1fde087f7b | ||
|
|
82b41c4e81 | ||
|
|
d0f96b6193 | ||
|
|
8a8bbf8bca | ||
|
|
0430988482 | ||
|
|
781ce70210 | ||
|
|
fcca33e908 | ||
|
|
52b74d70c9 | ||
|
|
6d180a671d | ||
|
|
583185911b | ||
|
|
549f79edd0 | ||
|
|
30894ac172 | ||
|
|
62a2c63f9d | ||
|
|
0aef85998f | ||
|
|
3bb215ae94 | ||
|
|
7d9428de14 | ||
|
|
c85c9206f6 | ||
|
|
2b986c16b8 | ||
|
|
a5ae622a30 | ||
|
|
b3a724b22e | ||
|
|
7f96dcd2bf | ||
|
|
534cf1e3c9 | ||
|
|
3ec3f5d23c | ||
|
|
2241b9b936 | ||
|
|
e8fd6a81be | ||
|
|
8b4672b202 | ||
|
|
9734041777 | ||
|
|
e15fe67790 | ||
|
|
937a5996bf | ||
|
|
d6c87e8be7 | ||
|
|
606e29f56b | ||
|
|
45d5ad04eb | ||
|
|
85c4dcb4a5 | ||
|
|
b803857d65 | ||
|
|
8b029410cb | ||
|
|
33cdbc1552 | ||
|
|
67c125faaf | ||
|
|
d21db4b220 | ||
|
|
7bbf0eb566 | ||
|
|
2eeb7a28fe | ||
|
|
6ae39414f4 | ||
|
|
6a22769199 | ||
|
|
8b12886422 | ||
|
|
bde723b9bd | ||
|
|
0f3720296a | ||
|
|
df9c99d596 | ||
|
|
864a7c38b6 | ||
|
|
edadf0b6b5 | ||
|
|
54ba265510 | ||
|
|
dc44327d4c | ||
|
|
309b6449e6 | ||
|
|
7dd5b2fb8d | ||
|
|
f32147658f | ||
|
|
ba511d37ef | ||
|
|
4fa595547f | ||
|
|
72282b180f | ||
|
|
813686d66d | ||
|
|
f03369719d | ||
|
|
c639b012bc | ||
|
|
4144ce96c0 | ||
|
|
38eed8b66e | ||
|
|
0315bfae0c | ||
|
|
305b87265c | ||
|
|
847a790132 | ||
|
|
ab27798283 | ||
|
|
4b521f775c | ||
|
|
0f68ec0677 | ||
|
|
ed4409661f | ||
|
|
789d56b1a4 | ||
|
|
6d6c3ddcb5 | ||
|
|
c4441029f8 | ||
|
|
4285110233 | ||
|
|
4ad33a7ea8 | ||
|
|
b41c800bb0 | ||
|
|
c4592d5ca6 | ||
|
|
5e9dba5d58 | ||
|
|
2cfd140d4a | ||
|
|
13e421bfba | ||
|
|
1933727f89 | ||
|
|
4e16bfcc18 | ||
|
|
398ee831de | ||
|
|
6d01280039 | ||
|
|
8aaa701348 | ||
|
|
733a36571b | ||
|
|
f40ac28781 | ||
|
|
d73e95d2e5 | ||
|
|
770338a68a | ||
|
|
f58dafbde8 | ||
|
|
004712e851 | ||
|
|
4e53ed2cf8 | ||
|
|
92ccad6253 | ||
|
|
74c5e9bb09 | ||
|
|
2724d6b4d3 | ||
|
|
212e144422 | ||
|
|
205a1b82e7 | ||
|
|
44b4604581 | ||
|
|
3d3454b5a4 | ||
|
|
67f1b04b67 | ||
|
|
fd7d299e55 | ||
|
|
ada492f3f0 | ||
|
|
8a4e4fd32b | ||
|
|
86ced2a217 | ||
|
|
c62251dfe9 | ||
|
|
8bf0f5d36e | ||
|
|
a4b6567947 | ||
|
|
6c5c628bbf | ||
|
|
1d6593340d | ||
|
|
0d992d205f | ||
|
|
f82e79efd4 | ||
|
|
5cdb6b6f75 | ||
|
|
7316a022be | ||
|
|
dbd8a29b73 | ||
|
|
d6a5a02d68 | ||
|
|
9eef00b913 | ||
|
|
3b9dd4824b | ||
|
|
902c1ad39e | ||
|
|
b4f6dea97f | ||
|
|
c1c252f54a | ||
|
|
303b5f8847 | ||
|
|
26f55a463b | ||
|
|
0fa2c366dc | ||
|
|
bf1588e414 | ||
|
|
3be0f25dfc | ||
|
|
0e53028922 | ||
|
|
bc458647a3 | ||
|
|
6c5080394a | ||
|
|
30d45ca2c3 | ||
|
|
614aa3184f | ||
|
|
cacd28bd87 | ||
|
|
a3dfe86a04 | ||
|
|
79c65cab63 | ||
|
|
52237b9385 | ||
|
|
b8d22e92ff | ||
|
|
faac0e29b5 | ||
|
|
899afac910 | ||
|
|
7b2cbfefcc | ||
|
|
793e532240 | ||
|
|
68c7bd251e | ||
|
|
656f82df43 | ||
|
|
1571358f13 | ||
|
|
b6326cf36a | ||
|
|
0b7818a9d3 | ||
|
|
b479d6086b | ||
|
|
6d06491ddc | ||
|
|
e15c6d44a1 | ||
|
|
8ab656a097 | ||
|
|
cb8642b9a9 | ||
|
|
1761d398ee | ||
|
|
eb33fd57a8 | ||
|
|
317b0b373a | ||
|
|
563c8d0085 | ||
|
|
80ced70267 | ||
|
|
3a89b43435 | ||
|
|
d117095a5f | ||
|
|
d78c1bf861 | ||
|
|
20da8034a1 | ||
|
|
0d053a3462 | ||
|
|
280e540f4f | ||
|
|
824cfd23ed | ||
|
|
695728df2e | ||
|
|
24deca75d2 | ||
|
|
8a1184f161 | ||
|
|
d61096d1b1 | ||
|
|
3b9d1be002 | ||
|
|
13262f8f10 | ||
|
|
9f05fc4954 | ||
|
|
3fce06ef63 | ||
|
|
3d13f69e5c | ||
|
|
deb19c6223 | ||
|
|
7466127832 | ||
|
|
af982c5fe0 | ||
|
|
b03f0150d8 | ||
|
|
d61ddafb44 | ||
|
|
fd89a197a5 | ||
|
|
31fa29ee62 | ||
|
|
c7e28b2ad6 | ||
|
|
bbc1343079 | ||
|
|
c7d4fb270b | ||
|
|
fcccdee105 | ||
|
|
887072f6c7 | ||
|
|
1932edba21 | ||
|
|
0c15415822 | ||
|
|
b8dc0870b5 | ||
|
|
9d0ad2ae45 | ||
|
|
7278b9f48c | ||
|
|
1aee95492a | ||
|
|
0cff889f4b | ||
|
|
9cd05362ac | ||
|
|
269eccc7ef | ||
|
|
aafd02090b | ||
|
|
e0e43dbfa4 | ||
|
|
37c358a48b | ||
|
|
2b81f7a106 | ||
|
|
cb9b606cb4 | ||
|
|
c1879c6527 | ||
|
|
035c54b2fd | ||
|
|
54b152207c | ||
|
|
034e159442 | ||
|
|
8a2533c182 | ||
|
|
a35cc4cc19 | ||
|
|
75b0cc80e3 | ||
|
|
c39a0bf462 | ||
|
|
10f19366f0 | ||
|
|
36ed9025f8 | ||
|
|
f67acc3fe1 | ||
|
|
dea6418d54 | ||
|
|
adf3f63cfe | ||
|
|
22b1f11577 | ||
|
|
bc9fb48f18 | ||
|
|
0d6e6cc289 | ||
|
|
6d75a50716 | ||
|
|
a8d9760a11 | ||
|
|
2889d3634f | ||
|
|
958e77e240 | ||
|
|
e36a75aa28 | ||
|
|
1d66852134 | ||
|
|
aab9764d33 | ||
|
|
cf1c75c5b3 | ||
|
|
a9c9d743b8 | ||
|
|
0213fda9a4 | ||
|
|
909867e116 | ||
|
|
b75f920201 | ||
|
|
18a2fff07b | ||
|
|
1826dd122d | ||
|
|
2cb19941ee | ||
|
|
00cd4d4b72 | ||
|
|
696cb5f48c | ||
|
|
e13b4f5377 | ||
|
|
4b0bb7f31a | ||
|
|
1f6badb81d | ||
|
|
65acc6aa39 | ||
|
|
44c41ed99f | ||
|
|
301eb4f1dc | ||
|
|
5aa7de8d86 | ||
|
|
28c03918aa | ||
|
|
d14b267305 | ||
|
|
f2f5d36090 | ||
|
|
aec8450c53 | ||
|
|
efc46341ae | ||
|
|
3a69eb4864 | ||
|
|
7044c2bd83 | ||
|
|
74af439deb | ||
|
|
faf7096743 | ||
|
|
6b3f99c8a7 | ||
|
|
23fd528540 | ||
|
|
c701ee002e | ||
|
|
238a755698 | ||
|
|
9cc95fb4c8 | ||
|
|
bc5887eab4 | ||
|
|
01335ad0ca | ||
|
|
18f7bd2bf7 | ||
|
|
d812deccb8 | ||
|
|
c7e5cbf26e | ||
|
|
e230ef0b41 | ||
|
|
5f547e593a | ||
|
|
a1c6caece1 | ||
|
|
0281c2df1c | ||
|
|
88bfc517f7 | ||
|
|
877b6b5764 | ||
|
|
6b73937acd | ||
|
|
95f7790870 | ||
|
|
0a536a518a | ||
|
|
330fa14c8c | ||
|
|
51d4767307 | ||
|
|
e5f12cdacd | ||
|
|
87525419a2 | ||
|
|
d427d32463 |
51
.cursor/rules/ai-features.mdc
Normal file
@@ -0,0 +1,51 @@
|
||||
# AI Features and LangChain Integration
|
||||
|
||||
## AI Components
|
||||
|
||||
- **LangChain Integration**: Uses `@langchain/core`, `@langchain/deepseek`,
|
||||
`@langchain/langgraph`, `@langchain/ollama`
|
||||
- **Whisper Transcription**: Local and online transcription via `whisper-rs` in
|
||||
Rust backend
|
||||
- **AI Agent**: Located in [src/lib/agent/](mdc:src/lib/agent/) directory
|
||||
|
||||
## Frontend AI Features
|
||||
|
||||
- **AI Page**: [src/page/AI.svelte](mdc:src/page/AI.svelte) - Main AI interface
|
||||
- **Agent Logic**: [src/lib/agent/](mdc:src/lib/agent/) - AI agent implementation
|
||||
- **Interface**: [src/lib/interface.ts](mdc:src/lib/interface.ts)
|
||||
\- AI communication layer
|
||||
|
||||
## Backend AI Features
|
||||
|
||||
- **Subtitle Generation**:
|
||||
[src-tauri/src/subtitle_generator/](mdc:src-tauri/src/subtitle_generator/) -
|
||||
AI-powered subtitle creation
|
||||
- **Whisper Integration**:
|
||||
[src-tauri/src/subtitle_generator.rs](mdc:src-tauri/src/subtitle_generator.rs)
|
||||
\- Speech-to-text processing
|
||||
- **CUDA Support**: Optional CUDA acceleration for Whisper via feature flag
|
||||
|
||||
## AI Workflows
|
||||
|
||||
- **Live Transcription**: Real-time speech-to-text during live streams
|
||||
- **Content Summarization**: AI-powered content analysis and summarization
|
||||
- **Smart Editing**: AI-assisted video editing and clip generation
|
||||
- **Danmaku Processing**: AI analysis of danmaku (bullet comments) streams
|
||||
|
||||
## Configuration
|
||||
|
||||
- **LLM Settings**: Configure AI models in [src-tauri/config.example.toml](mdc:src-tauri/config.example.toml)
|
||||
- **Whisper Models**: Local model configuration for offline transcription
|
||||
- **API Keys**: External AI service configuration for online features
|
||||
|
||||
## Development Notes
|
||||
|
||||
- AI features require proper model configuration
|
||||
- CUDA feature enables GPU acceleration for Whisper
|
||||
- LangChain integration supports multiple AI providers
|
||||
- AI agent can work with both local and cloud-based models
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
|
||||
---
|
||||
62
.cursor/rules/build-deployment.mdc
Normal file
@@ -0,0 +1,62 @@
|
||||
# Build and Deployment Configuration
|
||||
|
||||
## Build Scripts
|
||||
|
||||
- **PowerShell**: [build.ps1](mdc:build.ps1) - Windows build script
|
||||
- **FFmpeg Setup**: [ffmpeg_setup.ps1](mdc:ffmpeg_setup.ps1)
|
||||
\- FFmpeg installation script
|
||||
- **Version Bump**: [scripts/bump.cjs](mdc:scripts/bump.cjs)
|
||||
\- Version management script
|
||||
|
||||
## Package Management
|
||||
|
||||
- **Node.js**: [package.json](mdc:package.json) - Frontend dependencies and scripts
|
||||
- **Rust**: [src-tauri/Cargo.toml](mdc:src-tauri/Cargo.toml)
|
||||
\- Backend dependencies and features
|
||||
- **Lock Files**: [yarn.lock](mdc:yarn.lock) - Yarn dependency lock
|
||||
|
||||
## Build Configuration
|
||||
|
||||
- **Vite**: [vite.config.ts](mdc:vite.config.ts) - Frontend build tool configuration
|
||||
- **Tailwind**: [tailwind.config.cjs](mdc:tailwind.config.cjs) - CSS framework configuration
|
||||
- **PostCSS**: [postcss.config.cjs](mdc:postcss.config.cjs) - CSS processing configuration
|
||||
- **TypeScript**: [tsconfig.json](mdc:tsconfig.json),
|
||||
[tsconfig.node.json](mdc:tsconfig.node.json) - TypeScript configuration
|
||||
|
||||
## Tauri Configuration
|
||||
|
||||
- **Main Config**: [src-tauri/tauri.conf.json](mdc:src-tauri/tauri.conf.json)
|
||||
\- Core Tauri settings
|
||||
- **Platform Configs**:
|
||||
- [src-tauri/tauri.macos.conf.json](mdc:src-tauri/tauri.macos.conf.json)
|
||||
\- macOS specific
|
||||
- [src-tauri/tauri.linux.conf.json](mdc:src-tauri/tauri.linux.conf.json)
|
||||
\- Linux specific
|
||||
- [src-tauri/tauri.windows.conf.json](mdc:src-tauri/tauri.windows.conf.json)
|
||||
\- Windows specific
|
||||
- [src-tauri/tauri.windows.cuda.conf.json](mdc:src-tauri/tauri.windows.cuda.conf.json)
|
||||
\- Windows with CUDA
|
||||
|
||||
## Docker Support
|
||||
|
||||
- **Dockerfile**: [Dockerfile](mdc:Dockerfile) - Container deployment configuration
|
||||
- **Documentation**: [docs/](mdc:docs/) - VitePress-based documentation site
|
||||
|
||||
## Build Commands
|
||||
|
||||
- **Frontend**: `yarn build` - Build production frontend
|
||||
- **Tauri**: `yarn tauri build` - Build desktop application
|
||||
- **Documentation**: `yarn docs:build` - Build documentation site
|
||||
- **Type Check**: `yarn check` - TypeScript and Svelte validation
|
||||
|
||||
## Deployment Targets
|
||||
|
||||
- **Desktop**: Native Tauri applications for Windows, macOS, Linux
|
||||
- **Docker**: Containerized deployment option
|
||||
- **Documentation**: Static site deployment via VitePress
|
||||
- **Assets**: Static asset distribution for web components
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
|
||||
---
|
||||
61
.cursor/rules/database-data.mdc
Normal file
@@ -0,0 +1,61 @@
|
||||
# Database and Data Management
|
||||
|
||||
## Database Architecture
|
||||
|
||||
- **SQLite Database**: Primary data storage using `sqlx` with async runtime
|
||||
- **Database Module**: [src-tauri/src/database/](mdc:src-tauri/src/database/)
|
||||
\- Core database operations
|
||||
- **Migration System**: [src-tauri/src/migration.rs](mdc:src-tauri/src/migration.rs)
|
||||
\- Database schema management
|
||||
|
||||
## Data Models
|
||||
|
||||
- **Recording Data**: Stream metadata, recording sessions, and file information
|
||||
- **Room Configuration**: Stream room settings and platform credentials
|
||||
- **Task Management**: Recording task status and progress tracking
|
||||
- **User Preferences**: Application settings and user configurations
|
||||
|
||||
## Frontend Data Layer
|
||||
|
||||
- **Database Interface**: [src/lib/db.ts](mdc:src/lib/db.ts)
|
||||
\- Frontend database operations
|
||||
- **Stores**: [src/lib/stores/](mdc:src/lib/stores/) - State management for data
|
||||
- **Version Management**: [src/lib/stores/version.ts](mdc:src/lib/stores/version.ts)
|
||||
\- Version tracking
|
||||
|
||||
## Data Operations
|
||||
|
||||
- **CRUD Operations**: Create, read, update, delete for all data entities
|
||||
- **Query Optimization**: Efficient SQL queries with proper indexing
|
||||
- **Transaction Support**: ACID compliance for critical operations
|
||||
- **Data Validation**: Input validation and sanitization
|
||||
|
||||
## File Management
|
||||
|
||||
- **Cache Directory**: [src-tauri/cache/](mdc:src-tauri/cache/)
|
||||
\- Temporary file storage
|
||||
- **Upload Directory**: [src-tauri/cache/uploads/](mdc:src-tauri/cache/uploads/)
|
||||
\- User upload storage
|
||||
- **Bilibili Cache**: [src-tauri/cache/bilibili/](mdc:src-tauri/cache/bilibili/)
|
||||
\- Platform-specific cache
|
||||
|
||||
## Data Persistence
|
||||
|
||||
- **SQLite Files**: [src-tauri/data/data_v2.db](mdc:src-tauri/data/data_v2.db)
|
||||
\- Main database file
|
||||
- **Write-Ahead Logging**: WAL mode for concurrent access and performance
|
||||
- **Backup Strategy**: Database backup and recovery procedures
|
||||
- **Migration Handling**: Automatic schema updates and data migration
|
||||
|
||||
## Development Guidelines
|
||||
|
||||
- Use prepared statements to prevent SQL injection
|
||||
- Implement proper error handling for database operations
|
||||
- Use transactions for multi-step operations
|
||||
- Follow database naming conventions consistently
|
||||
- Test database operations with sample data
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
|
||||
---
|
||||
47
.cursor/rules/frontend-development.mdc
Normal file
@@ -0,0 +1,47 @@
|
||||
# Frontend Development Guidelines
|
||||
|
||||
## Svelte 3 Best Practices
|
||||
|
||||
- Use Svelte 3 syntax with `<script>` tags for component logic
|
||||
- Prefer reactive statements with `$:` for derived state
|
||||
- Use stores from [src/lib/stores/](mdc:src/lib/stores/) for global state management
|
||||
- Import components from [src/lib/components/](mdc:src/lib/components/)
|
||||
|
||||
## TypeScript Configuration
|
||||
|
||||
- Follow the configuration in [tsconfig.json](mdc:tsconfig.json)
|
||||
- Use strict type checking with `checkJs: true`
|
||||
- Extends `@tsconfig/svelte` for Svelte-specific TypeScript settings
|
||||
- Base URL is set to workspace root for clean imports
|
||||
|
||||
## Component Structure
|
||||
|
||||
- **Page components**: Located in [src/page/](mdc:src/page/) directory
|
||||
- **Reusable components**: Located in [src/lib/components/](mdc:src/lib/components/)
|
||||
directory
|
||||
- **Layout components**: [src/App.svelte](mdc:src/App.svelte),
|
||||
[src/AppClip.svelte](mdc:src/AppClip.svelte), [src/AppLive.svelte](mdc:src/AppLive.svelte)
|
||||
|
||||
## Styling
|
||||
|
||||
- Use Tailwind CSS classes for styling
|
||||
- Configuration in [tailwind.config.cjs](mdc:tailwind.config.cjs)
|
||||
- PostCSS configuration in [postcss.config.cjs](mdc:postcss.config.cjs)
|
||||
- Global styles in [src/styles.css](mdc:src/styles.css)
|
||||
|
||||
## Entry Points
|
||||
|
||||
- **Main app**: [src/main.ts](mdc:src/main.ts) - Main application entry
|
||||
- **Clip mode**: [src/main_clip.ts](mdc:src/main_clip.ts) - Clip editing interface
|
||||
- **Live mode**: [src/main_live.ts](mdc:src/main_live.ts) - Live streaming interface
|
||||
|
||||
## Development Workflow
|
||||
|
||||
- Use `yarn dev` for frontend-only development
|
||||
- Use `yarn tauri dev` for full Tauri development
|
||||
- Use `yarn check` for TypeScript and Svelte type checking
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
|
||||
---
|
||||
53
.cursor/rules/project-overview.mdc
Normal file
@@ -0,0 +1,53 @@
|
||||
# BiliBili ShadowReplay Project Overview
|
||||
|
||||
This is a Tauri-based desktop application for caching live streams and performing
|
||||
real-time editing and submission. It supports Bilibili and Douyin platforms.
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Frontend (Svelte + TypeScript)
|
||||
|
||||
- **Main entry points**: [src/main.ts](mdc:src/main.ts),
|
||||
[src/main_clip.ts](mdc:src/main_clip.ts), [src/main_live.ts](mdc:src/main_live.ts)
|
||||
- **App components**: [src/App.svelte](mdc:src/App.svelte),
|
||||
[src/AppClip.svelte](mdc:src/AppClip.svelte), [src/AppLive.svelte](mdc:src/AppLive.svelte)
|
||||
- **Pages**: Located in [src/page/](mdc:src/page/) directory
|
||||
- **Components**: Located in [src/lib/components/](mdc:src/lib/components/) directory
|
||||
- **Stores**: Located in [src/lib/stores/](mdc:src/lib/stores/) directory
|
||||
|
||||
### Backend (Rust + Tauri)
|
||||
|
||||
- **Main entry**: [src-tauri/src/main.rs](mdc:src-tauri/src/main.rs)
|
||||
- **Core modules**:
|
||||
- [src-tauri/src/recorder/](mdc:src-tauri/src/recorder/) - Stream recording functionality
|
||||
- [src-tauri/src/database/](mdc:src-tauri/src/database/) - Database operations
|
||||
- [src-tauri/src/handlers/](mdc:src-tauri/src/handlers/) - Tauri command handlers
|
||||
- **Custom crate**:
|
||||
[src-tauri/crates/danmu_stream/](mdc:src-tauri/crates/danmu_stream/) -
|
||||
Danmaku stream processing
|
||||
|
||||
### Configuration
|
||||
|
||||
- **Frontend config**: [tsconfig.json](mdc:tsconfig.json),
|
||||
[vite.config.ts](mdc:vite.config.ts), [tailwind.config.cjs](mdc:tailwind.config.cjs)
|
||||
- **Backend config**: [src-tauri/Cargo.toml](mdc:src-tauri/Cargo.toml), [src-tauri/tauri.conf.json](mdc:src-tauri/tauri.conf.json)
|
||||
- **Example config**: [src-tauri/config.example.toml](mdc:src-tauri/config.example.toml)
|
||||
|
||||
## Key Technologies
|
||||
|
||||
- **Frontend**: Svelte 3, TypeScript, Tailwind CSS, Flowbite
|
||||
- **Backend**: Rust, Tauri 2, SQLite, FFmpeg
|
||||
- **AI Features**: LangChain, Whisper for transcription
|
||||
- **Build Tools**: Vite, VitePress for documentation
|
||||
|
||||
## Development Commands
|
||||
|
||||
- `yarn dev` - Start development server
|
||||
- `yarn tauri dev` - Start Tauri development
|
||||
- `yarn build` - Build frontend
|
||||
- `yarn docs:dev` - Start documentation server
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
|
||||
---
|
||||
56
.cursor/rules/rust-backend.mdc
Normal file
@@ -0,0 +1,56 @@
|
||||
# Rust Backend Development Guidelines
|
||||
|
||||
## Project Structure
|
||||
|
||||
- **Main entry**: [src-tauri/src/main.rs](mdc:src-tauri/src/main.rs)
|
||||
\- Application entry point
|
||||
- **Core modules**:
|
||||
- [src-tauri/src/recorder/](mdc:src-tauri/src/recorder/)
|
||||
\- Stream recording and management
|
||||
- [src-tauri/src/database/](mdc:src-tauri/src/database/)
|
||||
\- SQLite database operations
|
||||
- [src-tauri/src/handlers/](mdc:src-tauri/src/handlers/)
|
||||
\- Tauri command handlers
|
||||
- [src-tauri/src/subtitle_generator/](mdc:src-tauri/src/subtitle_generator/)
|
||||
\- AI-powered subtitle generation
|
||||
|
||||
## Custom Crates
|
||||
|
||||
- **danmu_stream**: [src-tauri/crates/danmu_stream/](mdc:src-tauri/crates/danmu_stream/)
|
||||
\- Danmaku stream processing library
|
||||
|
||||
## Dependencies
|
||||
|
||||
- **Tauri 2**: Core framework for desktop app functionality
|
||||
- **FFmpeg**: Video/audio processing via `async-ffmpeg-sidecar`
|
||||
- **Whisper**: AI transcription via `whisper-rs` (CUDA support available)
|
||||
- **LangChain**: AI agent functionality
|
||||
- **SQLite**: Database via `sqlx` with async runtime
|
||||
|
||||
## Configuration
|
||||
|
||||
- **Cargo.toml**: [src-tauri/Cargo.toml](mdc:src-tauri/Cargo.toml)
|
||||
\- Dependencies and features
|
||||
- **Tauri config**: [src-tauri/tauri.conf.json](mdc:src-tauri/tauri.conf.json)
|
||||
\- App configuration
|
||||
- **Example config**: [src-tauri/config.example.toml](mdc:src-tauri/config.example.toml)
|
||||
\- User configuration template
|
||||
|
||||
## Features
|
||||
|
||||
- **default**: Includes GUI and core functionality
|
||||
- **cuda**: Enables CUDA acceleration for Whisper transcription
|
||||
- **headless**: Headless mode without GUI
|
||||
- **custom-protocol**: Required for production builds
|
||||
|
||||
## Development Commands
|
||||
|
||||
- `yarn tauri dev` - Start Tauri development with hot reload
|
||||
- `yarn tauri build` - Build production application
|
||||
- `cargo check` - Check Rust code without building
|
||||
- `cargo test` - Run Rust tests
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
|
||||
---
|
||||
60
.cursor/rules/streaming-recording.mdc
Normal file
@@ -0,0 +1,60 @@
|
||||
# Streaming and Recording System
|
||||
|
||||
## Core Recording Components
|
||||
|
||||
- **Recorder Manager**: [src-tauri/src/recorder_manager.rs](mdc:src-tauri/src/recorder_manager.rs)
|
||||
\- Main recording orchestration
|
||||
- **Recorder**: [src-tauri/src/recorder/](mdc:src-tauri/src/recorder/)
|
||||
\- Individual stream recording logic
|
||||
- **Danmaku Stream**: [src-tauri/crates/danmu_stream/](mdc:src-tauri/crates/danmu_stream/)
|
||||
\- Custom crate for bullet comment processing
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
- **Bilibili**: Main platform support with live stream caching
|
||||
- **Douyin**: TikTok's Chinese platform support
|
||||
- **Multi-stream**: Support for recording multiple streams simultaneously
|
||||
|
||||
## Recording Features
|
||||
|
||||
- **Live Caching**: Real-time stream recording and buffering
|
||||
- **Time-based Clipping**: Extract specific time segments from recorded streams
|
||||
- **Danmaku Capture**: Record bullet comments and chat messages
|
||||
- **Quality Control**: Configurable recording quality and format options
|
||||
|
||||
## Frontend Interfaces
|
||||
|
||||
- **Live Mode**: [src/AppLive.svelte](mdc:src/AppLive.svelte)
|
||||
\- Live streaming interface
|
||||
- **Clip Mode**: [src/AppClip.svelte](mdc:src/AppClip.svelte)
|
||||
\- Video editing and clipping
|
||||
- **Room Management**: [src/page/Room.svelte](mdc:src/page/Room.svelte)
|
||||
\- Stream room configuration
|
||||
- **Task Management**: [src/page/Task.svelte](mdc:src/page/Task.svelte)
|
||||
\- Recording task monitoring
|
||||
|
||||
## Technical Implementation
|
||||
|
||||
- **FFmpeg Integration**: Video/audio processing via `async-ffmpeg-sidecar`
|
||||
- **M3U8 Support**: HLS stream processing with `m3u8-rs`
|
||||
- **Async Processing**: Non-blocking I/O with `tokio` runtime
|
||||
- **Database Storage**: SQLite for metadata and recording information
|
||||
|
||||
## Configuration
|
||||
|
||||
- **Recording Settings**: Configure in [src-tauri/config.example.toml](mdc:src-tauri/config.example.toml)
|
||||
- **FFmpeg Path**: Set FFmpeg binary location for video processing
|
||||
- **Storage Paths**: Configure cache and output directories
|
||||
- **Quality Settings**: Adjust recording bitrate and format options
|
||||
|
||||
## Development Workflow
|
||||
|
||||
- Use [src-tauri/src/recorder/](mdc:src-tauri/src/recorder/) for core recording logic
|
||||
- Test with [src-tauri/tests/](mdc:src-tauri/tests/) directory
|
||||
- Monitor recording progress via progress manager
|
||||
- Handle errors gracefully with custom error types
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
|
||||
---
|
||||
36
.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,36 @@
|
||||
ARG VARIANT=bookworm-slim
|
||||
FROM debian:${VARIANT}
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Arguments
|
||||
ARG CONTAINER_USER=vscode
|
||||
ARG CONTAINER_GROUP=vscode
|
||||
|
||||
# Install dependencies
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y \
|
||||
build-essential \
|
||||
clang \
|
||||
cmake \
|
||||
curl \
|
||||
file \
|
||||
git \
|
||||
libayatana-appindicator3-dev \
|
||||
librsvg2-dev \
|
||||
libssl-dev \
|
||||
libwebkit2gtk-4.1-dev \
|
||||
libxdo-dev \
|
||||
pkg-config \
|
||||
wget \
|
||||
&& apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/library-scripts
|
||||
|
||||
# Set users
|
||||
RUN adduser --disabled-password --gecos "" ${CONTAINER_USER}
|
||||
USER ${CONTAINER_USER}
|
||||
WORKDIR /home/${CONTAINER_USER}
|
||||
|
||||
# Install rustup
|
||||
RUN curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal
|
||||
ENV PATH=${PATH}:/home/${CONTAINER_USER}/.cargo/bin
|
||||
|
||||
CMD [ "/bin/bash" ]
|
||||
31
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "vscode",
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile",
|
||||
"args": {
|
||||
"CONTAINER_USER": "vscode",
|
||||
"CONTAINER_GROUP": "vscode"
|
||||
}
|
||||
},
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/node:1": {
|
||||
"version": "latest"
|
||||
}
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {
|
||||
"lldb.executable": "/usr/bin/lldb",
|
||||
"files.watcherExclude": {
|
||||
"**/target/**": true
|
||||
}
|
||||
},
|
||||
"extensions": [
|
||||
"vadimcn.vscode-lldb",
|
||||
"rust-lang.rust-analyzer",
|
||||
"tamasfe.even-better-toml"
|
||||
]
|
||||
}
|
||||
},
|
||||
"remoteUser": "vscode"
|
||||
}
|
||||
39
.dockerignore
Normal file
@@ -0,0 +1,39 @@
|
||||
# Dependencies
|
||||
node_modules
|
||||
.pnpm-store
|
||||
.npm
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
|
||||
# Build outputs
|
||||
dist
|
||||
build
|
||||
target
|
||||
*.log
|
||||
|
||||
# Version control
|
||||
.git
|
||||
.gitignore
|
||||
|
||||
# IDE and editor files
|
||||
.idea
|
||||
.vscode
|
||||
*.swp
|
||||
*.swo
|
||||
.DS_Store
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Debug files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Tauri specific
|
||||
src-tauri/target
|
||||
src-tauri/dist
|
||||
44
.github/CONTRIBUTING.md
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
# BiliBili-ShadowReplay contribute guide
|
||||
|
||||
## Project Setup
|
||||
|
||||
### MacOS
|
||||
|
||||
项目无需额外配置,直接 `yarn tauri dev` 即可编译运行。
|
||||
|
||||
### Linux
|
||||
|
||||
也无需额外配置。
|
||||
|
||||
### Windows
|
||||
|
||||
Windows 下分为两个版本,分别是 `cpu` 和 `cuda` 版本。区别在于 Whisper 是否使用 GPU 加速。
|
||||
`cpu` 版本使用 CPU 进行推理,`cuda` 版本使用 GPU 进行推理。
|
||||
|
||||
默认运行为 `cpu` 版本,使用 `yarn tauri dev --features cuda` 命令运行 `cuda` 版本。
|
||||
|
||||
在运行前,须要安装以下依赖:
|
||||
|
||||
1. 安装 LLVM 且配置相关环境变量,详情见 [LLVM Windows Setup](https://llvm.org/docs/GettingStarted.html#building-llvm-on-windows);
|
||||
|
||||
2. 安装 CUDA Toolkit,详情见
|
||||
[CUDA Windows Setup](https://docs.nvidia.com/cuda/cuda-installation-guide-microsoft-windows/index.html);
|
||||
要注意,安装时请勾选 **VisualStudio integration**。
|
||||
|
||||
### 常见问题
|
||||
|
||||
#### 1. error C3688
|
||||
|
||||
构建前配置参数 `/utf-8`:
|
||||
|
||||
```powershell
|
||||
$env:CMAKE_CXX_FLAGS="/utf-8"
|
||||
```
|
||||
|
||||
#### 2. error: 'exists' is unavailable: introduced in macOS 10.15
|
||||
|
||||
配置环境变量 `CMAKE_OSX_DEPLOYMENT_TARGET`,不低于 `13.3`。
|
||||
|
||||
### 3. CUDA arch 错误
|
||||
|
||||
配置环境变量 `CMAKE_CUDA_ARCHITECTURES`,可以参考 Workflows 中的配置。
|
||||
47
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
name: Bug Report
|
||||
description: 提交 BUG 报告.
|
||||
title: "[bug] "
|
||||
labels: ["bug"]
|
||||
assignees:
|
||||
- Xinrea
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 提交须知
|
||||
description: 请确认以下内容
|
||||
options:
|
||||
- label: 我是在最新版本上发现的此问题
|
||||
required: true
|
||||
- label: 我已阅读 [常见问题](https://bsr.xinrea.cn/usage/faq.html) 的说明
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: app_type
|
||||
attributes:
|
||||
label: 以哪种方式使用的该软件?
|
||||
multiple: false
|
||||
options:
|
||||
- Docker 镜像
|
||||
- 桌面应用
|
||||
- type: dropdown
|
||||
id: os
|
||||
attributes:
|
||||
label: 运行环境
|
||||
multiple: false
|
||||
options:
|
||||
- Linux
|
||||
- Windows
|
||||
- MacOS
|
||||
- Docker
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: BUG 描述
|
||||
description: 请尽可能详细描述 BUG 的现象以及复现的方法
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: 日志
|
||||
description: 请粘贴日志内容或是上传日志文件(在主窗口的设置页面,提供了一键打开日志目录所在位置的按钮;当你打开日志目录所在位置后,进入 logs 目录,找到后缀名为 log 的文件)
|
||||
validations:
|
||||
required: true
|
||||
13
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
name: Feature Request
|
||||
description: 提交新功能的需求
|
||||
title: "[feature] "
|
||||
labels: ["feature"]
|
||||
assignees:
|
||||
- Xinrea
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 需求描述
|
||||
description: 请尽可能详细描述你想要的新功能
|
||||
validations:
|
||||
required: true
|
||||
111
.github/workflows/main.yml
vendored
@@ -1,57 +1,110 @@
|
||||
name: Release
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
|
||||
- "v*"
|
||||
jobs:
|
||||
release:
|
||||
publish-tauri:
|
||||
permissions:
|
||||
contents: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [macos-latest, ubuntu-20.04, windows-latest]
|
||||
include:
|
||||
- platform: "macos-latest" # for Intel based macs.
|
||||
args: "--target x86_64-apple-darwin"
|
||||
- platform: "ubuntu-22.04"
|
||||
args: ""
|
||||
- platform: "windows-latest"
|
||||
args: "--features cuda"
|
||||
features: "cuda"
|
||||
- platform: "windows-latest"
|
||||
args: ""
|
||||
features: "cpu"
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies (ubuntu only)
|
||||
if: matrix.platform == 'ubuntu-20.04'
|
||||
# You can remove libayatana-appindicator3-dev if you don't use the system tray feature.
|
||||
- name: Set build type
|
||||
id: build_type
|
||||
run: |
|
||||
if [[ "${{ github.ref }}" == *"rc"* ]]; then
|
||||
echo "debug=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "debug=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: install dependencies (ubuntu only)
|
||||
if: matrix.platform == 'ubuntu-22.04' # This must match the platform value defined above.
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libayatana-appindicator3-dev librsvg2-dev
|
||||
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
|
||||
|
||||
- name: Rust setup
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Rust cache
|
||||
uses: swatinem/rust-cache@v2
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
workspaces: './src-tauri -> target'
|
||||
node-version: lts/*
|
||||
cache: "yarn" # Set this to npm, yarn or pnpm.
|
||||
|
||||
- name: Sync node version and setup cache
|
||||
uses: actions/setup-node@v3
|
||||
- name: install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable # Set this to dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
cache: 'yarn' # Set this to npm, yarn or pnpm.
|
||||
# Those targets are only used on macos runners so it's in an `if` to slightly speed up windows and linux builds.
|
||||
targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
|
||||
|
||||
- name: Install frontend dependencies
|
||||
- name: Install CUDA toolkit (Windows CUDA only)
|
||||
if: matrix.platform == 'windows-latest' && matrix.features == 'cuda'
|
||||
uses: Jimver/cuda-toolkit@v0.2.24
|
||||
|
||||
- name: Setup ffmpeg
|
||||
if: matrix.platform == 'windows-latest'
|
||||
working-directory: ./
|
||||
shell: pwsh
|
||||
# running script ffmpeg_setup.ps1 to install ffmpeg on windows.
|
||||
# This script is located in the root of the repository.
|
||||
run: ./ffmpeg_setup.ps1
|
||||
|
||||
- name: install frontend dependencies
|
||||
# If you don't have `beforeBuildCommand` configured you may want to build your frontend here too.
|
||||
run: yarn install # Change this to npm, yarn or pnpm.
|
||||
run: yarn install # change this to npm or pnpm depending on which one you use.
|
||||
|
||||
- name: Build the app
|
||||
uses: tauri-apps/tauri-action@v0
|
||||
- name: Copy CUDA DLLs (Windows CUDA only)
|
||||
if: matrix.platform == 'windows-latest' && matrix.features == 'cuda'
|
||||
shell: pwsh
|
||||
run: |
|
||||
$cudaPath = "$env:CUDA_PATH\bin"
|
||||
$targetPath = "src-tauri"
|
||||
New-Item -ItemType Directory -Force -Path $targetPath
|
||||
Copy-Item "$cudaPath\cudart64*.dll" -Destination $targetPath
|
||||
Copy-Item "$cudaPath\cublas64*.dll" -Destination $targetPath
|
||||
Copy-Item "$cudaPath\cublasLt64*.dll" -Destination $targetPath
|
||||
|
||||
- name: Get previous tag
|
||||
id: get_previous_tag
|
||||
run: |
|
||||
# Get the previous tag (excluding the current one being pushed)
|
||||
PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD~1 2>/dev/null || echo "")
|
||||
if [ -z "$PREVIOUS_TAG" ]; then
|
||||
# If no previous tag found, use the first commit
|
||||
PREVIOUS_TAG=$(git rev-list --max-parents=0 HEAD | head -1)
|
||||
fi
|
||||
echo "previous_tag=$PREVIOUS_TAG" >> $GITHUB_OUTPUT
|
||||
echo "current_tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- uses: tauri-apps/tauri-action@v0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CMAKE_OSX_DEPLOYMENT_TARGET: "13.3"
|
||||
CMAKE_CUDA_ARCHITECTURES: "75"
|
||||
WHISPER_BACKEND: ${{ matrix.features }}
|
||||
with:
|
||||
tagName: ${{ github.ref_name }} # This only works if your workflow triggers on new tags.
|
||||
releaseName: 'Bilibili ShadowReplay v__VERSION__' # tauri-action replaces \_\_VERSION\_\_ with the app version.
|
||||
releaseBody: 'See the assets to download and install this version.'
|
||||
tagName: v__VERSION__
|
||||
releaseName: "BiliBili ShadowReplay v__VERSION__"
|
||||
releaseBody: "> [!NOTE]\n> 如果你是第一次下载安装,请参考 [安装准备](https://bsr.xinrea.cn/getting-started/installation/desktop.html) 选择合适的版本。\n> Changelog: https://github.com/Xinrea/bili-shadowreplay/compare/${{ steps.get_previous_tag.outputs.previous_tag }}...${{ steps.get_previous_tag.outputs.current_tag }}"
|
||||
releaseDraft: true
|
||||
prerelease: false
|
||||
args: ${{ matrix.args }} ${{ matrix.platform == 'windows-latest' && matrix.features == 'cuda' && '--config src-tauri/tauri.windows.cuda.conf.json' || '' }}
|
||||
|
||||
51
.github/workflows/package.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
name: Docker Build and Push
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
type=sha,format=long
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
66
.github/workflows/pages.yml
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
name: Deploy VitePress site to Pages
|
||||
|
||||
on:
|
||||
# Runs on pushes targeting the `main` branch. Change this to `master` if you're
|
||||
# using the `master` branch as the default branch.
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- docs/**
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
|
||||
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
|
||||
concurrency:
|
||||
group: pages
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
# Build job
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Not needed if lastUpdated is not enabled
|
||||
# - uses: pnpm/action-setup@v3 # Uncomment this block if you're using pnpm
|
||||
# with:
|
||||
# version: 9 # Not needed if you've set "packageManager" in package.json
|
||||
# - uses: oven-sh/setup-bun@v1 # Uncomment this if you're using Bun
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: npm # or pnpm / yarn
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v4
|
||||
- name: Install dependencies
|
||||
run: yarn install # or pnpm install / yarn install / bun install
|
||||
- name: Build with VitePress
|
||||
run: yarn run docs:build # or pnpm docs:build / yarn docs:build / bun run docs:build
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: docs/.vitepress/dist
|
||||
|
||||
# Deployment job
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
15
.gitignore
vendored
@@ -11,6 +11,7 @@ node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
/target/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
@@ -22,3 +23,17 @@ dist-ssr
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
*.zip
|
||||
|
||||
src-tauri/*.exe
|
||||
|
||||
# test files
|
||||
src-tauri/tests/audio/*.srt
|
||||
|
||||
.env
|
||||
|
||||
docs/.vitepress/cache
|
||||
docs/.vitepress/dist
|
||||
|
||||
*.debug.js
|
||||
*.debug.map
|
||||
|
||||
8
.helix/languages.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[[language]]
|
||||
name = "rust"
|
||||
auto-format = true
|
||||
rulers = []
|
||||
|
||||
[[language]]
|
||||
name = "svelte"
|
||||
auto-format = true
|
||||
5
.markdownlint.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"MD033": {
|
||||
"allowed_elements": ["nobr", "sup"]
|
||||
}
|
||||
}
|
||||
46
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,46 @@
|
||||
fail_fast: true
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v6.0.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
exclude: '(\.json$|public/)'
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: cargo-fmt
|
||||
name: cargo fmt
|
||||
entry: cargo fmt --manifest-path src-tauri/Cargo.toml --
|
||||
language: system
|
||||
types: [rust]
|
||||
pass_filenames: false # This makes it a lot faster
|
||||
|
||||
- id: cargo-clippy
|
||||
name: cargo clippy
|
||||
language: system
|
||||
types: [rust]
|
||||
pass_filenames: false
|
||||
entry: cargo clippy --manifest-path src-tauri/Cargo.toml
|
||||
|
||||
- id: cargo-clippy-headless
|
||||
name: cargo clippy headless
|
||||
language: system
|
||||
types: [rust]
|
||||
pass_filenames: false
|
||||
entry: cargo clippy --manifest-path src-tauri/Cargo.toml --no-default-features --features headless
|
||||
|
||||
- id: cargo-test
|
||||
name: cargo test
|
||||
language: system
|
||||
types: [rust]
|
||||
pass_filenames: false
|
||||
entry: cargo test --manifest-path src-tauri/Cargo.toml
|
||||
|
||||
- id: cargo-test-headless
|
||||
name: cargo test headless
|
||||
language: system
|
||||
types: [rust]
|
||||
pass_filenames: false
|
||||
entry: cargo test --manifest-path src-tauri/Cargo.toml --no-default-features --features headless
|
||||
7
.vscode/extensions.json
vendored
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"svelte.svelte-vscode",
|
||||
"tauri-apps.tauri-vscode",
|
||||
"rust-lang.rust-analyzer"
|
||||
]
|
||||
}
|
||||
85
Dockerfile
Normal file
@@ -0,0 +1,85 @@
|
||||
# Build frontend
|
||||
FROM node:20-bullseye AS frontend-builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
python3 \
|
||||
make \
|
||||
g++ \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy package files
|
||||
COPY package.json yarn.lock ./
|
||||
|
||||
# Install dependencies with specific flags
|
||||
RUN yarn install --frozen-lockfile
|
||||
|
||||
# Copy source files
|
||||
COPY . .
|
||||
|
||||
# Build frontend
|
||||
RUN yarn build
|
||||
|
||||
# Build Rust backend
|
||||
FROM rust:1.90-slim AS rust-builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install required system dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
cmake \
|
||||
pkg-config \
|
||||
libssl-dev \
|
||||
glib-2.0-dev \
|
||||
libclang-dev \
|
||||
g++ \
|
||||
wget \
|
||||
xz-utils \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy Rust project files
|
||||
COPY src-tauri/Cargo.toml src-tauri/Cargo.lock ./src-tauri/
|
||||
COPY src-tauri/src ./src-tauri/src
|
||||
COPY src-tauri/crates ./src-tauri/crates
|
||||
|
||||
# Build Rust backend
|
||||
WORKDIR /app/src-tauri
|
||||
RUN rustup component add rustfmt
|
||||
RUN cargo build --no-default-features --features headless --release
|
||||
|
||||
# Final stage
|
||||
FROM debian:trixie-slim AS final
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install runtime dependencies, SSL certificates and Chinese fonts
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libssl3 \
|
||||
ca-certificates \
|
||||
fonts-wqy-microhei \
|
||||
netbase \
|
||||
nscd \
|
||||
ffmpeg \
|
||||
&& update-ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
RUN touch /etc/netgroup
|
||||
RUN mkdir -p /var/run/nscd && chmod 755 /var/run/nscd
|
||||
|
||||
# Add /app to PATH
|
||||
ENV PATH="/app:${PATH}"
|
||||
|
||||
# Copy built frontend
|
||||
COPY --from=frontend-builder /app/dist ./dist
|
||||
|
||||
# Copy built Rust binary
|
||||
COPY --from=rust-builder /app/src-tauri/target/release/bili-shadowreplay .
|
||||
|
||||
# Expose port
|
||||
EXPOSE 3000
|
||||
|
||||
# Run the application
|
||||
CMD ["sh", "-c", "nscd && ./bili-shadowreplay"]
|
||||
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Xinrea
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
36
README.md
@@ -1,26 +1,32 @@
|
||||
# Bilibili ShadowReplay
|
||||
# BiliBili ShadowReplay
|
||||
|
||||

|
||||

|
||||
|
||||
> 点击关闭后程序仍会在后台运行,请找到托盘区的图标右键退出程序
|
||||

|
||||

|
||||

|
||||

|
||||
[](https://deepwiki.com/Xinrea/bili-shadowreplay)
|
||||
|
||||
## 介绍
|
||||
BiliBili ShadowReplay 是一个缓存直播并进行实时编辑投稿的工具。通过划定时间区间,并编辑简单的必需信息,即可完成直播切片以及投稿,将整个流程压缩到分钟级。同时,也支持对缓存的历史直播进行回放,以及相同的切片编辑投稿处理流程。
|
||||
|
||||
Bilibili ShadowReplay 是一个用于缓存B站直播的工具,可以将直播的视频缓存到本地,便于及时保存回放,方便后期剪辑工作。
|
||||
目前仅支持 B 站和抖音平台的直播。
|
||||
|
||||

|
||||
[](https://www.star-history.com/#Xinrea/bili-shadowreplay&Date)
|
||||
|
||||
除了在界面上手动操作外,还可以通过弹幕触发切片。
|
||||
## 安装和使用
|
||||
|
||||
> 只有管理员UID设置中的用户才能触发切片
|
||||

|
||||
|
||||

|
||||
前往网站查看说明:[BiliBili ShadowReplay](https://bsr.xinrea.cn/)
|
||||
|
||||
## 设置
|
||||
## 参与开发
|
||||
|
||||

|
||||
可以通过 [DeepWiki](https://deepwiki.com/Xinrea/bili-shadowreplay) 了解本项目。
|
||||
|
||||
- `缓存时长`:缓存的视频时长,单位为秒
|
||||
- `缓存目录`:缓存的视频存放目录
|
||||
- `切片目录`: 切片的视频存放目录
|
||||
- `管理员UID`:B站的UID,用于判断是否有权限在直播间通过弹幕触发切片;可设置多个,使用英文逗号分隔。
|
||||
贡献指南:[Contributing](.github/CONTRIBUTING.md)
|
||||
|
||||
## 赞助
|
||||
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
<img src="docs/public/images/donate.png" alt="donate" width="300">
|
||||
|
||||
2
_typos.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[default.extend-identifiers]
|
||||
pull_datas = "pull_datas"
|
||||
42
build.ps1
Normal file
@@ -0,0 +1,42 @@
|
||||
# run yarn tauri build
|
||||
|
||||
yarn tauri build
|
||||
yarn tauri build --debug
|
||||
|
||||
# rename the builds, "bili-shadowreplay" to "bili-shadowreplay-cpu"
|
||||
Get-ChildItem -Path ./src-tauri/target/release/bundle/msi/ | ForEach-Object {
|
||||
$newName = $_.Name -replace 'bili-shadowreplay', 'bili-shadowreplay-cpu'
|
||||
Rename-Item -Path $_.FullName -NewName $newName
|
||||
}
|
||||
Get-ChildItem -Path ./src-tauri/target/release/bundle/nsis/ | ForEach-Object {
|
||||
$newName = $_.Name -replace 'bili-shadowreplay', 'bili-shadowreplay-cpu'
|
||||
Rename-Item -Path $_.FullName -NewName $newName
|
||||
}
|
||||
|
||||
# rename the debug builds, "bili-shadowreplay" to "bili-shadowreplay-cpu"
|
||||
Get-ChildItem -Path ./src-tauri/target/debug/bundle/msi/ | ForEach-Object {
|
||||
$newName = $_.Name -replace 'bili-shadowreplay', 'bili-shadowreplay-cpu'
|
||||
Rename-Item -Path $_.FullName -NewName $newName
|
||||
}
|
||||
Get-ChildItem -Path ./src-tauri/target/debug/bundle/nsis/ | ForEach-Object {
|
||||
$newName = $_.Name -replace 'bili-shadowreplay', 'bili-shadowreplay-cpu'
|
||||
Rename-Item -Path $_.FullName -NewName $newName
|
||||
}
|
||||
|
||||
# move the build to the correct location
|
||||
Move-Item ./src-tauri/target/release/bundle/msi/* ./src-tauri/target/
|
||||
Move-Item ./src-tauri/target/release/bundle/nsis/* ./src-tauri/target/
|
||||
|
||||
# rename debug builds to add "-debug" suffix
|
||||
Get-ChildItem -Path ./src-tauri/target/debug/bundle/msi/ | ForEach-Object {
|
||||
$newName = $_.Name -replace '\.msi$', '-debug.msi'
|
||||
Rename-Item -Path $_.FullName -NewName $newName
|
||||
}
|
||||
Get-ChildItem -Path ./src-tauri/target/debug/bundle/nsis/ | ForEach-Object {
|
||||
$newName = $_.Name -replace '\.exe$', '-debug.exe'
|
||||
Rename-Item -Path $_.FullName -NewName $newName
|
||||
}
|
||||
|
||||
# move the debug builds to the correct location
|
||||
Move-Item ./src-tauri/target/debug/bundle/msi/* ./src-tauri/target/
|
||||
Move-Item ./src-tauri/target/debug/bundle/nsis/* ./src-tauri/target/
|
||||
78
cliff.toml
Normal file
@@ -0,0 +1,78 @@
|
||||
# git-cliff ~ default configuration file
|
||||
# https://git-cliff.org/docs/configuration
|
||||
#
|
||||
# Lines starting with "#" are comments.
|
||||
# Configuration options are organized into tables and keys.
|
||||
# See documentation for more information on available options.
|
||||
|
||||
[changelog]
|
||||
# template for the changelog header
|
||||
# header = """"""
|
||||
# template for the changelog body
|
||||
# https://keats.github.io/tera/docs/#introduction
|
||||
body = """
|
||||
{% if version %}\
|
||||
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
|
||||
{% else %}\
|
||||
## [unreleased]
|
||||
{% endif %}\
|
||||
{% for group, commits in commits | group_by(attribute="group") %}
|
||||
### {{ group | striptags | trim | upper_first }}
|
||||
{% for commit in commits %}
|
||||
- {% if commit.scope %}*({{ commit.scope }})* {% endif %}\
|
||||
{% if commit.breaking %}[**breaking**] {% endif %}\
|
||||
{{ commit.message | upper_first }} by @{{ commit.author.name }} - {{ commit.id }}\
|
||||
{% endfor %}
|
||||
{% endfor %}\n
|
||||
"""
|
||||
# template for the changelog footer
|
||||
# footer = """"""
|
||||
# remove the leading and trailing s
|
||||
trim = true
|
||||
# postprocessors
|
||||
postprocessors = [
|
||||
# { pattern = '<REPO>', replace = "https://github.com/orhun/git-cliff" }, # replace repository URL
|
||||
]
|
||||
# render body even when there are no releases to process
|
||||
# render_always = true
|
||||
# output file path
|
||||
# output = "test.md"
|
||||
|
||||
[git]
|
||||
# parse the commits based on https://www.conventionalcommits.org
|
||||
conventional_commits = true
|
||||
# filter out the commits that are not conventional
|
||||
filter_unconventional = true
|
||||
# process each line of a commit as an individual commit
|
||||
split_commits = false
|
||||
# regex for preprocessing the commit messages
|
||||
commit_preprocessors = [
|
||||
# Replace issue numbers
|
||||
#{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](<REPO>/issues/${2}))"},
|
||||
# Check spelling of the commit with https://github.com/crate-ci/typos
|
||||
# If the spelling is incorrect, it will be automatically fixed.
|
||||
#{ pattern = '.*', replace_command = 'typos --write-changes -' },
|
||||
]
|
||||
# regex for parsing and grouping commits
|
||||
commit_parsers = [
|
||||
{ message = "^feat", group = "<!-- 0 -->🚀 Features" },
|
||||
{ message = "^fix", group = "<!-- 1 -->🐛 Bug Fixes" },
|
||||
{ message = "^doc", group = "<!-- 3 -->📚 Documentation" },
|
||||
{ message = "^perf", group = "<!-- 4 -->⚡ Performance" },
|
||||
{ message = "^refactor", group = "<!-- 2 -->🚜 Refactor" },
|
||||
{ message = "^style", group = "<!-- 5 -->🎨 Styling" },
|
||||
{ message = "^test", group = "<!-- 6 -->🧪 Testing" },
|
||||
{ message = "^chore\\(release\\): prepare for", skip = true },
|
||||
{ message = "^chore\\(deps.*\\)", skip = true },
|
||||
{ message = "^chore\\(pr\\)", skip = true },
|
||||
{ message = "^chore\\(pull\\)", skip = true },
|
||||
{ message = "^chore|^ci", group = "<!-- 7 -->⚙️ Miscellaneous Tasks" },
|
||||
{ body = ".*security", group = "<!-- 8 -->🛡️ Security" },
|
||||
{ message = "^revert", group = "<!-- 9 -->◀️ Revert" },
|
||||
]
|
||||
# filter out the commits that are not matched by commit parsers
|
||||
filter_commits = false
|
||||
# sort the tags topologically
|
||||
topo_order = false
|
||||
# sort the commits inside sections by oldest/newest order
|
||||
sort_commits = "oldest"
|
||||
BIN
doc/clip.png
|
Before Width: | Height: | Size: 300 KiB |
|
Before Width: | Height: | Size: 24 KiB |
BIN
doc/main.png
|
Before Width: | Height: | Size: 127 KiB |
BIN
doc/output.png
|
Before Width: | Height: | Size: 28 KiB |
BIN
doc/setting.png
|
Before Width: | Height: | Size: 136 KiB |
78
docs/.vitepress/config.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { defineConfig } from "vitepress";
|
||||
import { withMermaid } from "vitepress-plugin-mermaid";
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default withMermaid({
|
||||
title: "BiliBili ShadowReplay",
|
||||
description: "直播录制/实时回放/剪辑/投稿工具",
|
||||
themeConfig: {
|
||||
// https://vitepress.dev/reference/default-theme-config
|
||||
nav: [
|
||||
{ text: "Home", link: "/" },
|
||||
{
|
||||
text: "Releases",
|
||||
link: "https://github.com/Xinrea/bili-shadowreplay/releases",
|
||||
},
|
||||
],
|
||||
|
||||
sidebar: [
|
||||
{
|
||||
text: "开始使用",
|
||||
items: [
|
||||
{
|
||||
text: "安装准备",
|
||||
items: [
|
||||
{
|
||||
text: "桌面端安装",
|
||||
link: "/getting-started/installation/desktop",
|
||||
},
|
||||
{
|
||||
text: "Docker 安装",
|
||||
link: "/getting-started/installation/docker",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "配置使用",
|
||||
items: [
|
||||
{ text: "账号配置", link: "/getting-started/config/account" },
|
||||
{ text: "FFmpeg 配置", link: "/getting-started/config/ffmpeg" },
|
||||
{ text: "Whisper 配置", link: "/getting-started/config/whisper" },
|
||||
{ text: "LLM 配置", link: "/getting-started/config/llm" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "说明文档",
|
||||
items: [
|
||||
{
|
||||
text: "功能说明",
|
||||
items: [
|
||||
{ text: "工作流程", link: "/usage/features/workflow" },
|
||||
{ text: "直播间管理", link: "/usage/features/room" },
|
||||
{ text: "切片功能", link: "/usage/features/clip" },
|
||||
{ text: "字幕功能", link: "/usage/features/subtitle" },
|
||||
{ text: "弹幕功能", link: "/usage/features/danmaku" },
|
||||
{ text: "Webhook", link: "/usage/features/webhook" },
|
||||
],
|
||||
},
|
||||
{ text: "常见问题", link: "/usage/faq" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "开发文档",
|
||||
items: [
|
||||
{
|
||||
text: "DeepWiki",
|
||||
link: "https://deepwiki.com/Xinrea/bili-shadowreplay",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
socialLinks: [
|
||||
{ icon: "github", link: "https://github.com/Xinrea/bili-shadowreplay" },
|
||||
],
|
||||
},
|
||||
});
|
||||
12
docs/getting-started/config/account.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# 账号配置
|
||||
|
||||
要添加直播间,至少需要配置一个同平台的账号。在账号页面,你可以通过添加账号按钮添加一个账号。
|
||||
|
||||
- B 站账号:目前支持扫码登录和 Cookie 手动配置两种方式,推荐使用扫码登录
|
||||
- 抖音账号:目前仅支持 Cookie 手动配置登陆
|
||||
|
||||
## 抖音账号配置
|
||||
|
||||
首先确保已经登录抖音,然后打开[个人主页](https://www.douyin.com/user/self),右键单击网页,在菜单中选择 `检查(Inspect)`,打开开发者工具,切换到 `网络(Network)` 选项卡,然后刷新网页,此时能在列表中找到 `self` 请求(一般是列表中第一个),单击该请求,查看`请求标头`,在 `请求标头` 中找到 `Cookie`,复制该字段的值,粘贴到配置页面的 `Cookie` 输入框中,要注意复制完全。
|
||||
|
||||

|
||||
47
docs/getting-started/config/ffmpeg.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# FFmpeg 配置
|
||||
|
||||
FFmpeg 是一个开源的音视频处理工具,支持多种格式的音视频编解码、转码、剪辑、合并等操作。
|
||||
在本项目中,FFmpeg 用于切片生成以及字幕和弹幕的硬编码处理,因此需要确保安装了 FFmpeg。
|
||||
|
||||
## MacOS
|
||||
|
||||
在 MacOS 上安装 FFmpeg 非常简单,可以使用 Homebrew 来安装:
|
||||
|
||||
```bash
|
||||
brew install ffmpeg
|
||||
```
|
||||
|
||||
如果没有安装 Homebrew,可以参考 [Homebrew 官网](https://brew.sh/) 进行安装。
|
||||
|
||||
## Linux
|
||||
|
||||
在 Linux 上安装 FFmpeg 可以使用系统自带的包管理器进行安装,例如:
|
||||
|
||||
- Ubuntu/Debian 系统:
|
||||
|
||||
```bash
|
||||
sudo apt install ffmpeg
|
||||
```
|
||||
|
||||
- Fedora 系统:
|
||||
|
||||
```bash
|
||||
sudo dnf install ffmpeg
|
||||
```
|
||||
|
||||
- Arch Linux 系统:
|
||||
|
||||
```bash
|
||||
sudo pacman -S ffmpeg
|
||||
```
|
||||
|
||||
- CentOS 系统:
|
||||
|
||||
```bash
|
||||
sudo yum install epel-release
|
||||
sudo yum install ffmpeg
|
||||
```
|
||||
|
||||
## Windows
|
||||
|
||||
Windows 版本安装后,FFmpeg 已经放置在了程序目录下,因此不需要额外安装。
|
||||
9
docs/getting-started/config/llm.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# LLM 配置
|
||||
|
||||

|
||||
|
||||
助手页面的 AI Agent 助手功能需要配置大模型,目前仅支持配置 OpenAI 协议兼容的大模型服务。
|
||||
|
||||
本软件并不提供大模型服务,请自行选择服务提供商。要注意,使用 AI Agent 助手需要消耗比普通对话更多的 Token,请确保有足够的 Token 余额。
|
||||
|
||||
此外,AI Agent 的功能需要大模型支持 Function Calling 功能,否则无法正常调用工具。
|
||||
46
docs/getting-started/config/whisper.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Whisper 配置
|
||||
|
||||
要使用 AI 字幕识别功能,需要在设置页面配置 Whisper。目前可以选择使用本地运行 Whisper 模型,或是使用在线的 Whisper 服务(通常需要付
|
||||
费获取 API Key)。
|
||||
|
||||
> [!NOTE]
|
||||
> 其实有许多更好的中文字幕识别解决方案,但是这类服务通常需要将文件上传到对象存储后异步处理,考虑到实现的复杂度,选择了使用本地运行 Whisper 模型或是使
|
||||
> 用在线的 Whisper 服务,在请求返回时能够直接获取字幕生成结果。
|
||||
|
||||
## 本地运行 Whisper 模型
|
||||
|
||||

|
||||
|
||||
如果需要使用本地运行 Whisper 模型进行字幕生成,需要下载 Whisper.cpp 模型,并在设置中指定模型路径。模型文件可以从网络上下载,例如:
|
||||
|
||||
- [Whisper.cpp(国内镜像,内容较旧)](https://www.modelscope.cn/models/cjc1887415157/whisper.cpp/files)
|
||||
- [Whisper.cpp](https://huggingface.co/ggerganov/whisper.cpp/tree/main)
|
||||
|
||||
可以跟据自己的需求选择不同的模型,要注意带有 `en` 的模型是英文模型,其他模型为多语言模型。
|
||||
|
||||
模型文件的大小通常意味着其在运行时资源占用的大小,因此请根据电脑配置选择合适的模型。此外,GPU 版本与 CPU 版本在字幕生成速度上存在**巨大差异**,因此
|
||||
推荐使用 GPU 版本进行本地处理(目前仅支持 Nvidia GPU)。
|
||||
|
||||
## 使用在线 Whisper 服务
|
||||
|
||||

|
||||
|
||||
如果需要使用在线的 Whisper 服务进行字幕生成,可以在设置中切换为在线 Whisper,并配置好 API Key。提供 Whisper 服务的平台并非只有
|
||||
OpenAI 一家,许多云服务平台也提供 Whisper 服务。
|
||||
|
||||
## 字幕识别质量的调优
|
||||
|
||||
目前在设置中支持设置 Whisper 语言和 Whisper 提示词,这些设置对于本地和在线的 Whisper 服务都有效。
|
||||
|
||||
通常情况下,`auto` 语言选项能够自动识别语音语言,并生成相应语言的字幕。如果需要生成其他语言的字幕,或是生成的字幕语言不匹配,可以手动配置指定的语言。
|
||||
根据 OpenAI 官方文档中对于 `language` 参数的描述,目前支持的语言包括
|
||||
|
||||
Afrikaans, Arabic, Armenian, Azerbaijani, Belarusian, Bosnian, Bulgarian,
|
||||
Catalan, Chinese, Croatian, Czech, Danish, Dutch, English, Estonian, Finnish,
|
||||
French, Galician, German, Greek, Hebrew, Hindi, Hungarian, Icelandic,
|
||||
Indonesian, Italian, Japanese, Kannada, Kazakh, Korean, Latvian, Lithuanian,
|
||||
Macedonian, Malay, Marathi, Maori, Nepali, Norwegian, Persian, Polish,
|
||||
Portuguese, Romanian, Russian, Serbian, Slovak, Slovenian, Spanish, Swahili,
|
||||
Swedish, Tagalog, Tamil, Thai, Turkish, Ukrainian, Urdu, Vietnamese, and Welsh.
|
||||
|
||||
提示词可以优化生成的字幕的风格(也会一定程度上影响质量),要注意,Whisper 无法理解复杂的提示词,你可以在提示词中使用一些简单的描述,让其在选择词汇时使用偏向于提示词所描述的领域相关的词汇,以避免出现毫不相干领域的词汇;或是让它在标点符号的使用上参照提示词的风格。
|
||||
24
docs/getting-started/installation/desktop.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# 桌面端安装
|
||||
|
||||
桌面端目前提供了 Windows、Linux 和 MacOS 三个平台的安装包。
|
||||
|
||||
由于程序会对账号等敏感信息进行管理,请从信任的来源进行下载;所有版本均可在 [GitHub Releases](https://github.com/Xinrea/bili-shadowreplay/releases) 页面下载安装。
|
||||
|
||||
对于 MacOS 用户,请先手动安装 FFmpeg,详情见 [FFmpeg 配置](../config/ffmpeg.md)。
|
||||
|
||||
## Windows
|
||||
|
||||
由于程序内置 Whisper 字幕识别模型支持,Windows 版本分为两种:
|
||||
|
||||
- **普通版本**:内置了 Whisper GPU 加速,字幕识别较快,体积较大,只支持 Nvidia 显卡
|
||||
- **CPU 版本**: 使用 CPU 进行字幕识别推理,速度较慢
|
||||
|
||||
请根据自己的显卡情况选择合适的版本进行下载。
|
||||
|
||||
## Linux
|
||||
|
||||
Linux 版本目前仅支持使用 CPU 推理,且测试较少,可能存在一些问题,遇到问题请及时反馈。
|
||||
|
||||
## MacOS
|
||||
|
||||
MacOS 版本内置 Metal GPU 加速;安装后首次运行,会提示无法打开从网络下载的软件,请在设置-隐私与安全性下,选择仍然打开以允许程序运行。
|
||||
41
docs/getting-started/installation/docker.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Docker 部署
|
||||
|
||||
BiliBili ShadowReplay 提供了服务端部署的能力,提供 Web 控制界面,可以用于在服务器等无图形界面环境下部署使用。
|
||||
|
||||
## 镜像获取
|
||||
|
||||
```bash
|
||||
# 拉取最新版本
|
||||
docker pull ghcr.io/xinrea/bili-shadowreplay:latest
|
||||
# 拉取指定版本
|
||||
docker pull ghcr.io/xinrea/bili-shadowreplay:2.5.0
|
||||
# 速度太慢?从镜像源拉取
|
||||
docker pull ghcr.nju.edu.cn/xinrea/bili-shadowreplay:latest
|
||||
```
|
||||
|
||||
## 镜像使用
|
||||
|
||||
使用方法:
|
||||
|
||||
```bash
|
||||
sudo docker run -it -d\
|
||||
-p 3000:3000 \
|
||||
-v $DATA_DIR:/app/data \
|
||||
-v $CACHE_DIR:/app/cache \
|
||||
-v $OUTPUT_DIR:/app/output \
|
||||
-v $WHISPER_MODEL:/app/whisper_model.bin \
|
||||
--name bili-shadowreplay \
|
||||
ghcr.io/xinrea/bili-shadowreplay:latest
|
||||
```
|
||||
|
||||
其中:
|
||||
|
||||
- `$DATA_DIR`:为数据目录,对应于桌面版的数据目录,
|
||||
|
||||
Windows 下位于 `C:\Users\{用户名}\AppData\Roaming\cn.vjoi.bilishadowreplay`;
|
||||
|
||||
MacOS 下位于 `/Users/{user}/Library/Application Support/cn.vjoi.bilishadowreplay`
|
||||
|
||||
- `$CACHE_DIR`:为缓存目录,对应于桌面版的缓存目录;
|
||||
- `$OUTPUT_DIR`:为输出目录,对应于桌面版的输出目录;
|
||||
- `$WHISPER_MODEL`:为 Whisper 模型文件路径,对应于桌面版的 Whisper 模型文件路径。
|
||||
70
docs/index.md
Normal file
@@ -0,0 +1,70 @@
|
||||
---
|
||||
# https://vitepress.dev/reference/default-theme-home-page
|
||||
layout: home
|
||||
|
||||
hero:
|
||||
name: "BiliBili ShadowReplay"
|
||||
tagline: "直播录制/实时回放/剪辑/投稿工具"
|
||||
image:
|
||||
src: /images/icon.png
|
||||
alt: BiliBili ShadowReplay
|
||||
actions:
|
||||
- theme: brand
|
||||
text: 开始使用
|
||||
link: /getting-started/installation/desktop
|
||||
- theme: alt
|
||||
text: 说明文档
|
||||
link: /usage/features/workflow
|
||||
|
||||
features:
|
||||
- icon: 📹
|
||||
title: 直播录制
|
||||
details: 缓存直播流,直播结束自动生成整场录播
|
||||
- icon: 📺
|
||||
title: 实时回放
|
||||
details: 实时回放当前直播,不错过任何内容
|
||||
- icon: ✂️
|
||||
title: 剪辑投稿
|
||||
details: 剪辑切片,封面编辑,一键投稿
|
||||
- icon: 📝
|
||||
title: 字幕生成
|
||||
details: 支持 Wisper 模型生成字幕,编辑与压制
|
||||
- icon: 📄
|
||||
title: 弹幕支持
|
||||
details: 直播间弹幕压制到切片,并支持直播弹幕发送和导出
|
||||
- icon: 🌐
|
||||
title: 多直播平台支持
|
||||
details: 目前支持 B 站和抖音直播
|
||||
- icon: 🔍
|
||||
title: 云端部署
|
||||
details: 支持 Docker 部署,提供 Web 控制界面
|
||||
- icon: 🤖
|
||||
title: AI Agent 支持
|
||||
details: 支持 AI 助手管理录播,分析直播内容,生成切片
|
||||
---
|
||||
|
||||
## 总览
|
||||
|
||||

|
||||
|
||||
## 直播间管理
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## 账号管理
|
||||
|
||||

|
||||
|
||||
## 预览窗口
|
||||
|
||||

|
||||
|
||||
## 封面编辑
|
||||
|
||||

|
||||
|
||||
## 设置
|
||||
|
||||

|
||||
BIN
docs/public/images/accounts.png
Normal file
|
After Width: | Height: | Size: 195 KiB |
BIN
docs/public/images/ai_agent.png
Normal file
|
After Width: | Height: | Size: 261 KiB |
BIN
docs/public/images/archives.png
Normal file
|
After Width: | Height: | Size: 434 KiB |
BIN
docs/public/images/clip_manage.png
Normal file
|
After Width: | Height: | Size: 234 KiB |
BIN
docs/public/images/clip_preview.png
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
docs/public/images/cover_edit.png
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
docs/public/images/donate.png
Normal file
|
After Width: | Height: | Size: 474 KiB |
BIN
docs/public/images/douyin_cookie.png
Normal file
|
After Width: | Height: | Size: 548 KiB |
BIN
docs/public/images/header.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
docs/public/images/icon.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
docs/public/images/livewindow.png
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
docs/public/images/model_config.png
Normal file
|
After Width: | Height: | Size: 383 KiB |
BIN
docs/public/images/rooms.png
Normal file
|
After Width: | Height: | Size: 949 KiB |
BIN
docs/public/images/settings.png
Normal file
|
After Width: | Height: | Size: 244 KiB |
BIN
docs/public/images/summary.png
Normal file
|
After Width: | Height: | Size: 372 KiB |
BIN
docs/public/images/tasks.png
Normal file
|
After Width: | Height: | Size: 201 KiB |
BIN
docs/public/images/whisper_local.png
Normal file
|
After Width: | Height: | Size: 194 KiB |
BIN
docs/public/images/whisper_online.png
Normal file
|
After Width: | Height: | Size: 199 KiB |
BIN
docs/public/images/whole_clip.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
docs/public/videos/deeplinking.mp4
Normal file
BIN
docs/public/videos/room_remove.mp4
Normal file
31
docs/usage/faq.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# 常见问题
|
||||
|
||||
## 一、在哪里反馈问题?
|
||||
|
||||
你可以前往 [Github Issues](https://github.com/Xinrea/bili-shadowreplay/issues/new?template=bug_report.md) 提交问题,或是加入[反馈交流群](https://qm.qq.com/q/v4lrE6gyum)。
|
||||
|
||||
1. 在提交问题前,请先阅读其它常见问题,确保你的问题已有解答;
|
||||
2. 其次,请确保你的程序已更新到最新版本;
|
||||
3. 最后,你应准备好提供你的程序日志文件,以便更好地定位问题。
|
||||
|
||||
## 二、在哪里查看日志?
|
||||
|
||||
在主窗口的设置页面,提供了一键打开日志目录所在位置的按钮。当你打开日志目录所在位置后,进入 `logs` 目录,找到后缀名为 `log` 的文件,这便是你需要提供给开发者的日志文件。
|
||||
|
||||
## 三、无法预览直播或是生成切片
|
||||
|
||||
如果你是 macOS 或 Linux 用户,请确保你已安装了 `ffmpeg` 和 `ffprobe` 工具;如果不知道如何安装,请参考 [FFmpeg 配置](/getting-started/config/ffmpeg)。
|
||||
|
||||
如果你是 Windows 用户,程序目录下应当自带了 `ffmpeg` 和 `ffprobe` 工具,如果无法预览直播或是生成切片,请向开发者反馈。
|
||||
|
||||
## 四、添加 B 站直播间出现 -352 错误
|
||||
|
||||
`-352` 错误是由 B 站风控机制导致的,如果你添加了大量的 B 站直播间进行录制,可以在设置页面调整直播间状态的检查间隔,尽量避免风控;如果你在直播间数量较少的情况下出现该错误,请向开发者反馈。
|
||||
|
||||
## 五、录播为什么都是碎片文件?
|
||||
|
||||
缓存目录下的录播文件并非用于直接播放或是投稿,而是用于直播流的预览与实时回放。如果你需要录播文件用于投稿,请打开对应录播的预览界面,使用快捷键创建选区,生成所需范围的切片,切片文件为常规的 mp4 文件,位于你所设置的切片目录下。
|
||||
|
||||
如果你将 BSR 作为单纯的录播软件使用,在设置中可以开启`整场录播生成`,这样在直播结束后,BSR 会自动生成整场录播的切片。
|
||||
|
||||

|
||||
1
docs/usage/features/clip.md
Normal file
@@ -0,0 +1 @@
|
||||
# 切片
|
||||
1
docs/usage/features/danmaku.md
Normal file
@@ -0,0 +1 @@
|
||||
# 弹幕
|
||||
40
docs/usage/features/room.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 直播间
|
||||
|
||||
> [!WARNING]
|
||||
> 在添加管理直播间前,请确保账号列表中有对应平台的可用账号。
|
||||
|
||||
## 添加直播间
|
||||
|
||||
### 手动添加直播间
|
||||
|
||||
你可以在 BSR 直播间页面,点击按钮手动添加直播间。你需要选择平台,并输入直播间号。
|
||||
|
||||
直播间号通常是直播间网页地址尾部的遗传数字,例如 `https://live.bilibili.com/123456` 中的 `123456`,或是 `https://live.douyin.com/123456` 中的 `123456`。
|
||||
|
||||
抖音直播间比较特殊,当未开播时,你无法找到直播间的入口,因此你需要当直播间开播时找到直播间网页地址,并记录其直播间号。
|
||||
|
||||
抖音直播间需要输入主播的 sec_uid,你可以在主播主页的 URL 中找到,例如 `https://www.douyin.com/user/MS4wLjABAAAA` 中的 `MS4wLjABAAAA`。
|
||||
|
||||
### 使用 DeepLinking 快速添加直播间
|
||||
|
||||
<!-- MD033 -->
|
||||
|
||||
<video src="/videos/deeplinking.mp4" loop autoplay muted style="border-radius: 10px;"></video>
|
||||
|
||||
在浏览器中观看直播时,替换地址栏中直播间地址中的 `https://` 为 `bsr://` 即可快速唤起 BSR 添加直播间。
|
||||
|
||||
## 启用/禁用直播间
|
||||
|
||||
你可以点击直播间卡片右上角的菜单按钮,选择启用/禁用直播间。
|
||||
|
||||
- 启用后,当直播间开播时,会自动开始录制
|
||||
- 禁用后,当直播间开播时,不会自动开始录制
|
||||
|
||||
## 移除直播间
|
||||
|
||||
> [!CAUTION]
|
||||
> 移除直播间后,该直播间相关的所有录播都会被删除,请谨慎操作。
|
||||
|
||||
你可以点击直播间卡片右上角的菜单按钮,选择移除直播间。
|
||||
|
||||
<video src="/videos/room_remove.mp4" loop autoplay muted style="border-radius: 10px;"></video>
|
||||
1
docs/usage/features/subtitle.md
Normal file
@@ -0,0 +1 @@
|
||||
# 字幕
|
||||
245
docs/usage/features/webhook.md
Normal file
@@ -0,0 +1,245 @@
|
||||
# Webhook
|
||||
|
||||
> [!NOTE]
|
||||
> 你可以使用 <https://webhook.site> 来测试 Webhook 功能。
|
||||
|
||||
## 设置 Webhook
|
||||
|
||||
打开 BSR 设置页面,在基础设置中设置 Webhook 地址。
|
||||
|
||||
## Webhook Events
|
||||
|
||||
### 直播间相关
|
||||
|
||||
#### 添加直播间
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "a96a5e9f-9857-4c13-b889-91da2ace208a",
|
||||
"event": "recorder.added",
|
||||
"payload": {
|
||||
"room_id": "26966466",
|
||||
"created_at": "2025-09-07T03:33:14.258796+00:00",
|
||||
"platform": "bilibili",
|
||||
"auto_start": true,
|
||||
"extra": ""
|
||||
},
|
||||
"timestamp": 1757215994
|
||||
}
|
||||
```
|
||||
|
||||
#### 移除直播间
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "e33623d4-e040-4390-88f5-d351ceeeace7",
|
||||
"event": "recorder.removed",
|
||||
"payload": {
|
||||
"room_id": "27183290",
|
||||
"created_at": "2025-08-30T10:54:18.569198+00:00",
|
||||
"platform": "bilibili",
|
||||
"auto_start": true,
|
||||
"extra": ""
|
||||
},
|
||||
"timestamp": 1757217015
|
||||
}
|
||||
```
|
||||
|
||||
### 直播相关
|
||||
|
||||
> [!NOTE]
|
||||
> 直播开始和结束,不意味着录制的开始和结束。
|
||||
|
||||
#### 直播开始
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "f12f3424-f7d8-4b2f-a8b7-55477411482e",
|
||||
"event": "live.started",
|
||||
"payload": {
|
||||
"room_id": "843610",
|
||||
"room_info": {
|
||||
"room_id": "843610",
|
||||
"room_title": "登顶!",
|
||||
"room_cover": "https://i0.hdslb.com/bfs/live/new_room_cover/73aea43f4b4624c314d62fea4b424822fb506dfb.jpg"
|
||||
},
|
||||
"user_info": {
|
||||
"user_id": "475210",
|
||||
"user_name": "Xinrea",
|
||||
"user_avatar": "https://i1.hdslb.com/bfs/face/91beb3bf444b295fe12bae1f3dc6d9fc4fe4c224.jpg"
|
||||
},
|
||||
"total_length": 0,
|
||||
"current_live_id": "",
|
||||
"live_status": false,
|
||||
"is_recording": false,
|
||||
"auto_start": true,
|
||||
"platform": "bilibili"
|
||||
},
|
||||
"timestamp": 1757217190
|
||||
}
|
||||
```
|
||||
|
||||
#### 直播结束
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "e8b0756a-02f9-4655-b5ae-a170bf9547bd",
|
||||
"event": "live.ended",
|
||||
"payload": {
|
||||
"room_id": "843610",
|
||||
"room_info": {
|
||||
"room_id": "843610",
|
||||
"room_title": "登顶!",
|
||||
"room_cover": "https://i0.hdslb.com/bfs/live/new_room_cover/73aea43f4b4624c314d62fea4b424822fb506dfb.jpg"
|
||||
},
|
||||
"user_info": {
|
||||
"user_id": "475210",
|
||||
"user_name": "Xinrea",
|
||||
"user_avatar": "https://i1.hdslb.com/bfs/face/91beb3bf444b295fe12bae1f3dc6d9fc4fe4c224.jpg"
|
||||
},
|
||||
"total_length": 0,
|
||||
"current_live_id": "",
|
||||
"live_status": true,
|
||||
"is_recording": false,
|
||||
"auto_start": true,
|
||||
"platform": "bilibili"
|
||||
},
|
||||
"timestamp": 1757217365
|
||||
}
|
||||
```
|
||||
|
||||
### 录播相关
|
||||
|
||||
#### 开始录制
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "5ec1ea10-2b31-48fd-8deb-f2d7d2ea5985",
|
||||
"event": "record.started",
|
||||
"payload": {
|
||||
"room_id": "26966466",
|
||||
"room_info": {
|
||||
"room_id": "26966466",
|
||||
"room_title": "早安獭獭栞!下播前抽fufu",
|
||||
"room_cover": "https://i0.hdslb.com/bfs/live/user_cover/b810c36855168034557e905e5916b1dba1761fa4.jpg"
|
||||
},
|
||||
"user_info": {
|
||||
"user_id": "1609526545",
|
||||
"user_name": "栞栞Shiori",
|
||||
"user_avatar": "https://i1.hdslb.com/bfs/face/47e8dbabb895de44ec6cace085d4dc1d40307277.jpg"
|
||||
},
|
||||
"total_length": 0,
|
||||
"current_live_id": "1757216045412",
|
||||
"live_status": true,
|
||||
"is_recording": false,
|
||||
"auto_start": true,
|
||||
"platform": "bilibili"
|
||||
},
|
||||
"timestamp": 1757216045
|
||||
}
|
||||
```
|
||||
|
||||
#### 结束录制
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "56fd03e5-3965-4c2e-a6a9-bb6932347eb3",
|
||||
"event": "record.ended",
|
||||
"payload": {
|
||||
"room_id": "26966466",
|
||||
"room_info": {
|
||||
"room_id": "26966466",
|
||||
"room_title": "早安獭獭栞!下播前抽fufu",
|
||||
"room_cover": "https://i0.hdslb.com/bfs/live/user_cover/b810c36855168034557e905e5916b1dba1761fa4.jpg"
|
||||
},
|
||||
"user_info": {
|
||||
"user_id": "1609526545",
|
||||
"user_name": "栞栞Shiori",
|
||||
"user_avatar": "https://i1.hdslb.com/bfs/face/47e8dbabb895de44ec6cace085d4dc1d40307277.jpg"
|
||||
},
|
||||
"total_length": 52.96700000000001,
|
||||
"current_live_id": "1757215994597",
|
||||
"live_status": true,
|
||||
"is_recording": true,
|
||||
"auto_start": true,
|
||||
"platform": "bilibili"
|
||||
},
|
||||
"timestamp": 1757216040
|
||||
}
|
||||
```
|
||||
|
||||
#### 删除录播
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "c32bc811-ab4b-49fd-84c7-897727905d16",
|
||||
"event": "archive.deleted",
|
||||
"payload": {
|
||||
"platform": "bilibili",
|
||||
"live_id": "1756607084705",
|
||||
"room_id": "1967212929",
|
||||
"title": "灶台O.o",
|
||||
"length": 9,
|
||||
"size": 1927112,
|
||||
"created_at": "2025-08-31T02:24:44.728616+00:00",
|
||||
"cover": "bilibili/1967212929/1756607084705/cover.jpg"
|
||||
},
|
||||
"timestamp": 1757176219
|
||||
}
|
||||
```
|
||||
|
||||
### 切片相关
|
||||
|
||||
#### 切片生成
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "f542e0e1-688b-4f1a-8ce1-e5e51530cf5d",
|
||||
"event": "clip.generated",
|
||||
"payload": {
|
||||
"id": 316,
|
||||
"room_id": "27183290",
|
||||
"cover": "[27183290][1757172501727][一起看凡人修仙传][2025-09-07_00-16-11].jpg",
|
||||
"file": "[27183290][1757172501727][一起看凡人修仙传][2025-09-07_00-16-11].mp4",
|
||||
"note": "",
|
||||
"length": 121,
|
||||
"size": 53049119,
|
||||
"status": 0,
|
||||
"bvid": "",
|
||||
"title": "",
|
||||
"desc": "",
|
||||
"tags": "",
|
||||
"area": 0,
|
||||
"created_at": "2025-09-07T00:16:11.747461+08:00",
|
||||
"platform": "bilibili"
|
||||
},
|
||||
"timestamp": 1757175371
|
||||
}
|
||||
```
|
||||
|
||||
#### 切片删除
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "5c7ca728-753d-4a7d-a0b4-02c997ad2f92",
|
||||
"event": "clip.deleted",
|
||||
"payload": {
|
||||
"id": 313,
|
||||
"room_id": "27183290",
|
||||
"cover": "[27183290][1756903953470][不出非洲之心不下播][2025-09-03_21-10-54].jpg",
|
||||
"file": "[27183290][1756903953470][不出非洲之心不下播][2025-09-03_21-10-54].mp4",
|
||||
"note": "",
|
||||
"length": 32,
|
||||
"size": 18530098,
|
||||
"status": 0,
|
||||
"bvid": "",
|
||||
"title": "",
|
||||
"desc": "",
|
||||
"tags": "",
|
||||
"area": 0,
|
||||
"created_at": "2025-09-03T21:10:54.943682+08:00",
|
||||
"platform": "bilibili"
|
||||
},
|
||||
"timestamp": 1757147617
|
||||
}
|
||||
```
|
||||
30
docs/usage/features/workflow.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# 工作流程
|
||||
|
||||
- 直播间:各个平台的直播间
|
||||
- 录播:直播流的存档,每次录制会自动生成一场录播记录
|
||||
- 切片:从直播流中剪切生成的视频片段
|
||||
- 投稿:将切片上传到各个平台(目前仅支持 Bilibili)
|
||||
|
||||
下图展示了它们之间的关系:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[直播间] -->|录制| B[录播 01]
|
||||
A -->|录制| C[录播 02]
|
||||
A -->|录制| E[录播 N]
|
||||
|
||||
B --> F[直播流预览窗口]
|
||||
|
||||
F -->|区间生成| G[切片 01]
|
||||
F -->|区间生成| H[切片 02]
|
||||
F -->|区间生成| I[切片 N]
|
||||
|
||||
G --> J[切片预览窗口]
|
||||
|
||||
J -->|字幕压制| K[新切片]
|
||||
|
||||
K --> J
|
||||
|
||||
J -->|投稿| L[Bilibili]
|
||||
|
||||
```
|
||||
32
ffmpeg_setup.ps1
Normal file
@@ -0,0 +1,32 @@
|
||||
# download ffmpeg from https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip
|
||||
$ffmpegUrl = "https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip"
|
||||
$ffmpegPath = "ffmpeg-release-essentials.zip"
|
||||
# download the file if it doesn't exist
|
||||
if (-not (Test-Path $ffmpegPath)) {
|
||||
Invoke-WebRequest -Uri $ffmpegUrl -OutFile $ffmpegPath
|
||||
}
|
||||
# extract the 7z file
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
$extractPath = "ffmpeg"
|
||||
# check if the directory exists, if not create it
|
||||
if (-not (Test-Path $extractPath)) {
|
||||
New-Item -ItemType Directory -Path $extractPath
|
||||
}
|
||||
|
||||
[System.IO.Compression.ZipFile]::ExtractToDirectory($ffmpegPath, $extractPath)
|
||||
|
||||
# move the bin directory to the src-tauri directory
|
||||
# ffmpeg/ffmpeg-*-essentials_build/bin to src-tauri
|
||||
$ffmpegDir = Get-ChildItem -Path $extractPath -Directory | Where-Object { $_.Name -match "ffmpeg-.*-essentials_build" }
|
||||
if ($ffmpegDir) {
|
||||
$binPath = Join-Path $ffmpegDir.FullName "bin"
|
||||
} else {
|
||||
Write-Host "No ffmpeg directory found in the extracted files."
|
||||
exit 1
|
||||
}
|
||||
|
||||
$destPath = Join-Path $PSScriptRoot "src-tauri"
|
||||
Copy-Item -Path "$binPath/*" -Destination $destPath -Recurse
|
||||
|
||||
# remove the extracted directory
|
||||
Remove-Item $extractPath -Recurse -Force
|
||||
25
index.html
@@ -1,16 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>BiliBili ShadowReplay</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
|
||||
<html lang="zh-cn">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>BiliBili ShadowReplay</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
13
index_clip.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-cn">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>切片窗口</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main_clip.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
63
index_live.html
Normal file
@@ -0,0 +1,63 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-cn" class="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="shaka-player/controls.css" />
|
||||
<link rel="stylesheet" href="shaka-player/youtube-theme.css" />
|
||||
<script src="shaka-player/shaka-player.ui.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="src/main_live.ts"></script>
|
||||
<style>
|
||||
input[type="range"]::-webkit-slider-thumb {
|
||||
width: 12px;
|
||||
/* 设置滑块按钮宽度 */
|
||||
height: 12px;
|
||||
/* 设置滑块按钮高度 */
|
||||
border-radius: 50%;
|
||||
/* 设置为圆形 */
|
||||
}
|
||||
|
||||
html {
|
||||
scrollbar-face-color: #646464;
|
||||
scrollbar-base-color: #646464;
|
||||
scrollbar-3dlight-color: #646464;
|
||||
scrollbar-highlight-color: #646464;
|
||||
scrollbar-track-color: #000;
|
||||
scrollbar-arrow-color: #000;
|
||||
scrollbar-shadow-color: #646464;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 3px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-button {
|
||||
background-color: #666;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: #646464;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track-piece {
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
height: 50px;
|
||||
background-color: #666;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-corner {
|
||||
background-color: #646464;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
||||
3936
package-lock.json
generated
43
package.json
@@ -1,25 +1,50 @@
|
||||
{
|
||||
"name": "bili-shadowreplay",
|
||||
"private": true,
|
||||
"version": "0.0.4",
|
||||
"version": "2.16.2",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"check": "svelte-check --tsconfig ./tsconfig.json",
|
||||
"tauri": "tauri"
|
||||
"tauri": "tauri",
|
||||
"docs:dev": "vitepress dev docs",
|
||||
"docs:build": "vitepress build docs",
|
||||
"docs:preview": "vitepress preview docs",
|
||||
"bump": "node scripts/bump.cjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^1.2.0"
|
||||
"@langchain/core": "^0.3.64",
|
||||
"@langchain/deepseek": "^0.1.0",
|
||||
"@langchain/langgraph": "^0.3.10",
|
||||
"@langchain/ollama": "^0.2.3",
|
||||
"@tauri-apps/api": "^2.6.2",
|
||||
"@tauri-apps/plugin-deep-link": "~2",
|
||||
"@tauri-apps/plugin-dialog": "~2",
|
||||
"@tauri-apps/plugin-fs": "~2",
|
||||
"@tauri-apps/plugin-http": "~2",
|
||||
"@tauri-apps/plugin-notification": "~2",
|
||||
"@tauri-apps/plugin-os": "~2",
|
||||
"@tauri-apps/plugin-shell": "~2",
|
||||
"@tauri-apps/plugin-sql": "~2",
|
||||
"lucide-svelte": "^0.479.0",
|
||||
"marked": "^16.1.1",
|
||||
"qrcode": "^1.5.4",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"wavesurfer.js": "^7.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "^2.0.0",
|
||||
"@tauri-apps/cli": "^1.2.2",
|
||||
"@tauri-apps/cli": "^2.4.1",
|
||||
"@tsconfig/svelte": "^3.0.0",
|
||||
"@types/node": "^18.7.10",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"daisyui": "^2.51.5",
|
||||
"flowbite": "^2.5.1",
|
||||
"flowbite-svelte": "^0.46.16",
|
||||
"flowbite-svelte-icons": "^1.6.1",
|
||||
"mermaid": "^11.9.0",
|
||||
"postcss": "^8.4.21",
|
||||
"svelte": "^3.54.0",
|
||||
"svelte-check": "^3.0.0",
|
||||
@@ -27,7 +52,9 @@
|
||||
"tailwindcss": "^3.3.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^4.6.4",
|
||||
"vite": "^4.0.0"
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^4.0.0",
|
||||
"vitepress": "^1.6.3",
|
||||
"vitepress-plugin-mermaid": "^2.0.17"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
public/imgs/bilibili.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
public/imgs/bilibili_avatar.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
public/imgs/donate.png
Normal file
|
After Width: | Height: | Size: 474 KiB |
BIN
public/imgs/douyin.png
Normal file
|
After Width: | Height: | Size: 246 KiB |
BIN
public/imgs/douyin_avatar.png
Normal file
|
After Width: | Height: | Size: 153 KiB |
BIN
public/imgs/huya.png
Normal file
|
After Width: | Height: | Size: 219 KiB |
BIN
public/imgs/huya_avatar.png
Normal file
|
After Width: | Height: | Size: 865 KiB |
983
public/shaka-player/controls.css
Normal file
@@ -0,0 +1,983 @@
|
||||
/*! @license
|
||||
* Shaka Player
|
||||
* Copyright 2016 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
.shaka-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
.shaka-video-container {
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
font-family: Roboto, sans-serif, TengwarTelcontar;
|
||||
font-weight: 400;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
.shaka-video-container .material-svg-icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
.shaka-video-container:fullscreen {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #000;
|
||||
}
|
||||
.shaka-video-container:fullscreen .shaka-text-container {
|
||||
font-size: 4.4vmin;
|
||||
}
|
||||
.shaka-video-container:-webkit-full-screen {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #000;
|
||||
}
|
||||
.shaka-video-container:-webkit-full-screen .shaka-text-container {
|
||||
font-size: 4.4vmin;
|
||||
}
|
||||
.shaka-video-container:-moz-full-screen {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #000;
|
||||
}
|
||||
.shaka-video-container:-moz-full-screen .shaka-text-container {
|
||||
font-size: 4.4vmin;
|
||||
}
|
||||
.shaka-video-container:-ms-fullscreen {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #000;
|
||||
}
|
||||
.shaka-video-container:-ms-fullscreen .shaka-text-container {
|
||||
font-size: 4.4vmin;
|
||||
}
|
||||
.shaka-controls-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
z-index: 1;
|
||||
}
|
||||
.shaka-video-container:not([shaka-controls="true"]) .shaka-controls-container {
|
||||
display: none;
|
||||
}
|
||||
.shaka-controls-container * {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.shaka-controls-container[casting="true"] .shaka-fullscreen-button {
|
||||
display: none;
|
||||
}
|
||||
.shaka-canvas-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
.shaka-vr-canvas-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
.shaka-bottom-controls {
|
||||
width: 98%;
|
||||
padding: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
.shaka-controls-button-panel {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
min-width: 48px;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
opacity: 0;
|
||||
transition: opacity cubic-bezier(0.4, 0, 0.6, 1) 0.6s;
|
||||
}
|
||||
.shaka-controls-container[casting="true"] .shaka-controls-button-panel,
|
||||
.shaka-controls-container[shown="true"] .shaka-controls-button-panel {
|
||||
opacity: 1;
|
||||
}
|
||||
.shaka-controls-button-panel > * {
|
||||
color: #fff;
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
line-height: 0.5;
|
||||
padding: 0 2px;
|
||||
background: 0 0;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
opacity: 0.9;
|
||||
transition: opacity cubic-bezier(0.4, 0, 0.6, 1) 0.1s;
|
||||
text-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.shaka-controls-button-panel > .shaka-fast-forward-button .material-svg-icon,
|
||||
.shaka-controls-button-panel > .shaka-rewind-button .material-svg-icon,
|
||||
.shaka-controls-button-panel > .shaka-skip-next-button .material-svg-icon,
|
||||
.shaka-controls-button-panel > .shaka-skip-previous-button .material-svg-icon,
|
||||
.shaka-controls-button-panel > .shaka-small-play-button .material-svg-icon {
|
||||
font-size: 32px;
|
||||
}
|
||||
.shaka-controls-button-panel > .shaka-fullscreen-button .material-svg-icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
.shaka-controls-button-panel > .shaka-overflow-menu-button {
|
||||
position: relative;
|
||||
}
|
||||
.shaka-controls-button-panel > .shaka-overflow-menu-button .material-svg-icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
.shaka-controls-button-panel > :hover {
|
||||
opacity: 1;
|
||||
}
|
||||
.shaka-controls-button-panel .shaka-overflow-menu-only {
|
||||
display: none;
|
||||
}
|
||||
.shaka-play-button-container {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex-shrink: 1;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1;
|
||||
}
|
||||
.shaka-statistics-container {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
scrollbar-color: white rgba(0, 0, 0, 0.5);
|
||||
scrollbar-width: thin;
|
||||
min-width: 300px;
|
||||
color: #fff;
|
||||
background-color: rgba(35, 35, 35, 0.9);
|
||||
font-size: 14px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 2px;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
left: 15px;
|
||||
top: 15px;
|
||||
max-height: calc(100% - 115px);
|
||||
opacity: 0;
|
||||
transition: opacity cubic-bezier(0.4, 0, 0.6, 1) 0.6s;
|
||||
}
|
||||
.shaka-controls-container[casting="true"] .shaka-statistics-container,
|
||||
.shaka-controls-container[shown="true"] .shaka-statistics-container {
|
||||
opacity: 1;
|
||||
}
|
||||
.shaka-statistics-container div {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.shaka-statistics-container span {
|
||||
color: #969696;
|
||||
}
|
||||
.shaka-ad-statistics-container {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
scrollbar-color: white rgba(0, 0, 0, 0.5);
|
||||
scrollbar-width: thin;
|
||||
min-width: 150px;
|
||||
color: #fff;
|
||||
background-color: rgba(35, 35, 35, 0.9);
|
||||
font-size: 14px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 2px;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
right: 15px;
|
||||
top: 15px;
|
||||
max-height: calc(100% - 115px);
|
||||
opacity: 0;
|
||||
transition: opacity cubic-bezier(0.4, 0, 0.6, 1) 0.6s;
|
||||
}
|
||||
.shaka-controls-container[casting="true"] .shaka-ad-statistics-container,
|
||||
.shaka-controls-container[shown="true"] .shaka-ad-statistics-container {
|
||||
opacity: 1;
|
||||
}
|
||||
.shaka-ad-statistics-container div {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.shaka-ad-statistics-container span {
|
||||
color: #969696;
|
||||
}
|
||||
.shaka-context-menu {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
white-space: nowrap;
|
||||
background: rgba(28, 28, 28, 0.9);
|
||||
border-radius: 2px;
|
||||
min-width: 190px;
|
||||
opacity: 0;
|
||||
transition: opacity cubic-bezier(0.4, 0, 0.6, 1) 0.6s;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
}
|
||||
.shaka-controls-container[casting="true"] .shaka-context-menu,
|
||||
.shaka-controls-container[shown="true"] .shaka-context-menu {
|
||||
opacity: 1;
|
||||
}
|
||||
.shaka-context-menu button {
|
||||
font-size: 14px;
|
||||
background: 0 0;
|
||||
color: #fff;
|
||||
border: none;
|
||||
min-height: 30px;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.shaka-context-menu button:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.shaka-context-menu button label {
|
||||
cursor: pointer;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.shaka-keyboard-navigation .shaka-context-menu button:focus {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.shaka-context-menu button .shaka-current-selection-span {
|
||||
display: none;
|
||||
}
|
||||
.shaka-scrim-container {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
flex-shrink: 1;
|
||||
opacity: 0;
|
||||
transition: opacity cubic-bezier(0.4, 0, 0.6, 1) 0.6s;
|
||||
height: 61px;
|
||||
background: linear-gradient(rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 0.5) 100%);
|
||||
}
|
||||
.shaka-controls-container[casting="true"] .shaka-scrim-container,
|
||||
.shaka-controls-container[shown="true"] .shaka-scrim-container {
|
||||
opacity: 1;
|
||||
}
|
||||
.shaka-text-container {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
pointer-events: none;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
min-width: 48px;
|
||||
transition: bottom cubic-bezier(0.4, 0, 0.6, 1) 0.1s;
|
||||
transition-delay: 0.5s;
|
||||
font-size: 20px;
|
||||
line-height: 1.4;
|
||||
color: #fff;
|
||||
}
|
||||
.shaka-text-container span.shaka-text-wrapper {
|
||||
display: inline;
|
||||
background: 0 0;
|
||||
}
|
||||
.shaka-controls-container[shown="true"] ~ .shaka-text-container {
|
||||
transition-delay: 0s;
|
||||
}
|
||||
.shaka-spinner-container {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex-shrink: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.shaka-video-container:not([shaka-controls="true"]) .shaka-spinner-container {
|
||||
display: none;
|
||||
}
|
||||
.shaka-hidden-fast-forward-container,
|
||||
.shaka-hidden-rewind-container {
|
||||
height: 100%;
|
||||
width: 40%;
|
||||
flex-shrink: 1;
|
||||
z-index: 1;
|
||||
}
|
||||
.shaka-hidden-fast-forward-container {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 60%;
|
||||
}
|
||||
.shaka-hidden-rewind-container {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.shaka-video-container.no-cursor {
|
||||
cursor: none !important;
|
||||
}
|
||||
.shaka-video-container.no-cursor * {
|
||||
cursor: none !important;
|
||||
}
|
||||
.shaka-play-button {
|
||||
box-sizing: border-box;
|
||||
padding: calc(15% / 2);
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin: 0;
|
||||
border-radius: 50%;
|
||||
box-shadow: rgba(0, 0, 0, 0.1) 0 0 20px 0;
|
||||
border: none;
|
||||
background-size: 50%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
opacity: 0;
|
||||
transition: opacity cubic-bezier(0.4, 0, 0.6, 1) 0.6s;
|
||||
}
|
||||
.shaka-controls-container[casting="true"] .shaka-play-button,
|
||||
.shaka-controls-container[shown="true"] .shaka-play-button {
|
||||
opacity: 1;
|
||||
}
|
||||
.shaka-play-button[icon="play"] {
|
||||
background-image: url("data:image/svg+xml,%3Csvg%20fill%3D%22%23000000%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20width%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpath%20d%3D%22M8%205v14l11-7z%22%2F%3E%0A%20%20%20%20%3Cpath%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%0A%3C%2Fsvg%3E");
|
||||
}
|
||||
.shaka-play-button[icon="pause"] {
|
||||
background-image: url("data:image/svg+xml,%3Csvg%20fill%3D%22%23000000%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20width%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpath%20d%3D%22M6%2019h4V5H6v14zm8-14v14h4V5h-4z%22%2F%3E%0A%20%20%20%20%3Cpath%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%0A%3C%2Fsvg%3E");
|
||||
}
|
||||
.shaka-play-button[icon="replay"] {
|
||||
background-image: url("data:image/svg+xml,%3Csvg%20fill%3D%22%231f1f1f%22%20height%3D%2224px%22%20viewBox%3D%220%20-960%20960%20960%22%20width%3D%2224px%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Cpath%20d%3D%22M480-80q-75%200-140.5-28.5t-114-77q-48.5-48.5-77-114T120-440h80q0%20117%2081.5%20198.5T480-160q117%200%20198.5-81.5T760-440q0-117-81.5-198.5T480-720h-6l62%2062-56%2058-160-160%20160-160%2056%2058-62%2062h6q75%200%20140.5%2028.5t114%2077q48.5%2048.5%2077%20114T840-440q0%2075-28.5%20140.5t-77%20114q-48.5%2048.5-114%2077T480-80Z%22%2F%3E%0A%3C%2Fsvg%3E");
|
||||
}
|
||||
@media (prefers-reduced-transparency: no-preference) {
|
||||
.shaka-controls-container[shown="true"] .shaka-play-button {
|
||||
opacity: 0.75;
|
||||
}
|
||||
}
|
||||
.shaka-current-time {
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
width: auto;
|
||||
padding: 0 5px;
|
||||
}
|
||||
.shaka-current-time[disabled] {
|
||||
background-color: transparent;
|
||||
color: #fff;
|
||||
cursor: default;
|
||||
}
|
||||
.shaka-controls-container button:focus,
|
||||
.shaka-controls-container input:focus {
|
||||
outline: 1px solid Highlight;
|
||||
}
|
||||
.shaka-controls-container button:-moz-focus-inner,
|
||||
.shaka-controls-container input:-moz-focus-outer {
|
||||
outline: 0;
|
||||
border: 0;
|
||||
}
|
||||
.shaka-controls-container:not(.shaka-keyboard-navigation) button:focus,
|
||||
.shaka-controls-container:not(.shaka-keyboard-navigation) input:focus {
|
||||
outline: 0;
|
||||
}
|
||||
.shaka-fast-forward-container,
|
||||
.shaka-rewind-container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
flex-shrink: 1;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
border: none;
|
||||
color: #fff;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
cursor: default;
|
||||
font-size: 20px;
|
||||
opacity: 0;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
.shaka-fast-forward-container {
|
||||
border-radius: 40% 0 0 40%;
|
||||
}
|
||||
.shaka-rewind-container {
|
||||
border-radius: 0 40% 40% 0;
|
||||
}
|
||||
.shaka-forward-rewind-container-icon {
|
||||
font-size: 32px;
|
||||
}
|
||||
.shaka-range-container {
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
margin: calc((12px - 4px) / 2) 6px;
|
||||
height: 4px;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
.shaka-volume-bar-container {
|
||||
width: 100px;
|
||||
padding: 0;
|
||||
transition-property: opacity, width;
|
||||
transition-duration: 250ms;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.6, 1);
|
||||
}
|
||||
.shaka-volume-bar-container:hover {
|
||||
width: 100px !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
@media (max-width: 474px) {
|
||||
.shaka-volume-bar-container {
|
||||
width: 50px;
|
||||
}
|
||||
.shaka-volume-bar-container:hover {
|
||||
width: 50px !important;
|
||||
}
|
||||
.shaka-mute-button:hover + .shaka-volume-bar-container-allow-hiding {
|
||||
width: 50px;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.shaka-mute-button
|
||||
+ .shaka-volume-bar-container-allow-hiding:not(:focus-within) {
|
||||
width: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
@media (min-width: 475px) {
|
||||
.shaka-mute-button:hover + .shaka-volume-bar-container-allow-hiding {
|
||||
width: 100px;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.shaka-range-element {
|
||||
-webkit-appearance: none;
|
||||
background: 0 0;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
height: 12px;
|
||||
top: calc((4px - 12px) / 2);
|
||||
z-index: 1;
|
||||
}
|
||||
.shaka-range-element::-webkit-slider-runnable-track {
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
height: 12px;
|
||||
background: 0 0;
|
||||
color: transparent;
|
||||
border: none;
|
||||
}
|
||||
.shaka-range-element::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
background: #fff;
|
||||
}
|
||||
.shaka-range-element::-moz-range-track {
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
height: 12px;
|
||||
background: 0 0;
|
||||
color: transparent;
|
||||
border: none;
|
||||
}
|
||||
.shaka-range-element::-moz-range-thumb {
|
||||
-webkit-appearance: none;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
background: #fff;
|
||||
}
|
||||
.shaka-seek-bar-container {
|
||||
opacity: 0;
|
||||
transition: opacity cubic-bezier(0.4, 0, 0.6, 1) 0.6s;
|
||||
top: 5px;
|
||||
height: 5px;
|
||||
margin-bottom: 0;
|
||||
background-clip: padding-box !important;
|
||||
border-top: 4px solid transparent;
|
||||
border-bottom: 4px solid transparent;
|
||||
}
|
||||
.shaka-controls-container[casting="true"] .shaka-seek-bar-container,
|
||||
.shaka-controls-container[shown="true"] .shaka-seek-bar-container {
|
||||
opacity: 1;
|
||||
}
|
||||
.shaka-seek-bar-container .shaka-seek-bar {
|
||||
transition: opacity cubic-bezier(0.4, 0, 0.6, 1) 250ms;
|
||||
opacity: 0;
|
||||
}
|
||||
.shaka-seek-bar-container:hover .shaka-seek-bar {
|
||||
opacity: 1;
|
||||
}
|
||||
.shaka-ad-markers {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.shaka-spacer {
|
||||
cursor: default;
|
||||
flex-shrink: 1;
|
||||
flex-grow: 1;
|
||||
margin: 0;
|
||||
}
|
||||
.shaka-overflow-menu,
|
||||
.shaka-settings-menu {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
scrollbar-color: white rgba(0, 0, 0, 0.5);
|
||||
scrollbar-width: thin;
|
||||
white-space: nowrap;
|
||||
background: rgba(28, 28, 28, 0.9);
|
||||
border-radius: 15px;
|
||||
max-height: 250px;
|
||||
min-width: 190px;
|
||||
padding: 5px 0;
|
||||
opacity: 0;
|
||||
transition: opacity cubic-bezier(0.4, 0, 0.6, 1) 0.6s;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
right: 15px;
|
||||
bottom: 62px;
|
||||
}
|
||||
.shaka-controls-container[casting="true"] .shaka-overflow-menu,
|
||||
.shaka-controls-container[casting="true"] .shaka-settings-menu,
|
||||
.shaka-controls-container[shown="true"] .shaka-overflow-menu,
|
||||
.shaka-controls-container[shown="true"] .shaka-settings-menu {
|
||||
opacity: 1;
|
||||
}
|
||||
.shaka-overflow-menu button,
|
||||
.shaka-settings-menu button {
|
||||
font-size: 14px;
|
||||
background: 0 0;
|
||||
color: #fff;
|
||||
border: none;
|
||||
min-height: 30px;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.shaka-overflow-menu button:hover,
|
||||
.shaka-settings-menu button:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.shaka-overflow-menu button label,
|
||||
.shaka-settings-menu button label {
|
||||
cursor: pointer;
|
||||
}
|
||||
.shaka-keyboard-navigation .shaka-overflow-menu button:focus,
|
||||
.shaka-keyboard-navigation .shaka-settings-menu button:focus {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.shaka-overflow-menu .material-svg-icon,
|
||||
.shaka-settings-menu .material-svg-icon {
|
||||
padding-left: 0;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.shaka-overflow-menu .material-svg-icon.shaka-chosen-item,
|
||||
.shaka-settings-menu .material-svg-icon.shaka-chosen-item {
|
||||
order: -1;
|
||||
line-height: 17px;
|
||||
font-size: 18px;
|
||||
}
|
||||
.shaka-overflow-menu.shaka-low-position,
|
||||
.shaka-settings-menu.shaka-low-position {
|
||||
bottom: 48px;
|
||||
}
|
||||
.shaka-overflow-menu span {
|
||||
text-align: left;
|
||||
}
|
||||
.shaka-overflow-button-label {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.shaka-overflow-button-label-inline {
|
||||
box-sizing: border-box;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
width: calc(100% - 34px);
|
||||
padding-right: 28px;
|
||||
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMjRweCIgZmlsbD0iI2VlZWVlZSI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTguNTkgMTYuNTlMMTMuMTcgMTIgOC41OSA3LjQxIDEwIDZsNiA2LTYgNi0xLjQxLTEuNDF6Ii8+PC9zdmc+");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 5px center;
|
||||
background-size: 24px 24px;
|
||||
}
|
||||
.shaka-simple-overflow-button-label-inline {
|
||||
box-sizing: border-box;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
width: calc(100% - 50px);
|
||||
}
|
||||
.shaka-current-selection-span {
|
||||
font-size: 12px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.shaka-current-auto-quality {
|
||||
margin-left: 5px;
|
||||
font-size: 11px;
|
||||
color: #ccc;
|
||||
}
|
||||
.shaka-current-quality-mark,
|
||||
.shaka-quality-mark {
|
||||
color: red;
|
||||
margin-left: 2px !important;
|
||||
font-size: 10px;
|
||||
height: 17px;
|
||||
}
|
||||
.shaka-quality-mark {
|
||||
line-height: 6px;
|
||||
}
|
||||
.shaka-overflow-playback-rate-mark,
|
||||
.shaka-overflow-quality-mark {
|
||||
background: red;
|
||||
color: #fff;
|
||||
border-radius: 2px;
|
||||
font-family: Roboto, sans-serif, TengwarTelcontar;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
line-height: 10px;
|
||||
text-shadow: none;
|
||||
padding: 1px;
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: 10px;
|
||||
}
|
||||
.shaka-settings-menu span {
|
||||
margin-left: 28px;
|
||||
}
|
||||
.shaka-settings-menu span.shaka-chosen-item {
|
||||
margin-left: 0;
|
||||
}
|
||||
.shaka-settings-menu .shaka-chapter {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.shaka-back-to-overflow-button {
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.2) !important;
|
||||
}
|
||||
.shaka-back-to-overflow-button span {
|
||||
margin-left: 0;
|
||||
}
|
||||
.shaka-back-to-overflow-button .material-svg-icon {
|
||||
padding-right: 10px;
|
||||
font-size: 18px !important;
|
||||
}
|
||||
.shaka-back-to-overflow-button:hover {
|
||||
background: 0 0 !important;
|
||||
}
|
||||
.shaka-controls-container[ad-active="true"] {
|
||||
pointer-events: none;
|
||||
}
|
||||
.shaka-controls-container[ad-active="true"] .shaka-bottom-controls {
|
||||
pointer-events: auto;
|
||||
}
|
||||
.shaka-client-side-ad-container,
|
||||
.shaka-server-side-ad-container {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.shaka-video-container[shaka-controls="true"]
|
||||
.shaka-client-side-ad-container
|
||||
iframe,
|
||||
.shaka-video-container[shaka-controls="true"]
|
||||
.shaka-server-side-ad-container
|
||||
iframe {
|
||||
height: 90%;
|
||||
}
|
||||
.shaka-ad-controls {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
z-index: 1;
|
||||
padding-bottom: 1%;
|
||||
}
|
||||
.shaka-video-container:not([shaka-controls="true"]) .shaka-ad-controls {
|
||||
display: none;
|
||||
}
|
||||
.shaka-ad-controls button,
|
||||
.shaka-ad-controls div {
|
||||
color: #fff;
|
||||
font-size: initial;
|
||||
}
|
||||
.shaka-ad-info {
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
width: auto;
|
||||
padding: 0 5px;
|
||||
}
|
||||
.shaka-ad-info[disabled] {
|
||||
background-color: transparent;
|
||||
color: #fff;
|
||||
cursor: default;
|
||||
padding: 0;
|
||||
}
|
||||
.shaka-skip-ad-container {
|
||||
position: relative;
|
||||
right: calc((100% - 98%) / 2 * -1);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: 0;
|
||||
margin-left: auto;
|
||||
}
|
||||
.shaka-skip-ad-button {
|
||||
padding: 5px 15px;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.shaka-skip-ad-button:disabled {
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.shaka-skip-ad-counter {
|
||||
padding: 5px;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
margin: 0;
|
||||
} /*!
|
||||
* @license
|
||||
* The tooltip is based on https://github.com/felipefialho/css-components/
|
||||
* Local modifications have been performed.
|
||||
*
|
||||
* Copyright (c) 2017 Felipe Fialho
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
.shaka-tooltips-on {
|
||||
overflow: visible;
|
||||
}
|
||||
.shaka-tooltips-on > .shaka-tooltip,
|
||||
.shaka-tooltips-on > .shaka-tooltip-status {
|
||||
position: relative;
|
||||
}
|
||||
.shaka-tooltips-on > .shaka-tooltip-status:active:after,
|
||||
.shaka-tooltips-on > .shaka-tooltip-status:focus-visible:after,
|
||||
.shaka-tooltips-on > .shaka-tooltip-status:hover:after,
|
||||
.shaka-tooltips-on > .shaka-tooltip:active:after,
|
||||
.shaka-tooltips-on > .shaka-tooltip:focus-visible:after,
|
||||
.shaka-tooltips-on > .shaka-tooltip:hover:after {
|
||||
content: attr(aria-label);
|
||||
font-family: Roboto, sans-serif, TengwarTelcontar;
|
||||
line-height: 20px;
|
||||
white-space: nowrap;
|
||||
font-size: 14px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
color: #fff;
|
||||
border-radius: 2px;
|
||||
padding: 2px 10px;
|
||||
position: absolute;
|
||||
bottom: 62px;
|
||||
left: calc(48px / 2);
|
||||
-webkit-transform: translateX(-50%);
|
||||
-moz-transform: translateX(-50%);
|
||||
-ms-transform: translateX(-50%);
|
||||
-o-transform: translateX(-50%);
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
@media (prefers-reduced-transparency) {
|
||||
.shaka-tooltips-on > .shaka-tooltip-status:active:after,
|
||||
.shaka-tooltips-on > .shaka-tooltip-status:focus-visible:after,
|
||||
.shaka-tooltips-on > .shaka-tooltip-status:hover:after,
|
||||
.shaka-tooltips-on > .shaka-tooltip:active:after,
|
||||
.shaka-tooltips-on > .shaka-tooltip:focus-visible:after,
|
||||
.shaka-tooltips-on > .shaka-tooltip:hover:after {
|
||||
background-color: rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
}
|
||||
.shaka-tooltips-on.shaka-tooltips-low-position > .shaka-tooltip:active:after,
|
||||
.shaka-tooltips-on.shaka-tooltips-low-position
|
||||
> .shaka-tooltip:focus-visible:after,
|
||||
.shaka-tooltips-on.shaka-tooltips-low-position > .shaka-tooltip:hover:after {
|
||||
bottom: 48px;
|
||||
}
|
||||
.shaka-tooltips-on > .shaka-tooltip-status:active:after,
|
||||
.shaka-tooltips-on > .shaka-tooltip-status:focus-visible:after,
|
||||
.shaka-tooltips-on > .shaka-tooltip-status:hover:after {
|
||||
content: attr(aria-label) " (" attr(shaka-status) ")";
|
||||
}
|
||||
.shaka-tooltips-on button:first-child:active:after,
|
||||
.shaka-tooltips-on button:first-child:focus-visible:after,
|
||||
.shaka-tooltips-on button:first-child:hover:after {
|
||||
left: 0;
|
||||
-webkit-transform: translateX(0);
|
||||
-moz-transform: translateX(0);
|
||||
-ms-transform: translateX(0);
|
||||
-o-transform: translateX(0);
|
||||
transform: translateX(0);
|
||||
}
|
||||
.shaka-tooltips-on button:last-child:active:after,
|
||||
.shaka-tooltips-on button:last-child:focus-visible:after,
|
||||
.shaka-tooltips-on button:last-child:hover:after {
|
||||
left: 48px;
|
||||
-webkit-transform: translateX(-100%);
|
||||
-moz-transform: translateX(-100%);
|
||||
-ms-transform: translateX(-100%);
|
||||
-o-transform: translateX(-100%);
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
#shaka-player-ui-thumbnail-container {
|
||||
background-color: #000;
|
||||
border: 1px solid #000;
|
||||
box-shadow: 0 8px 8px 0 rgba(0, 0, 0, 0.5);
|
||||
min-width: 150px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
width: 15%;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
#shaka-player-ui-thumbnail-container #shaka-player-ui-thumbnail-image {
|
||||
position: absolute;
|
||||
}
|
||||
#shaka-player-ui-thumbnail-container #shaka-player-ui-thumbnail-time-container {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
#shaka-player-ui-thumbnail-container
|
||||
#shaka-player-ui-thumbnail-time-container
|
||||
#shaka-player-ui-thumbnail-time {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
border-radius: 14px;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
@media (prefers-reduced-transparency) {
|
||||
#shaka-player-ui-thumbnail-container
|
||||
#shaka-player-ui-thumbnail-time-container
|
||||
#shaka-player-ui-thumbnail-time {
|
||||
background-color: rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
}
|
||||
#shaka-player-ui-thumbnail-container.portrait-thumbnail {
|
||||
min-width: 75px;
|
||||
width: 7.5%;
|
||||
}
|
||||
#shaka-player-ui-time-container {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
border-radius: 5px;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
padding: 0 3px;
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
z-index: 1;
|
||||
}
|
||||
@media (prefers-reduced-transparency) {
|
||||
#shaka-player-ui-time-container {
|
||||
background-color: rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
}
|
||||
.material-svg-icon {
|
||||
display: inline-block;
|
||||
fill: currentcolor;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: normal;
|
||||
src: url(./fonts/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbVmUiA8.ttf)
|
||||
format("truetype");
|
||||
} /*# sourceMappingURL=controls.css.map */
|
||||
1
public/shaka-player/controls.css.map
Normal file
BIN
public/shaka-player/fonts/KFOlCnqEu92Fr1MmEU9vAw.ttf
Normal file
BIN
public/shaka-player/fonts/KFOmCnqEu92Fr1Me5Q.ttf
Normal file
7727
public/shaka-player/shaka-player.ui.debug.externs.js
Normal file
7727
public/shaka-player/shaka-player.ui.externs.js
Normal file
2156
public/shaka-player/shaka-player.ui.js
Normal file
8
public/shaka-player/shaka-player.ui.map
Normal file
288
public/shaka-player/youtube-theme.css
Normal file
@@ -0,0 +1,288 @@
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(./fonts/KFOmCnqEu92Fr1Me5Q.ttf) format("truetype");
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Roboto";
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(./fonts/KFOlCnqEu92Fr1MmEU9vAw.ttf) format("truetype");
|
||||
}
|
||||
.youtube-theme {
|
||||
font-family: "Roboto", sans-serif;
|
||||
}
|
||||
.youtube-theme .shaka-bottom-controls {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
padding-bottom: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
.youtube-theme .shaka-bottom-controls {
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
}
|
||||
.youtube-theme .shaka-ad-controls {
|
||||
-webkit-box-ordinal-group: 2;
|
||||
-ms-flex-order: 1;
|
||||
order: 1;
|
||||
}
|
||||
.youtube-theme .shaka-controls-button-panel {
|
||||
-webkit-box-ordinal-group: 3;
|
||||
-ms-flex-order: 2;
|
||||
order: 2;
|
||||
height: 40px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.youtube-theme .shaka-range-container {
|
||||
margin: 4px 10px 4px 10px;
|
||||
top: 0;
|
||||
}
|
||||
.youtube-theme .shaka-small-play-button {
|
||||
-webkit-box-ordinal-group: -2;
|
||||
-ms-flex-order: -3;
|
||||
order: -3;
|
||||
}
|
||||
.youtube-theme .shaka-mute-button {
|
||||
-webkit-box-ordinal-group: -1;
|
||||
-ms-flex-order: -2;
|
||||
order: -2;
|
||||
}
|
||||
.youtube-theme .shaka-controls-button-panel > * {
|
||||
margin: 0;
|
||||
padding: 3px 8px;
|
||||
color: #eee;
|
||||
height: 40px;
|
||||
}
|
||||
.youtube-theme .shaka-controls-button-panel > *:focus {
|
||||
outline: none;
|
||||
-webkit-box-shadow: inset 0 0 0 2px rgba(27, 127, 204, 0.8);
|
||||
box-shadow: inset 0 0 0 2px rgba(27, 127, 204, 0.8);
|
||||
color: #fff;
|
||||
}
|
||||
.youtube-theme .shaka-controls-button-panel > *:hover {
|
||||
color: #fff;
|
||||
}
|
||||
.youtube-theme .shaka-controls-button-panel .shaka-volume-bar-container {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
left: -1px;
|
||||
-webkit-box-ordinal-group: 0;
|
||||
-ms-flex-order: -1;
|
||||
order: -1;
|
||||
opacity: 0;
|
||||
width: 0px;
|
||||
-webkit-transition: width 0.2s cubic-bezier(0.4, 0, 1, 1);
|
||||
height: 3px;
|
||||
transition: width 0.2s cubic-bezier(0.4, 0, 1, 1);
|
||||
padding: 0;
|
||||
}
|
||||
.youtube-theme .shaka-controls-button-panel .shaka-volume-bar-container:hover,
|
||||
.youtube-theme .shaka-controls-button-panel .shaka-volume-bar-container:focus {
|
||||
display: block;
|
||||
width: 50px;
|
||||
opacity: 1;
|
||||
padding: 0 6px;
|
||||
}
|
||||
.youtube-theme .shaka-mute-button:hover + div {
|
||||
opacity: 1;
|
||||
width: 50px;
|
||||
padding: 0 6px;
|
||||
}
|
||||
.youtube-theme .shaka-current-time {
|
||||
padding: 0 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.youtube-theme .shaka-seek-bar-container {
|
||||
height: 3px;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
border-radius: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.youtube-theme .shaka-seek-bar-container .shaka-range-element {
|
||||
opacity: 0;
|
||||
}
|
||||
.youtube-theme .shaka-seek-bar-container:hover {
|
||||
height: 5px;
|
||||
top: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.youtube-theme .shaka-seek-bar-container:hover .shaka-range-element {
|
||||
opacity: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
.youtube-theme
|
||||
.shaka-seek-bar-container
|
||||
input[type="range"]::-webkit-slider-thumb {
|
||||
background: #ff0000;
|
||||
cursor: pointer;
|
||||
}
|
||||
.youtube-theme .shaka-seek-bar-container input[type="range"]::-moz-range-thumb {
|
||||
background: #ff0000;
|
||||
cursor: pointer;
|
||||
}
|
||||
.youtube-theme .shaka-seek-bar-container input[type="range"]::-ms-thumb {
|
||||
background: #ff0000;
|
||||
cursor: pointer;
|
||||
}
|
||||
.youtube-theme .shaka-video-container * {
|
||||
font-family: "Roboto", sans-serif;
|
||||
}
|
||||
.youtube-theme .shaka-video-container .material-icons-round {
|
||||
font-family: "Material Icons Sharp";
|
||||
}
|
||||
.youtube-theme .shaka-overflow-menu,
|
||||
.youtube-theme .shaka-settings-menu {
|
||||
border-radius: 2px;
|
||||
background: rgba(28, 28, 28, 0.9);
|
||||
text-shadow: 0 0 2px rgb(0 0 0%);
|
||||
-webkit-transition: opacity 0.1s cubic-bezier(0, 0, 0.2, 1);
|
||||
transition: opacity 0.1s cubic-bezier(0, 0, 0.2, 1);
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
right: 10px;
|
||||
bottom: 50px;
|
||||
padding: 8px 0;
|
||||
min-width: 200px;
|
||||
}
|
||||
.youtube-theme .shaka-settings-menu {
|
||||
padding: 0 0 8px 0;
|
||||
}
|
||||
.youtube-theme .shaka-settings-menu button {
|
||||
font-size: 12px;
|
||||
}
|
||||
.youtube-theme .shaka-settings-menu button span {
|
||||
margin-left: 33px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.youtube-theme .shaka-settings-menu button[aria-selected="true"] {
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
.youtube-theme .shaka-settings-menu button[aria-selected="true"] span {
|
||||
-webkit-box-ordinal-group: 3;
|
||||
-ms-flex-order: 2;
|
||||
order: 2;
|
||||
margin-left: 0;
|
||||
}
|
||||
.youtube-theme .shaka-settings-menu button[aria-selected="true"] i {
|
||||
-webkit-box-ordinal-group: 2;
|
||||
-ms-flex-order: 1;
|
||||
order: 1;
|
||||
font-size: 18px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
.youtube-theme .shaka-overflow-menu button {
|
||||
padding: 0;
|
||||
}
|
||||
.youtube-theme .shaka-overflow-menu button i {
|
||||
display: none;
|
||||
}
|
||||
.youtube-theme .shaka-overflow-menu button .shaka-overflow-button-label {
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-pack: justify;
|
||||
-ms-flex-pack: justify;
|
||||
justify-content: space-between;
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-box-direction: normal;
|
||||
-ms-flex-direction: row;
|
||||
flex-direction: row;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
cursor: default;
|
||||
outline: none;
|
||||
height: 40px;
|
||||
-webkit-box-flex: 0;
|
||||
-ms-flex: 0 0 100%;
|
||||
flex: 0 0 100%;
|
||||
}
|
||||
.youtube-theme .shaka-overflow-menu button .shaka-overflow-button-label span {
|
||||
-ms-flex-negative: initial;
|
||||
flex-shrink: initial;
|
||||
padding-left: 15px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
}
|
||||
.youtube-theme .shaka-overflow-menu span + span {
|
||||
color: #fff;
|
||||
font-weight: 400 !important;
|
||||
font-size: 12px !important;
|
||||
padding-right: 8px;
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
.youtube-theme .shaka-overflow-menu span + span:after {
|
||||
content: "navigate_next";
|
||||
font-family: "Material Icons Sharp";
|
||||
font-size: 20px;
|
||||
}
|
||||
.youtube-theme .shaka-overflow-menu .shaka-pip-button span + span {
|
||||
padding-right: 15px !important;
|
||||
}
|
||||
.youtube-theme .shaka-overflow-menu .shaka-pip-button span + span:after {
|
||||
content: "";
|
||||
}
|
||||
.youtube-theme .shaka-back-to-overflow-button {
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
|
||||
font-size: 12px;
|
||||
color: #eee;
|
||||
height: 40px;
|
||||
}
|
||||
.youtube-theme .shaka-back-to-overflow-button .material-icons-round {
|
||||
font-size: 15px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.youtube-theme .shaka-back-to-overflow-button span {
|
||||
margin-left: 3px !important;
|
||||
}
|
||||
.youtube-theme .shaka-overflow-menu button:hover,
|
||||
.youtube-theme .shaka-settings-menu button:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
cursor: pointer;
|
||||
}
|
||||
.youtube-theme .shaka-overflow-menu button:hover label,
|
||||
.youtube-theme .shaka-settings-menu button:hover label {
|
||||
cursor: pointer;
|
||||
}
|
||||
.youtube-theme .shaka-overflow-menu button:focus,
|
||||
.youtube-theme .shaka-settings-menu button:focus {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
.youtube-theme .shaka-overflow-menu button,
|
||||
.youtube-theme .shaka-settings-menu button {
|
||||
color: #eee;
|
||||
}
|
||||
.youtube-theme .shaka-captions-off {
|
||||
color: #bfbfbf;
|
||||
}
|
||||
.youtube-theme .shaka-overflow-menu-button {
|
||||
font-size: 18px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.youtube-theme .shaka-fullscreen-button:hover {
|
||||
font-size: 25px;
|
||||
-webkit-transition: font-size 0.1s cubic-bezier(0, 0, 0.2, 1);
|
||||
transition: font-size 0.1s cubic-bezier(0, 0, 0.2, 1);
|
||||
}
|
||||
58
scripts/bump.cjs
Normal file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
function updatePackageJson(version) {
|
||||
const packageJsonPath = path.join(process.cwd(), "package.json");
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
||||
packageJson.version = version;
|
||||
fs.writeFileSync(
|
||||
packageJsonPath,
|
||||
JSON.stringify(packageJson, null, 2) + "\n"
|
||||
);
|
||||
console.log(`✅ Updated package.json version to ${version}`);
|
||||
}
|
||||
|
||||
function updateCargoToml(version) {
|
||||
const cargoTomlPath = path.join(process.cwd(), "src-tauri", "Cargo.toml");
|
||||
let cargoToml = fs.readFileSync(cargoTomlPath, "utf8");
|
||||
|
||||
// Update the version in the [package] section
|
||||
cargoToml = cargoToml.replace(/^version = ".*"$/m, `version = "${version}"`);
|
||||
|
||||
fs.writeFileSync(cargoTomlPath, cargoToml);
|
||||
console.log(`✅ Updated Cargo.toml version to ${version}`);
|
||||
}
|
||||
|
||||
function main() {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length === 0) {
|
||||
console.error("❌ Please provide a version number");
|
||||
console.error("Usage: yarn bump <version>");
|
||||
console.error("Example: yarn bump 3.1.0");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const version = args[0];
|
||||
|
||||
// Validate version format (simple check)
|
||||
if (!/^\d+\.\d+\.\d+/.test(version)) {
|
||||
console.error(
|
||||
"❌ Invalid version format. Please use semantic versioning (e.g., 3.1.0)"
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
updatePackageJson(version);
|
||||
updateCargoToml(version);
|
||||
console.log(`🎉 Successfully bumped version to ${version}`);
|
||||
} catch (error) {
|
||||
console.error("❌ Error updating version:", error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
4
src-tauri/.gitignore
vendored
@@ -1,5 +1,9 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
cache
|
||||
output
|
||||
tmps
|
||||
clips
|
||||
data
|
||||
config.toml
|
||||
7607
src-tauri/Cargo.lock
generated
@@ -1,39 +1,157 @@
|
||||
[workspace]
|
||||
members = ["crates/danmu_stream", "crates/recorder"]
|
||||
resolver = "2"
|
||||
|
||||
[package]
|
||||
name = "bili-shadowreplay"
|
||||
version = "0.0.3"
|
||||
description = "A Tauri App"
|
||||
version = "2.16.2"
|
||||
description = "BiliBili ShadowReplay"
|
||||
authors = ["Xinrea"]
|
||||
license = ""
|
||||
repository = ""
|
||||
edition = "2021"
|
||||
|
||||
[lints.clippy]
|
||||
correctness="deny"
|
||||
suspicious="deny"
|
||||
complexity="deny"
|
||||
style="deny"
|
||||
perf="deny"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "1.2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
tauri = { version = "1.2", features = ["dialog-all", "fs-all", "http-all", "protocol-asset", "shell-open", "system-tray"] }
|
||||
danmu_stream = { path = "crates/danmu_stream" }
|
||||
recorder = { path = "crates/recorder" }
|
||||
serde_json = "1.0"
|
||||
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||
reqwest = { workspace = true}
|
||||
serde_derive = "1.0.158"
|
||||
serde = "1.0.158"
|
||||
sysinfo = "0.32.0"
|
||||
m3u8-rs = "5.0.3"
|
||||
async-std = "1.12.0"
|
||||
futures = "0.3.27"
|
||||
ffmpeg-sidecar = "0.3.3"
|
||||
sqlite = "0.30.4"
|
||||
chrono = "0.4.24"
|
||||
async-ffmpeg-sidecar = "0.0.1"
|
||||
chrono = { version = "0.4.24", features = ["serde"] }
|
||||
toml = "0.7.3"
|
||||
custom_error = "1.9.2"
|
||||
felgens = "0.3.1"
|
||||
regex = "1.7.3"
|
||||
tokio = "1.27.0"
|
||||
tokio = { version = "1.27.0", features = ["process"] }
|
||||
platform-dirs = "0.3.0"
|
||||
pct-str = "1.2.0"
|
||||
md5 = "0.7.0"
|
||||
hyper = { version = "0.14", features = ["full"] }
|
||||
dashmap = "6.1.0"
|
||||
urlencoding = "2.1.3"
|
||||
log = "0.4.22"
|
||||
simplelog = "0.12.2"
|
||||
sqlx = { version = "0.8", features = ["runtime-tokio", "sqlite"] }
|
||||
rand = "0.8.5"
|
||||
base64 = "0.21"
|
||||
mime_guess = "2.0"
|
||||
async-trait = "0.1.87"
|
||||
whisper-rs = "0.14.2"
|
||||
hound = "3.5.1"
|
||||
uuid = { workspace = true }
|
||||
axum = { version = "0.7", features = ["macros", "multipart"] }
|
||||
tower-http = { version = "0.5", features = ["cors", "fs"] }
|
||||
futures-core = "0.3"
|
||||
futures = "0.3"
|
||||
tokio-util = { version = "0.7", features = ["io"] }
|
||||
tokio-stream = "0.1"
|
||||
clap = { version = "4.5.37", features = ["derive"] }
|
||||
url = "2.5.4"
|
||||
srtparse = "0.2.0"
|
||||
thiserror = "2"
|
||||
deno_core = "0.355"
|
||||
sanitize-filename = "0.6.0"
|
||||
socketioxide = "0.17.2"
|
||||
scraper = "0.24.0"
|
||||
|
||||
[features]
|
||||
# this feature is used for production builds or when `devPath` points to the filesystem
|
||||
# DO NOT REMOVE!!
|
||||
custom-protocol = ["tauri/custom-protocol"]
|
||||
cuda = ["whisper-rs/cuda"]
|
||||
headless = []
|
||||
default = ["gui"]
|
||||
gui = [
|
||||
"tauri",
|
||||
"tauri-plugin-single-instance",
|
||||
"tauri-plugin-dialog",
|
||||
"tauri-plugin-shell",
|
||||
"tauri-plugin-fs",
|
||||
"tauri-plugin-http",
|
||||
"tauri-plugin-sql",
|
||||
"tauri-utils",
|
||||
"tauri-plugin-os",
|
||||
"tauri-plugin-notification",
|
||||
"tauri-plugin-deep-link",
|
||||
"fix-path-env",
|
||||
"tauri-build",
|
||||
]
|
||||
|
||||
[dependencies.tauri]
|
||||
version = "2"
|
||||
features = ["protocol-asset", "tray-icon"]
|
||||
optional = true
|
||||
|
||||
[dependencies.tauri-plugin-single-instance]
|
||||
version = "2"
|
||||
optional = true
|
||||
features = ["deep-link"]
|
||||
|
||||
[dependencies.tauri-plugin-dialog]
|
||||
version = "2"
|
||||
optional = true
|
||||
|
||||
[dependencies.tauri-plugin-shell]
|
||||
version = "2"
|
||||
optional = true
|
||||
|
||||
[dependencies.tauri-plugin-fs]
|
||||
version = "2"
|
||||
optional = true
|
||||
|
||||
[dependencies.tauri-plugin-http]
|
||||
version = "2"
|
||||
optional = true
|
||||
|
||||
[dependencies.tauri-plugin-sql]
|
||||
version = "2"
|
||||
optional = true
|
||||
features = ["sqlite"]
|
||||
|
||||
[dependencies.tauri-utils]
|
||||
version = "2"
|
||||
optional = true
|
||||
|
||||
[dependencies.tauri-plugin-os]
|
||||
version = "2"
|
||||
optional = true
|
||||
|
||||
[dependencies.tauri-plugin-notification]
|
||||
version = "2"
|
||||
optional = true
|
||||
|
||||
[dependencies.tauri-plugin-deep-link]
|
||||
version = "2"
|
||||
optional = true
|
||||
|
||||
[dependencies.fix-path-env]
|
||||
git = "https://github.com/tauri-apps/fix-path-env-rs"
|
||||
optional = true
|
||||
|
||||
[build-dependencies.tauri-build]
|
||||
version = "2"
|
||||
features = []
|
||||
optional = true
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
whisper-rs = { version = "0.14.2", default-features = false }
|
||||
|
||||
[target.'cfg(darwin)'.dependencies.whisper-rs]
|
||||
version = "0.14.2"
|
||||
features = ["metal"]
|
||||
|
||||
[workspace.dependencies]
|
||||
reqwest = { version = "0.11", features = ["blocking", "json", "multipart", "gzip"] }
|
||||
uuid = { version = "1.4", features = ["v4"] }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
fn main() {
|
||||
tauri_build::build()
|
||||
#[cfg(feature = "gui")]
|
||||
tauri_build::build();
|
||||
}
|
||||
|
||||
62
src-tauri/capabilities/migrated.json
Normal file
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"identifier": "migrated",
|
||||
"description": "permissions that were migrated from v1",
|
||||
"local": true,
|
||||
"windows": ["main", "Live*", "Clip*"],
|
||||
"permissions": [
|
||||
"core:default",
|
||||
"fs:allow-read-file",
|
||||
"fs:allow-write-file",
|
||||
"fs:allow-read-dir",
|
||||
"fs:allow-copy-file",
|
||||
"fs:allow-mkdir",
|
||||
"fs:allow-remove",
|
||||
"fs:allow-remove",
|
||||
"fs:allow-rename",
|
||||
"fs:allow-exists",
|
||||
{
|
||||
"identifier": "fs:scope",
|
||||
"allow": ["**"]
|
||||
},
|
||||
"core:window:default",
|
||||
"core:window:allow-start-dragging",
|
||||
"core:window:allow-close",
|
||||
"core:window:allow-minimize",
|
||||
"core:window:allow-maximize",
|
||||
"core:window:allow-unmaximize",
|
||||
"core:window:allow-set-title",
|
||||
"sql:allow-execute",
|
||||
"shell:allow-open",
|
||||
"dialog:allow-open",
|
||||
"dialog:allow-save",
|
||||
"dialog:allow-message",
|
||||
"dialog:allow-ask",
|
||||
"dialog:allow-confirm",
|
||||
{
|
||||
"identifier": "http:default",
|
||||
"allow": [
|
||||
{
|
||||
"url": "https://*.*"
|
||||
},
|
||||
{
|
||||
"url": "http://*.*"
|
||||
}
|
||||
]
|
||||
},
|
||||
"dialog:default",
|
||||
"shell:default",
|
||||
"fs:default",
|
||||
"http:default",
|
||||
"sql:default",
|
||||
"os:default",
|
||||
"notification:default",
|
||||
"dialog:default",
|
||||
"fs:default",
|
||||
"http:default",
|
||||
"shell:default",
|
||||
"sql:default",
|
||||
"os:default",
|
||||
"dialog:default",
|
||||
"deep-link:default"
|
||||
]
|
||||
}
|
||||
16
src-tauri/config.example.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
cache = "./cache"
|
||||
output = "./output"
|
||||
live_start_notify = true
|
||||
live_end_notify = true
|
||||
clip_notify = true
|
||||
post_notify = true
|
||||
auto_subtitle = false
|
||||
subtitle_generator_type = "whisper_online"
|
||||
whisper_model = "./whisper_model.bin"
|
||||
whisper_prompt = "这是一段中文 你们好"
|
||||
openai_api_key = ""
|
||||
clip_name_format = "[{room_id}][{live_id}][{title}][{created_at}].mp4"
|
||||
|
||||
[auto_generate]
|
||||
enabled = false
|
||||
encode_danmu = false
|
||||
48
src-tauri/crates/danmu_stream/Cargo.toml
Normal file
@@ -0,0 +1,48 @@
|
||||
[package]
|
||||
name = "danmu_stream"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
name = "danmu_stream"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[example]]
|
||||
name = "bilibili"
|
||||
path = "examples/bilibili.rs"
|
||||
|
||||
[[example]]
|
||||
name = "douyin"
|
||||
path = "examples/douyin.rs"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-tungstenite = { version = "0.27", features = ["native-tls"] }
|
||||
futures-util = "0.3"
|
||||
prost = "0.14"
|
||||
chrono = "0.4"
|
||||
log = "0.4"
|
||||
env_logger = "0.11"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
url = "2.4"
|
||||
md5 = "0.8"
|
||||
regex = "1.9"
|
||||
deno_core = "0.355"
|
||||
pct-str = "2.0"
|
||||
thiserror = "2.0"
|
||||
flate2 = "1.0"
|
||||
scroll = "0.13"
|
||||
scroll_derive = "0.13"
|
||||
brotli = "8.0"
|
||||
http = "1.0"
|
||||
rand = "0.9"
|
||||
urlencoding = "2.1"
|
||||
gzip = "0.1.2"
|
||||
hex = "0.4.3"
|
||||
async-trait = "0.1"
|
||||
uuid = { workspace = true}
|
||||
|
||||
[build-dependencies]
|
||||
tonic-build = "0.14"
|
||||