@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 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
- This is a configurable headless file uploader that utilizes HTML5 attributes for easy customization. With this module, users can easily upload files to a server without requiring a formal UI or browser interaction. By leveraging HTML5 attributes, it's easy to customize and fine-tune the behavior of the uploader to fit specific needs. This uploader is perfect for developers looking to implement file uploads in a headless environment. Take it for a spin in our [playground!](https://cocreate.app/docs/file)
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
  ![minified](https://img.badgesize.io/https://cdn.cocreate.app/file/latest/CoCreate-file.min.js?style=flat-square&label=minified&color=orange)
6
6
  ![gzip](https://img.badgesize.io/https://cdn.cocreate.app/file/latest/CoCreate-file.min.js?compression=gzip&style=flat-square&label=gzip&color=yellow)
7
7
  ![brotli](https://img.badgesize.io/https://cdn.cocreate.app/file/latest/CoCreate-file.min.js?compression=brotli&style=flat-square&label=brotli)
8
- ![GitHub latest release](https://img.shields.io/github/v/release/CoCreate-app/CoCreate-action?style=flat-square)
9
- ![License](https://img.shields.io/github/license/CoCreate-app/CoCreate-action?style=flat-square)
8
+ ![GitHub latest release](https://img.shields.io/github/v/release/CoCreate-app/CoCreate-file?style=flat-square)
9
+ ![License](https://img.shields.io/github/license/CoCreate-app/CoCreate-file?style=flat-square)
10
10
  ![Hiring](https://img.shields.io/static/v1?style=flat-square&label=&message=Hiring&color=blueviolet)
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
  ![CoCreate-file](https://cdn.cocreate.app/docs/CoCreate-file.gif)
13
20
 
14
21
  ## [Docs & Demo](https://cocreate.app/docs/file)
package/demo/index.html CHANGED
@@ -18,7 +18,7 @@
18
18
  </form>
19
19
 
20
20
  <form>
21
- <input type="file" />
21
+ <input type="file" path="/assets/test" />
22
22
  <button actions="upload">upload</button>
23
23
  </form>
24
24
 
@@ -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.16.0",
4
- "description": "A headless file uploader that uses HTML5 attributes for customization. Allows easy upload of files to server.",
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
- await readFile(file)
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 inputs = []
427
+ const fileInputs = []
414
428
  if (element[i].type === 'file')
415
- inputs.push(element[i])
429
+ fileInputs.push(element[i])
416
430
  else if (element[i].tagName === 'form') {
417
- let fileInputs = element[i].querySelectorAll('input[type="file"]')
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
- inputs.push(...form.querySelectorAll('input[type="file"]'))
435
+ fileInputs.push(...form.querySelectorAll('input[type="file"]'))
423
436
  }
424
437
 
425
- for (let input of inputs) {
438
+ for (let input of fileInputs) {
426
439
  let Data = Elements.getObject(input);
427
- if (Data.type) {
428
- if (input.getFilter)
429
- Data.$filter = await input.getFilter()
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
- let files = await getFiles(input)
459
+ if (!Data.host)
460
+ Data.host = ['*']
432
461
 
433
- let key = getAttribute('key')
434
- if (Data.type === 'key')
435
- Data.type = 'object'
462
+ if (!Data.public)
463
+ Data.public = true
436
464
 
437
- let object = input.getAttribute('object')
438
- if (key) {
439
- Data[Data.type] = { _id: object, [key]: files }
440
- } else {
441
- Data[Data.type] = files
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
- data.push(response)
452
- if (response && (!object || object !== response.object)) {
453
- Elements.setTypeValue(element, response);
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
  }