@pixwel/pixwel-sdk 1.1.0

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.
Files changed (65) hide show
  1. package/.babelrc +3 -0
  2. package/.editorconfig +36 -0
  3. package/.vs/slnx.sqlite +0 -0
  4. package/.vscode/launch.json +0 -0
  5. package/coverage/clover.xml +7 -0
  6. package/coverage/coverage-final.json +1 -0
  7. package/coverage/lcov-report/Ass.js.html +651 -0
  8. package/coverage/lcov-report/Asset.js.html +129 -0
  9. package/coverage/lcov-report/Filename.js.html +849 -0
  10. package/coverage/lcov-report/Ingest.js.html +591 -0
  11. package/coverage/lcov-report/IngestFile.js.html +87 -0
  12. package/coverage/lcov-report/IngestFileDrop.js.html +540 -0
  13. package/coverage/lcov-report/Order.js.html +162 -0
  14. package/coverage/lcov-report/Srt.js.html +333 -0
  15. package/coverage/lcov-report/Sub.js.html +177 -0
  16. package/coverage/lcov-report/TimeFormat.js.html +210 -0
  17. package/coverage/lcov-report/base.css +223 -0
  18. package/coverage/lcov-report/block-navigation.js +63 -0
  19. package/coverage/lcov-report/index.html +84 -0
  20. package/coverage/lcov-report/lib/Filename.js.html +720 -0
  21. package/coverage/lcov-report/lib/index.html +97 -0
  22. package/coverage/lcov-report/prettify.css +1 -0
  23. package/coverage/lcov-report/prettify.js +1 -0
  24. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  25. package/coverage/lcov-report/sorter.js +158 -0
  26. package/coverage/lcov-report/test/index.html +97 -0
  27. package/coverage/lcov-report/test/mocks.js.html +423 -0
  28. package/coverage/lcov.info +0 -0
  29. package/dist/index.js +61 -0
  30. package/dist/src/Ass.js +200 -0
  31. package/dist/src/Asset.js +41 -0
  32. package/dist/src/Filename.js +252 -0
  33. package/dist/src/Ingest.js +200 -0
  34. package/dist/src/IngestFile.js +17 -0
  35. package/dist/src/IngestFileDrop.js +178 -0
  36. package/dist/src/Order.js +58 -0
  37. package/dist/src/Srt.js +102 -0
  38. package/dist/src/Sub.js +42 -0
  39. package/dist/src/TimeFormat.js +48 -0
  40. package/index.js +6 -0
  41. package/package.json +26 -0
  42. package/src/Ass.js +195 -0
  43. package/src/Asset.js +20 -0
  44. package/src/Filename.js +260 -0
  45. package/src/Ingest.js +175 -0
  46. package/src/IngestFile.js +6 -0
  47. package/src/IngestFileDrop.js +157 -0
  48. package/src/Order.js +31 -0
  49. package/src/Srt.js +89 -0
  50. package/src/Sub.js +37 -0
  51. package/src/TimeFormat.js +48 -0
  52. package/src/docs.json +1 -0
  53. package/test/Ass.spec.js +150 -0
  54. package/test/Asset.spec.js +24 -0
  55. package/test/Filename.spec.js +728 -0
  56. package/test/Ingest.spec.js +95 -0
  57. package/test/Srt.spec.js +111 -0
  58. package/test/Sub.spec.js +75 -0
  59. package/test/TimeFormat.spec.js +84 -0
  60. package/test/mocks/mediainfo.mock.js +67 -0
  61. package/test/mocks/mediainfo.png.mock.js +22 -0
  62. package/test/mocks/mocks.js +118 -0
  63. package/test/mocks/prores.mock.js +71 -0
  64. package/test/parse.spec.ts +927 -0
  65. package/tsconfig.json +11 -0
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+
7
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
8
+
9
+ var IngestFile = function IngestFile(file, order) {
10
+ _classCallCheck(this, IngestFile);
11
+
12
+ this.file = file;
13
+ }
14
+ // renderedName, ingest, options
15
+ ;
16
+
17
+ exports.default = IngestFile;
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+
7
+ var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
8
+
9
+ var _pixwelSdk = require("@pixwel/pixwel-sdk");
10
+
11
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
12
+
13
+ /**
14
+ * Collects file handles from HTML5, full paths from Aspera, inspects
15
+ * the dropped file using mediainfo, then renames the file according to
16
+ * the data from Mediainfo and the Order and submits it for upload.
17
+ * @type {Object}
18
+ */
19
+ var IngestFileDrop = function () {
20
+ /**
21
+ * Map that contains data from HTML5 file drop and Aspera file drop
22
+ * @type {Object}
23
+ */
24
+ function IngestFileDrop(scope, Mediainfo, IngestUpload) {
25
+ _classCallCheck(this, IngestFileDrop);
26
+
27
+ this.map = {};
28
+ this.scope = scope;
29
+ this.IngestUpload = IngestUpload;
30
+ this.Mediainfo = Mediainfo;
31
+ this.asperaDropHandler = this.asperaDropHandler.bind(this);
32
+ this.htmlDropHandler = this.htmlDropHandler.bind(this);
33
+ }
34
+ /**
35
+ * HTML file drop handler
36
+ * @param {[type]} e [description]
37
+ * @return {[type]} [description]
38
+ */
39
+
40
+
41
+ _createClass(IngestFileDrop, [{
42
+ key: "htmlDropHandler",
43
+ value: function htmlDropHandler(e) {
44
+ var files = e.dataTransfer.files;
45
+ for (var i = 0; i < files.length; i++) {
46
+ this.registerHtmlFile(files[i]);
47
+ }
48
+ }
49
+ /**
50
+ * Aspera drop handler
51
+ * @param {[type]} data [description]
52
+ * @return {[type]} [description]
53
+ */
54
+
55
+ }, {
56
+ key: "asperaDropHandler",
57
+ value: function asperaDropHandler(data) {
58
+ var that = this;
59
+ var files = data.files.dataTransfer.files;
60
+ if (!files.length) {
61
+ return;
62
+ }
63
+ for (var i = 0; i < files.length; i++) {
64
+ this.registerAsperaFile(files[i]);
65
+ }
66
+ return Promise.all(this.export()).then(function (renamed) {
67
+ that.IngestUpload.uploadFiles(renamed, {}).then(function () {
68
+ that.map = {};
69
+ });
70
+ });
71
+ }
72
+ /**
73
+ * Registers and HTML file drop
74
+ * @param {[type]} file [description]
75
+ * @return {[type]} [description]
76
+ */
77
+
78
+ }, {
79
+ key: "registerHtmlFile",
80
+ value: function registerHtmlFile(file) {
81
+ this.map[file.name] = file;
82
+ }
83
+ /**
84
+ * Registers and Aspera file drop
85
+ * @param {[type]} file [description]
86
+ * @return {[type]} [description]
87
+ */
88
+
89
+ }, {
90
+ key: "registerAsperaFile",
91
+ value: function registerAsperaFile(file) {
92
+ var basename = this.getBasename(file);
93
+ this.map[basename]["path"] = file.name;
94
+ }
95
+ /**
96
+ * Gets correct basename for MacOS and Windows
97
+ * @param {[type]} file [description]
98
+ * @return {[type]} [description]
99
+ */
100
+
101
+ }, {
102
+ key: "getBasename",
103
+ value: function getBasename(file) {
104
+ return file.name[0] === "/" ? file.name.split("/").pop() : file.name.split("\\").pop();
105
+ }
106
+ /**
107
+ * Renames and generates ingest record for internal filemap
108
+ * @return {[type]} [description]
109
+ */
110
+
111
+ }, {
112
+ key: "export",
113
+ value: function _export() {
114
+ var that = this;
115
+ return Object.values(this.map).map(function (file) {
116
+ return that.rename(file);
117
+ });
118
+ }
119
+ /**
120
+ * Renames the file and decorates it with ingest data using data from
121
+ * mediainfo and the Order
122
+ * @param {[type]} file [description]
123
+ * @return {[type]} [description]
124
+ */
125
+
126
+ }, {
127
+ key: "rename",
128
+ value: function rename(file) {
129
+ var that = this;
130
+ return this.Mediainfo.inform(file).then(function (meta) {
131
+ var order = void 0;
132
+ var asset = void 0;
133
+ var mediaType = file.type.split("/")[0];
134
+
135
+ var tags = _pixwelSdk.Ingest.mapMetaToTags(meta, mediaType);
136
+
137
+ if (!that.scope.workRequest) {
138
+ order = new _pixwelSdk.Order();
139
+ asset = new _pixwelSdk.Asset();
140
+ tags.Text = "txtd";
141
+ file.ingest = _pixwelSdk.Ingest.mapFromKnownData(that.scope.context, order, tags, that.scope.watermarks);
142
+ return file;
143
+ }
144
+
145
+ order = new _pixwelSdk.Order(that.scope.workRequest);
146
+ asset = _pixwelSdk.Asset.mapFromOrder(order);
147
+
148
+ var options = {
149
+ tags: tags,
150
+ country: order.country,
151
+ language: order.language
152
+ };
153
+
154
+ if (that.scope.request && that.scope.request.tag) {
155
+ options.tags.Extra = [that.scope.request.tag.replace(" ", "-")];
156
+ }
157
+
158
+ options.tags.SubDub = {
159
+ sub: order.isSub(),
160
+ dub: order.isDub()
161
+ };
162
+ if (!order.isSub() && !order.isDub()) {
163
+ options.tags.Text = "txtd";
164
+ }
165
+
166
+ var basename = _pixwelSdk.Filename.render(that.scope.tags, that.scope.assetTypes, asset, options, mediaType);
167
+ var extension = file.name.split(".").pop();
168
+ file.renderedName = basename + "." + extension;
169
+ file.ingest = _pixwelSdk.Ingest.mapFromKnownData(that.scope.context, order, tags, that.scope.watermarks);
170
+ return file;
171
+ });
172
+ }
173
+ }]);
174
+
175
+ return IngestFileDrop;
176
+ }();
177
+
178
+ exports.default = IngestFileDrop;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+
7
+ var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
8
+
9
+ var _lodash = require("lodash");
10
+
11
+ var _lodash2 = _interopRequireDefault(_lodash);
12
+
13
+ var _Filename = require("./Filename.js");
14
+
15
+ var _Filename2 = _interopRequireDefault(_Filename);
16
+
17
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
18
+
19
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
20
+
21
+ var Order = function () {
22
+ function Order(data) {
23
+ _classCallCheck(this, Order);
24
+
25
+ var schema = {
26
+ _id: null,
27
+ asset: null,
28
+ project: null,
29
+ assetType: null,
30
+ country: null,
31
+ language: null,
32
+ dng: null
33
+ };
34
+ Object.assign(this, schema, data);
35
+ }
36
+
37
+ _createClass(Order, [{
38
+ key: "isDub",
39
+ value: function isDub() {
40
+ if (!this.dng) {
41
+ return null;
42
+ }
43
+ return _lodash2.default.flattenDeep(Object.entries(this.dng)).includes("Dedicated / Localized");
44
+ }
45
+ }, {
46
+ key: "isSub",
47
+ value: function isSub() {
48
+ if (!this.dng) {
49
+ return null;
50
+ }
51
+ return _lodash2.default.flattenDeep(Object.entries(this.dng)).includes("Subtitled");
52
+ }
53
+ }]);
54
+
55
+ return Order;
56
+ }();
57
+
58
+ exports.default = Order;
@@ -0,0 +1,102 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+
7
+ var _lodash = require('lodash');
8
+
9
+ var _lodash2 = _interopRequireDefault(_lodash);
10
+
11
+ var _TimeFormat = require('./TimeFormat');
12
+
13
+ var _TimeFormat2 = _interopRequireDefault(_TimeFormat);
14
+
15
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16
+
17
+ function stripTags(input, allowed) {
18
+ allowed = (((allowed || '') + '').toLowerCase().match(/<[a-z][a-z0-9]*>/g) || []).join('');
19
+ var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi;
20
+ var commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi;
21
+ return input.replace(commentsAndPhpTags, '').replace(tags, function ($0, $1) {
22
+ return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : '';
23
+ });
24
+ }
25
+
26
+ var SRT_STATE_SUBNUMBER = 0,
27
+ SRT_STATE_TIME = 1,
28
+ SRT_STATE_TEXT = 2,
29
+ SRT_STATE_BLANK = 3;
30
+
31
+ exports.default = {
32
+ parse: function parse(lines) {
33
+ var result = [],
34
+ state = SRT_STATE_SUBNUMBER,
35
+ subText = '',
36
+ subTime = '';
37
+
38
+ function time(subTime) {
39
+
40
+ var range = subTime.split(' --> ');
41
+ if (!_TimeFormat2.default.isValidTime(range[0]) || !_TimeFormat2.default.isValidTime(range[1])) {
42
+ throw new Error('Malformed SRT file');
43
+ }
44
+
45
+ return {
46
+ startTime: range[0],
47
+ stopTime: range[1]
48
+ };
49
+ }
50
+
51
+ lines.forEach(function (line) {
52
+ line = line.replace(/^\s+|\s+$/g, '');
53
+
54
+ switch (state) {
55
+ case SRT_STATE_SUBNUMBER:
56
+ state = SRT_STATE_TIME;
57
+ break;
58
+
59
+ case SRT_STATE_TIME:
60
+ subTime = line;
61
+ state = SRT_STATE_TEXT;
62
+ break;
63
+
64
+ case SRT_STATE_TEXT:
65
+ if (line !== '') {
66
+ subText += subText ? "\n" + line : line;
67
+ return;
68
+ }
69
+ result.push(Object.assign(time(subTime), {
70
+ text: subText
71
+ }));
72
+ subText = '';
73
+ state = SRT_STATE_SUBNUMBER;
74
+ break;
75
+ }
76
+ });
77
+
78
+ return result;
79
+ },
80
+ export: function _export(lines) {
81
+ var result = '',
82
+ i = 1;
83
+
84
+ lines.forEach(function (line) {
85
+ if (line.text === undefined) {
86
+ return;
87
+ }
88
+
89
+ // Replace <br> tags with newlines. These tags may accidentally be stored in the translation
90
+ var text = _lodash2.default.unescape(line.text.replace(/<br>/g, '\n'));
91
+
92
+ // Remove any tags that may have made it into the translation (div tags seem to be the main problem).
93
+ // <b> <i> are the only two supported tags currently
94
+ text = stripTags(text, '<b><i>');
95
+
96
+ result += i++ + "\n";
97
+ result += _TimeFormat2.default.startTime(line) + " --> " + _TimeFormat2.default.stopTime(line) + "\n";
98
+ result += text + "\n\n";
99
+ });
100
+ return result;
101
+ }
102
+ };
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = {
7
+ parse: function parse(lines) {
8
+ var result = [],
9
+ current = {
10
+ text: ""
11
+ };
12
+
13
+ lines.forEach(function (line) {
14
+ line = line.replace(/^\s+|\s+$/g, '');
15
+ var subTime = line.match(/^([0-9:\.]+),([0-9:\.]+)$/);
16
+
17
+ if (subTime) {
18
+ if (current.text) {
19
+ result.push(current);
20
+ }
21
+ current = {
22
+ startTime: subTime[1],
23
+ stopTime: subTime[2],
24
+ text: ""
25
+ };
26
+ return;
27
+ }
28
+
29
+ if (line) {
30
+ if (current.startTime === undefined || current.stopTime === 'undefined') {
31
+ throw new Error('Malformed SUB file');
32
+ }
33
+ current.text += current.text ? "\n" + line : line;
34
+ }
35
+ });
36
+
37
+ if (current.text) {
38
+ result.push(current);
39
+ }
40
+ return result;
41
+ }
42
+ };
@@ -0,0 +1,48 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ var stringRegex = /^(\d+):(\d+):(\d+)[,|.](\d{2,3})?$/;
7
+
8
+ exports.default = {
9
+ isValidTime: function isValidTime(time) {
10
+ return !!time.match(stringRegex);
11
+ },
12
+ timeToTimestamp: function timeToTimestamp(time) {
13
+ var match = time.match(stringRegex);
14
+ if (!match) {
15
+ return 0;
16
+ }
17
+ var precision = match[4].length === 2 ? 100 : 1000;
18
+ return parseInt(match[1], 10) * 60 * 60 + parseInt(match[2], 10) * 60 + parseInt(match[3], 10) + parseInt(match[4], 10) / precision;
19
+ },
20
+ timestampToTime: function timestampToTime(second, decimalMark) {
21
+ var hours = ~~(second / 3600);
22
+ var min = ~~(second / 60) % 60;
23
+ var sec = second % 60;
24
+ var fixedSec = sec.toFixed(3).replace('.', decimalMark ? decimalMark : ',');
25
+ return (hours < 10 ? '0' + hours : hours) + ":" + (min < 10 ? '0' + min : min) + ':' + (sec < 10 ? '0' + fixedSec : fixedSec);
26
+ },
27
+ timeDiff: function timeDiff(time1, time2) {
28
+ if (time1 === undefined || time2 === undefined) {
29
+ return 0;
30
+ }
31
+ return this.timestampDiff(this.timeToTimestamp(time1), this.timeToTimestamp(time2));
32
+ },
33
+ timestampDiff: function timestampDiff(time1, time2) {
34
+ if (time1 === undefined || time2 === undefined || time1 === time2) {
35
+ return 0;
36
+ }
37
+ var diff = time2 - time1;
38
+ return Math.round(Math.max(-1, Math.min(1, diff)) * 10) / 10;
39
+ },
40
+ startTime: function startTime(line) {
41
+ var timestamp = this.timeToTimestamp(line.startTime) + (line.inOffset || 0);
42
+ return this.timestampToTime(timestamp);
43
+ },
44
+ stopTime: function stopTime(line) {
45
+ var timestamp = this.timeToTimestamp(line.stopTime) + (line.outOffset || 0);
46
+ return this.timestampToTime(timestamp);
47
+ }
48
+ };
package/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { default as Asset } from './src/Asset';
2
+ export { default as Filename } from './src/Filename';
3
+ export { default as Ingest } from './src/Ingest';
4
+ export { default as IngestFile } from './src/IngestFile';
5
+ export { default as IngestFileDrop } from './src/IngestFileDrop';
6
+ export { default as Order } from './src/Order';
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@pixwel/pixwel-sdk",
3
+ "description": "",
4
+ "version": "1.1.0",
5
+ "author": "Pixwel",
6
+ "license": "UNLICENSED",
7
+ "main": "./dist/index.js",
8
+ "scripts": {
9
+ "build": "babel -d dist index.js src/*.js",
10
+ "test": "jest --coverage"
11
+ },
12
+ "jest": {
13
+ "verbose": true,
14
+ "coverageDirectory": "coverage",
15
+ "collectCoverage": true,
16
+ "collectCoverageFrom": [
17
+ "src/*.js"
18
+ ]
19
+ },
20
+ "devDependencies": {
21
+ "babel-cli": "^6.26.0",
22
+ "babel-preset-env": "^1.7.0",
23
+ "jest": "^22.4.4",
24
+ "lodash": "^4.17.10"
25
+ }
26
+ }
package/src/Ass.js ADDED
@@ -0,0 +1,195 @@
1
+ import _ from 'lodash';
2
+ import TimeFormat from './TimeFormat';
3
+
4
+ function trim(str) {
5
+ return str.replace(/^\s+|\s+$/g, '');
6
+ }
7
+
8
+ function searchIndex(lines, pattern, startIndex) {
9
+ var len = lines.length;
10
+ startIndex = startIndex || 0;
11
+
12
+ for (var i = startIndex; i < len; i++) {
13
+ var line = lines[i];
14
+ if (line.match(pattern)) {
15
+ return i;
16
+ }
17
+ }
18
+ return false;
19
+ }
20
+
21
+ function extractStyles(lines) {
22
+ var line, styles = {};
23
+
24
+ var currentIndex = searchIndex(lines, /^\[V4\+ Styles\]$/);
25
+
26
+ if (currentIndex === false) {
27
+ return styles;
28
+ }
29
+
30
+ currentIndex = searchIndex(lines, /^Format:/, ++currentIndex);
31
+
32
+ if (currentIndex === false) {
33
+ return styles;
34
+ }
35
+
36
+ line = trim(lines[currentIndex]);
37
+ var fields = line.replace(/^Format:\s+/, '').split(/\s*,\s*/);
38
+ var nameIndex = fields.indexOf('Name');
39
+ var alignmentIndex = fields.indexOf('Alignment');
40
+
41
+ if (nameIndex === -1) {
42
+ return styles;
43
+ }
44
+
45
+ var pattern = new RegExp('^' + (new Array(fields.length).join('\s*(.*?)\s*,')) + '(.*?)$');
46
+
47
+ currentIndex = searchIndex(lines, /^Style:/, ++currentIndex);
48
+
49
+ while (currentIndex !== false) {
50
+ line = trim(lines[currentIndex]);
51
+
52
+ var parts = line.replace(/^Style:\s+/, '').match(pattern);
53
+ if (!parts) {
54
+ throw new Error('Error, missing fields in a `Style` entry');
55
+ }
56
+ if (parts.length) {
57
+ var style = {};
58
+ if (alignmentIndex !== -1) {
59
+ var alignmentNumber = parts[alignmentIndex + 1];
60
+ style.alignment = alignmentNumber >= 7 ? 'top' : 'bottom';
61
+ }
62
+ styles[parts[nameIndex + 1]] = style;
63
+ }
64
+ currentIndex = searchIndex(lines, /^Style:/, ++currentIndex);
65
+ }
66
+ return styles;
67
+ }
68
+
69
+ export default {
70
+ parse: function(lines) {
71
+ var line;
72
+ var currentIndex = 0;
73
+
74
+ function normalizeTime(time) {
75
+ if (time.charAt(1) === ':') {
76
+ time = '0' + time;
77
+ }
78
+ return time.replace('.', ',') + '0';
79
+ }
80
+
81
+ function normalizeText(text) {
82
+ return text.replace(/\\n|\\N/, '\n')
83
+ .replace(/{\\(.*)1}(.*){\\\1[0]}/g, function(match, tag, content) {
84
+ return '<' + tag + '>' + content + '</' + tag + '>';
85
+ });
86
+ }
87
+
88
+ var styles = extractStyles(lines);
89
+
90
+ currentIndex = searchIndex(lines, /^\[Events\]$/);
91
+
92
+ if (currentIndex === false) {
93
+ throw new Error('Error, missing `[Events]` section');
94
+ }
95
+
96
+ currentIndex = searchIndex(lines, /^Format:/, ++currentIndex);
97
+
98
+ if (currentIndex === false) {
99
+ throw new Error('Error, missing `Format` entry');
100
+ }
101
+
102
+ line = trim(lines[currentIndex]);
103
+ var fields = line.replace(/^Format:\s+/, '').split(/\s*,\s*/);
104
+ var startIndex = fields.indexOf('Start');
105
+ var endIndex = fields.indexOf('End');
106
+ var styleIndex = fields.indexOf('Style');
107
+ var textIndex = fields.indexOf('Text');
108
+
109
+ if (startIndex === -1 || endIndex === -1 || textIndex === -1) {
110
+ throw new Error('Error, missing fields in a `Format` entry');
111
+ }
112
+
113
+ var result = [];
114
+ var pattern = new RegExp('^' + (new Array(fields.length).join('\s*(.*?)\s*,')) + '(.*?)$');
115
+
116
+ currentIndex = searchIndex(lines, /^Dialogue:/, ++currentIndex);
117
+
118
+ while (currentIndex !== false) {
119
+ line = trim(lines[currentIndex]);
120
+ var parts = line.replace(/^Dialogue:\s+/, '').match(pattern);
121
+ if (!parts) {
122
+ throw new Error('Error, missing fields in a `Dialogue` entry');
123
+ }
124
+ if (parts.length) {
125
+ var entry = {
126
+ startTime: normalizeTime(parts[startIndex + 1]),
127
+ stopTime: normalizeTime(parts[endIndex + 1]),
128
+ text: normalizeText(parts[textIndex + 1]),
129
+ alignment: 'bottom'
130
+ };
131
+ if (styleIndex !== -1) {
132
+ var style = styles[parts[styleIndex + 1]];
133
+ if (style && style.alignment) {
134
+ entry.alignment = style.alignment;
135
+ }
136
+ }
137
+ result.push(entry);
138
+ }
139
+ currentIndex = searchIndex(lines, /^Dialogue:/, ++currentIndex);
140
+ }
141
+ return result;
142
+ },
143
+ export: function(lines, options) {
144
+ var result = '';
145
+
146
+ options = options || {};
147
+
148
+ var font = options.font || {
149
+ face: 'Arial',
150
+ size: 20
151
+ };
152
+
153
+ result += "[Script Info]\n";
154
+ result += "ScriptType: v4.00+\n\n";
155
+
156
+ result += "[V4+ Styles]\n";
157
+ result += "Format: Name, Fontname, Fontsize, PrimaryColour,Shadow,MarginV,Alignment\n";
158
+ result += "Style: Bottom," + font.face + "," + font.size + ",&H00FFFFFF,1,20,2\n";
159
+ result += "Style: Top," + font.face + "," + font.size + ",&H00FFFFFF,1,20,8\n\n";
160
+
161
+ result += "[Events]\n";
162
+ result += "Format: Layer, Start, End, Style, Text\n";
163
+
164
+ function normalizeTime(time) {
165
+ if (time.charAt(0) === '0') {
166
+ time = time.substr(1);
167
+ }
168
+ return time.replace(',', '.').slice(0, -1);
169
+ }
170
+
171
+ function normalizeText(text) {
172
+ return _.unescape(text
173
+ .replace(/<div>/g, '\\N')
174
+ .replace(/<\/div>/g, '')
175
+ .replace(/\n/g, '\\N')
176
+ .replace(/<br>/g, '\\N')
177
+ .replace(/<i>/g, '{\\i1}')
178
+ .replace(/<\/i>/g, '{\\i0}')
179
+ .replace(/<b>/g, '{\\b1}')
180
+ .replace(/<\/b>/g, '{\\b0}'))
181
+ }
182
+
183
+ lines.forEach(function(line) {
184
+ if (line.text === undefined) {
185
+ return;
186
+ }
187
+ result += "Dialogue: 0";
188
+ result += "," + normalizeTime(TimeFormat.startTime(line));
189
+ result += "," + normalizeTime(TimeFormat.stopTime(line));
190
+ result += line.alignment === 'top' ? ",Top" : ",Bottom";
191
+ result += "," + normalizeText(line.text) + "\n";
192
+ });
193
+ return result;
194
+ }
195
+ };
package/src/Asset.js ADDED
@@ -0,0 +1,20 @@
1
+ export default class Asset {
2
+ static mapFromOrder(order) {
3
+ if (!order) {
4
+ throw new Error("missing order");
5
+ }
6
+ if (!order.asset) {
7
+ throw new Error("missing order.asset");
8
+ }
9
+ if (!order.project) {
10
+ throw new Error("missing order.project");
11
+ }
12
+ if (!order.project.slug) {
13
+ throw new Error("missing order.project.slug");
14
+ }
15
+ var asset = order.asset;
16
+ asset.project = order.project;
17
+ asset.project.filePrefix = order.project.slug.toUpperCase();
18
+ return asset;
19
+ }
20
+ }