im_reader 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f693637efa72f8b0c59dce1206e1e9716b3f5cac3335b2dac02bc1d6cc025548
4
- data.tar.gz: feac962d03b51b08322c7beb9258be0f39b6811f2e1f0f848f3e1ab996797fd0
3
+ metadata.gz: 9df80b54a543f446c9a12e0f09d296813cb861cd44f1ed283d5b30e3df57797c
4
+ data.tar.gz: 48e211b702f2dd7e3261db75d8be8d48c7c759e0dd7c23b85fdaa63148f0ad08
5
5
  SHA512:
6
- metadata.gz: '038aacfbf6507b20e57ce5cf156d0ceb4a666b1eb0e555595d2d719744e64ad3ddb88ec83ca5b62bfbb19e56ba35af5be9f394fa8da102f794c06a39fc434a65'
7
- data.tar.gz: 746605c1a88fecbc0402082d58f5b54fcf93fced074182c20146da00d9d7aee7b8d756780b482c08592878e8350eece1566b2345fc02ddaf0f2c5871736e56b1
6
+ metadata.gz: df2f8086643d74000d8fed67ad0f0068b56966757c4de95fe72ed38f4f8a113486294f0240a16c95d353b6cd3b0784b3313de5188e18a7ce4b10d02cd9959788
7
+ data.tar.gz: 066ed3cb0d3ead6971f51589aa722c872b77f9394d107246e89b1b9cb02d74aacbb5446d5247d4e53e3fafab02ce7103914db1ea8e8e3010b020a9dac875db64
@@ -19,17 +19,43 @@
19
19
  $("#reader-root").append(overlay);
20
20
 
21
21
  var bookUrl = $root.data("book-url");
22
+ if (!bookUrl) {
23
+ $("#cover-content").html(`<p class="ui red text">${i18n.missing_url || "URL manquante."}</p>`);
24
+ return;
25
+ }
22
26
  var theme = "light";
23
27
  var fontSize = 100;
28
+ fetch(bookUrl, { method: "HEAD" })
29
+ .then((res) => {
30
+ if (!res.ok) {
31
+ // Si HEAD échoue, on tente GET pour récupérer le message texte
32
+ return fetch(bookUrl).then(async (resp) => {
33
+ const msg = await resp.text();
34
+ throw new Error(msg || i18n.reading_error || "Erreur de lecture.");
35
+ });
36
+ }
37
+ })
38
+ .then(() => startReader(bookUrl, i18n, overlay))
39
+ .catch((err) => {
40
+ console.error("[im_reader] load error:", err);
41
+ $("#cover-content").html(`<p class="ui red text">${err.message}</p>`);
42
+ });
43
+ }
24
44
 
45
+ function startReader(bookUrl, i18n, overlay) {
25
46
  var $viewer = $("#viewer"), $toc = $("#toc");
26
- var $prev_button = $("#prev_button"), $next_button = $("#next_button"), $btnTheme = $("#btnTheme");
47
+ var $prev_button = $("#prev_button"), $next_button = $("#next_button");
27
48
 
28
- if (!window.ePub) { console.error("ePub introuvable"); return; }
49
+ if (!window.ePub) {
50
+ $("#cover-content").html(`<p class="ui red text">ePub introuvable</p>`);
51
+ return;
52
+ }
29
53
 
30
- window.book = window.ePub(bookUrl, { openAs: "epub",
54
+ window.book = window.ePub(bookUrl, {
55
+ openAs: "epub",
31
56
  replacements: "blob",
32
- restore: false });
57
+ restore: false
58
+ });
33
59
 
34
60
  var rendition = book.renderTo("viewer", {
35
61
  width: "100%",
@@ -53,34 +79,17 @@
53
79
  if (iframe) iframe.setAttribute("sandbox", "allow-same-origin allow-scripts");
54
80
  });
55
81
 
56
- $prev_button.on("click", () => {
57
- rendition.prev();
58
- });
59
-
60
- $next_button.on("click", () => {
61
- rendition.next();
62
- });
63
-
64
- rendition.on("relocated", (location) => {
65
- if (book.locations.total > 0) {
66
- const current = book.locations.locationFromCfi(location.start.cfi);
67
- const total = book.locations.total;
68
- let percentage = current / total;
69
- if (location.atEnd) percentage = 1.0;
70
-
71
- $('#progressBar').progress('set percent', Math.round(percentage * 100));
72
- }
73
- });
82
+ $prev_button.on("click", () => rendition.prev());
83
+ $next_button.on("click", () => rendition.next());
74
84
 
75
85
  book.ready.then(() => Promise.all([book.loaded.navigation, book.loaded.manifest]))
76
86
  .then(([toc, manifest]) => {
77
-
78
87
  book.coverUrl().then((url) => {
79
88
  if (url) {
80
89
  $("#cover-content").html(`
81
- <img src="${url}" alt="${i18n.book_cover}">
82
- <button id="start_button" class="ui primary button">${i18n.start}</button>
83
- `);
90
+ <img src="${url}" alt="${i18n.book_cover}">
91
+ <button id="start_button" class="ui primary button">${i18n.start}</button>
92
+ `);
84
93
 
85
94
  overlay.on("click", "#start_button", () => {
86
95
  overlay.remove();
@@ -93,51 +102,24 @@
93
102
  });
94
103
 
95
104
  const $toc = $("#toc");
96
-
97
- function normalizeHref(href) {
98
- for (const key in manifest) {
99
- const entry = manifest[key];
100
- if (
101
- entry.href.endsWith(href) ||
102
- entry.href.endsWith("x" + href) ||
103
- entry.href.endsWith("/" + href)
104
- ) {
105
- return entry.href;
106
- }
107
- }
108
-
109
- // Par défaut
110
- return href;
111
- }
112
-
113
105
  toc.toc.forEach((chapter) => {
114
- const resolvedHref = normalizeHref(chapter.href);
115
-
116
106
  $("<a>")
117
107
  .addClass("item")
118
108
  .attr("href", "#")
119
109
  .text(chapter.label)
120
110
  .on("click", (e) => {
121
111
  e.preventDefault();
122
- rendition
123
- .display(resolvedHref)
124
- .catch((err) => {
125
- console.error("[im_reader] Display error:", err.message, resolvedHref);
126
- });
112
+ rendition.display(chapter.href).catch((err) => {
113
+ console.error("[im_reader] Display error:", err.message);
114
+ });
127
115
  })
128
116
  .appendTo($toc);
129
117
  });
130
118
  })
131
- .catch((err) => console.error("[im_reader] TOC load error:", err));
132
-
133
- book.ready.then(() => {
134
- return book.locations.generate(1000);
135
- }).then(() => {
136
- }).catch(e => console.error("Error, book not ready", e));
137
-
138
- rendition.on("displayError", (e)=> console.error("Error on display", e));
139
-
140
- rendition.display().catch(e=>console.error("Error while rendering book", e));
119
+ .catch((err) => {
120
+ console.error("[im_reader] TOC load error:", err);
121
+ $("#cover-content").html(`<p class="ui red text">${i18n.reading_error || "Erreur de lecture du livre."}</p>`);
122
+ });
141
123
  }
142
124
 
143
125
  $(function(){ var $root = $("#reader-root"); if ($root.length) initReader($root); });
@@ -8,12 +8,14 @@ module ImReader
8
8
  end
9
9
 
10
10
  def remote
11
- url = params[:url]
12
- uri = URI.parse(url)
11
+ raw_url = params[:url].to_s.strip
12
+ return render plain: I18n.t('im_reader.messages.missing_url'), status: 400 if raw_url.empty?
13
+ uri = parse_uri(raw_url)
14
+ return render plain: I18n.t('im_reader.messages.invalid_url'), status: 400 unless uri
15
+
13
16
  response = fetch_with_redirect(uri)
14
17
 
15
18
  if response.is_a?(Net::HTTPSuccess)
16
-
17
19
  send_data response.body,
18
20
  filename: File.basename(uri.path.presence || "remote.epub"),
19
21
  type: "application/epub+zip",
@@ -25,6 +27,24 @@ module ImReader
25
27
 
26
28
  private
27
29
 
30
+ def parse_uri(value)
31
+ raw = value.to_s.strip
32
+ return nil if raw.empty?
33
+
34
+ candidates = [raw, CGI.unescape(raw)].uniq
35
+
36
+ candidates.each do |c|
37
+ begin
38
+ uri = URI.parse(c)
39
+ return uri if uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
40
+ rescue URI::InvalidURIError, URI::InvalidComponentError
41
+ next
42
+ end
43
+ end
44
+
45
+ nil
46
+ end
47
+
28
48
  def fetch_with_redirect(uri, limit = 5)
29
49
  raise "Too many redirects" if limit == 0
30
50
 
@@ -3,6 +3,7 @@
3
3
  data-i18n='<%= {
4
4
  start: t("im_reader.buttons.start"),
5
5
  loading: t("im_reader.messages.loading"),
6
+ missing_url: t("im_reader.messages.missing_url"),
6
7
  book_cover: t("im_reader.elements.book_cover")
7
8
  }.to_json %>'>
8
9
  <aside class="ui vertical menu">
@@ -7,5 +7,7 @@ de:
7
7
  buttons:
8
8
  start: "Lesen beginnen"
9
9
  messages:
10
+ invalid_url: "Die URL zum Lesen ist ungültig."
11
+ missing_url: "Die Lese-URL fehlt."
10
12
  loading: "Buch wird geladen..."
11
13
  reading_error: "EPUB-Datei konnte nicht abgerufen werden"
@@ -7,5 +7,7 @@ en:
7
7
  buttons:
8
8
  start: "Start to Read"
9
9
  messages:
10
+ invalid_url: "The URL provide is invalid."
11
+ missing_url: "The reading URL is missing."
10
12
  loading: "Book is loading ..."
11
13
  reading_error: "Unable to retrieve the EPUB"
@@ -7,5 +7,7 @@ es:
7
7
  buttons:
8
8
  start: "Comenzar la lectura"
9
9
  messages:
10
+ invalid_url: "La URL para leer no es válida."
11
+ missing_url: "Falta la URL de lectura."
10
12
  loading: "Cargando el libro..."
11
13
  reading_error: "No se pudo recuperar el EPUB"
@@ -7,5 +7,7 @@ fr:
7
7
  buttons:
8
8
  start: "Commencer la lecture"
9
9
  messages:
10
+ invalid_url: "L'url de lecture est invalide."
11
+ missing_url: "L'url de lecture est manquante."
10
12
  loading: "Chargement du livre ..."
11
13
  reading_error: "Impossible de récupérer l’EPUB"
@@ -7,5 +7,7 @@ pt:
7
7
  buttons:
8
8
  start: "Começar a leitura"
9
9
  messages:
10
+ invalid_url: "O URL para leitura é inválido."
11
+ missing_url: "O URL de leitura está em falta."
10
12
  loading: "Carregando o livro..."
11
13
  reading_error: "Não foi possível recuperar o EPUB."
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ImReader
4
- VERSION = "1.0.1"
4
+ VERSION = "1.0.2"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: im_reader
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elodie Ailleaume
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-10-30 00:00:00.000000000 Z
11
+ date: 2025-11-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sass