@cocreate/file 1.16.0 → 1.17.1
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.
- package/CHANGELOG.md +22 -0
- package/README.md +10 -3
- package/demo/index.html +1 -1
- package/demo/video-segmenting.html +22 -0
- package/package.json +12 -7
- package/src/client.js +211 -33
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
## [1.17.1](https://github.com/CoCreate-app/CoCreate-file/compare/v1.17.0...v1.17.1) (2024-04-26)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* condition to check input.ProcessFIle exists ([478d652](https://github.com/CoCreate-app/CoCreate-file/commit/478d65274f52ac8c0de9341ee5ca1766e0542f7f))
|
|
7
|
+
* getFiles returns files as is without conversion ([ad57ce3](https://github.com/CoCreate-app/CoCreate-file/commit/ad57ce3895c4246268b49b192d69e2bae09cf0c7))
|
|
8
|
+
* method set to static object.update ([27dbf8e](https://github.com/CoCreate-app/CoCreate-file/commit/27dbf8e717a4bc5ffd5c2f6a6bae28e081e8a7b2))
|
|
9
|
+
* replaced keyword spaces with hyphens and lowercase letters only ([77f9d89](https://github.com/CoCreate-app/CoCreate-file/commit/77f9d896ded45e1341d58faa6973d58198a4e308))
|
|
10
|
+
|
|
11
|
+
# [1.17.0](https://github.com/CoCreate-app/CoCreate-file/compare/v1.16.0...v1.17.0) (2024-03-18)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* descriptions ([fe2cffc](https://github.com/CoCreate-app/CoCreate-file/commit/fe2cffc731ba9ffecbe6c5768ae43e242d9dea92))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
* handle mediaSegmentation, define path ([4c8e478](https://github.com/CoCreate-app/CoCreate-file/commit/4c8e478bf7ff6a382c18bfbe7af60886af9e0d9e))
|
|
22
|
+
|
|
1
23
|
# [1.16.0](https://github.com/CoCreate-app/CoCreate-file/compare/v1.15.2...v1.16.0) (2024-02-16)
|
|
2
24
|
|
|
3
25
|
|
package/README.md
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
# CoCreate-file
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
CoCreate-file is an advanced, configurable headless file uploader designed for comprehensive file management capabilities without a traditional user interface. Accessible via a JavaScript API and HTML5 attributes, it offers unparalleled flexibility in handling file operations on both local and server environments. Take it for a spin in our [playground!](https://cocreate.app/docs/file)
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|

|
|
7
7
|

|
|
8
|
-

|
|
9
|
+

|
|
10
10
|

|
|
11
11
|
|
|
12
|
+
## Key Features
|
|
13
|
+
|
|
14
|
+
- **JavaScript API & HTML5 Customization**: Easily manage file uploads and operations through a powerful JavaScript API or by utilizing HTML5 attributes for easy integration and customization.
|
|
15
|
+
- **Local & Server File Operations**: Supports reading, writing, and uploading files both locally and to the server, catering to a wide range of application needs.
|
|
16
|
+
- **Graceful Fallbacks**: Implements graceful fallbacks to the standard HTML5 file input API, ensuring compatibility and functionality across different browsers and environments.
|
|
17
|
+
- **Headless File Management**: Designed for use in headless applications or scenarios where a UI is not required or desired, providing a clean, efficient backend solution for file management.
|
|
18
|
+
|
|
12
19
|

|
|
13
20
|
|
|
14
21
|
## [Docs & Demo](https://cocreate.app/docs/file)
|
package/demo/index.html
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<title>Video Segmenting | CoCreateJS</title>
|
|
5
|
+
|
|
6
|
+
<!-- CoCreate Favicon -->
|
|
7
|
+
<link
|
|
8
|
+
rel="icon"
|
|
9
|
+
type="image/png"
|
|
10
|
+
sizes="32x32"
|
|
11
|
+
href="../assets/favicon.ico" />
|
|
12
|
+
<link rel="manifest" href="/manifest.webmanifest" />
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<input id="fileInput" type="file" accept="video/mp4" />
|
|
16
|
+
|
|
17
|
+
<script src="./video-segmenting.js"></script>
|
|
18
|
+
<!-- <script src="../dist/CoCreate-file.js"></script> -->
|
|
19
|
+
<!-- <script src="../../../CoCreateJS/dist/CoCreate.js"></script> -->
|
|
20
|
+
<!-- <script src="https://CoCreate.app/dist/CoCreate.js"></script> -->
|
|
21
|
+
</body>
|
|
22
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cocreate/file",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "A headless file uploader
|
|
3
|
+
"version": "1.17.1",
|
|
4
|
+
"description": "A versatile, configurable headless file uploader supporting both local and server file operations. Accessible via a JavaScript API and HTML5 attributes, it offers seamless file reading, writing, and uploading capabilities with graceful fallbacks to the standard HTML5 file input API. Ideal for developers requiring robust file management in headless or UI-less environments.",
|
|
5
5
|
"keywords": [
|
|
6
|
-
"file",
|
|
6
|
+
"file-uploader",
|
|
7
|
+
"headless",
|
|
8
|
+
"html5-attributes",
|
|
9
|
+
"server-upload",
|
|
10
|
+
"customizable-uploader",
|
|
11
|
+
"file-upload-api",
|
|
12
|
+
"no-ui-upload",
|
|
13
|
+
"background-file-upload",
|
|
14
|
+
"programmatic-file-upload",
|
|
15
|
+
"headless-file-management",
|
|
7
16
|
"cocreate",
|
|
8
17
|
"low-code-framework",
|
|
9
|
-
"no-code-framework",
|
|
10
18
|
"cocreatejs",
|
|
11
19
|
"cocreatejs-component",
|
|
12
20
|
"cocreate-framework",
|
|
13
|
-
"no-code",
|
|
14
21
|
"low-code",
|
|
15
|
-
"collaborative-framework",
|
|
16
22
|
"realtime",
|
|
17
23
|
"realtime-framework",
|
|
18
|
-
"collaboration",
|
|
19
24
|
"shared-editing",
|
|
20
25
|
"html5-framework",
|
|
21
26
|
"javascript-framework"
|
package/src/client.js
CHANGED
|
@@ -150,6 +150,11 @@ async function fileEvent(event) {
|
|
|
150
150
|
|
|
151
151
|
if (!files[i].src)
|
|
152
152
|
await readFile(files[i])
|
|
153
|
+
// if (!files[i].src)
|
|
154
|
+
// files[i].src = files[i]
|
|
155
|
+
|
|
156
|
+
// if (!files[i].src.name)
|
|
157
|
+
// files[i].src = files[i]
|
|
153
158
|
|
|
154
159
|
if (!files[i].size)
|
|
155
160
|
files[i].size = handle.size
|
|
@@ -173,7 +178,7 @@ async function fileEvent(event) {
|
|
|
173
178
|
console.log("Files selected:", selected);
|
|
174
179
|
|
|
175
180
|
if (input.renderValue)
|
|
176
|
-
input.renderValue(Array.from(selected.values()))
|
|
181
|
+
input.renderValue(Array.from(selected.values()));
|
|
177
182
|
|
|
178
183
|
const isImport = input.getAttribute('import')
|
|
179
184
|
const isRealtime = input.getAttribute('realtime')
|
|
@@ -226,7 +231,7 @@ async function getFileId(file) {
|
|
|
226
231
|
}
|
|
227
232
|
}
|
|
228
233
|
|
|
229
|
-
async function getFiles(fileInputs) {
|
|
234
|
+
async function getFiles(fileInputs, readAs) {
|
|
230
235
|
const files = [];
|
|
231
236
|
|
|
232
237
|
if (!Array.isArray(fileInputs))
|
|
@@ -236,8 +241,13 @@ async function getFiles(fileInputs) {
|
|
|
236
241
|
const selected = inputs.get(input)
|
|
237
242
|
if (selected) {
|
|
238
243
|
for (let file of Array.from(selected.values())) {
|
|
239
|
-
if (!file.src)
|
|
240
|
-
|
|
244
|
+
if (!file.src) {
|
|
245
|
+
// if (readAs === 'blob')
|
|
246
|
+
file.src = file
|
|
247
|
+
// else
|
|
248
|
+
// await readFile(file, readAs)
|
|
249
|
+
}
|
|
250
|
+
|
|
241
251
|
let fileObject = { ...file }
|
|
242
252
|
fileObject.size = file.size
|
|
243
253
|
await getCustomData(fileObject)
|
|
@@ -251,6 +261,7 @@ async function getFiles(fileInputs) {
|
|
|
251
261
|
}
|
|
252
262
|
|
|
253
263
|
async function getCustomData(file) {
|
|
264
|
+
// TODO: Consider potential replacment of file_id, perhaps supporting selector
|
|
254
265
|
let form = document.querySelector(`[file_id="${file.id}"]`);
|
|
255
266
|
if (form) {
|
|
256
267
|
let elements = form.querySelectorAll('[file]');
|
|
@@ -268,13 +279,15 @@ async function getCustomData(file) {
|
|
|
268
279
|
}
|
|
269
280
|
|
|
270
281
|
// This function reads the file and returns its src
|
|
271
|
-
function readFile(file) {
|
|
282
|
+
function readFile(file, readAs) {
|
|
272
283
|
return new Promise((resolve) => {
|
|
273
284
|
const fileType = file.type.split('/');
|
|
274
|
-
let readAs;
|
|
275
285
|
|
|
276
286
|
if (fileType[1] === 'directory') {
|
|
277
287
|
return resolve(file)
|
|
288
|
+
} else if (readAs) {
|
|
289
|
+
if (readAs === 'blob')
|
|
290
|
+
return resolve(file)
|
|
278
291
|
} else if (fileType[0] === 'image') {
|
|
279
292
|
readAs = 'readAsDataURL';
|
|
280
293
|
} else if (fileType[0] === 'video') {
|
|
@@ -321,6 +334,7 @@ function setFiles(element, files) {
|
|
|
321
334
|
render({ source: element, data: Array.from(selected.values()) })
|
|
322
335
|
}
|
|
323
336
|
|
|
337
|
+
// TODO: Could this benifit from media processing to save results locally
|
|
324
338
|
async function save(element, action, data) {
|
|
325
339
|
try {
|
|
326
340
|
if (!data)
|
|
@@ -410,48 +424,211 @@ async function upload(element, data) {
|
|
|
410
424
|
element = [element]
|
|
411
425
|
|
|
412
426
|
for (let i = 0; i < element.length; i++) {
|
|
413
|
-
const
|
|
427
|
+
const fileInputs = []
|
|
414
428
|
if (element[i].type === 'file')
|
|
415
|
-
|
|
429
|
+
fileInputs.push(element[i])
|
|
416
430
|
else if (element[i].tagName === 'form') {
|
|
417
|
-
|
|
418
|
-
inputs.push(...fileInputs)
|
|
431
|
+
fileInputs.push(...element[i].querySelectorAll('input[type="file"]'))
|
|
419
432
|
} else {
|
|
420
433
|
const form = element[i].closest('form')
|
|
421
434
|
if (form)
|
|
422
|
-
|
|
435
|
+
fileInputs.push(...form.querySelectorAll('input[type="file"]'))
|
|
423
436
|
}
|
|
424
437
|
|
|
425
|
-
for (let input of
|
|
438
|
+
for (let input of fileInputs) {
|
|
426
439
|
let Data = Elements.getObject(input);
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
440
|
+
let object = input.getAttribute('object') || ''
|
|
441
|
+
let key = input.getAttribute('key')
|
|
442
|
+
|
|
443
|
+
Data.broadcastBrowser = false
|
|
444
|
+
Data.method = 'object.update'
|
|
445
|
+
if (!Data.array)
|
|
446
|
+
Data.array = 'files'
|
|
447
|
+
|
|
448
|
+
let path = input.getAttribute('path')
|
|
449
|
+
let directory = '/'
|
|
450
|
+
|
|
451
|
+
if (path) {
|
|
452
|
+
directory = path.split('/');
|
|
453
|
+
directory = directory[directory.length - 1];
|
|
454
|
+
if (!path.endsWith('/'))
|
|
455
|
+
path += '/'
|
|
456
|
+
} else
|
|
457
|
+
path = directory = '/'
|
|
430
458
|
|
|
431
|
-
|
|
459
|
+
if (!Data.host)
|
|
460
|
+
Data.host = ['*']
|
|
432
461
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
Data.type = 'object'
|
|
462
|
+
if (!Data.public)
|
|
463
|
+
Data.public = true
|
|
436
464
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
465
|
+
if (input.getFilter) {
|
|
466
|
+
Data.$filter = await input.getFilter()
|
|
467
|
+
if (!Data.$filter.query)
|
|
468
|
+
Data.$filter.query = {}
|
|
469
|
+
} else
|
|
470
|
+
Data.$filter = {
|
|
471
|
+
query: {}
|
|
442
472
|
}
|
|
443
473
|
|
|
444
|
-
Data.method = Data.type + '.update'
|
|
445
|
-
let response = await Crud.send(Data)({
|
|
446
|
-
array,
|
|
447
|
-
object,
|
|
448
|
-
upsert: true
|
|
449
|
-
});
|
|
450
474
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
475
|
+
// let files = await getFiles(input, 'blob')
|
|
476
|
+
let files
|
|
477
|
+
const selected = inputs.get(input)
|
|
478
|
+
if (selected) {
|
|
479
|
+
files = Array.from(selected.values())
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
let segmentSize = 10 * 1024 * 1024
|
|
483
|
+
for (let i = 0; i < files.length; i++) {
|
|
484
|
+
files[i].path = path
|
|
485
|
+
files[i].pathname = path + files[i].name
|
|
486
|
+
files[i].directory = directory
|
|
487
|
+
|
|
488
|
+
// let fileObject = { ...file }
|
|
489
|
+
// fileObject.size = file.size
|
|
490
|
+
// await getCustomData(fileObject)
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
if (input.processFile && files[i].size > segmentSize) {
|
|
494
|
+
// let test = await input.processFile(files[i], null, segmentSize, null, null, null, input);
|
|
495
|
+
let { playlist, segments } = await input.processFile(files[i], null, segmentSize);
|
|
496
|
+
|
|
497
|
+
// Create a video element
|
|
498
|
+
const videoElement = document.createElement('video');
|
|
499
|
+
videoElement.setAttribute('controls', ''); // Add controls so you can play/pause
|
|
500
|
+
videoElement.style.width = '100%';
|
|
501
|
+
document.body.appendChild(videoElement);
|
|
502
|
+
|
|
503
|
+
const mediaSource = new MediaSource();
|
|
504
|
+
videoElement.src = URL.createObjectURL(mediaSource);
|
|
505
|
+
|
|
506
|
+
mediaSource.addEventListener('sourceopen', () => {
|
|
507
|
+
const sourceBuffer = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.42E01E"');
|
|
508
|
+
|
|
509
|
+
sourceBuffer.addEventListener('updateend', () => {
|
|
510
|
+
console.log('Append operation completed.');
|
|
511
|
+
try {
|
|
512
|
+
console.log('Buffered ranges:', sourceBuffer.buffered);
|
|
513
|
+
// Append next segment here if applicable
|
|
514
|
+
} catch (e) {
|
|
515
|
+
console.error('Error accessing buffered property:', e);
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
function appendSegment(index) {
|
|
520
|
+
if (index >= segments.length) {
|
|
521
|
+
console.log('All segments have been appended.');
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (!sourceBuffer.updating) {
|
|
526
|
+
segments[index].src.arrayBuffer().then(arrayBuffer => {
|
|
527
|
+
console.log(`Appending segment ${index}`);
|
|
528
|
+
sourceBuffer.appendBuffer(arrayBuffer);
|
|
529
|
+
// Next segment will be appended on 'updateend' event
|
|
530
|
+
}).catch(error => {
|
|
531
|
+
console.error(`Error reading segment[${index}] as ArrayBuffer:`, error);
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Append the first segment to start
|
|
537
|
+
appendSegmfent(0);
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
// mediaSource.addEventListener('sourceopen', () => {
|
|
542
|
+
// const sourceBuffer = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.42E01E"'); // avc1.4D401E, avc1.4D401F, avc1.4D4028, avc1.4D4020, avc1.4D4029, avc1.4D402A
|
|
543
|
+
|
|
544
|
+
// // Append the first segment to start
|
|
545
|
+
// if (!sourceBuffer.updating) {
|
|
546
|
+
|
|
547
|
+
// segments[0].src.arrayBuffer().then(arrayBuffer => {
|
|
548
|
+
// sourceBuffer.appendBuffer(arrayBuffer);
|
|
549
|
+
|
|
550
|
+
// // Wait for 3 seconds before logging the sourceBuffer state
|
|
551
|
+
// setTimeout(() => {
|
|
552
|
+
// console.log(sourceBuffer);
|
|
553
|
+
// }, 3000); // 3000 milliseconds = 3 seconds
|
|
554
|
+
|
|
555
|
+
// sourceBuffer.addEventListener('updateend', () => {
|
|
556
|
+
// console.log('Append operation completed.');
|
|
557
|
+
// try {
|
|
558
|
+
// console.log('Buffered ranges:', sourceBuffer.buffered);
|
|
559
|
+
// } catch (e) {
|
|
560
|
+
// console.error('Error accessing buffered property:', e);
|
|
561
|
+
// }
|
|
562
|
+
// // Proceed with additional operations here
|
|
563
|
+
// });
|
|
564
|
+
|
|
565
|
+
// }).catch(error => {
|
|
566
|
+
// console.error('Error reading segment[0] as ArrayBuffer:', error);
|
|
567
|
+
// });
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
// // segments[0].src.arrayBuffer().then(arrayBuffer => {
|
|
571
|
+
// // sourceBuffer.appendBuffer(arrayBuffer);
|
|
572
|
+
// // })
|
|
573
|
+
|
|
574
|
+
// // let segmentLength = 0
|
|
575
|
+
// // sourceBuffer.addEventListener('updateend', () => {
|
|
576
|
+
// // segmentLength += 1
|
|
577
|
+
// // if (segments[segmentLength])
|
|
578
|
+
// // segments[segmentLength].src.arrayBuffer().then(arrayBuffer => {
|
|
579
|
+
// // console.log(sourceBuffer)
|
|
580
|
+
// // // sourceBuffer.appendBuffer(arrayBuffer);
|
|
581
|
+
// // })
|
|
582
|
+
// // });
|
|
583
|
+
// }
|
|
584
|
+
// });
|
|
585
|
+
|
|
454
586
|
}
|
|
587
|
+
|
|
588
|
+
// files[i].src = playlist
|
|
589
|
+
// for (let j = 0; j < segments.length; j++) {
|
|
590
|
+
// segments[j].path = path
|
|
591
|
+
// segments[j].pathname = path + segments[j].name
|
|
592
|
+
// segments[j].directory = directory
|
|
593
|
+
// segments[j] = { ...segments[j], ...await readFile(segments[j].src) }
|
|
594
|
+
// segments[j].public = true
|
|
595
|
+
// segments[j].host = ['*']
|
|
596
|
+
|
|
597
|
+
// playlist.segments[j].src = segments[j].pathname
|
|
598
|
+
// Data.$filter.query.pathname = segments[j].pathname
|
|
599
|
+
// Crud.send({
|
|
600
|
+
// ...Data,
|
|
601
|
+
// object: segments[j],
|
|
602
|
+
// upsert: true
|
|
603
|
+
// });
|
|
604
|
+
// }
|
|
605
|
+
|
|
606
|
+
// } else {
|
|
607
|
+
// files[i] = { ...files[i], ...await readFile(files[i].src) }
|
|
608
|
+
// }
|
|
609
|
+
|
|
610
|
+
// if (!key) {
|
|
611
|
+
// Data.object = { ...files[i] }
|
|
612
|
+
// } else {
|
|
613
|
+
// Data.object = { [key]: { ...files[i] } }
|
|
614
|
+
// }
|
|
615
|
+
|
|
616
|
+
// if (object) {
|
|
617
|
+
// Data.object._id = object // test
|
|
618
|
+
// }
|
|
619
|
+
|
|
620
|
+
// delete Data.object.input
|
|
621
|
+
// Data.$filter.query.pathname = files[i].pathname
|
|
622
|
+
// let response = await Crud.send({
|
|
623
|
+
// ...Data,
|
|
624
|
+
// upsert: true
|
|
625
|
+
// });
|
|
626
|
+
|
|
627
|
+
// console.log(response, 'tes')
|
|
628
|
+
// if (response && (!object || object !== response.object)) {
|
|
629
|
+
// Elements.setTypeValue(element, response);
|
|
630
|
+
// }
|
|
631
|
+
|
|
455
632
|
}
|
|
456
633
|
}
|
|
457
634
|
|
|
@@ -515,6 +692,7 @@ async function Import(element, data) {
|
|
|
515
692
|
|
|
516
693
|
if (data.length) {
|
|
517
694
|
for (let i = 0; i < data.length; i++) {
|
|
695
|
+
// TODO: if _id exist use update method
|
|
518
696
|
data[i].method = data[i].type + '.create'
|
|
519
697
|
data[i] = await Crud.send(data[i])
|
|
520
698
|
}
|