1 |
|
2 | "use strict";
|
3 |
|
4 | const debug = require('debug')('upnpserver:contentHandlers:Exif');
|
5 | const Exif = require('exif');
|
6 |
|
7 | const ContentHandler = require('./contentHandler');
|
8 |
|
9 | const logger = require('../logger');
|
10 |
|
11 | class ExifContentHandler extends ContentHandler {
|
12 |
|
13 | get name() {
|
14 | return "exif";
|
15 | }
|
16 |
|
17 | |
18 |
|
19 |
|
20 | prepareMetas(contentInfos, context, callback) {
|
21 | debug("Prepare metas of", contentInfos);
|
22 |
|
23 | var contentURL = contentInfos.contentURL;
|
24 |
|
25 | contentURL.readContent(null, (error, imageData) => {
|
26 | if (error) {
|
27 | logger.error("Can not get content of '" + contentURL + "'", error);
|
28 | return callback(error);
|
29 | }
|
30 |
|
31 | new Exif.ExifImage({image: imageData, fixThumbnailOffset: true}, (error, exifData) => {
|
32 | if (error) {
|
33 | logger.error("Can not parse exif '" + contentURL + "'", error);
|
34 | return callback(error);
|
35 | }
|
36 |
|
37 | filter(exifData);
|
38 |
|
39 | var res;
|
40 | var thumbnail = exifData.thumbnail;
|
41 | if (thumbnail) {
|
42 | delete exifData.thumbnail;
|
43 |
|
44 | if (thumbnail.ThumbnailOffset && thumbnail.ThumbnailLength) {
|
45 |
|
46 | var thumbnailMimeType;
|
47 | if (isJPEG(imageData, thumbnail.ThumbnailOffset)) {
|
48 | thumbnailMimeType = "image/jpeg";
|
49 | }
|
50 |
|
51 | if (thumbnailMimeType) {
|
52 | res = res || [{}];
|
53 |
|
54 | res.push({
|
55 | contentHandlerKey: this.name,
|
56 | key: "thumbnail",
|
57 | mimeType: thumbnailMimeType,
|
58 | size: thumbnail.ThumbnailLength,
|
59 | _start: thumbnail.ThumbnailOffset
|
60 | });
|
61 | }
|
62 | }
|
63 | }
|
64 | var exif = exifData;
|
65 | if (exif) {
|
66 | res = res || [{}];
|
67 |
|
68 | if (exif.ExifImageWidth) {
|
69 | res[0].width = exif.ExifImageWidth;
|
70 | }
|
71 | if (exif.ExifImageHeight) {
|
72 | res[0].height = exif.ExifImageHeight;
|
73 | }
|
74 | if (exif.DateTimeOriginal) {
|
75 | var ds = exif.DateTimeOriginal;
|
76 | var reg = /^(\d{4}).(\d{2}).(\d{2}).(\d{2}).(\d{2}).(\d{2})/.exec(ds);
|
77 | if (reg) {
|
78 | exifData.dateTimeOriginal = new Date(parseInt(reg[1]), parseInt(reg[2]) - 1, parseInt(reg[3]),
|
79 | parseInt(reg[4]), parseInt(reg[5]), parseInt(reg[6]));
|
80 | exifData.date = exifData.dateTimeOriginal;
|
81 | }
|
82 | }
|
83 | }
|
84 |
|
85 | if (res) {
|
86 | exifData.res = res;
|
87 | }
|
88 |
|
89 | callback(null, exifData);
|
90 | });
|
91 | });
|
92 | }
|
93 |
|
94 | |
95 |
|
96 |
|
97 | processRequest(node, request, response, path, parameters, callback) {
|
98 |
|
99 | var resKey = parameters[0];
|
100 | var res = this.getResourceByParameter(node, resKey);
|
101 |
|
102 | debug("ProcessRequest", "resKey=", resKey, "=>", res);
|
103 |
|
104 | if (!res) {
|
105 | var error = new Error("Invalid resKey parameter (" + resKey + ")");
|
106 | return callback(error, true);
|
107 | }
|
108 |
|
109 | this.sendResource(node.contentURL, res, request, response, callback);
|
110 | }
|
111 | }
|
112 |
|
113 | module.exports = ExifContentHandler;
|
114 |
|
115 | function isJPEG(data, offset) {
|
116 | debug("IsJPEG: offset=", offset, "data=", data[offset], data[offset + 1], data[offset + 2]);
|
117 | return (data[offset] === 0xff) && (data[offset + 1] === 0xd8) && (data[offset + 2] === 0xff) && (data[offset + 3] === 0xdb);
|
118 | }
|
119 |
|
120 | function filter(json) {
|
121 | for (var n in json) {
|
122 | var v = json[n];
|
123 | if (typeof(v) !== "object") {
|
124 | continue;
|
125 | }
|
126 |
|
127 | if (v === null) {
|
128 | continue;
|
129 | }
|
130 | if (v instanceof Array) {
|
131 | continue;
|
132 | }
|
133 | if (Buffer.isBuffer(v)) {
|
134 | delete json[n];
|
135 | continue;
|
136 | }
|
137 | if (v instanceof Date) {
|
138 | continue;
|
139 | }
|
140 | if (!Object.keys(v).length) {
|
141 | delete json[n];
|
142 | continue;
|
143 | }
|
144 |
|
145 | filter(v);
|
146 | }
|
147 | }
|