Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions src/common/models/dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@
// ==LICENSE-END==

import { TPublication } from "readium-desktop/common/type/publication.type";
import { IOpdsFeedView } from "readium-desktop/common/views/opds";
import { IOpdsFeedView, IOpdsPublicationView } from "readium-desktop/common/views/opds";
import { PublicationView } from "readium-desktop/common/views/publication";

import { MiniLocatorExtended } from "readium-desktop/common/redux/states/locatorInitialState";
import { IReaderDialogOrDockSettingsMenuState } from "./reader";

interface IPubInfoState {
publication?: TPublication;
interface IPubInfoState<T extends TPublication = TPublication> {
publication?: T;
coverZoom?: boolean;
}
interface IPubInfoStateReader extends IPubInfoState {
interface IPubInfoStateReader extends IPubInfoState<PublicationView> {
focusWhereAmI: boolean;
pdfPlayerNumberOfPages: number | undefined; // super hacky :(
divinaNumberOfPages: number | undefined; // super hacky :(
Expand Down Expand Up @@ -54,8 +54,8 @@ export interface DialogType {
[DialogTypeName.FileImport]: {
files: IFileImport[];
};
[DialogTypeName.PublicationInfoOpds]: IPubInfoState;
[DialogTypeName.PublicationInfoLib]: IPubInfoState;
[DialogTypeName.PublicationInfoOpds]: IPubInfoState<IOpdsPublicationView>;
[DialogTypeName.PublicationInfoLib]: IPubInfoState<PublicationView>;
[DialogTypeName.PublicationInfoReader]: IPubInfoStateReader;
[DialogTypeName.OpdsFeedAddForm]: {};
[DialogTypeName.OpdsFeedUpdateForm]: {
Expand Down
4 changes: 2 additions & 2 deletions src/common/redux/actions/dialog/updateRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import { Action } from "readium-desktop/common/models/redux";
export const ID = "DIALOG_UPDATE_REQUEST";

export interface Payload<T extends keyof DialogType> {
data: DialogType[T];
data: Partial<DialogType[T]>;
}

export function build<T extends keyof DialogType>(data: DialogType[T]):
export function build<T extends keyof DialogType>(data: Partial<DialogType[T]>):
Action<typeof ID, Payload<T>> {

return {
Expand Down
9 changes: 5 additions & 4 deletions src/main/converter/publication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { diMainGet } from "../di";
import { lcpLicenseIsNotWellFormed } from "readium-desktop/common/lcp";
import { LCP } from "@r2-lcp-js/parser/epub/lcp";
import { MiniLocatorExtended } from "readium-desktop/common/redux/states/locatorInitialState";
import { applyOpdsPublicationViewFallback } from "./tools/publicationViewFallback";
// import { type Store } from "redux";
// import { RootState } from "../redux/states";

Expand Down Expand Up @@ -226,7 +227,7 @@ export class PublicationViewConverter {
isOpenable,
} = await getPublicationStorageState(document.identifier);

return {
return applyOpdsPublicationViewFallback({

isOpenable,
identifier: document.identifier, // preserve Identifiable identifier
Expand All @@ -240,7 +241,7 @@ export class PublicationViewConverter {
customCover: document.customCover,
r2PublicationJson: undefined,
lastReadingLocation: readerStateLocator,
};
}, document.opdsPublicationView);

}

Expand Down Expand Up @@ -342,7 +343,7 @@ export class PublicationViewConverter {
isOpenable,
} = await getPublicationStorageState(document.identifier);

return {
return applyOpdsPublicationViewFallback({

isOpenable,
isAudio,
Expand Down Expand Up @@ -407,6 +408,6 @@ export class PublicationViewConverter {
// r2PublicationBase64,

lastReadingLocation: readerStateLocator,
};
}, document.opdsPublicationView);
}
}
98 changes: 98 additions & 0 deletions src/main/converter/tools/publicationViewFallback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// ==LICENSE-BEGIN==
// Copyright 2017 European Digital Reading Lab. All rights reserved.
// Licensed to the Readium Foundation under one or more contributor license agreements.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file exposed on Github (readium) in the project repository.
// ==LICENSE-END==

import type { IOpdsPublicationView } from "readium-desktop/common/views/opds";
import type { PublicationView } from "readium-desktop/common/views/publication";

const metadataIsMissing = (value: unknown): boolean => {
if (value === undefined || value === null) {
return true;
}
if (typeof value === "string") {
return !value.trim();
}
if (Array.isArray(value)) {
return value.length === 0;
}
if (typeof value === "object") {
return Object.values(value).every(metadataIsMissing);
}
return false;
};

const metadataFallback = <T>(value: T | undefined, fallback: T | undefined): T | undefined =>
metadataIsMissing(value) ? fallback : value;

const titleWithoutPlaceholder = <T>(title: T | undefined): T | undefined =>
title === "-" ? undefined : title;

export const applyOpdsPublicationViewFallback = (
publicationView: PublicationView,
opdsPublicationView?: IOpdsPublicationView,
): PublicationView => {
if (!opdsPublicationView) {
return publicationView;
}

const documentTitle = metadataFallback(
titleWithoutPlaceholder(publicationView.documentTitle),
opdsPublicationView.documentTitle,
) || publicationView.documentTitle;
const publicationTitle = metadataFallback(
titleWithoutPlaceholder(publicationView.publicationTitle),
opdsPublicationView.documentTitle,
) || documentTitle;
const authorsLangString = opdsPublicationView.authorsLangString?.map(
(contributor) => contributor.nameLangString,
);
const publishersLangString = opdsPublicationView.publishersLangString?.map(
(contributor) => contributor.nameLangString,
);
const tags = opdsPublicationView.tags?.map((tag) => tag.name);

return {
...publicationView,
documentTitle,
publicationTitle,
authorsLangString: metadataFallback(publicationView.authorsLangString, authorsLangString) || [],
publishersLangString: metadataFallback(publicationView.publishersLangString, publishersLangString),
description: metadataFallback(publicationView.description, opdsPublicationView.description),
languages: metadataFallback(publicationView.languages, opdsPublicationView.languages),
workIdentifier: metadataFallback(publicationView.workIdentifier, opdsPublicationView.workIdentifier),
publishedAt: metadataFallback(publicationView.publishedAt, opdsPublicationView.publishedAt),
tags: metadataFallback(publicationView.tags, tags),
duration: metadataFallback(publicationView.duration, opdsPublicationView.duration),
nbOfTracks: metadataFallback(publicationView.nbOfTracks, opdsPublicationView.nbOfTracks),
a11y_accessMode: metadataFallback(publicationView.a11y_accessMode, opdsPublicationView.a11y_accessMode),
a11y_accessibilityFeature: metadataFallback(
publicationView.a11y_accessibilityFeature,
opdsPublicationView.a11y_accessibilityFeature,
),
a11y_accessibilityHazard: metadataFallback(
publicationView.a11y_accessibilityHazard,
opdsPublicationView.a11y_accessibilityHazard,
),
a11y_certifiedBy: metadataFallback(publicationView.a11y_certifiedBy, opdsPublicationView.a11y_certifiedBy),
a11y_certifierCredential: metadataFallback(
publicationView.a11y_certifierCredential,
opdsPublicationView.a11y_certifierCredential,
),
a11y_certifierReport: metadataFallback(
publicationView.a11y_certifierReport,
opdsPublicationView.a11y_certifierReport,
),
a11y_conformsTo: metadataFallback(publicationView.a11y_conformsTo, opdsPublicationView.a11y_conformsTo),
a11y_accessModeSufficient: metadataFallback(
publicationView.a11y_accessModeSufficient,
opdsPublicationView.a11y_accessModeSufficient,
),
a11y_accessibilitySummary: metadataFallback(
publicationView.a11y_accessibilitySummary,
opdsPublicationView.a11y_accessibilitySummary,
),
};
};
2 changes: 2 additions & 0 deletions src/main/db/document/publication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { File } from "readium-desktop/common/models/file";
import { Identifiable } from "readium-desktop/common/models/identifiable";
import { LcpInfo } from "readium-desktop/common/models/lcp";
import { Timestampable } from "readium-desktop/common/models/timestampable";
import type { IOpdsPublicationView } from "readium-desktop/common/views/opds";
// import { JsonMap } from "readium-desktop/typings/json";

// export interface Resources {
Expand Down Expand Up @@ -42,6 +43,7 @@ export interface PublicationDocument extends Identifiable, Timestampable {
hash: string;

opdsPublicationStringified?: string;
opdsPublicationView?: IOpdsPublicationView;
opdsPublication?: {
url?: string;
type?: string;
Expand Down
8 changes: 4 additions & 4 deletions src/main/redux/sagas/api/publication/import/importFromLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { customizationWellKnownFolder } from "readium-desktop/main/customization
import * as fs from "node:fs";
import { URL_PATH_PREFIX_CUSTOMPROFILEZIP } from "readium-desktop/common/streamerProtocol";
import { downloadOpdsCoverData, selectOpdsCoverLink } from "./opdsCover";
import { buildOpdsPublicationDocumentPatch } from "readium-desktop/main/tools/publicationDocument";

// Logger
const debug = debug_("readium-desktop:main#saga/api/publication/importFromLinkService");
Expand Down Expand Up @@ -114,6 +115,7 @@ function* importLinkFromPath(
}

let returnPublicationDocument = publicationDocumentWithCover;
const opdsPublicationDocumentPatch = buildOpdsPublicationDocumentPatch(link, pub);
if (!alreadyImported && publicationDocumentWithCover) {

const tags = pub?.tags?.map((v) => v.name) || [];
Expand All @@ -137,8 +139,7 @@ function* importLinkFromPath(
// // r2OpdsPublicationBase64: pub?.r2OpdsPublicationBase64 || "",
// } as Resources,
tags,
opdsPublicationStringified: pub?.opdsPublicationStringified,
opdsPublication: { url: link.url, type: link.type, selfLinkUrl: pub?.selfLink?.url, identifier: pub?.workIdentifier },
...opdsPublicationDocumentPatch,
},
);

Expand All @@ -165,8 +166,7 @@ function* importLinkFromPath(
// // r2LSDBase64: publicationDocument.resources.r2LSDBase64,
// // r2OpdsPublicationBase64: pub?.r2OpdsPublicationBase64 || "",
// } as Resources,
opdsPublicationStringified: pub?.opdsPublicationStringified,
opdsPublication: { url: link.url, type: link.type, selfLinkUrl: pub?.selfLink?.url, identifier: pub?.workIdentifier },
...opdsPublicationDocumentPatch,
},
);

Expand Down
21 changes: 21 additions & 0 deletions src/main/tools/publicationDocument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,20 @@

import { CustomCover, RandomCustomCovers } from "readium-desktop/common/models/custom-cover";
import { File } from "readium-desktop/common/models/file";
import type { IOpdsLinkView, IOpdsPublicationView } from "readium-desktop/common/views/opds";
import type { PublicationDocument } from "readium-desktop/main/db/document/publication";

export interface IPublicationFilesDocumentPatch {
coverFile?: File;
customCover?: CustomCover;
files: File[];
}

export type TOpdsPublicationDocumentPatch = Pick<
PublicationDocument,
"opdsPublication" | "opdsPublicationStringified" | "opdsPublicationView"
>;

export const pickRandomCustomCover = (): CustomCover =>
RandomCustomCovers[Math.floor(Math.random() * RandomCustomCovers.length)];

Expand All @@ -39,3 +46,17 @@ export const buildPublicationFilesDocumentPatch = (
files,
};
};

export const buildOpdsPublicationDocumentPatch = (
link: IOpdsLinkView,
publication?: IOpdsPublicationView,
): TOpdsPublicationDocumentPatch => ({
opdsPublicationStringified: publication?.opdsPublicationStringified,
opdsPublicationView: publication,
opdsPublication: {
url: link.url,
type: link.type,
selfLinkUrl: publication?.selfLink?.url,
identifier: publication?.workIdentifier,
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ export interface IProps {
pdfPlayerNumberOfPages: number | undefined; // super hacky :(
divinaNumberOfPages: number | undefined; // super hacky :(
divinaContinousEqualTrue: boolean;
readerReadingLocation: MiniLocatorExtended;
readerReadingLocation: MiniLocatorExtended | undefined;
onClickLinkCb?: (tag: IOpdsContributorView) => () => void | undefined;
closeDialogCb: () => void;
}

const Duration = (props: {
duration: number;
duration: number | undefined;
__: I18nFunction;
}) => {

Expand Down Expand Up @@ -94,7 +94,7 @@ const Progression = (props: {
r2Publication: R2Publication | null,
manifestUrlR2Protocol: string | null,
handleLinkUrl: ((url: string) => void) | undefined;
locatorExt: MiniLocatorExtended,
locatorExt: MiniLocatorExtended | undefined,
focusWhereAmI: boolean,
pdfPlayerNumberOfPages: number | undefined, // super hacky :(
divinaNumberOfPages: number | undefined, // super hacky :(
Expand All @@ -117,11 +117,17 @@ const Progression = (props: {
// (Audiobooks, PDF, Divina, EPUB FXL and reflow ... page number vs. string types)
try {

const isAudio = locatorExt.audioPlaybackInfo
const audioPlaybackInfo = locatorExt.audioPlaybackInfo;
const globalTime = audioPlaybackInfo?.globalTime;
const globalDuration = audioPlaybackInfo?.globalDuration;
const locatorPosition = locatorExt.locator.locations.position;
const isAudio = audioPlaybackInfo
// total duration can be undefined with badly-constructed publications,
// for example we found some LibriVox W3C LPF audiobooks missing duration property on reading order resources
&& locatorExt.audioPlaybackInfo.globalDuration
&& typeof locatorExt.locator.locations.position === "number"; // .progression is local to audio item in reading order playlist
&& typeof globalTime === "number"
&& typeof globalDuration === "number"
&& globalDuration > 0
&& typeof locatorPosition === "number"; // .progression is local to audio item in reading order playlist

const isDivina = r2Publication && isDivinaFn(r2Publication);
const isPdf = r2Publication && isPdfFn(r2Publication);
Expand All @@ -135,12 +141,13 @@ const Progression = (props: {
let txtHeadings: JSX.Element | undefined;

if (isAudio) {
const percent = Math.round(locatorExt.locator.locations.position * 100);
const percent = Math.round(locatorPosition * 100);
// const p = Math.round(100 * (locatorExt.audioPlaybackInfo.globalTime / locatorExt.audioPlaybackInfo.globalDuration));
txtProgression = `${percent}% [${formatTime(Math.round(locatorExt.audioPlaybackInfo.globalTime))} / ${formatTime(Math.round(locatorExt.audioPlaybackInfo.globalDuration))}]`;
txtProgression = `${percent}% [${formatTime(Math.round(globalTime))} / ${formatTime(Math.round(globalDuration))}]`;
} else if (isDivina) {
// console.log("----- ".repeat(100), divinaNumberOfPages, r2Publication?.Spine?.length);
let totalPages = (divinaNumberOfPages && !divinaContinousEqualTrue) ? divinaNumberOfPages : (r2Publication?.Spine?.length ? r2Publication.Spine.length : undefined);
const spineLength = r2Publication?.Spine?.length;
let totalPages = (divinaNumberOfPages && !divinaContinousEqualTrue) ? divinaNumberOfPages : spineLength;
if (typeof totalPages === "string") {
try {
totalPages = parseInt(totalPages, 10);
Expand All @@ -149,9 +156,11 @@ const Progression = (props: {
}
}

let pageNum = !divinaContinousEqualTrue ?
(locatorExt.locator.locations.position || 0) :
(Math.floor(locatorExt.locator.locations.progression * r2Publication.Spine.length) - 1);
let pageNum = !divinaContinousEqualTrue
? (locatorPosition || 0)
: spineLength
? (Math.floor(locatorExt.locator.locations.progression * spineLength) - 1)
: undefined;
if (typeof pageNum === "string") {
try {
pageNum = parseInt(pageNum, 10) + 1;
Expand Down Expand Up @@ -243,7 +252,7 @@ const Progression = (props: {
const percent = Math.round(locatorExt.locator.locations.progression * 100);
txtProgression = `${spineIndex + 1}/${r2Publication.Spine.length}${locatorExt.locator.title ? ` (${locatorExt.locator.title})` : ""} [${percent}%]`;

if (locatorExt.headings && manifestUrlR2Protocol) { // focusWhereAmI
if (locatorExt.headings && manifestUrlR2Protocol && handleLinkUrl) { // focusWhereAmI

let rank = 999;
const hs = locatorExt.headings.filter((h, _i) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,13 @@ class AddTag extends React.Component<IProps, IState> {

const { __, tagArray: tagArrayPublication, tags: allTagsLocally } = this.props;

const tagsOptions = allTagsLocally.filter((name) => !(tagArrayPublication || []).includes(name as any)).map((v, i) => ({ id: i, value: i, name: v }));
if (!__) {
return <></>;
}

const tagsOptions = (allTagsLocally ?? []).filter(
(name) => !(tagArrayPublication ?? []).includes(name as any),
).map((v, i) => ({ id: i, value: i, name: v }));

return (
this.props.pubId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const TagButton: React.FC<React.PropsWithChildren<IProps>> = (props) => {
tagString = tag.name;
}

if (pubId && onClickDeleteCb) {
if (pubId && onClickDeleteCb && typeof index === "number") {
button = (
<>
<Link
Expand Down
Loading
Loading