potential crash on inforeader
[nikiroo-utils.git] / supported / Cbz.java
1 package be.nikiroo.fanfix.supported;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.net.URL;
7 import java.util.ArrayList;
8 import java.util.Collections;
9 import java.util.HashMap;
10 import java.util.List;
11 import java.util.Map;
12 import java.util.zip.ZipEntry;
13 import java.util.zip.ZipInputStream;
14
15 import be.nikiroo.fanfix.Instance;
16 import be.nikiroo.fanfix.bundles.Config;
17 import be.nikiroo.fanfix.data.Chapter;
18 import be.nikiroo.fanfix.data.MetaData;
19 import be.nikiroo.fanfix.data.Paragraph;
20 import be.nikiroo.fanfix.data.Paragraph.ParagraphType;
21 import be.nikiroo.fanfix.data.Story;
22 import be.nikiroo.utils.IOUtils;
23 import be.nikiroo.utils.Image;
24 import be.nikiroo.utils.Progress;
25 import be.nikiroo.utils.streams.MarkableFileInputStream;
26
27 /**
28 * Support class for CBZ files (works better with CBZ created with this program,
29 * as they have some metadata available).
30 *
31 * @author niki
32 */
33 class Cbz extends Epub {
34 @Override
35 protected boolean supports(URL url) {
36 return url.toString().toLowerCase().endsWith(".cbz");
37 }
38
39 @Override
40 protected String getDataPrefix() {
41 return "";
42 }
43
44 @Override
45 protected boolean requireInfo() {
46 return false;
47 }
48
49 @Override
50 protected boolean isImagesDocumentByDefault() {
51 return true;
52 }
53
54 @Override
55 protected boolean getCover() {
56 return false;
57 }
58
59 @Override
60 public Story doProcess(Progress pg) throws IOException {
61 if (pg == null) {
62 pg = new Progress();
63 } else {
64 pg.setMinMax(0, 100);
65 }
66
67 pg.setName("Initialising");
68
69 Progress pgMeta = new Progress();
70 pg.addProgress(pgMeta, 10);
71 Story story = processMeta(true, pgMeta);
72 MetaData meta = story.getMeta();
73
74 pgMeta.done(); // 10%
75
76 pg.setName(meta.getTitle());
77
78 File tmpDir = Instance.getInstance().getTempFiles().createTempDir("info-text");
79 String basename = null;
80
81 Map<String, Image> images = new HashMap<String, Image>();
82 InputStream cbzIn = null;
83 ZipInputStream zipIn = null;
84 try {
85 cbzIn = new MarkableFileInputStream(getSourceFileOriginal());
86 zipIn = new ZipInputStream(cbzIn);
87 for (ZipEntry entry = zipIn.getNextEntry(); entry != null; entry = zipIn
88 .getNextEntry()) {
89 if (!entry.isDirectory()
90 && entry.getName().startsWith(getDataPrefix())) {
91 String entryLName = entry.getName().toLowerCase();
92 boolean imageEntry = false;
93 for (String ext : bsImages.getImageExt(false)) {
94 if (entryLName.endsWith(ext)) {
95 imageEntry = true;
96 }
97 }
98
99 if (imageEntry) {
100 String uuid = meta.getUuid() + "_" + entry.getName();
101 try {
102 images.put(uuid, new Image(zipIn));
103 } catch (Exception e) {
104 Instance.getInstance().getTraceHandler().error(e);
105 }
106
107 if (pg.getProgress() < 85) {
108 pg.add(1);
109 }
110 } else if (entryLName.endsWith(".info")) {
111 basename = entryLName.substring(0, entryLName.length()
112 - ".info".length());
113 IOUtils.write(zipIn, new File(tmpDir, entryLName));
114 } else if (entryLName.endsWith(".txt")) {
115 IOUtils.write(zipIn, new File(tmpDir, entryLName));
116 }
117 }
118 }
119
120 String ext = "."
121 + Instance.getInstance().getConfig().getString(Config.FILE_FORMAT_IMAGE_FORMAT_COVER).toLowerCase();
122 String coverName = meta.getUuid() + "_" + basename + ext;
123 Image cover = images.get(coverName);
124 images.remove(coverName);
125
126 pg.setProgress(85);
127
128 // ZIP order is not correct for us
129 List<String> imagesList = new ArrayList<String>(images.keySet());
130 Collections.sort(imagesList);
131
132 pg.setProgress(90);
133
134 // only the description/cover is kept
135 Story origStory = getStoryFromTxt(tmpDir, basename);
136 if (origStory != null) {
137 if (origStory.getMeta().getCover() == null) {
138 origStory.getMeta().setCover(story.getMeta().getCover());
139 }
140 story.setMeta(origStory.getMeta());
141 }
142 if (story.getMeta().getCover() == null) {
143 story.getMeta().setCover(cover);
144 }
145 story.setChapters(new ArrayList<Chapter>());
146
147 // Check if we can find non-images chapters, for hybrid-cbz support
148 if (origStory != null) {
149 for (Chapter chap : origStory) {
150 Boolean isImages = null;
151 for (Paragraph para : chap) {
152 ParagraphType t = para.getType();
153 if (isImages == null && !t.isText(true)) {
154 isImages = true;
155 }
156 if (t.isText(false)) {
157 String line = para.getContent();
158 // Images are saved in text mode as "[image-link]"
159 if (!(line.startsWith("[") && line.endsWith("]"))) {
160 isImages = false;
161 }
162 }
163 }
164
165 if (isImages != null && !isImages) {
166 story.getChapters().add(chap);
167 chap.setNumber(story.getChapters().size());
168 }
169 }
170 }
171
172 if (!imagesList.isEmpty()) {
173 Chapter chap = new Chapter(story.getChapters().size() + 1, null);
174 story.getChapters().add(chap);
175
176 for (String uuid : imagesList) {
177 try {
178 chap.getParagraphs().add(
179 new Paragraph(images.get(uuid)));
180 } catch (Exception e) {
181 Instance.getInstance().getTraceHandler().error(e);
182 }
183 }
184 }
185
186 if (meta.getCover() == null && !images.isEmpty()) {
187 meta.setCover(images.get(imagesList.get(0)));
188 meta.setFakeCover(true);
189 }
190 } finally {
191 IOUtils.deltree(tmpDir);
192 if (zipIn != null) {
193 zipIn.close();
194 }
195 if (cbzIn != null) {
196 cbzIn.close();
197 }
198 }
199
200 pg.setName(meta.getTitle());
201 pg.done();
202
203 return story;
204 }
205
206 private Story getStoryFromTxt(File tmpDir, String basename) {
207 Story origStory = null;
208
209 File txt = new File(tmpDir, basename + ".txt");
210 if (!txt.exists()) {
211 basename = null;
212 }
213 if (basename != null) {
214 try {
215 BasicSupport support = BasicSupport.getSupport(txt.toURI()
216 .toURL());
217 origStory = support.process(null);
218 } catch (Exception e) {
219 basename = null;
220 }
221 }
222
223 return origStory;
224
225 }
226 }