fix missing values for some old files
[nikiroo-utils.git] / supported / InfoReader.java
CommitLineData
68686a37
NR
1package be.nikiroo.fanfix.supported;
2
3import java.io.File;
68686a37
NR
4import java.io.FileNotFoundException;
5import java.io.IOException;
6import java.io.InputStream;
333f0e7b 7import java.net.URL;
68686a37
NR
8import java.util.ArrayList;
9import java.util.List;
7445f856 10import java.util.Scanner;
68686a37 11
31e27ee3
NR
12import org.jsoup.nodes.Document;
13
2d2a3222
NR
14import be.nikiroo.fanfix.Instance;
15import be.nikiroo.fanfix.bundles.Config;
68686a37 16import be.nikiroo.fanfix.data.MetaData;
bb7021f2 17import be.nikiroo.utils.Image;
67837328 18import be.nikiroo.utils.streams.MarkableFileInputStream;
68686a37
NR
19
20// not complete: no "description" tag
21public class InfoReader {
8d59ce07
NR
22 static protected BasicSupportHelper bsHelper = new BasicSupportHelper();
23 // static protected BasicSupportImages bsImages = new BasicSupportImages();
31e27ee3
NR
24 // static protected BasicSupportPara bsPara = new BasicSupportPara(new
25 // BasicSupportHelper(), new BasicSupportImages());
8d59ce07 26
57f02339
NR
27 public static MetaData readMeta(File infoFile, boolean withCover)
28 throws IOException {
68686a37
NR
29 if (infoFile == null) {
30 throw new IOException("File is null");
31 }
32
33 if (infoFile.exists()) {
67837328 34 InputStream in = new MarkableFileInputStream(infoFile);
68686a37 35 try {
31e27ee3
NR
36 MetaData meta = createMeta(infoFile.toURI().toURL(), in,
37 withCover);
38
39 // Some old .info files don't have those now required fields...
40 // So we check if we can find the info in another way (many
41 // formats have a copy of the original text file)
42 if (!hasIt(meta.getTitle(), meta.getAuthor(), meta.getDate(),
43 meta.getUrl())) {
44
45 // TODO: not nice, would be better to do it properly...
46
47 String base = infoFile.getPath();
48 if (base.endsWith(".info")) {
49 base = base.substring(0,
50 base.length() - ".info".length());
51 }
52 File textFile = new File(base);
53 if (!textFile.exists()) {
54 textFile = new File(base + ".txt");
55 }
56 if (!textFile.exists()) {
57 textFile = new File(base + ".text");
58 }
59
60 if (textFile.exists()) {
61 final URL source = textFile.toURI().toURL();
62 final MetaData[] superMetaA = new MetaData[1];
63 @SuppressWarnings("unused")
64 Text unused = new Text() {
65 private boolean loaded = loadDocument();
66
67 @Override
68 public SupportType getType() {
69 return SupportType.TEXT;
70 }
71
72 protected boolean loadDocument()
73 throws IOException {
74 loadDocument(source);
75 superMetaA[0] = getMeta();
76 return true;
77 }
78
79 @Override
80 protected Image getCover(File sourceFile) {
81 return null;
82 }
83 };
84
85 MetaData superMeta = superMetaA[0];
86 if (!hasIt(meta.getTitle())) {
87 meta.setTitle(superMeta.getTitle());
88 }
89 if (!hasIt(meta.getAuthor())) {
90 meta.setAuthor(superMeta.getAuthor());
91 }
92 if (!hasIt(meta.getDate())) {
93 meta.setDate(superMeta.getDate());
94 }
95 if (!hasIt(meta.getUrl())) {
96 meta.setUrl(superMeta.getUrl());
97 }
98 }
99 }
100
101 return meta;
68686a37
NR
102 } finally {
103 in.close();
68686a37 104 }
68686a37 105 }
211f7ddb
NR
106
107 throw new FileNotFoundException(
108 "File given as argument does not exists: "
109 + infoFile.getAbsolutePath());
68686a37
NR
110 }
111
31e27ee3
NR
112 /**
113 * Check if we have non-empty values for all the given {@link String}s.
114 *
115 * @param values
116 * the values to check
117 *
118 * @return TRUE if none of them was NULL or empty
119 */
120 static private boolean hasIt(String... values) {
121 for (String value : values) {
122 if (value == null || value.trim().isEmpty()) {
123 return false;
124 }
125 }
126
127 return true;
128 }
129
57f02339
NR
130 private static MetaData createMeta(URL sourceInfoFile, InputStream in,
131 boolean withCover) throws IOException {
68686a37
NR
132 MetaData meta = new MetaData();
133
134 meta.setTitle(getInfoTag(in, "TITLE"));
135 meta.setAuthor(getInfoTag(in, "AUTHOR"));
136 meta.setDate(getInfoTag(in, "DATE"));
137 meta.setTags(getInfoTagList(in, "TAGS", ","));
138 meta.setSource(getInfoTag(in, "SOURCE"));
2206ef66 139 meta.setUrl(getInfoTag(in, "URL"));
68686a37
NR
140 meta.setPublisher(getInfoTag(in, "PUBLISHER"));
141 meta.setUuid(getInfoTag(in, "UUID"));
142 meta.setLuid(getInfoTag(in, "LUID"));
143 meta.setLang(getInfoTag(in, "LANG"));
144 meta.setSubject(getInfoTag(in, "SUBJECT"));
145 meta.setType(getInfoTag(in, "TYPE"));
146 meta.setImageDocument(getInfoTagBoolean(in, "IMAGES_DOCUMENT", false));
57f02339 147 if (withCover) {
16a81ef7
NR
148 String infoTag = getInfoTag(in, "COVER");
149 if (infoTag != null && !infoTag.trim().isEmpty()) {
31e27ee3 150 meta.setCover(bsHelper.getImage(null, sourceInfoFile, infoTag));
16a81ef7 151 }
2d2a3222 152 if (meta.getCover() == null) {
bb7021f2
NR
153 // Second chance: try to check for a cover next to the info file
154 meta.setCover(getCoverByName(sourceInfoFile));
2d2a3222 155 }
57f02339 156 }
793f1071
NR
157 try {
158 meta.setWords(Long.parseLong(getInfoTag(in, "WORDCOUNT")));
159 } catch (NumberFormatException e) {
160 meta.setWords(0);
161 }
162 meta.setCreationDate(getInfoTag(in, "CREATION_DATE"));
a9eb3f46 163 meta.setFakeCover(Boolean.parseBoolean(getInfoTag(in, "FAKE_COVER")));
68686a37 164
57f02339 165 if (withCover && meta.getCover() == null) {
8d59ce07 166 meta.setCover(bsHelper.getDefaultCover(meta.getSubject()));
68686a37
NR
167 }
168
169 return meta;
170 }
171
bb7021f2
NR
172 /**
173 * Return the cover image if it is next to the source file.
174 *
175 * @param sourceInfoFile
176 * the source file
177 *
178 * @return the cover if present, NULL if not
179 */
180 public static Image getCoverByName(URL sourceInfoFile) {
30478c5b
NR
181 Image cover = null;
182
183 File basefile = new File(sourceInfoFile.getFile());
184
31e27ee3
NR
185 String ext = "." + Instance.getInstance().getConfig()
186 .getString(Config.FILE_FORMAT_IMAGE_FORMAT_COVER).toLowerCase();
30478c5b
NR
187
188 // Without removing ext
8d59ce07 189 cover = bsHelper.getImage(null, sourceInfoFile,
30478c5b
NR
190 basefile.getAbsolutePath() + ext);
191
192 // Try without ext
193 String name = basefile.getName();
194 int pos = name.lastIndexOf(".");
195 if (cover == null && pos > 0) {
196 name = name.substring(0, pos);
9ac581ab 197 basefile = new File(basefile.getParent(), name);
30478c5b 198
8d59ce07 199 cover = bsHelper.getImage(null, sourceInfoFile,
30478c5b 200 basefile.getAbsolutePath() + ext);
bb7021f2
NR
201 }
202
30478c5b 203 return cover;
bb7021f2
NR
204 }
205
68686a37
NR
206 private static boolean getInfoTagBoolean(InputStream in, String key,
207 boolean def) throws IOException {
208 Boolean value = getInfoTagBoolean(in, key);
209 return value == null ? def : value;
210 }
211
212 private static Boolean getInfoTagBoolean(InputStream in, String key)
213 throws IOException {
214 String value = getInfoTag(in, key);
215 if (value != null && !value.trim().isEmpty()) {
216 value = value.toLowerCase().trim();
217 return value.equals("1") || value.equals("on")
218 || value.equals("true") || value.equals("yes");
219 }
220
221 return null;
222 }
223
224 private static List<String> getInfoTagList(InputStream in, String key,
225 String separator) throws IOException {
226 List<String> list = new ArrayList<String>();
227 String tt = getInfoTag(in, key);
228 if (tt != null) {
229 for (String tag : tt.split(separator)) {
230 list.add(tag.trim());
231 }
232 }
233
234 return list;
235 }
236
237 /**
238 * Return the value of the given tag in the <tt>.info</tt> file if present.
239 *
240 * @param key
241 * the tag key
242 *
243 * @return the value or NULL
244 *
245 * @throws IOException
246 * in case of I/O error
247 */
248 private static String getInfoTag(InputStream in, String key)
249 throws IOException {
250 key = "^" + key + "=";
251
252 if (in != null) {
253 in.reset();
7445f856 254 String value = getLine(in, key, 0);
68686a37
NR
255 if (value != null && !value.isEmpty()) {
256 value = value.trim().substring(key.length() - 1).trim();
257 if (value.startsWith("'") && value.endsWith("'")
258 || value.startsWith("\"") && value.endsWith("\"")) {
259 value = value.substring(1, value.length() - 1).trim();
260 }
261
262 return value;
263 }
264 }
265
266 return null;
267 }
7445f856
NR
268
269 /**
270 * Return the first line from the given input which correspond to the given
271 * selectors.
272 *
273 * @param in
274 * the input
275 * @param needle
276 * a string that must be found inside the target line (also
277 * supports "^" at start to say "only if it starts with" the
278 * needle)
279 * @param relativeLine
280 * the line to return based upon the target line position (-1 =
281 * the line before, 0 = the target line...)
282 *
283 * @return the line
284 */
285 static private String getLine(InputStream in, String needle,
286 int relativeLine) {
287 return getLine(in, needle, relativeLine, true);
288 }
289
290 /**
291 * Return a line from the given input which correspond to the given
292 * selectors.
293 *
294 * @param in
295 * the input
296 * @param needle
297 * a string that must be found inside the target line (also
298 * supports "^" at start to say "only if it starts with" the
299 * needle)
300 * @param relativeLine
301 * the line to return based upon the target line position (-1 =
302 * the line before, 0 = the target line...)
303 * @param first
304 * takes the first result (as opposed to the last one, which will
305 * also always spend the input)
306 *
307 * @return the line
308 */
309 static private String getLine(InputStream in, String needle,
310 int relativeLine, boolean first) {
311 String rep = null;
312
313 List<String> lines = new ArrayList<String>();
314 @SuppressWarnings("resource")
315 Scanner scan = new Scanner(in, "UTF-8");
316 int index = -1;
317 scan.useDelimiter("\\n");
318 while (scan.hasNext()) {
319 lines.add(scan.next());
320
321 if (index == -1) {
322 if (needle.startsWith("^")) {
31e27ee3
NR
323 if (lines.get(lines.size() - 1)
324 .startsWith(needle.substring(1))) {
7445f856
NR
325 index = lines.size() - 1;
326 }
327
328 } else {
329 if (lines.get(lines.size() - 1).contains(needle)) {
330 index = lines.size() - 1;
331 }
332 }
333 }
334
335 if (index >= 0 && index + relativeLine < lines.size()) {
336 rep = lines.get(index + relativeLine);
337 if (first) {
338 break;
339 }
340 }
341 }
342
343 return rep;
344 }
68686a37 345}