fli_video 0.0.2 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.builders/generators/back/scripts/01-get-technical-design-and-feature-list.rb +32 -0
  3. data/.builders/generators/back/scripts/02-get-feature-list-and-components.rb +42 -0
  4. data/.builders/generators/back/scripts/03-get-structure.rb +104 -0
  5. data/.builders/generators/back/scripts/generated/application-structure.json +231 -0
  6. data/.builders/generators/back/scripts/generated/features-and-components.md +988 -0
  7. data/.builders/generators/back/scripts/generated/technical-design-and-features.md +424 -0
  8. data/.builders/klues/add_episode.klue +25 -0
  9. data/.builders/klues/change_chapter_name.klue +32 -0
  10. data/.builders/klues/create_chapter_video.klue +34 -0
  11. data/.builders/klues/create_project.klue +37 -0
  12. data/.builders/klues/empty_trash.klue +24 -0
  13. data/.builders/klues/episode_path.klue +77 -0
  14. data/.builders/klues/global_config.klue +31 -0
  15. data/.builders/klues/move_ecamm_file.klue +21 -0
  16. data/.builders/klues/move_to_trash.klue +35 -0
  17. data/.builders/klues/open_in_finder.klue +25 -0
  18. data/.builders/klues/project_config.klue +123 -0
  19. data/.builders/klues/project_meta_data_store.klue +28 -0
  20. data/.builders/klues/project_path.klue +77 -0
  21. data/.builders/klues/recording_file_watcher.klue +28 -0
  22. data/.builders/klues/recording_filename.klue +112 -0
  23. data/.builders/klues/restore_from_trash.klue +29 -0
  24. data/.builders/klues/switch_focus.klue +24 -0
  25. data/.builders/klues/text_to_speech.klue +29 -0
  26. data/.builders/klues/transcript_data_store.klue +28 -0
  27. data/.rubocop.yml +2 -0
  28. data/CHANGELOG.md +26 -0
  29. data/README.md +13 -20
  30. data/bin/fli_video +6 -0
  31. data/docs/feature-list.md +76 -0
  32. data/docs/generated/application-structure.json +53 -0
  33. data/docs/generated/features-and-components.md +993 -0
  34. data/docs/generated/technical-design-and-features.md +437 -0
  35. data/docs/technical-specifications.md +360 -0
  36. data/fli.rb +138 -0
  37. data/lib/fli_video/cli.rb +30 -0
  38. data/lib/fli_video/version.rb +1 -1
  39. data/package-lock.json +2 -2
  40. data/package.json +1 -1
  41. data/scripts/01-get-technical-design-and-feature-list.md +5 -0
  42. data/scripts/01-get-technical-design-and-feature-list.rb +39 -0
  43. data/scripts/02-get-feature-list-and-components.md +26 -0
  44. data/scripts/02-get-feature-list-and-components.rb +56 -0
  45. data/scripts/03-get-code-structure.md +33 -0
  46. data/scripts/03-get-code-structure.rb +73 -0
  47. metadata +41 -3
  48. data/README-features.md +0 -36
@@ -0,0 +1,993 @@
1
+ ## Features
2
+
3
+ ### Feature <-> DSL GPTs
4
+ https://chat.openai.com/g/g-AyFi0UOXn-code-simplifier
5
+
6
+ **Global Configuration**
7
+ Access and apply global configuration settings for video asset management and state consistency.
8
+
9
+ **Project Configuration**
10
+ Access and apply video or episode settings and state. Infers project settings from existing project folders and files.
11
+
12
+ **CLI Project Commands**
13
+ Efficiently execute and manage video project commands using a command-line interface, enhancing control and flexibility in project handling.
14
+
15
+ **FileWatch Processor**
16
+ Utilize a FileWatch processor to automate file event responses, directing new recordings to designated folders for efficient content management.
17
+
18
+ **Create Project**
19
+ Setup a new video project for standalone YouTube video or Podcast.
20
+
21
+ **Add Episode**
22
+ Add a new episode to an existing podcast project.
23
+
24
+ **Switch Video Focus**
25
+ Easily switch between different video projects or episodes to accommodate changing content priorities.
26
+
27
+ **Move eCamm File**
28
+ Seamlessly transfer eCamm recordings to the current focused video or podcast episode recordings subfolder.
29
+
30
+ **Project Path**
31
+ Construct and manage project paths dynamically using configurable values and existing folder name segments.
32
+
33
+ **Episode Path**
34
+ Construct and manage episode paths dynamically using configurable values and existing folder name segments.
35
+
36
+ **Recording Namer**
37
+ Dynamically generate and update video recording filenames, incorporating chapter and part sequences, chapter names, and tags, with capabilities to modify chapter sequences and tags for improved organization and content identification.
38
+
39
+ **Change Chapter Name**
40
+ Alter the name of a chapter based on its sequence in the project, facilitating better organization and identification of video content.
41
+
42
+ **Trash**
43
+ Moves suboptimal video takes to a designated 'trash' or 'archive' folder, optimizing storage and maintaining project clarity by segregating lesser-quality content.
44
+
45
+ **Trash Undo**
46
+ Retrieves video takes from the '.trash' folder and moves them back into the target project folder, allowing for reconsideration or re-evaluation of previously discarded content.
47
+
48
+ **Clean Trash**
49
+ Permanently deletes video takes from the 'trash' or 'archive' folder, freeing up storage space and ensuring project clarity.
50
+
51
+ **Open in Finder**
52
+ Quickly access video project and episode folders within the Finder, streamlining file navigation.
53
+
54
+ **Create Chapter Video**
55
+ Combine and review video chapter segments independently, facilitating content evaluation and editing.
56
+
57
+ **Text to Speech**
58
+ Transcribe spoken content to text and store transcriptions folder in multiple transcription formats.
59
+
60
+ Create a DSL using the interactor pattern for:
61
+
62
+ **Transcript Data Store**
63
+ Builds a JSON datastore of transcripts for an entire project based on existing transcript folders found within project, episode, recording, chapter and post-produced folders.
64
+
65
+ **Project Meta Data Store**
66
+ Build a JSON datastore of files for an entire project based on existing project, episode, recording, chapter and post-produced folders and infer metadata based on KEYWORDS, transcripts or other useful data.
67
+
68
+ ## Future Ideas
69
+
70
+ **Web Command Interface for Video Project Management**
71
+ Introduce a streamlined, web-based interface for managing video project commands, enabling efficient control and organization of project components through simple browser interactions.
72
+
73
+ **Project Meta Report**
74
+ Generate a detailed report for a specific video project, including the episodes, chapters, recordings, a list of recording IDs (chapter sequence + part sequence), and the name for the next video recording, file sizes.
75
+ This should be extracted to an AstroJS Website or HTML template servered by a local webserver and provide viewing and navigation for all my video projects.
76
+
77
+
78
+
79
+ ## Klue Components
80
+ Klue Component: `add_episode.klue`
81
+
82
+ ```ruby
83
+ component :add_episode do
84
+ desc "Add a new episode to an existing podcast project."
85
+
86
+ pattern "Command"
87
+
88
+ comments <<~TEXT
89
+ - Facilitates the addition of a new episode to a specific podcast project.
90
+ - Requires the project code of the podcast to ensure correct association.
91
+ - Allows setting of episode-specific details like episode code and name.
92
+ - Ensures that the episode is added only to podcast projects and not to other project types.
93
+ - Will create recording, chapter and support folders for the episode.
94
+ TEXT
95
+
96
+ method :run(:project_code, :episode_code, :episode_name)
97
+
98
+ sample :add_episode_to_podcast, <<~RUBY
99
+ # Add a new episode to existing podcast folder
100
+ add_episode.run('a21', '01', 'Episode About Something')
101
+ # Create:
102
+ # ~/video-projects/a21-ac-some-podcast/01-episode-about-something/assets
103
+ # ~/video-projects/a21-ac-some-podcast/01-episode-about-something/chapters
104
+ # ~/video-projects/a21-ac-some-podcast/01-episode-about-something/recordings
105
+ # ~/video-projects/a21-ac-some-podcast/01-episode-about-something/.trash
106
+ RUBY
107
+ end
108
+
109
+ ```
110
+ Klue Component: `change_chapter_name.klue`
111
+
112
+ ```ruby
113
+ component :change_chapter_name do
114
+ desc "Alter the name of a chapter based on its sequence in the project, facilitating better organization and identification of video content."
115
+
116
+ pattern "Command"
117
+ note "This should be a method on the project"
118
+
119
+ comments <<~TEXT
120
+ - Allows the renaming of a chapter within a video project.
121
+ - Utilizes the chapter's sequence number to identify and target the specific chapter for renaming.
122
+ TEXT
123
+
124
+ method :rename_chapter(:parent, :chapter_sequence, :new_name)
125
+
126
+ <<~FILE_SYSTEM
127
+ 01-a-introduction-GPTIMPROVE.mov
128
+ 01-b-introduction-more-content.mov
129
+ 01-c-introduction.mov
130
+ 01-d-introduction-CTA.mov
131
+ 02-a-overview.mov
132
+ 03-a-scenario-TITLE.mov
133
+ FILE_SYSTEM
134
+
135
+ sample :change_chapter_name_in_project, <<~RUBY
136
+ # Example of changing the name of a specific chapter in a project
137
+ change_chapter_name.rename_chapter('a27', '01', 'intro')
138
+
139
+ # 01-a-intro-GPTIMPROVE.mov
140
+ # 01-b-intro.mov
141
+ # 01-c-intro.mov
142
+ # 01-d-intro-CTA.mov
143
+ RUBY
144
+ end
145
+
146
+ ```
147
+ Klue Component: `create_chapter_video.klue`
148
+
149
+ ```ruby
150
+ component :create_chapter_video do
151
+ desc "Combine and all video parts for a chapter sequence and put into the chapters folder."
152
+
153
+ pattern "Command"
154
+
155
+ comments <<~TEXT
156
+ - Combines individual video segments into a single chapter video.
157
+ - Facilitates independent review and editing of each chapter.
158
+ TEXT
159
+
160
+ constructor(:parent_project)
161
+
162
+ method :run(:chapter_seq)
163
+
164
+ sample :create_chapter_video, <<~RUBY
165
+ project_path = ProjectPath.new('a27')
166
+
167
+ # Instantiate the component with the parent project
168
+ create_chapter_video = generate_chapter_video.new(project_path)
169
+
170
+ # Automatically combine all video segments for a chapter
171
+ create_chapter_video.run(1)
172
+ # Output: '~/video-projects/a27-ac-some-podcast/chapters/01-complete-chapter.mov'
173
+ RUBY
174
+
175
+ sample :create_chapter_video_for_segments, <<~RUBY
176
+ project_path = ProjectPath.new('a27')
177
+
178
+ create_chapter_video = generate_chapter_video.new(project_path)
179
+
180
+ create_chapter_video.run(1, ['01-intro-a.mov', '02-intro-b.mov'], output_file_name: '01-intro-custom.mov')
181
+ # Output: '~/video-projects/a27-ac-some-podcast/chapters/01-intro-custom.mov'
182
+ RUBY
183
+ end
184
+
185
+ ```
186
+ Klue Component: `create_project.klue`
187
+
188
+ ```ruby
189
+ component :create_project do
190
+ desc "Setup a new video project for standalone YouTube video or Podcast."
191
+
192
+ pattern "Command"
193
+
194
+ comments <<~TEXT
195
+ - Allows the creation of a new project, specifying if it's for a YouTube video or a Podcast.
196
+ - For YouTube videos, the project will be single-episode based.
197
+ - For Podcasts, the project can include multiple episodes.
198
+ - Stores project-specific settings and metadata.
199
+ - Will create recording, chapter and support folders for project_type: video
200
+ TEXT
201
+
202
+ enum :project_type, %w(video podcast)
203
+
204
+ method :run(:project_type, :project_code, :channel_code, :project_name)
205
+
206
+ sample :new_video_project, <<~RUBY
207
+ # Create a new video project for AppyDave
208
+ create_project.run(:video, 'a20', 'ad', 'Some Video')
209
+ # Create:
210
+ # ~/video-projects/a20-ad-some-video
211
+ # ~/video-projects/a20-ad-some-video/.fv.json
212
+ # ~/video-projects/a20-ad-some-video/assets
213
+ # ~/video-projects/a20-ad-some-video/chapters
214
+ # ~/video-projects/a20-ad-some-video/recordings
215
+ # ~/video-projects/a20-ad-some-video/.trash
216
+ RUBY
217
+
218
+ sample :new_podcast_project, <<~RUBY
219
+ # Create a new podcast project for AppyCast
220
+ create_project.run(:podcast, 'a21', 'ac', 'Some Podcast')
221
+ # Creates folder:
222
+ # ~/video-projects/a21-ac-some-podcast
223
+ # ~/video-projects/a21-ac-some-podcast/.fv.json
224
+ RUBY
225
+ end
226
+
227
+ ```
228
+ Klue Component: `empty_trash.klue`
229
+
230
+ ```ruby
231
+ component :empty_trash do
232
+ desc "Permanently delete video takes from the '.trash' folder."
233
+
234
+ pattern "Command"
235
+
236
+ comments <<~TEXT
237
+ - Permanently removes all recordings from the trash folder.
238
+ - Frees up storage space by deleting unnecessary files.
239
+ - Maintains project clarity by clearing clutter.
240
+ - Ensures that only unwanted takes are deleted after a final review.
241
+ TEXT
242
+
243
+ constructor(:parent_project)
244
+
245
+ method :run
246
+
247
+ sample :permanently_delete_trash, <<~RUBY
248
+ project_path = ProjectPath.new('a27')
249
+
250
+ # Permanently deleting all contents of the trash folder
251
+ clean_trash.empty_trash
252
+ # Empties the entire .trash folder in "~/video-projects/a27-ac-categorize-mp4-CI/.trash/
253
+ RUBY
254
+ end
255
+
256
+ ```
257
+ Klue Component: `episode_path.klue`
258
+
259
+ ```ruby
260
+ component :episode_path do
261
+ desc "Construct episode path using project/episode code and deducing state based on existing folder name."
262
+
263
+ pattern "Adapter"
264
+
265
+ comments <<~TEXT
266
+ - Infers episode path based on episode code and existing folder name.
267
+ - Maintains state for episode code and descriptive name and keywords.
268
+ - Will use current project code if not provided.
269
+ - Path is relative to project path.
270
+ - Uses existing folder name and keywords to build internal state.
271
+ - Provides method for building absolute, relative and sub folder.
272
+ - Provides method for building a new path for rename operations.
273
+ - Provides access to associated project.
274
+ - Stores state in an internal data object.
275
+ - Designed for building folder names, not create or modify actual directories.
276
+ TEXT
277
+
278
+ accessors :episode_code, :episode_name, :keywords, :episode_path, :project
279
+
280
+ method :constructor(:episode_code, episode_name: nil, keywords: nil, project_code: nil)
281
+ method :instance(:folder)
282
+
283
+ method :change_path(episode_code: nil, episode_name: nil, keywords: nil)
284
+
285
+ # project_path.recording_path # => "~/video-projects/a27-ac-categorize-mp4-CI/recordings"
286
+ # ~/video-projects/a27-ac-categorize-mp4-CI/01-flivideo-project-kickoff-TODO/recordings
287
+
288
+ sample :create_episode_path, <<~RUBY
289
+ # Create a new episode path
290
+ episode_path = EpisodePath.new('01', episode_name: 'flivideo-project-kickoff', keywords: ['TODO'] )
291
+ episode_path.episode_path # => "~/video-projects/a27-ac-categorize-mp4-CI/01-flivideo-project-kickoff-TODO"
292
+ episode_path.episode_code # => "01"
293
+ episode_path.episode_name # => "flivideo-project-kickoff"
294
+ episode_path.keywords # => ["TODO"]
295
+
296
+ project_path = episode_path.project
297
+ project_path.project_path # => "~/video-projects/a27-ac-categorize-mp4-CI"
298
+ RUBY
299
+
300
+ sample :infer_episode_path, <<~RUBY
301
+ # Infer episode path based on episode code and existing folder
302
+ episode_path = EpisodePath.new('01')
303
+ episode_path.episode_path # => "~/video-projects/a27-ac-categorize-mp4-CI/01-flivideo-project-kickoff-TODO"
304
+ episode_path.episode_code # => "01"
305
+ episode_path.episode_name # => "flivideo-project-kickoff"
306
+ episode_path.keywords # => ["TODO"]
307
+
308
+ RUBY
309
+
310
+ sample :rename_episode_path, <<~RUBY
311
+ # Rename episode path
312
+ episode1 = EpisodePath.new('01')
313
+ episode1.episode_path # => "~/video-projects/a27-ac-categorize-mp4-CI/01-flivideo-project-kickoff-TODO"
314
+
315
+ episode2 = episode1.change_path(episode_code: '02')
316
+ episode2.episode_path # => "~/video-projects/a27-ac-categorize-mp4-CI/02-flivideo-project-kickoff-TODO"
317
+
318
+ episode3 = episode1.change_path(episode_code: '03', episode_name: 'build-gpts', keywords: [])
319
+ episode3.episode_path # => "~/video-projects/a27-ac-categorize-mp4-CI/03-build-gpts"
320
+ RUBY
321
+
322
+ sample :next_episode_code, <<~RUBY
323
+ # Get next episode code
324
+ episode_path = EpisodePath.new('01')
325
+ episode_path.next_episode_code # => "02"
326
+ RUBY
327
+
328
+ sample :folder_to_episode_path, <<~RUBY
329
+ # Convert folder to episode path
330
+ episode_path = EpisodePath.instance('~/video-projects/a27-ac-categorize-mp4-CI/01-flivideo-project-kickoff-TODO')
331
+ episode_path.episode_path # => "~/video-projects/a27-ac-categorize-mp4-CI/01-flivideo-project-kickoff-TODO"
332
+ episode_path.episode_code # => "01"
333
+ episode_path.episode_name # => "flivideo-project-kickoff"
334
+ episode_path.keywords # => ["TODO"]
335
+ RUBY
336
+ end
337
+
338
+ ```
339
+ Klue Component: `global_config.klue`
340
+
341
+ ```ruby
342
+ component :global_configuation do
343
+ pattern "Singleton"
344
+
345
+ desc "Access and apply global configuration settings for video asset management and state consistency."
346
+
347
+ comments <<~TEXT
348
+ Configuration file path: ~/.fli-video.json
349
+ - Automatically creates a configuration file if it does not exist.
350
+ - Responsible for reading and updating global configuration.
351
+ - Provides methods to manage folder paths and various settings.
352
+ TEXT
353
+
354
+ sample :configuration, <<~JSON
355
+ {
356
+ "folders": {
357
+ "ecamm-recordings": "~/Movies/Ecamm Live Recordings",
358
+ "project-root": "/Volumes/Expansion/Sync/tube-channels/a-cast/cast-active",
359
+ },
360
+ "settings": {
361
+ "focus-project-code": "a27",
362
+ },
363
+ }
364
+ JSON
365
+
366
+ method :set_folder(:key, :folder)
367
+ method :folder(:key)
368
+ method :set_setting(:key, :value)
369
+ method :setting(:key)
370
+ method :load
371
+ method :save
372
+ end
373
+
374
+ ```
375
+ Klue Component: `move_ecamm_file.klue`
376
+
377
+ ```ruby
378
+ component :move_ecamm_file do
379
+ desc "Seamlessly transfer eCamm recordings to the current focused video or podcast episode recordings subfolder."
380
+
381
+ comments <<~TEXT
382
+ - Facilitates the transfer of eCamm recording files to the appropriate project's recording subfolder.
383
+ - Automatically identifies the current focused project or episode to ensure correct file placement.
384
+ - Handles the file transfer process, maintaining file integrity and updating any necessary metadata.
385
+ - Uses global configuration to determine eCamm recording folder location.
386
+ - Uses current project configuration to determine recording subfolder location.
387
+ - Aborts the transfer process if target folder does not exist.
388
+ TEXT
389
+
390
+ method :execute(:ecamm_file_name)
391
+
392
+ sample :move_ecamm_file_to_project, <<~RUBY
393
+ # Move an eCamm recording to the currently focused project's recordings subfolder
394
+ move_ecamm_file.execute('Ecamm Live Recording on 2023-08-25 at 14.51.58.mov')
395
+
396
+ # ~/video-projects/a20-ad-some-video/recordings/Ecamm Live Recording on 2023-08-25 at 14.51.58.mov
397
+ RUBY
398
+ end
399
+
400
+ ```
401
+ Klue Component: `move_to_trash.klue`
402
+
403
+ ```ruby
404
+ component :move_to_trash do
405
+ desc "Move suboptimal video takes to '.trash' folder"
406
+
407
+ pattern "Command"
408
+
409
+ comments <<~TEXT
410
+ - Will trash a specific recording in a project folder.
411
+ - Will identify the last eCamm recording in a project folder if no specific recording is provided.
412
+ - Enhances project clarity and organization.
413
+ TEXT
414
+
415
+ constructure(:parent_project)
416
+
417
+ method :run(:file_name = nil)
418
+
419
+ sample :move_named_video_to_trash, <<~RUBY
420
+ project_path = ProjectPath.new('a27')
421
+
422
+ command = move_to_trash.new(project_path)
423
+
424
+ # Example of moving a specific suboptimal video take to the trash folder
425
+ command.run('01-a-introduction.mov')
426
+ # => "~/video-projects/a27-ac-categorize-mp4-CI/.trash/01-a-introduction.mov"
427
+ RUBY
428
+
429
+ sample :move_last_ecamm__video_to_trash, <<~RUBY
430
+ project_path = ProjectPath.new('a27')
431
+
432
+ command = move_to_trash.new(project_path)
433
+
434
+ # Example of moving the last suboptimal video take to the trash folder
435
+ command.run
436
+ # => "~/video-projects/a27-ac-categorize-mp4-CI/.trash/Ecamm Live Recording on 2023-08-25 at 14.51.58.mov
437
+ RUBY
438
+ end
439
+
440
+ ```
441
+ Klue Component: `open_in_finder.klue`
442
+
443
+ ```ruby
444
+ component :open_in_finder do
445
+ desc "Quickly access video project and episode folders within the Finder."
446
+
447
+ pattern "Command"
448
+
449
+ comments <<~TEXT
450
+ - Provides shortcuts to open project and episode folders in the Finder.
451
+ - Enhances efficiency in navigating to specific video project locations.
452
+ - Streamlines the process of locating and managing video files and folders.
453
+ TEXT
454
+
455
+ constructor(:parent_project)
456
+
457
+ method :run
458
+
459
+ sample :access_project_in_finder, <<~RUBY
460
+ project_path = ProjectPath.new('a27')
461
+
462
+ command = open_in_finder.new(project_path)
463
+ # Opens the folder for project 'a27' in Finder
464
+
465
+ command.run
466
+ # Opens the folder for episode '01' of project 'a27' in Finder
467
+ RUBY
468
+ end
469
+
470
+ ```
471
+ Klue Component: `project_config.klue`
472
+
473
+ ```ruby
474
+ component :project_configuration do
475
+ desc "Manage project / episode settings and state."
476
+
477
+ pattern "Adapter"
478
+
479
+ comments <<~TEXT
480
+ Configuration file: .fv.json
481
+ - Handles settings for single videos or the episodes in a Podcast.
482
+ - Responsible for reading and updating settings.
483
+ - Supports state management for single video, episodes, recording and chapter progression.
484
+ - This class is a reflection of the configuration file and the folders and files it manages.
485
+
486
+ Project type: video
487
+ - A single video file.
488
+ - Supports recording and chapter progression
489
+
490
+ Project type: podcast
491
+ - Multiple video files known as episodes.
492
+ - Supports recording and chapter progression for each episode.
493
+
494
+ Infers project type from the presence of the "episodes" key.
495
+ Infers project name from the name of the folder containing the configuration file.
496
+ Infers episode name from the name of the folder containing the video file.
497
+ Keeps track of prefered chapter name for each chapter sequence.
498
+ TEXT
499
+
500
+ method :load
501
+ method :save
502
+ method :resync_project_files() # refreshes the configuration based on the file system
503
+ method :current_chapter() # if type == :video
504
+ method :current_episode() # if type == :podcast
505
+
506
+ data_object :project_config do
507
+ constructor(:json)
508
+
509
+ attribute :type # video, podcast
510
+ attribute :code
511
+ attribute :chapters # if type == :video
512
+ attribute :episodes # if type == :podcast
513
+ end
514
+
515
+ data_object :episode_config do
516
+ constructor(:json)
517
+
518
+ attribute :seq # episode number store as number, but displayed as 2 digit string
519
+ attribute :chapters
520
+ attribute :current # true if this is the current episode
521
+
522
+ method :current_chapter()
523
+ end
524
+
525
+ data_object :chapter_config do
526
+ constructor(:json)
527
+
528
+ attribute :seq
529
+ attribute :preferred_name
530
+ attribute :current # true if this is the current chapter
531
+ end
532
+
533
+ sample :configuration, :for_video, <<~JSON
534
+ {
535
+ "type": "video",
536
+ "code": "e28",
537
+ "chapters": [
538
+ {
539
+ seq: 1,
540
+ preferred_name: "intro"
541
+ },
542
+ {
543
+ seq: 2,
544
+ preferred_name: "example"
545
+ },
546
+ {
547
+ seq: 3,
548
+ preferred_name: "outro",
549
+ current: true
550
+ }
551
+ ]
552
+ }
553
+ JSON
554
+
555
+ sample :configuration, :for_podcast, <<~JSON
556
+ {
557
+ "type": "podcast",
558
+ "code": "e27",
559
+ "episodes": [
560
+ {
561
+ "seq": 1,
562
+ "chapters": [
563
+ {
564
+ "seq": 1,
565
+ "preferred_name": "introduction",
566
+ },
567
+ {
568
+ "seq": 2,
569
+ "preferred_name": "scenario",
570
+ "current": true
571
+ }
572
+ ]
573
+ },
574
+ {
575
+ "seq": 2,
576
+ "current": true,
577
+ "chapters": [
578
+ {
579
+ "seq": 1,
580
+ "preferred_name": "introduction",
581
+ },
582
+ {
583
+ "seq": 2,
584
+ "preferred_name": "scenario",
585
+ },
586
+ {
587
+ "seq": 3,
588
+ "preferred_name": "story",
589
+ "current": true
590
+ }
591
+ ]
592
+ }
593
+ ]
594
+ }
595
+ JSON
596
+ end
597
+
598
+ ```
599
+ Klue Component: `project_meta_data_store.klue`
600
+
601
+ ```ruby
602
+ component :project_meta_data_store do
603
+ desc "Build a JSON datastore of project files with inferred metadata."
604
+
605
+ pattern "Command"
606
+
607
+ comments <<~TEXT
608
+ - Aggregates file data from project, episode, recording, chapter, and post-produced folders.
609
+ - Infers metadata from sources like KEYWORDS, transcripts, and other relevant data.
610
+ - Creates a comprehensive JSON datastore for the entire project.
611
+ - Facilitates advanced data analysis and management for the project.
612
+ - The extracted data can be used for automated post-production processes like keyword generation, B-roll prompts, chapter names, title slides, and time code markers for calls to action.
613
+ TEXT
614
+
615
+ constructor(:parent_project)
616
+
617
+ method :run
618
+
619
+ sample :build_project_metadata, <<~RUBY
620
+ project_path = ProjectPath.new('a27')
621
+
622
+ # Instantiate the component with the parent project
623
+ metadata_store = project_meta_data_store.new(project_path)
624
+
625
+ # Execute the process to aggregate files and infer metadata
626
+ metadata_store.run
627
+ # Output: '~/video-projects/a27-ac-categorize-mp4-CI/project_metadata.json'
628
+ RUBY
629
+ end
630
+
631
+ ```
632
+ Klue Component: `project_path.klue`
633
+
634
+ ```ruby
635
+ component :project_path do
636
+ desc "Construct project path using project code and deducing state based on existing folder name."
637
+
638
+ pattern "Adapter"
639
+
640
+ comments <<~TEXT
641
+ - Infers project path based on project code and existing folder name.
642
+ - Maintains state for project code, channel code, descriptive name, keywords and episodes.
643
+ - Uses global configuration to determine root folder.
644
+ - Uses existing folder name and keywords to build internal state.
645
+ - Provides method for building absolute, relative and sub folder.
646
+ - Provides method for building new path for rename operations.
647
+ - Provides list of associated episode paths.
648
+ - Stores state in an internal data object.
649
+ - Designed for building folder names, not create or modify actual directories.
650
+ TEXT
651
+
652
+ accessors :project_code, :channel_code, :project_name, :keywords, :project_path, :episodes
653
+
654
+ method :constructor(:project_code, channel_code: nil, project_name: nil, keywords: nil)
655
+ method :instance(:folder)
656
+
657
+ method :change_path(project_code: nil, channel_code: nil, project_name: nil, keywords: nil)
658
+
659
+ sample :create_project_path, <<~RUBY
660
+ # Create a new project path
661
+ project_path = ProjectPath.new('a27', channel_code: 'ac', project_name: 'categorize-mp4', keywords: ['CI'] )
662
+ project_path.project_path # => "~/video-projects/a27-ac-categorize-mp4-CI"
663
+ project_path.project_code # => "a27"
664
+ project_path.channel_code # => "ac"
665
+ project_path.project_name # => "categorize-mp4"
666
+ project_path.keywords # => "CI"
667
+ project_path.episodes.count # => 0
668
+ RUBY
669
+
670
+ sample :infer_project_path, <<~RUBY
671
+ # Infer project path based on project code and existing folder
672
+ project_path = ProjectPath.new('a27')
673
+ project_path.project_path # => "~/video-projects/a27-ac-categorize-mp4-CI"
674
+ project_path.project_code # => "a27"
675
+ project_path.channel_code # => "ac"
676
+ project_path.project_name # => "categorize-mp4"
677
+ project_path.keywords # => "CI"
678
+ project_path.episodes.count # => 2
679
+ RUBY
680
+
681
+ sample :rename_project_path, <<~RUBY
682
+ # Rename project path
683
+ project_path = ProjectPath.new('a27')
684
+
685
+ path1 = project_path.change_path(project_code: 'a28')
686
+ path1.project_path # => "~/video-projects/a28-ac-categorize-mp4-CI"
687
+ path2 = project_path.change_path(channel_code: 'ad', project_name: 'categorize-mp4-for-appydave')
688
+ path2.project_path # => "~/video-projects/a27-ad-categorize-mp4-for-appydave-CI"
689
+ path3 = project_path.change_path(keywords: [])
690
+ path3.project_path # => "~/video-projects/a27-ac-categorize-mp4"
691
+ RUBY
692
+
693
+ sample :next_project_code, <<~RUBY
694
+ # Get next project code
695
+ project_path = ProjectPath.new('a27')
696
+ project_path.next_project_code # => "a28"
697
+
698
+ project_path = ProjectPath.new('a99')
699
+ project_path.next_project_code # => "b00"
700
+ RUBY
701
+
702
+ sample :folder_to_project_path, <<~RUBY
703
+ # Convert folder to project path
704
+ project_path = ProjectPath.instance('~/video-projects/a27-ac-categorize-mp4-CI')
705
+ project_path.project_path # => "~/video-projects/a27-ac-categorize-mp4-CI"
706
+ project_path.project_code # => "a27"
707
+ project_path.channel_code # => "ac"
708
+ project_path.project_name # => "categorize-mp4"
709
+ project_path.keywords # => "CI"
710
+ RUBY
711
+ end
712
+
713
+ ```
714
+ Klue Component: `recording_file_watcher.klue`
715
+
716
+ ```ruby
717
+ component :filewatch_processor do
718
+ desc "Automate file event responses, directing new recordings to current project folders."
719
+
720
+ pattern "Observer"
721
+
722
+ comments <<~TEXT
723
+ - Monitors new eCamm recordings file events.
724
+ - Automates the process of moving new recordings to specified project folders.
725
+ - Enhances content management by ensuring recordings are organized efficiently.
726
+ - Configurable to respond to different types of file events and target folders.
727
+ TEXT
728
+
729
+ method :watch(:folder_path, :event_type)
730
+
731
+ # Will move the file to current project's recordings folder
732
+ method :move_file
733
+
734
+ sample :automate_recording_organization, <<~RUBY
735
+ config = GlobalConfig.instance
736
+
737
+ # Watch a specific folder for new and existing recordings
738
+ filewatch_processor.watch(config.folders['ecamm-recordings'], '*.mov', :moved_file)
739
+
740
+ # Example usage
741
+ # When a new file is detected in Ecamm Live Recordings, it is automatically moved to the current project's recordings folder.
742
+ filewatch_processor.move_file
743
+ RUBY
744
+ end
745
+
746
+ ```
747
+ Klue Component: `recording_filename.klue`
748
+
749
+ ```ruby
750
+ component :recording_filename do
751
+ desc "Calculate the next video recording file name based on chapter sequence, part number, chapter name, and tags if available."
752
+
753
+ pattern "Adapter"
754
+
755
+ comments <<~TEXT
756
+ - File name construction for new and existing video recordings.
757
+ - Utilizes the chapter and part sequence for systematic naming.
758
+ - Incorporates the chapter name for descriptive clarity.
759
+ - Has support for tags in the file name to aid content identification and post-processing workflows.
760
+ - Set episode & project via the episode if recording belongs to an episode
761
+ - Set project if recording belongs to a project
762
+ - Parent returns episode || project
763
+ TEXT
764
+
765
+ accessors :chapter_seq, :part_seq, :chapter_name, :tags, :filename, :episode, :project, :parent
766
+
767
+ method :constructor(:chapter_seq, :part_seq, :chapter_name, tags: [], parent: nil)
768
+ method :instance(:full_path)
769
+
770
+ # Use the current chapter sequence if not provided
771
+ # Part sequence will increment based on the previous part letter in the chapter if not provided
772
+ # Chapter name will defaults to previous recording chapter name if not provided
773
+ method :next_filename(:previous_filename, chapter_name: nil, tags: [], chapter_seq: nil, part_seq: nil)
774
+ method :update_chapter_sequence(:current_chapter_seq, :new_chapter_seq)
775
+ method :resequence_chapters(:starting_chapter_seq)
776
+ method :update_tags(:new_tags)
777
+
778
+ sample :create_recording_filename, <<~RUBY
779
+ project_path = ProjectPath.new('a27')
780
+ project_path.project_path # => "~/video-projects/a27-ac-categorize-mp4-CI"
781
+
782
+ # Generate a filename for a new video recording
783
+ filename1 = RecordingFilename.new(1, 'a' 'introduction', tags: [], parent: project_path)
784
+ filename1.filename # => "01-a-introduction.mov"
785
+ filename1.chapter_seq # => 1
786
+ filename1.part_seq # => "a"
787
+ filename1.chapter_name # => "introduction"
788
+ filename1.tags # => []
789
+ filename1.full_path # => "~/video-projects/a27-ac-categorize-mp4-CI/01-a-introduction.mov"
790
+ RUBY
791
+
792
+ sample :next_recording_filename, <<~RUBY
793
+ filename1 = "~/video-projects/a27-ac-categorize-mp4-CI/01-a-introduction.mov"
794
+
795
+ project = ProjectConfig.new('a27')
796
+
797
+ # Class method to generate a filename for a new video recording
798
+ filename2 = RecordingFilename.next_filename(filename1, chapter_name: 'introduction-more-content', tags: ['GPTIMPROVE'])
799
+
800
+ # Fluent method to generate a filename for a new video recording
801
+ filename3 = filename2.next_filename(chapter_name: 'introduction')
802
+ filename4 = filename3.next_filename(tags: ['CTA'])
803
+
804
+ project.next_chapter('overview')
805
+
806
+ filename5 = RecordingFilename.next_filename(project.last_file)
807
+
808
+ project.next_chapter('scenario')
809
+
810
+ filename6 = RecordingFilename.next_filename(project.last_file, tags: ['TITLE'])
811
+
812
+ filename1.filename # => "01-a-introduction-GPTIMPROVE.mov" # Send transcription to GPT Bot for improvement
813
+ filename2.filename # => "01-b-introduction-more-content.mov"
814
+ filename3.filename # => "01-c-introduction.mov"
815
+ filename4.filename # => "01-d-introduction-CTA.mov" # Instruction for video editor to add a CTA
816
+ filename5.filename # => "02-a-overview.mov"
817
+ filename6.filename # => "03-a-scenario-TITLE.mov" # Instruction for video editor to add a title slide
818
+ RUBY
819
+
820
+ sample :file_to_recording_filename, <<~RUBY
821
+ # Convert absolute file to recording filename
822
+ filename = RecordingFilename.instance('~/video-projects/a27-ac-categorize-mp4-CI/01-a-introduction.mov')
823
+
824
+ filename.filename # => "01-a-introduction.mov"
825
+ filename.chapter_seq # => 1
826
+ filename.part_seq # => "a"
827
+ filename.chapter_name # => "introduction"
828
+ filename.tags # => []
829
+ filename.full_path # => "~/video-projects/a27-ac-categorize-mp4-CI/01-a-introduction.mov"
830
+ RUBY
831
+
832
+ sample :rename_recording_filename, <<~RUBY
833
+ filename = RecordingFilename.instance('~/video-projects/a27-ac-categorize-mp4-CI/01-a-introduction.mov')
834
+ filename.filename # => "01-a-introduction.mov"
835
+
836
+ filename.change_filename(chapter_name: 'introduction-trim-23s', tags: ['GPTDALE3'])
837
+ filename.filename # => "01-a-introduction-trim-23s-GPTDALE3.mov"
838
+ RUBY
839
+
840
+ sample :update_chapter_sequence, <<~RUBY
841
+ project_path = ProjectPath.new('a27')
842
+
843
+ # Update chapter sequence in a project
844
+ filename = RecordingFilename.new(2, 'b', 'overview', parent: project_path)
845
+ filename.update_chapter_sequence(2, 3)
846
+ filename.filename # => "03-b-overview.mov"
847
+ RUBY
848
+
849
+ sample :resequence_chapters, <<~RUBY
850
+ # Resequence chapters starting from a given sequence
851
+ filename.resequence_chapters(2)
852
+ # Adjusts chapter sequence of all subsequent chapters starting from '02'
853
+ RUBY
854
+
855
+ sample :update_tags, <<~RUBY
856
+ # Update tags for a recording
857
+ filename = RecordingFilename.new(1, 'a', 'introduction', tags: ['OLD_TAG'], parent: project_path)
858
+ filename.update_tags(['NEW_TAG'])
859
+ filename.filename # => "01-a-introduction-NEW_TAG.mov"
860
+ RUBY
861
+ end
862
+
863
+ ```
864
+ Klue Component: `restore_from_trash.klue`
865
+
866
+ ```ruby
867
+ component :restore_from_trash do
868
+ desc "Retrieve video takes from '.trash' folder and move them back to the project folder."
869
+
870
+ pattern "Command"
871
+
872
+ comments <<~TEXT
873
+ - Restores a specific recording from the trash to the project folder.
874
+ - If no specific recording is provided, restores the most recently trashed recording.
875
+ - Facilitates reconsideration or re-evaluation of previously discarded content.
876
+ TEXT
877
+
878
+ constructor(:parent_project)
879
+
880
+ method :run(:file_name)
881
+
882
+ sample :restore_from_trash, <<~RUBY
883
+ project_path = ProjectPath.new('a27')
884
+
885
+ command = undo_trash.new(project_path)
886
+
887
+ # Example of restoring a specific recording from the trash folder
888
+ command.run('01-a-introduction.mov')
889
+ # => "~/video-projects/a27-ac-categorize-mp4-CI/01-a-introduction.mov"
890
+
891
+ # Example of restoring the most recently trashed recording
892
+ command.run
893
+ # => "~/video-projects/a27-ac-categorize-mp4-CI/Ecamm Live Recording on 2023-08-25 at 14.51.58.mov"
894
+ RUBY
895
+ end
896
+
897
+ ```
898
+ Klue Component: `switch_focus.klue`
899
+
900
+ ```ruby
901
+ component :change_focus do
902
+ desc "Easily switch between different video projects or episodes to accommodate changing content priorities."
903
+
904
+ pattern "Interactor"
905
+
906
+ comments <<~TEXT
907
+ - Allows switching the focus to a different video project or a specific episode within a podcast.
908
+ - Video recordings belong to a specific project or episode and this interaction ensures that the correct project or episode is in focus.
909
+ - Ensures seamless transition and updates the working context to the selected project or episode.
910
+ - Maintains state consistency across switches using the global configuration.
911
+ TEXT
912
+
913
+ method :run(:project_code, episode_code: nil)
914
+
915
+ sample :switch_focus_to_video_project, <<~RUBY
916
+ # Switch focus to a different video project
917
+ change_focus.run('a20')
918
+ RUBY
919
+
920
+ sample :switch_focus_to_podcast_episode, <<~RUBY
921
+ # Switch focus to a specific episode within a podcast project
922
+ change_focus.run('a21', '02')
923
+ RUBY
924
+ end
925
+
926
+ ```
927
+ Klue Component: `text_to_speech.klue`
928
+
929
+ ```ruby
930
+ component :text_to_speech do
931
+ desc "Transcribe spoken content to text and store in multiple transcription formats."
932
+
933
+ pattern "Command"
934
+
935
+ comments <<~TEXT
936
+ - Transcribes audio content from videos into text.
937
+ - Supports storing transcriptions in various formats for accessibility and further processing.
938
+ - Uses the transcript folder under the recording, chapter or post-production folder and stores the transcriptions in a subfolder called 'transcript'.
939
+ TEXT
940
+
941
+ constructor(:parent_project)
942
+
943
+ method :run(:recording_file, output_formats:)
944
+
945
+ sample :transcribe_audio_to_text, <<~RUBY
946
+ project_path = ProjectPath.new('a27')
947
+
948
+ # ~/video-projects/a27-ac-categorize-mp4-CI/01-a-introduction.mov
949
+ # Instantiate the component with the parent project
950
+ tts = text_to_speech.new(project_path)
951
+
952
+ # Execute the transcription against
953
+ tts.run('01-introduction.mov', output_formats: ['txt', 'srt', 'vtt', 'json', 'tsv'])
954
+
955
+ # Transcribes '01-introduction.mov' and stores the transcriptions in the specified formats
956
+ # Output: Transcriptions saved in '~/video-projects/a27-ac-categorize-mp4-CI/recordings/transcript/01-introduction.txt|srt|vtt|json|tsv'
957
+ RUBY
958
+ end
959
+
960
+ ```
961
+ Klue Component: `transcript_data_store.klue`
962
+
963
+ ```ruby
964
+ component :transcript_data_store do
965
+ desc "Build a JSON datastore of transcripts for an entire project."
966
+
967
+ pattern "Command"
968
+
969
+ comments <<~TEXT
970
+ - Aggregates transcripts from various project levels into a single JSON datastore.
971
+ - Scans project, episode, recording, chapter, and post-produced folders for existing transcripts.
972
+ - Facilitates easy access and management of transcript data across the entire project.
973
+ - Enhances data retrieval and analysis capabilities for the project's transcripts.
974
+ - Stores data in a JSON file called 'transcripts.json' in the project folder.
975
+ TEXT
976
+
977
+ constructor(:parent_project)
978
+
979
+ method :run
980
+
981
+ sample :build_transcript_datastore, <<~RUBY
982
+ project_path = ProjectPath.new('a27')
983
+
984
+ # Instantiate the component with the parent project
985
+ transcript_store = transcript_data_store.new(project_path)
986
+
987
+ # Execute the process to aggregate and store transcript data
988
+ transcript_store.run
989
+ # Output: '~/video-projects/a27-ac-categorize-mp4-CI/project_transcripts.json'
990
+ RUBY
991
+ end
992
+
993
+ ```