Skip to content

Commit 7bc48d1

Browse files
committed
feat: ✨ 尽量在下载时保持原文件名
https://sleazyfork.org/scripts/374903/discussions/297125
1 parent 52493e8 commit 7bc48d1

22 files changed

Lines changed: 133 additions & 79 deletions

File tree

src/components/DownloadButton.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export const DownloadButton = () => {
4949
let fileName: string;
5050
try {
5151
data = await downloadImg(url);
52-
fileName = `${index}.${getExtName(data.type)}`;
52+
fileName = img.name || `${index}.${getExtName(data.type)}`;
5353
} catch {
5454
fileName = `${index} - ${t('alert.download_failed')}`;
5555
setState('errorNum', (num) => num + 1);

src/components/Manga/actions/image.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ updatePageData.throttle = throttle(() => setState(updatePageData), 100);
4141
* 3. updatePageData
4242
*/
4343
export const resetImgState = (state: State) => {
44+
if (state.imgList.length === 0) {
45+
state.fillEffect = { '-1': true };
46+
return;
47+
}
48+
4449
// 如果用户没有手动修改过首页填充,才将其恢复初始
4550
if (typeof state.fillEffect['-1'] === 'boolean')
4651
state.fillEffect['-1'] =

src/components/Manga/actions/imageLoad.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export const handleImgLoaded = (url: string, e?: HTMLImageElement) => {
4040
updateImgSize(url, e.naturalWidth, e.naturalHeight);
4141

4242
if (store.option.imgRecognition.enabled && e.src === img.blobUrl)
43-
setTimeout(handleImgRecognition, 0, e, url);
43+
setTimeout(handleImgRecognition, 0, url, e);
4444

4545
translationAll();
4646
};

src/components/Manga/actions/imageSize.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ import {
1616
} from './memo';
1717

1818
/** 获取指定图片的显示尺寸 */
19-
export const getImgDisplaySize = (state: State, url: string) => {
20-
const img = state.imgMap[url];
21-
19+
export const getImgDisplaySize = (
20+
state: State,
21+
img: { width?: number; height?: number },
22+
) => {
2223
let height = img.height ?? placeholderSize().height;
2324
let width = img.width ?? placeholderSize().width;
2425

@@ -48,7 +49,7 @@ export const updateImgSize = (url: string, width: number, height: number) =>
4849
if (img.width === width && img.height === height) return;
4950
img.width = width;
5051
img.height = height;
51-
img.size = getImgDisplaySize(state, url);
52+
img.size = getImgDisplaySize(state, img);
5253
updateImgType(state, img);
5354
});
5455

@@ -66,7 +67,7 @@ createEffectOn(
6667
if (length === 0) return;
6768
setState((state) => {
6869
for (const url of state.imgList)
69-
state.imgMap[url].size = getImgDisplaySize(state, url);
70+
state.imgMap[url].size = getImgDisplaySize(state, state.imgMap[url]);
7071
});
7172
},
7273
);

src/components/Manga/actions/imageType.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const 横幅比例 = 1920 / 1080;
1717
const 条漫比例 = 1920 / 2 / 1080 / 2;
1818

1919
/** 根据比例判断图片类型 */
20-
const getImgType = (img: { width: number; height: number }) => {
20+
export const getImgType = (img: { width: number; height: number }) => {
2121
const imgRatio = img.width / img.height;
2222
if (imgRatio <= 单页比例) return imgRatio < 条漫比例 ? 'vertical' : '';
2323
return imgRatio > 横幅比例 ? 'long' : 'wide';

src/components/Manga/hooks/useInit.ts

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import type { SetRequired } from 'type-fest';
2+
13
import { createEffect, on } from 'solid-js';
4+
import { unwrap } from 'solid-js/store';
25

36
import {
47
assign,
@@ -8,14 +11,16 @@ import {
811
throttle,
912
} from 'helper';
1013

11-
import type { MangaProps } from '..';
14+
import type { ComicImgData, MangaProps } from '..';
1215
import type { State } from '../store';
1316
import type { ComicImg } from '../store/image';
1417
import type { Option } from '../store/option';
1518

1619
import {
1720
defaultHotkeys,
1821
focus,
22+
getImgDisplaySize,
23+
getImgType,
1924
placeholderSize,
2025
resetImgState,
2126
resumeReadProgress,
@@ -30,13 +35,6 @@ import classes from '../index.module.css';
3035
import { refs, setState } from '../store';
3136
import { defaultOption } from '../store/option';
3237

33-
const createComicImg = (src: string): ComicImg => ({
34-
src,
35-
loadType: 'wait',
36-
size: placeholderSize(),
37-
blobUrl: src.startsWith('blob:') ? src : undefined,
38-
});
39-
4038
export const useInit = (props: MangaProps) => {
4139
watchDomSize('rootSize', refs.root);
4240

@@ -134,8 +132,36 @@ export const useInit = (props: MangaProps) => {
134132

135133
const handleImgList = () => {
136134
setState((state) => {
137-
// 使用相对协议路径,防止 Mixed Content 报错
138-
const imgList = props.imgList.map((url) => url?.replace(/^http:/, ''));
135+
const newImgMap: State['imgMap'] = {};
136+
const newImgList: string[] = []; // 因为会有相同 url 的图片,所以不能用 Set
137+
for (const img of unwrap(props.imgList)) {
138+
// 使用相对协议路径,防止 Mixed Content 报错
139+
const url =
140+
(typeof img === 'object' ? img.src : img)?.replace(/^http:/, '') ??
141+
'';
142+
newImgList.push(url);
143+
144+
if (Reflect.has(newImgMap, url)) continue;
145+
if (Reflect.has(state.imgMap, url)) {
146+
newImgMap[url] = state.imgMap[url];
147+
continue;
148+
}
149+
150+
const imgItem: ComicImgData =
151+
typeof img === 'string' ? { src: url } : img;
152+
imgItem.loadType ??= 'wait';
153+
if (imgItem.width && imgItem.height) {
154+
imgItem.size = getImgDisplaySize(state, imgItem);
155+
imgItem.type = getImgType(
156+
imgItem as SetRequired<ComicImg, 'width' | 'height'>,
157+
);
158+
}
159+
imgItem.size ??= placeholderSize();
160+
if (!imgItem.blobUrl && url.startsWith('blob:'))
161+
imgItem.blobUrl = imgItem.src;
162+
163+
newImgMap[url] = imgItem as ComicImg;
164+
}
139165

140166
/** 修改前的当前显示图片 */
141167
const oldActiveImg =
@@ -147,21 +173,22 @@ export const useInit = (props: MangaProps) => {
147173
const fillEffectList = Object.keys(state.fillEffect).map(Number);
148174
for (const pageIndex of fillEffectList) {
149175
if (pageIndex === -1) continue;
150-
if (state.imgList[pageIndex] === imgList[pageIndex]) continue;
176+
if (state.imgList[pageIndex] === newImgList[pageIndex]) continue;
151177
needResetFillEffect = true;
152178
break;
153179
}
154180

155-
const newImgList = new Set(imgList);
156181
const oldImgList = new Set(state.imgList);
157182

158-
if (oldImgList.size === 0 && newImgList.size > 0) {
183+
if (oldImgList.size === 0 && newImgList.length > 0) {
159184
resumeReadProgress(state);
160185
updateSelfhostedOptions(true);
161186
}
162187

163188
/** 被删除的图片 */
164-
const deleteList = [...oldImgList].filter((url) => !newImgList.has(url));
189+
const deleteList = [...oldImgList].filter(
190+
(url) => !newImgList.includes(url),
191+
);
165192
for (const url of deleteList)
166193
if (state.imgMap[url].blobUrl && state.imgMap[url].blobUrl !== url)
167194
URL.revokeObjectURL(state.imgMap[url].blobUrl);
@@ -175,15 +202,11 @@ export const useInit = (props: MangaProps) => {
175202
/** 是否需要更新页面 */
176203
const needUpdatePageData =
177204
needResetFillEffect ||
178-
state.imgList.length !== imgList.length ||
205+
state.imgList.length !== newImgList.length ||
179206
deleteNum > 0;
180207

181-
const newImgMap: State['imgMap'] = {};
182-
for (const url of imgList)
183-
newImgMap[url] = state.imgMap[url] ?? createComicImg(url);
184-
185208
state.imgMap = newImgMap;
186-
state.imgList = imgList;
209+
state.imgList = [...newImgList];
187210

188211
state.prop.onLoading?.(state.imgList.map((url) => state.imgMap[url]));
189212

@@ -211,7 +234,7 @@ export const useInit = (props: MangaProps) => {
211234
// 尽量使当前显示的图片在修改后依然不变
212235
oldActiveImg.some((url) => {
213236
// 跳过填充页和已被删除的图片
214-
if (!url || imgList.includes(url)) return false;
237+
if (!url || newImgList.includes(url)) return false;
215238

216239
const newPageIndex = state.pageList.findIndex((page) =>
217240
page.some((index) => state.imgList?.[index] === url),

src/components/Manga/index.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { createEffect, enableScheduling, onMount } from 'solid-js';
55

66
import { boolDataVal } from 'helper';
77

8-
import type { FillEffect } from './store/image';
8+
import type { ComicImg, FillEffect } from './store/image';
99
import type { State } from './store/index';
1010
import type { Option } from './store/option';
1111

@@ -31,12 +31,14 @@ import { store } from './store/index';
3131

3232
enableScheduling();
3333

34+
export type ComicImgData = Partial<ComicImg> & { src: string };
35+
3436
export type MangaProps = {
3537
class?: string;
3638
classList?: ClassList;
3739

3840
/** 图片url列表 */
39-
imgList: string[];
41+
imgList: (ComicImgData | string)[];
4042
/** 页面填充数据 */
4143
fillEffect?: FillEffect;
4244
/** 初始化配置 */

src/components/Manga/store/image.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export type ComicImg = {
44
src: string;
55
width?: number;
66
height?: number;
7+
name?: string;
78

89
size: { height: number; width: number };
910
blobUrl?: string;

src/helper/other.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ export const clamp = (min: number, val: number, max: number) =>
3434
export const inRange = (min: number, val: number, max: number) =>
3535
val >= min && val <= max;
3636

37+
export const getFileName = (url: string) => url.match(/.+\/([^?]+)/)?.[1];
38+
3739
/** 判断两个数是否在指定误差范围内相等 */
3840
export const approx = (val: number, target: number, range: number) =>
3941
Math.abs(target - val) <= range;
@@ -281,8 +283,14 @@ export async function wait<T>(
281283
}
282284

283285
/** 等到指定的 dom 出现 */
284-
export const waitDom = (selector: string, timeout?: number) =>
285-
wait(() => querySelector(selector), timeout);
286+
export async function waitDom(selector: string): Promise<HTMLElement>;
287+
export async function waitDom(
288+
selector: string,
289+
timeout?: number,
290+
): Promise<HTMLElement | null>;
291+
export async function waitDom(selector: string, timeout?: number) {
292+
return wait(() => querySelector(selector), timeout);
293+
}
286294

287295
/** 等待指定的图片元素加载完成 */
288296
export const waitImgLoad = (

src/pwa/src/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export const Root: Component = () => (
106106
<Manga
107107
class={classes.manga}
108108
show={store.show}
109-
imgList={store.imgList.map(({ url }) => url)}
109+
imgList={store.imgList}
110110
option={{ alwaysLoadAllImg: true, ...getSaveOption() }}
111111
onOptionChange={handleOptionChange}
112112
editButtonList={editButtonList}

0 commit comments

Comments
 (0)