Browse Source

Add video_content embeds

master
The Dod 1 year ago
parent
commit
7e2d4cf17b

+ 1
- 0
.gitignore View File

1
 venv/
1
 venv/
2
 __pycache__/
2
 __pycache__/
3
 archive/*.json
3
 archive/*.json
4
+editor-archive/*.json
4
 flask_session/
5
 flask_session/
5
 work/
6
 work/
6
 .env
7
 .env

+ 90
- 8
app.py View File

45
         for slide in column.get("slides", []):
45
         for slide in column.get("slides", []):
46
             slide["content"] = preprocess_content(slide)
46
             slide["content"] = preprocess_content(slide)
47
 
47
 
48
+RE_YOUTUBE = re.compile("https://(?:www.)?youtube.com/watch\?v=(?P<code>[^/?]*)")
49
+RE_YOUTU_BE = re.compile("https://(?:www.)?youtu.be/(?P<code>[^/?]*)")
50
+RE_VIMEO = re.compile("https://(?:www.)?vimeo.com/(?P<code>[^/?]*)")
51
+def preprocess_embed(e):
52
+    res = {}
53
+    url = e.get("url")
54
+    if not url:
55
+        return None
56
+    for key in ["id", "description", "proportions"]:
57
+        val = e.get(key)
58
+        if not val:
59
+            return None
60
+        res[key] = val
61
+    m = RE_YOUTUBE.search(url) or RE_YOUTU_BE.search(url)
62
+    if m:
63
+        res["type"] = "youtube"
64
+        res["code"] = m.group("code")
65
+    else:
66
+        m = RE_VIMEO.search(url)
67
+        if m:
68
+            res["type"] = "vimeo"
69
+            res["code"] = m.group("code")
70
+        else:
71
+            return None
72
+    return res
73
+
74
+def get_all_embeds():
75
+    embeds = json.load(open("static/media/video-embed.json"))
76
+    embeds = [preprocess_embed(e) for e in embeds]
77
+    embeds = [e for e in embeds if e]
78
+    return embeds
79
+
48
 def deref_schema(val):
80
 def deref_schema(val):
49
     "Deref all `$ref` entries in a json-schema"
81
     "Deref all `$ref` entries in a json-schema"
50
     if type(val)==type([]):
82
     if type(val)==type([]):
116
                 payload = json.loads(message["content"])
148
                 payload = json.loads(message["content"])
117
                 payload["prompt"] = prompt.replace('"', '\"')
149
                 payload["prompt"] = prompt.replace('"', '\"')
118
                 payload["content"] = preprocess_content(payload)
150
                 payload["content"] = preprocess_content(payload)
151
+                if payload.get("use_video_content"):
152
+                    embed_id = payload.get("video_content")
153
+                    if embed_id:
154
+                        embeds = get_all_embeds()
155
+                        embeds = [e for e in embeds if e["id"]==embed_id]
156
+                        if embeds:
157
+                            payload["embed"] = embeds[0]
119
                 session["history"] += [dict(payload)] # shallow copy so history doesn't get added later on
158
                 session["history"] += [dict(payload)] # shallow copy so history doesn't get added later on
120
             except Exception as e:
159
             except Exception as e:
121
                 print(repr(e))
160
                 print(repr(e))
122
-                session["messages"] += [{"role": "system", "content": "reply was ignored because it's not a valid json or doesn't comply with the schema. user is unaware. do not apologize to them"}]
161
+                session["messages"] += [{"role": "system", "content": "reply was ignored because it's not valid JSON. User is unaware. Do NOT apologize to them. Simply rephrase your previous answer as valid JSON that complies with the schema in the initial system prompt of this chat. It is followed by an example you can learn from."}]
123
             print("=====")
162
             print("=====")
124
             print(reply["choices"][0]["message"]["content"])
163
             print(reply["choices"][0]["message"]["content"])
125
             print("-----")
164
             print("-----")
149
 
188
 
150
 @application.get("/slides")
189
 @application.get("/slides")
151
 def slides():
190
 def slides():
152
-    "Legacy multy-slide format"
191
+    "Legacy multi-slide format"
153
     payload = json.load(open("static/slides.json"))
192
     payload = json.load(open("static/slides.json"))
154
     preprocess_payload(payload)
193
     preprocess_payload(payload)
155
     return render_template("slides.html", generate_indices=False, **payload)
194
     return render_template("slides.html", generate_indices=False, **payload)
156
 
195
 
196
+@application.get("/example")
197
+def example():
198
+    payload = json.load(open("static/chat-example.json"))
199
+    payload["content"] = preprocess_content(payload)
200
+    if payload.get("use_video_content"):
201
+        embed_id = payload.get("video_content")
202
+        if embed_id:
203
+            embeds = get_all_embeds()
204
+            embeds = [e for e in embeds if e["id"]==embed_id]
205
+            if embeds:
206
+                payload["embed"] = embeds[0]
207
+    return render_template("example.html", **payload)
208
+
209
+@application.get("/embeds")
210
+def embeds():
211
+    embeds = get_all_embeds()
212
+    return render_template("embeds.html", embeds=embeds)
213
+
157
 @application.post("/update")
214
 @application.post("/update")
158
 def update():
215
 def update():
159
-    "Legacy multy-slide format"
216
+    "Legacy multi-slide format"
160
     shutil.copy(
217
     shutil.copy(
161
         "static/slides.json",
218
         "static/slides.json",
162
         time.strftime(
219
         time.strftime(
163
-            "archive/slides-%Y-%m-%d-%H.%M.%S.json",
220
+            "editor-archive/slides-%Y-%m-%d-%H.%M.%S.json",
164
             time.localtime()))
221
             time.localtime()))
165
     payload = request.get_json()
222
     payload = request.get_json()
166
     print(type(payload))
223
     print(type(payload))
168
     json.dump(payload, open("static/slides.json", "w"), indent=4)
225
     json.dump(payload, open("static/slides.json", "w"), indent=4)
169
     return {"status": "success"}
226
     return {"status": "success"}
170
 
227
 
228
+@application.post("/update-embeds")
229
+def update_embeds():
230
+    shutil.copy(
231
+        "static/media/video-embed.json",
232
+        time.strftime(
233
+            "editor-archive/embeds-%Y-%m-%d-%H.%M.%S.json",
234
+            time.localtime()))
235
+    payload = request.get_json()
236
+    print(type(payload))
237
+    json.dump(payload, sys.stdout, indent=4)
238
+    json.dump(payload, open("static/media/video-embed.json", "w"), indent=4)
239
+    return {"status": "success"}
240
+
171
 @application.post("/update-chat")
241
 @application.post("/update-chat")
172
 def update_chat():
242
 def update_chat():
173
     shutil.copy(
243
     shutil.copy(
174
-        "static/chat.json",
244
+        "static/chat-example.json",
175
         time.strftime(
245
         time.strftime(
176
-            "archive/chat-%Y-%m-%d-%H.%M.%S.json",
246
+            "editor-archive/chat-%Y-%m-%d-%H.%M.%S.json",
177
             time.localtime()))
247
             time.localtime()))
178
     payload = request.get_json()
248
     payload = request.get_json()
179
     print(type(payload))
249
     print(type(payload))
180
     json.dump(payload, sys.stdout, indent=4)
250
     json.dump(payload, sys.stdout, indent=4)
181
-    json.dump(payload, open("static/chat.json", "w"), indent=4)
251
+    json.dump(payload, open("static/chat-example.json", "w"), indent=4)
182
     return {"status": "success"}
252
     return {"status": "success"}
183
 
253
 
184
 @application.get("/enum/<topic>")
254
 @application.get("/enum/<topic>")
199
         }
269
         }
200
     abort(404)
270
     abort(404)
201
 
271
 
272
+@application.get("/embeds_enum")
273
+def embeds_enum():
274
+    embeds = get_all_embeds()
275
+    return {
276
+        "enum": [e["id"] for e in embeds],
277
+        "options": { "enum_titles": [e["description"] for e in embeds] }
278
+    }
279
+
202
 @application.route("/save", methods=['GET', 'POST'])
280
 @application.route("/save", methods=['GET', 'POST'])
203
 def save():
281
 def save():
204
     if request.method=="POST":
282
     if request.method=="POST":
240
         return render_template("load.html",
318
         return render_template("load.html",
241
                 files = [os.path.basename(path).rsplit(".",1)[0] for path in glob("archive/*.json")])
319
                 files = [os.path.basename(path).rsplit(".",1)[0] for path in glob("archive/*.json")])
242
 
320
 
321
+@application.get("/embed-editor")
322
+def embed_editor():
323
+    return render_template("embed-editor.html")
324
+
243
 @application.get("/chat-editor")
325
 @application.get("/chat-editor")
244
 def chat_editor():
326
 def chat_editor():
245
     return render_template("chat-editor.html")
327
     return render_template("chat-editor.html")
246
 
328
 
247
 @application.get("/editor")
329
 @application.get("/editor")
248
 def editor():
330
 def editor():
249
-    "Legacy multy-slide format"
331
+    "Legacy multi-slide format"
250
     return render_template("editor.html")
332
     return render_template("editor.html")
251
 
333
 
252
 @application.route("/img", methods=['GET', 'POST'])
334
 @application.route("/img", methods=['GET', 'POST'])

+ 1
- 1
archive/README.md View File

1
-This is where we keep old versions of `slides.json`
1
+This is where we keep saved moments

+ 1
- 0
editor-archive/README.md View File

1
+This is where we keep old versions of edited json files

+ 9
- 1
static/chat-example.json View File

1
-{"title": "Stress Relief", "markdown": "I understand that stress relief could be helpful for you right now. Let's take a moment to focus on calming and relaxing activities. Here are a few suggestions:\n\n1. **Deep Breathing:** Close your eyes and take a deep breath in through your nose, filling your lungs with air. Hold for a few seconds and then exhale slowly through your mouth. Repeat this a few times, focusing on the sensation of your breath.\n\n2. **Progressive Muscle Relaxation:** Start by tensing the muscles in your toes and then gradually work your way up to your head, tensing and then releasing each muscle group. This exercise can help release tension from your body.\n\n3. **Guided Meditation:** Find a quiet and comfortable place to sit or lie down. You can search for guided meditations online or use apps that offer meditation exercises. These can help you relax and find inner peace.\n\n4. **Nature Sounds:** Consider listening to calming nature sounds, such as gentle rain, ocean waves, or bird songs. These sounds can create a soothing atmosphere and help you relax.\n\n5. **Journaling:** Take a few minutes to write down your thoughts and emotions. This can be a cathartic experience and help you process any stress or overwhelm that you may be feeling.\n\nRemember to be gentle with yourself during this time. Stress relief is important for your well-being. Let me know if there's anything specific you'd like to explore or if you have any other requests.", "bg_video": "static/media/bg-video/underwater.mp4", "use_soundtrack": true, "soundtrack": "static/media/audio/soft-rain-ambient-111154.mp3"}
1
+{
2
+    "title": "Coping with grief of others",
3
+    "markdown": "Although the movie is about grief, the message is also true for the feelings of those coping with the fact that *you* will soon be gone.\n\nAccept their feelings. Don't try to change them. As the video says:\n\n> The soul doesn't want to be advised or fixed or saved. It simply wants to be witnessed, exactly as it is.\n",
4
+    "bg_video": "static/media/bg-video/candles.mp4",
5
+    "use_soundtrack": false,
6
+    "soundtrack": "static/media/audio/evening-birds-singing-in-spring-background-sounds-of-nature-146388.mp3",
7
+    "use_video_content": true,
8
+    "video_content": "how_to_help_a_grieving_friend"
9
+}

+ 5
- 5
static/chat.json View File

1
 {
1
 {
2
-    "title": "Welcome",
3
-    "markdown": "Hello again.\n\nHow do you feel today?",
4
-    "bg_video": "static/media/bg-video/clouds1.mp4",
2
+    "title": "Stress Relief",
3
+    "markdown": "I understand that stress relief could be helpful for you right now. Let's take a moment to focus on calming and relaxing activities. Here are a few suggestions:\n\n1. **Deep Breathing:** Close your eyes and take a deep breath in through your nose, filling your lungs with air. Hold for a few seconds and then exhale slowly through your mouth. Repeat this a few times, focusing on the sensation of your breath.\n\n2. **Progressive Muscle Relaxation:** Start by tensing the muscles in your toes and then gradually work your way up to your head, tensing and then releasing each muscle group. This exercise can help release tension from your body.\n\n3. **Guided Meditation:** Find a quiet and comfortable place to sit or lie down. You can search for guided meditations online or use apps that offer meditation exercises. These can help you relax and find inner peace.\n\n4. **Nature Sounds:** Consider listening to calming nature sounds, such as gentle rain, ocean waves, or bird songs. These sounds can create a soothing atmosphere and help you relax.\n\n5. **Journaling:** Take a few minutes to write down your thoughts and emotions. This can be a cathartic experience and help you process any stress or overwhelm that you may be feeling.\n\nRemember to be gentle with yourself during this time. Stress relief is important for your well-being. Let me know if there's anything specific you'd like to explore or if you have any other requests.",
4
+    "bg_video": "static/media/bg-video/underwater.mp4",
5
     "use_soundtrack": false,
5
     "use_soundtrack": false,
6
-    "soundtrack": "static/media/audio/sandy-beach-calm-waves-water-nature-sounds-8052.mp3"
7
-}
6
+    "soundtrack": "static/media/audio/soft-rain-ambient-111154.mp3"
7
+}

+ 16
- 1
static/chat.schema.json View File

54
       "type": "string",
54
       "type": "string",
55
       "title": "Soundtrack",
55
       "title": "Soundtrack",
56
       "$ref": "/enum/audio"
56
       "$ref": "/enum/audio"
57
+    },
58
+    "use_video_content": {
59
+      "title": "Use video content",
60
+      "type": "boolean",
61
+      "format": "checkbox"
62
+    },
63
+    "video_content": {
64
+      "dependencies": {
65
+        "use_soundtrack": true
66
+      },
67
+      "type": "string",
68
+      "title": "Video content",
69
+      "$ref": "/embeds_enum"
57
     }
70
     }
58
   },
71
   },
59
   "required": [
72
   "required": [
61
     "markdown",
74
     "markdown",
62
     "bg_video",
75
     "bg_video",
63
     "use_soundtrack",
76
     "use_soundtrack",
64
-    "soundtrack"
77
+    "soundtrack",
78
+    "use_video_content",
79
+    "video_content"
65
   ]
80
   ]
66
 }
81
 }

+ 39
- 0
static/css/style.css View File

105
 	margin: 0.5rem 0;
105
 	margin: 0.5rem 0;
106
 	padding: 0.2rem 0.5rem;
106
 	padding: 0.2rem 0.5rem;
107
 }
107
 }
108
+
109
+.iframe-16x9 {
110
+    width: 48vh;
111
+    height: 27vh;
112
+    margin: 0.5rem auto;
113
+}
114
+
115
+.iframe-4x3 {
116
+    width: 36vh;
117
+    height: 27vh;
118
+    margin: 0.5rem auto;
119
+}
120
+
121
+.iframe-container-16x9,
122
+.iframe-container-4x3 {
123
+    clear: both;
124
+    position: relative;
125
+    width: 50%;
126
+    height: 0;
127
+    margin: 0 auto !important;
128
+ }
129
+
130
+.iframe-container-16x9 {
131
+     padding-bottom: 56.25%;
132
+ }
133
+
134
+.iframe-container-4x3 {
135
+     padding-bottom: 75%;
136
+}
137
+
138
+.iframe-container-16x9 iframe,
139
+.iframe-container-4x3 iframe {
140
+     position: absolute;
141
+     top: 0;
142
+     left: 0;
143
+     width: 100%;
144
+     height: 100%;
145
+ }
146
+

+ 40
- 0
static/embed.schema.json View File

1
+{
2
+    "type": "array",
3
+    "title": "Embedded videos",
4
+    "items": {
5
+        "type": "object",
6
+        "title": "Embed",
7
+        "options": {
8
+        "collapsed": "true"
9
+        },
10
+        "headerTemplate": "{{self.id}}",
11
+        "properties": {
12
+            "id": {
13
+                "type": "string"
14
+            },
15
+            "url": {
16
+                "type": "string",
17
+                "links": [
18
+                  {
19
+                    "rel": "Preview video",
20
+                    "class": "link-info",
21
+                    "href": "{{self}}"
22
+                  }
23
+                ]
24
+            },
25
+            "proportions": {
26
+                "type": "string",
27
+                "enum": [ "16x9", "4x3" ]
28
+            },
29
+            "description": {
30
+                "type": "string"
31
+            }
32
+        },
33
+        "required": [
34
+            "id",
35
+            "url",
36
+            "proportions",
37
+            "description"
38
+        ]
39
+    }
40
+}

+ 4
- 2
static/initial-chat.json View File

1
 {
1
 {
2
     "title": "Welcome",
2
     "title": "Welcome",
3
-    "markdown": "New ceremony.\n\nPlease hit enter when ready.",
3
+    "markdown": "Are you ready to begin?",
4
     "bg_video": "static/media/bg-video/clouds1.mp4",
4
     "bg_video": "static/media/bg-video/clouds1.mp4",
5
     "use_soundtrack": false,
5
     "use_soundtrack": false,
6
-    "soundtrack": "static/media/audio/forest-with-small-river-birds-and-nature-field-recording-6735.mp3"
6
+    "soundtrack": "static/media/audio/evening-birds-singing-in-spring-background-sounds-of-nature-146388.mp3",
7
+    "use_video_content": false,
8
+    "video_content": "how_to_help_a_grieving_friend"
7
 }
9
 }

+ 26
- 0
static/media/video-embed.json View File

1
+[
2
+    {
3
+        "id": "the_evening_thread",
4
+        "url": "https://vimeo.com/349941340",
5
+        "proportions": "4x3",
6
+        "description": "old lady departing from her daughter or granddaughter, preparing together a small box of memories, she\u2019s flying away as an angel after handing her legacy to the young"
7
+    },
8
+    {
9
+        "id": "sesame_street_when_families_grieve",
10
+        "url": "https://www.youtube.com/watch?v=s8M_5_JxY7k",
11
+        "proportions": "4x3",
12
+        "description": "puppets. talking to children about death. accept our own emotions without judgement"
13
+    },
14
+    {
15
+        "id": "coping_with_grief",
16
+        "url": "https://www.youtube.com/watch?v=gsYL4PC0hyk",
17
+        "proportions": "16x9",
18
+        "description": "coping with death, stages, rollercoaster for better or worse, it\u2019s natural, don't be afraid of the pain"
19
+    },
20
+    {
21
+        "id": "how_to_help_a_grieving_friend",
22
+        "url": "https://www.youtube.com/watch?v=l2zLCCRT-nE",
23
+        "proportions": "16x9",
24
+        "description": "the soul doesn't want to be advised or fixed or saved. it simply wants to be witnessed, exactly as it is"
25
+    }
26
+]

+ 3
- 3
templates/chat-editor.html View File

18
       <div id='editor_holder'></div>
18
       <div id='editor_holder'></div>
19
       <div class="btn-group mt-2">
19
       <div class="btn-group mt-2">
20
         <button class="btn btn-success" id='submit'>Submit</button>
20
         <button class="btn btn-success" id='submit'>Submit</button>
21
-        <a class="btn btn-secondary" href="/slides">Cancel</a>
21
+        <a class="btn btn-secondary" href="/example">Cancel</a>
22
       </div>
22
       </div>
23
     </div>
23
     </div>
24
     <script src="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.js"></script>
24
     <script src="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.js"></script>
28
       fetch("/static/chat.schema.json")
28
       fetch("/static/chat.schema.json")
29
         .then(res => res.json())
29
         .then(res => res.json())
30
         .then(schema => {
30
         .then(schema => {
31
-          fetch("/static/chat.json")
31
+          fetch("/static/chat-example.json")
32
             .then(res => res.json())
32
             .then(res => res.json())
33
             .then(slides => {
33
             .then(slides => {
34
               // Initialize the editor with a JSON schema
34
               // Initialize the editor with a JSON schema
49
                   headers: {
49
                   headers: {
50
                       "Content-type": "application/json; charset=UTF-8"
50
                       "Content-type": "application/json; charset=UTF-8"
51
                   }
51
                   }
52
-                }).then((response) => { location.replace('/') });
52
+                }).then((response) => { location.replace('/example') });
53
               });
53
               });
54
             });
54
             });
55
           });
55
           });

+ 15
- 1
templates/chat.html View File

41
                                     <section id="latest" data-background-size="cover" data-background-video="{{bg_video}}" data-background-opacity="0.75" data-background-video-loop="True" data-background-video-mute="True">
41
                                     <section id="latest" data-background-size="cover" data-background-video="{{bg_video}}" data-background-opacity="0.75" data-background-video-loop="True" data-background-video-mute="True">
42
                                         <h3 class="title slide_title">{{title}}</h3>
42
                                         <h3 class="title slide_title">{{title}}</h3>
43
                                         <div class="scrollable">
43
                                         <div class="scrollable">
44
+                                            {% if embed %}
45
+                                                {% if embed.type == "youtube" %}
46
+                                                    <iframe class="iframe-{{embed.proportions}}" src="https://www.youtube.com/embed/{{ embed.code }}" allowfullscreen="allowfullscreen"></iframe>
47
+                                                {% elif embed.type == "vimeo" %}
48
+                                                    <iframe class="iframe-{{embed.proportions}}" src="https://player.vimeo.com/video/{{ embed.code }}" allowfullscreen="allowfullscreen"></iframe>
49
+                                                {% endif %}
50
+                                            {% endif %}
44
                                             {{ content|safe }}
51
                                             {{ content|safe }}
45
                                         </div>
52
                                         </div>
46
                                         <form method="POST" action=""
53
                                         <form method="POST" action=""
55
 					        <input class="prompt" value="{{moment.prompt}}" disabled />
62
 					        <input class="prompt" value="{{moment.prompt}}" disabled />
56
                                             {% endif %}
63
                                             {% endif %}
57
                                             <div class="scrollable">
64
                                             <div class="scrollable">
58
-						    {{ moment.content|safe }}
65
+                                                {% if moment.embed %}
66
+                                                    {% if moment.embed.type == "youtube" %}
67
+                                                        <iframe class="iframe-{{moment.embed.proportions}}" src="https://www.youtube.com/embed/{{ moment.embed.code }}" allowfullscreen="allowfullscreen"></iframe>
68
+                                                    {% elif moment.embed.type == "vimeo" %}
69
+                                                        <iframe class="iframe-{{moment.embed.proportions}}" src="https://player.vimeo.com/video/{{ moment.embed.code }}" allowfullscreen="allowfullscreen"></iframe>
70
+                                                    {% endif %}
71
+                                                {% endif %}
72
+						{{ moment.content|safe }}
59
                                             </div>
73
                                             </div>
60
                                         </section>
74
                                         </section>
61
 				    {% endfor %}
75
 				    {% endfor %}

+ 59
- 0
templates/embed-editor.html View File

1
+<!doctype html>
2
+<html lang="en">
3
+  <head>
4
+    <meta charset="utf-8">
5
+    <meta name="viewport" content="width=device-width, initial-scale=1">
6
+    <title>Embed collection editor</title>
7
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
8
+    <link rel="stylesheet" href=
9
+        "https://use.fontawesome.com/releases/v5.6.3/css/all.css"
10
+        integrity=
11
+            "sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/"
12
+            crossorigin="anonymous">
13
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.css">
14
+  </head>
15
+  <body>
16
+    <div class="container">
17
+      <h1>Embed editor</h1>
18
+      <div id='editor_holder'></div>
19
+      <div class="btn-group mt-2">
20
+        <button class="btn btn-success" id='submit'>Submit</button>
21
+        <a class="btn btn-secondary" href="/embeds">Cancel</a>
22
+      </div>
23
+    </div>
24
+    <script src="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.js"></script>
25
+    <script src="https://cdn.jsdelivr.net/npm/@json-editor/json-editor@latest/dist/jsoneditor.min.js"></script>
26
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
27
+    <script>
28
+      fetch("/static/embed.schema.json")
29
+        .then(res => res.json())
30
+        .then(schema => {
31
+          console.log(schema)
32
+          fetch("/static/media/video-embed.json")
33
+            .then(res => res.json())
34
+            .then(embeds => {
35
+              // Initialize the editor with a JSON schema
36
+              var editor = new JSONEditor(
37
+                document.getElementById('editor_holder'), {
38
+                  theme: "bootstrap5",
39
+                  iconlib: "fontawesome5",
40
+                  ajax: true,
41
+                  schema: schema,
42
+                  startval: embeds
43
+                });
44
+              // Hook up the submit button to log to the console
45
+              document.getElementById('submit').addEventListener('click',() => {
46
+                // Get the value from the editor
47
+                fetch("/update-embeds", {
48
+                  method: "POST",
49
+                  body: JSON.stringify(editor.getValue()),
50
+                  headers: {
51
+                      "Content-type": "application/json; charset=UTF-8"
52
+                  }
53
+                }).then((response) => { location.replace('/embeds') });
54
+              });
55
+            });
56
+          });
57
+    </script>
58
+  </body>
59
+</html>

+ 102
- 0
templates/embeds.html View File

1
+<!doctype html>
2
+<html lang="en">
3
+	<head>
4
+		<meta charset="utf-8">
5
+		<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
+
7
+		<title>Embed library</title>
8
+
9
+		<link rel="stylesheet" href="static/css/style.css" />
10
+		<link rel="stylesheet" href="static/reveal/dist/reset.css" />
11
+		<link rel="stylesheet" href="static/reveal/dist/reveal.css" />
12
+		<link id="theme" rel="stylesheet" href="static/reveal/dist/theme/sky.css" />
13
+		<link id="nav-theme" rel="stylesheet" href="static/css/nav/sky.css" />
14
+                <link rel="stylesheet" href=
15
+                    "https://use.fontawesome.com/releases/v5.6.3/css/all.css"
16
+                    integrity=
17
+                        "sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/"
18
+                        crossorigin="anonymous">
19
+	</head>
20
+	<body>
21
+                <div id="custom-nav">
22
+                  <!-- the zero-width-space is a tweak against tidy removing empty tags -->
23
+                  <a href="/embed-editor" title="Edit"><i class="fa fa-user-edit">​</i></a>
24
+                </div>
25
+		<div class="reveal">
26
+			<div class="slides">
27
+                          {% with messages = get_flashed_messages() %}
28
+                            {% if messages %}
29
+				<section data-theme="solarized">
30
+                                    {% for m in messages %}
31
+                                      <h4>{{ m }}</h4>
32
+                                    {% endfor %}
33
+                                  <a href="#/latest">Continue...</a>
34
+                                </section>
35
+                            {% endif %}
36
+                          {% endwith %}
37
+				<section data-theme="sky">
38
+				    {% for embed in embeds %}
39
+				        <section id="{{embed.id}}">
40
+					    <h3 class="title slide_title">{{embed.id}}</h3>
41
+                                            <div class="scrollable">
42
+                                                {% if embed.type == "youtube" %}
43
+                                                    <iframe class="iframe-{{embed.proportions}}" src="https://www.youtube.com/embed/{{ embed.code }}" allowfullscreen="allowfullscreen"></iframe>
44
+                                                {% elif embed.type == "vimeo" %}
45
+                                                    <iframe class="iframe-{{embed.proportions}}" src="https://player.vimeo.com/video/{{ embed.code }}" allowfullscreen="allowfullscreen"></iframe>
46
+                                                {% endif %}
47
+					        <p>{{ embed.description }}</p>
48
+                                            </div>
49
+                                        </section>
50
+				    {% endfor %}
51
+				</section>
52
+			</div>
53
+		</div>
54
+		<script src="/static/reveal/dist/reveal.js"></script>
55
+		<script src="/static/reveal/plugin/notes/notes.js"></script>
56
+		<script src="/static/reveal/plugin/markdown/markdown.js"></script>
57
+		<script src="/static/reveal/plugin/highlight/highlight.js"></script>
58
+                <script src="/static/js/jquery-3.7.1.min.js"></script> 
59
+		<script>
60
+			// More info about initialization & config:
61
+			// - https://revealjs.com/initialization/
62
+			// - https://revealjs.com/config/
63
+			Reveal.initialize({
64
+				hash: true,
65
+                                progress: false,
66
+                                help: false,
67
+                                transition: 'convex',
68
+                                controlsBackArrows: 'visible',  // would hopefully solve the faded-arrow thing
69
+                                // slideNumber: "h.v",
70
+				// Learn about plugins: https://revealjs.com/plugins/
71
+				plugins: [ RevealMarkdown, RevealHighlight, RevealNotes ]
72
+			});
73
+                        $(function() {
74
+                            // force autoplay for bg videos (but why?!?)
75
+                            $('.reveal .slide-background-content video').each((index, element) => element.play());
76
+                            // open external links in new tab
77
+                            $('.reveal a:not([href^="#"])').attr('target','_blank');
78
+                            // extract css classes from img alt
79
+                            $('.reveal img').addClass("w-50 centered");
80
+			    // make all headers max-size
81
+                            // $('.reveal h1, .reveal h2, .reveal h3').addClass('r-fit-text');
82
+                            var enterSlide = function() {
83
+                                // set theme
84
+                                var the_slide=$(Reveal.getCurrentSlide()),
85
+                                    the_theme=the_slide.data('theme') ||
86
+                                        the_slide.parent().data('theme') || 'sky';
87
+                                $('#theme').attr('href',`static/reveal/dist/theme/${the_theme}.css`);
88
+                                $('#nav-theme').attr('href',`static/css/nav/${the_theme}.css`);
89
+                                // set (or hide) top link
90
+                                var here = Reveal.getIndices();
91
+                                if (here.v) {
92
+                                    $('#top-link').removeAttr('disabled').attr('href', `#/${here.h}`);
93
+                                } else {
94
+                                    $('#top-link').removeAttr('href').attr('disabled', 'disabled');
95
+                                }
96
+                            };
97
+                            enterSlide();
98
+                            Reveal.addEventListener('slidechanged', enterSlide);
99
+                        });
100
+		</script>
101
+	</body>
102
+</html>

+ 124
- 0
templates/example.html View File

1
+<!doctype html>
2
+<html lang="en">
3
+	<head>
4
+		<meta charset="utf-8">
5
+		<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
+
7
+		<title>{{title}}</title>
8
+
9
+		<link rel="stylesheet" href="static/css/style.css" />
10
+		<link rel="stylesheet" href="static/reveal/dist/reset.css" />
11
+		<link rel="stylesheet" href="static/reveal/dist/reveal.css" />
12
+		<link id="theme" rel="stylesheet" href="static/reveal/dist/theme/sky.css" />
13
+		<link id="nav-theme" rel="stylesheet" href="static/css/nav/sky.css" />
14
+                <link rel="stylesheet" href=
15
+                    "https://use.fontawesome.com/releases/v5.6.3/css/all.css"
16
+                    integrity=
17
+                        "sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/"
18
+                        crossorigin="anonymous">
19
+	</head>
20
+	<body>
21
+                <div id="custom-nav">
22
+                  <!-- the zero-width-space is a tweak against tidy removing empty tags -->
23
+                  <a href="/chat-editor" title="Edit"><i class="fa fa-user-edit">​</i></a>
24
+                </div>
25
+		<div class="reveal">
26
+			<div class="slides">
27
+                          {% with messages = get_flashed_messages() %}
28
+                            {% if messages %}
29
+				<section data-theme="solarized">
30
+                                    {% for m in messages %}
31
+                                      <h4>{{ m }}</h4>
32
+                                    {% endfor %}
33
+                                  <a href="#/latest">Continue...</a>
34
+                                </section>
35
+                            {% endif %}
36
+                          {% endwith %}
37
+				<section data-theme="sky">
38
+                                    <section id="latest" data-background-size="cover" data-background-video="{{bg_video}}" data-background-opacity="0.75" data-background-video-loop="True" data-background-video-mute="True">
39
+                                        <h3 class="title slide_title">{{title}}</h3>
40
+                                        <div class="scrollable">
41
+                                            {% if embed %}
42
+                                                {% if embed.type == "youtube" %}
43
+                                                    <iframe class="iframe-{{embed.proportions}}" src="https://www.youtube.com/embed/{{ embed.code }}" allowfullscreen="allowfullscreen"></iframe>
44
+                                                {% elif embed.type == "vimeo" %}
45
+                                                    <iframe class="iframe-{{embed.proportions}}" src="https://player.vimeo.com/video/{{ embed.code }}" allowfullscreen="allowfullscreen"></iframe>
46
+                                                {% endif %}
47
+                                            {% endif %}
48
+                                            {{ content|safe }}
49
+                                        </div>
50
+                                        <form method="POST" action=""
51
+                                            onsubmit='setTimeout(() => { document.getElementById("prompt").setAttribute("disabled", ""); }, 100)'>
52
+                                            <input class="prompt" id="prompt" name="prompt" placeholder="Talk to me" />
53
+                                        </form>
54
+                                    </section>
55
+				    {% for moment in history %}
56
+				    <section {% if loop.last %}id="oldest" {% endif %}data-background-size="cover" data-background-video="{{moment.bg_video}}" data-background-opacity="0.75" data-background-video-loop="True" data-background-video-mute="True">
57
+					    <h3 class="title slide_title">{{moment.title}}</h3>
58
+                                            {% if moment.prompt %}
59
+					        <input class="prompt" value="{{moment.prompt}}" disabled />
60
+                                            {% endif %}
61
+                                            <div class="scrollable">
62
+                                                {% if moment.embed %}
63
+                                                    {% if moment.embed.type == "youtube" %}
64
+                                                        <iframe class="iframe-{{moment.embed.proportions}}" src="https://www.youtube.com/embed/{{ moment.embed.code }}" allowfullscreen="allowfullscreen"></iframe>
65
+                                                    {% elif moment.embed.type == "vimeo" %}
66
+                                                        <iframe class="iframe-{{moment.embed.proportions}}" src="https://player.vimeo.com/video/{{ moment.embed.code }}" allowfullscreen="allowfullscreen"></iframe>
67
+                                                    {% endif %}
68
+                                                {% endif %}
69
+						{{ moment.content|safe }}
70
+                                            </div>
71
+                                        </section>
72
+				    {% endfor %}
73
+				</section>
74
+			</div>
75
+		</div>
76
+		<script src="/static/reveal/dist/reveal.js"></script>
77
+		<script src="/static/reveal/plugin/notes/notes.js"></script>
78
+		<script src="/static/reveal/plugin/markdown/markdown.js"></script>
79
+		<script src="/static/reveal/plugin/highlight/highlight.js"></script>
80
+                <script src="/static/js/jquery-3.7.1.min.js"></script> 
81
+		<script>
82
+			// More info about initialization & config:
83
+			// - https://revealjs.com/initialization/
84
+			// - https://revealjs.com/config/
85
+			Reveal.initialize({
86
+				hash: true,
87
+                                progress: false,
88
+                                help: false,
89
+                                transition: 'convex',
90
+                                controlsBackArrows: 'visible',  // would hopefully solve the faded-arrow thing
91
+                                // slideNumber: "h.v",
92
+				// Learn about plugins: https://revealjs.com/plugins/
93
+				plugins: [ RevealMarkdown, RevealHighlight, RevealNotes ]
94
+			});
95
+                        $(function() {
96
+                            // force autoplay for bg videos (but why?!?)
97
+                            $('.reveal .slide-background-content video').each((index, element) => element.play());
98
+                            // open external links in new tab
99
+                            $('.reveal a:not([href^="#"])').attr('target','_blank');
100
+                            // extract css classes from img alt
101
+                            $('.reveal img').addClass("w-50 centered");
102
+			    // make all headers max-size
103
+                            // $('.reveal h1, .reveal h2, .reveal h3').addClass('r-fit-text');
104
+                            var enterSlide = function() {
105
+                                // set theme
106
+                                var the_slide=$(Reveal.getCurrentSlide()),
107
+                                    the_theme=the_slide.data('theme') ||
108
+                                        the_slide.parent().data('theme') || 'sky';
109
+                                $('#theme').attr('href',`static/reveal/dist/theme/${the_theme}.css`);
110
+                                $('#nav-theme').attr('href',`static/css/nav/${the_theme}.css`);
111
+                                // set (or hide) top link
112
+                                var here = Reveal.getIndices();
113
+                                if (here.v) {
114
+                                    $('#top-link').removeAttr('disabled').attr('href', `#/${here.h}`);
115
+                                } else {
116
+                                    $('#top-link').removeAttr('href').attr('disabled', 'disabled');
117
+                                }
118
+                            };
119
+                            enterSlide();
120
+                            Reveal.addEventListener('slidechanged', enterSlide);
121
+                        });
122
+		</script>
123
+	</body>
124
+</html>

+ 6
- 4
templates/prompt.txt View File

4
 
4
 
5
 The goal of your session is to create ceremonies or enchanted moments via the multimedia resources available to you (see below), but first you should inquire how the user feels now and keep monitoring the changes in their emotions. this can help you decide the media that could suit the moment.
5
 The goal of your session is to create ceremonies or enchanted moments via the multimedia resources available to you (see below), but first you should inquire how the user feels now and keep monitoring the changes in their emotions. this can help you decide the media that could suit the moment.
6
 
6
 
7
-Your responses should be JSON that is valid according to this json-schema
7
+Your responses should be JSON (NEVER FORGET THIS!!!!!) that is valid according to this json-schema
8
 
8
 
9
 ```
9
 ```
10
 {{schema}}
10
 {{schema}}
15
 {{example}}
15
 {{example}}
16
 ```
16
 ```
17
 
17
 
18
-Try to pick bg_video and [optionally] soundtrack that fit the conversation topic and is beneficial to the user emotionally. Do not bore the user by using the same values for too long.
18
+Try to pick bg_video and [optionally] soundtrack or video_content that fit the conversation topic and is beneficial to the user emotionally. Do not bore the user by using the same values for too long.
19
 
19
 
20
-You can also incorporate images inside the json in the format ![alt](src) but SRC should be a valid option according to the following enum (as alt you should use the corresponding value at options/enum_titles)
20
+Unlike bg_video, any *video_content* value can only be used ONCE in a session. If you run out of options (or don't find anything appropriate for the situation), simply set use_video_content to false
21
+
22
+You can also incorporate images inside the json in the format ![alt](src) but SRC should be a valid option according to the following enum (you can use the corresponding enum_titles value as alt)
21
 
23
 
22
 ```
24
 ```
23
 {{img_enum}}
25
 {{img_enum}}
25
 
27
 
26
 IMPORTANT!
28
 IMPORTANT!
27
 
29
 
28
-1. Your response MUST be valid JSON: linebreaks should be escaped as \n, quotes as \", the [escaped] "markdown" value should include \n\n when you expect a paragraph break, etc.
30
+1. As we said above, your response MUST be valid JSON!!!!! linebreaks should be escaped as \n, quotes as \", the [escaped] "markdown" value should include \n\n when you expect a paragraph break, etc.
29
 
31
 
30
 2. It should also be valid according to the json-schema definition above:
32
 2. It should also be valid according to the json-schema definition above:
31
 
33
 

Loading…
Cancel
Save