Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
Commit
·
8d1d1b5
1
Parent(s):
cc8c83c
debugigng the simulation loop
Browse files- assets/config/tikslop.yaml +5 -2
- build/web/assets/assets/config/tikslop.yaml +5 -2
- build/web/flutter_bootstrap.js +1 -1
- build/web/flutter_service_worker.js +3 -3
- build/web/index.html +1 -1
- build/web/main.dart.js +0 -0
- lib/config/config.dart +3 -0
- lib/screens/settings_screen.dart +47 -0
- lib/screens/video_screen.dart +99 -9
- lib/services/clip_queue/clip_queue_manager.dart +57 -8
- lib/services/settings_service.dart +16 -0
- lib/services/websocket_api_service.dart +10 -2
- lib/widgets/ad_banner.dart +2 -2
- lib/widgets/video_player/video_player_widget.dart +34 -0
assets/config/tikslop.yaml
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
ui:
|
2 |
product_name: "#tikslop"
|
3 |
-
showChatInVideoView:
|
4 |
|
5 |
render_queue:
|
6 |
# how many clips should be stored in advance
|
@@ -21,9 +21,12 @@ advertising:
|
|
21 |
link: https://huggingface.co/docs/smolagents/index
|
22 |
|
23 |
simulation:
|
|
|
|
|
|
|
24 |
# how often the description should evolve (in seconds)
|
25 |
# setting to 0 disables description evolution
|
26 |
-
sim_loop_frequency_in_sec:
|
27 |
|
28 |
# it's OK to use high values here,
|
29 |
# because some of those values are limited by the backend config,
|
|
|
1 |
ui:
|
2 |
product_name: "#tikslop"
|
3 |
+
showChatInVideoView: true
|
4 |
|
5 |
render_queue:
|
6 |
# how many clips should be stored in advance
|
|
|
21 |
link: https://huggingface.co/docs/smolagents/index
|
22 |
|
23 |
simulation:
|
24 |
+
# whether to enable simulation loop to evolve descriptions over time
|
25 |
+
enable_sim_loop: true
|
26 |
+
|
27 |
# how often the description should evolve (in seconds)
|
28 |
# setting to 0 disables description evolution
|
29 |
+
sim_loop_frequency_in_sec: 8
|
30 |
|
31 |
# it's OK to use high values here,
|
32 |
# because some of those values are limited by the backend config,
|
build/web/assets/assets/config/tikslop.yaml
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
ui:
|
2 |
product_name: "#tikslop"
|
3 |
-
showChatInVideoView:
|
4 |
|
5 |
render_queue:
|
6 |
# how many clips should be stored in advance
|
@@ -21,9 +21,12 @@ advertising:
|
|
21 |
link: https://huggingface.co/docs/smolagents/index
|
22 |
|
23 |
simulation:
|
|
|
|
|
|
|
24 |
# how often the description should evolve (in seconds)
|
25 |
# setting to 0 disables description evolution
|
26 |
-
sim_loop_frequency_in_sec:
|
27 |
|
28 |
# it's OK to use high values here,
|
29 |
# because some of those values are limited by the backend config,
|
|
|
1 |
ui:
|
2 |
product_name: "#tikslop"
|
3 |
+
showChatInVideoView: true
|
4 |
|
5 |
render_queue:
|
6 |
# how many clips should be stored in advance
|
|
|
21 |
link: https://huggingface.co/docs/smolagents/index
|
22 |
|
23 |
simulation:
|
24 |
+
# whether to enable simulation loop to evolve descriptions over time
|
25 |
+
enable_sim_loop: true
|
26 |
+
|
27 |
# how often the description should evolve (in seconds)
|
28 |
# setting to 0 disables description evolution
|
29 |
+
sim_loop_frequency_in_sec: 8
|
30 |
|
31 |
# it's OK to use high values here,
|
32 |
# because some of those values are limited by the backend config,
|
build/web/flutter_bootstrap.js
CHANGED
@@ -39,6 +39,6 @@ _flutter.buildConfig = {"engineRevision":"382be0028d370607f76215a9be322e5514b263
|
|
39 |
|
40 |
_flutter.loader.load({
|
41 |
serviceWorkerSettings: {
|
42 |
-
serviceWorkerVersion: "
|
43 |
}
|
44 |
});
|
|
|
39 |
|
40 |
_flutter.loader.load({
|
41 |
serviceWorkerSettings: {
|
42 |
+
serviceWorkerVersion: "2278791886"
|
43 |
}
|
44 |
});
|
build/web/flutter_service_worker.js
CHANGED
@@ -3,12 +3,12 @@ const MANIFEST = 'flutter-app-manifest';
|
|
3 |
const TEMP = 'flutter-temp-cache';
|
4 |
const CACHE_NAME = 'flutter-app-cache';
|
5 |
|
6 |
-
const RESOURCES = {"flutter_bootstrap.js": "
|
7 |
"version.json": "68350cac7987de2728345c72918dd067",
|
8 |
"tikslop.png": "570e1db759046e2d224fef729983634e",
|
9 |
"index.html": "3a7029b3672560e7938aab6fa4d30a46",
|
10 |
"/": "3a7029b3672560e7938aab6fa4d30a46",
|
11 |
-
"main.dart.js": "
|
12 |
"tikslop.svg": "26140ba0d153b213b122bc6ebcc17f6c",
|
13 |
"flutter.js": "83d881c1dbb6d6bcd6b42e274605b69c",
|
14 |
"favicon.png": "c8a183c516004e648a7bac7497c89b97",
|
@@ -31,7 +31,7 @@ const RESOURCES = {"flutter_bootstrap.js": "04ff3a8576d911a2f01466a30986bd48",
|
|
31 |
"assets/assets/config/README.md": "07a87720dd00dd1ca98c9d6884440e31",
|
32 |
"assets/assets/config/custom.yaml": "52bd30aa4d8b980626a5eb02d0871c01",
|
33 |
"assets/assets/config/default.yaml": "9ca1d05d06721c2b6f6382a1ba40af48",
|
34 |
-
"assets/assets/config/tikslop.yaml": "
|
35 |
"canvaskit/skwasm.js": "ea559890a088fe28b4ddf70e17e60052",
|
36 |
"canvaskit/skwasm.js.symbols": "9fe690d47b904d72c7d020bd303adf16",
|
37 |
"canvaskit/canvaskit.js.symbols": "27361387bc24144b46a745f1afe92b50",
|
|
|
3 |
const TEMP = 'flutter-temp-cache';
|
4 |
const CACHE_NAME = 'flutter-app-cache';
|
5 |
|
6 |
+
const RESOURCES = {"flutter_bootstrap.js": "c711e457cccbe0bcbccc12f740ac44af",
|
7 |
"version.json": "68350cac7987de2728345c72918dd067",
|
8 |
"tikslop.png": "570e1db759046e2d224fef729983634e",
|
9 |
"index.html": "3a7029b3672560e7938aab6fa4d30a46",
|
10 |
"/": "3a7029b3672560e7938aab6fa4d30a46",
|
11 |
+
"main.dart.js": "494e091a049a511f985f81a61cedf669",
|
12 |
"tikslop.svg": "26140ba0d153b213b122bc6ebcc17f6c",
|
13 |
"flutter.js": "83d881c1dbb6d6bcd6b42e274605b69c",
|
14 |
"favicon.png": "c8a183c516004e648a7bac7497c89b97",
|
|
|
31 |
"assets/assets/config/README.md": "07a87720dd00dd1ca98c9d6884440e31",
|
32 |
"assets/assets/config/custom.yaml": "52bd30aa4d8b980626a5eb02d0871c01",
|
33 |
"assets/assets/config/default.yaml": "9ca1d05d06721c2b6f6382a1ba40af48",
|
34 |
+
"assets/assets/config/tikslop.yaml": "45d535b24df6e81ce983bf4f550c9be7",
|
35 |
"canvaskit/skwasm.js": "ea559890a088fe28b4ddf70e17e60052",
|
36 |
"canvaskit/skwasm.js.symbols": "9fe690d47b904d72c7d020bd303adf16",
|
37 |
"canvaskit/canvaskit.js.symbols": "27361387bc24144b46a745f1afe92b50",
|
build/web/index.html
CHANGED
@@ -156,7 +156,7 @@
|
|
156 |
</script>
|
157 |
|
158 |
<!-- Add version parameter for cache busting -->
|
159 |
-
<script src="flutter_bootstrap.js?v=
|
160 |
|
161 |
<!-- Add cache busting script -->
|
162 |
<script>
|
|
|
156 |
</script>
|
157 |
|
158 |
<!-- Add version parameter for cache busting -->
|
159 |
+
<script src="flutter_bootstrap.js?v=1747395579" async></script>
|
160 |
|
161 |
<!-- Add cache busting script -->
|
162 |
<script>
|
build/web/main.dart.js
CHANGED
The diff for this file is too large to render.
See raw diff
|
|
lib/config/config.dart
CHANGED
@@ -129,6 +129,9 @@ class Configuration {
|
|
129 |
_config['video']['default_negative_prompt'] ?? 'captions, subtitles, logo, text, watermark, low quality, worst quality, gore, sex, blood, nudity, nude, porn, erotic';
|
130 |
|
131 |
// Simulation settings
|
|
|
|
|
|
|
132 |
int get simLoopFrequencyInSec =>
|
133 |
_config['simulation']?['sim_loop_frequency_in_sec'] ?? 0;
|
134 |
|
|
|
129 |
_config['video']['default_negative_prompt'] ?? 'captions, subtitles, logo, text, watermark, low quality, worst quality, gore, sex, blood, nudity, nude, porn, erotic';
|
130 |
|
131 |
// Simulation settings
|
132 |
+
bool get enableSimLoop =>
|
133 |
+
_config['simulation']?['enable_sim_loop'] ?? true;
|
134 |
+
|
135 |
int get simLoopFrequencyInSec =>
|
136 |
_config['simulation']?['sim_loop_frequency_in_sec'] ?? 0;
|
137 |
|
lib/screens/settings_screen.dart
CHANGED
@@ -15,6 +15,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|
15 |
final _negativePromptController = TextEditingController();
|
16 |
final _hfApiKeyController = TextEditingController();
|
17 |
final _settingsService = SettingsService();
|
|
|
|
|
18 |
|
19 |
@override
|
20 |
void initState() {
|
@@ -22,6 +24,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|
22 |
_promptController.text = _settingsService.videoPromptPrefix;
|
23 |
_negativePromptController.text = _settingsService.negativeVideoPrompt;
|
24 |
_hfApiKeyController.text = _settingsService.huggingfaceApiKey;
|
|
|
|
|
25 |
}
|
26 |
|
27 |
@override
|
@@ -161,6 +165,49 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|
161 |
),
|
162 |
),
|
163 |
const SizedBox(height: 16),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
164 |
// Custom Video Model Card
|
165 |
Card(
|
166 |
child: Padding(
|
|
|
15 |
final _negativePromptController = TextEditingController();
|
16 |
final _hfApiKeyController = TextEditingController();
|
17 |
final _settingsService = SettingsService();
|
18 |
+
bool _showSceneDebugInfo = false;
|
19 |
+
bool _enableSimulation = true;
|
20 |
|
21 |
@override
|
22 |
void initState() {
|
|
|
24 |
_promptController.text = _settingsService.videoPromptPrefix;
|
25 |
_negativePromptController.text = _settingsService.negativeVideoPrompt;
|
26 |
_hfApiKeyController.text = _settingsService.huggingfaceApiKey;
|
27 |
+
_showSceneDebugInfo = _settingsService.showSceneDebugInfo;
|
28 |
+
_enableSimulation = _settingsService.enableSimulation;
|
29 |
}
|
30 |
|
31 |
@override
|
|
|
165 |
),
|
166 |
),
|
167 |
const SizedBox(height: 16),
|
168 |
+
// Display Options Card
|
169 |
+
Card(
|
170 |
+
child: Padding(
|
171 |
+
padding: const EdgeInsets.all(16),
|
172 |
+
child: Column(
|
173 |
+
crossAxisAlignment: CrossAxisAlignment.start,
|
174 |
+
children: [
|
175 |
+
const Text(
|
176 |
+
'Display Options',
|
177 |
+
style: TextStyle(
|
178 |
+
color: TikSlopColors.onBackground,
|
179 |
+
fontSize: 20,
|
180 |
+
fontWeight: FontWeight.bold,
|
181 |
+
),
|
182 |
+
),
|
183 |
+
const SizedBox(height: 16),
|
184 |
+
SwitchListTile(
|
185 |
+
title: const Text('Show scene debug information'),
|
186 |
+
subtitle: const Text('Display initial, current, and last description in video view'),
|
187 |
+
value: _showSceneDebugInfo,
|
188 |
+
onChanged: (value) {
|
189 |
+
setState(() {
|
190 |
+
_showSceneDebugInfo = value;
|
191 |
+
});
|
192 |
+
_settingsService.setShowSceneDebugInfo(value);
|
193 |
+
},
|
194 |
+
),
|
195 |
+
SwitchListTile(
|
196 |
+
title: const Text('Enable simulation'),
|
197 |
+
subtitle: const Text('Allow video descriptions to evolve over time'),
|
198 |
+
value: _enableSimulation,
|
199 |
+
onChanged: (value) {
|
200 |
+
setState(() {
|
201 |
+
_enableSimulation = value;
|
202 |
+
});
|
203 |
+
_settingsService.setEnableSimulation(value);
|
204 |
+
},
|
205 |
+
),
|
206 |
+
],
|
207 |
+
),
|
208 |
+
),
|
209 |
+
),
|
210 |
+
const SizedBox(height: 16),
|
211 |
// Custom Video Model Card
|
212 |
Card(
|
213 |
child: Padding(
|
lib/screens/video_screen.dart
CHANGED
@@ -1,5 +1,6 @@
|
|
1 |
// lib/screens/video_screen.dart
|
2 |
import 'dart:async';
|
|
|
3 |
|
4 |
import 'package:tikslop/screens/home_screen.dart';
|
5 |
import 'package:tikslop/widgets/chat_widget.dart';
|
@@ -39,6 +40,9 @@ class _VideoScreenState extends State<VideoScreen> {
|
|
39 |
// Subscription for limit statuses
|
40 |
StreamSubscription? _anonLimitSubscription;
|
41 |
StreamSubscription? _deviceLimitSubscription;
|
|
|
|
|
|
|
42 |
|
43 |
@override
|
44 |
void initState() {
|
@@ -484,6 +488,19 @@ class _VideoScreenState extends State<VideoScreen> {
|
|
484 |
video: _videoData,
|
485 |
initialThumbnailUrl: _videoData.thumbnailUrl,
|
486 |
autoPlay: true,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
487 |
),
|
488 |
const SizedBox(height: 16),
|
489 |
|
@@ -496,6 +513,10 @@ class _VideoScreenState extends State<VideoScreen> {
|
|
496 |
}
|
497 |
|
498 |
Widget _buildCollapsibleInfoSection() {
|
|
|
|
|
|
|
|
|
499 |
return ExpansionTile(
|
500 |
initiallyExpanded: false,
|
501 |
tilePadding: EdgeInsets.zero,
|
@@ -526,16 +547,85 @@ class _VideoScreenState extends State<VideoScreen> {
|
|
526 |
Column(
|
527 |
crossAxisAlignment: CrossAxisAlignment.start,
|
528 |
children: [
|
529 |
-
// Description Section
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
|
|
|
|
536 |
),
|
537 |
-
|
538 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
539 |
],
|
540 |
),
|
541 |
],
|
|
|
1 |
// lib/screens/video_screen.dart
|
2 |
import 'dart:async';
|
3 |
+
import 'dart:math' as math;
|
4 |
|
5 |
import 'package:tikslop/screens/home_screen.dart';
|
6 |
import 'package:tikslop/widgets/chat_widget.dart';
|
|
|
40 |
// Subscription for limit statuses
|
41 |
StreamSubscription? _anonLimitSubscription;
|
42 |
StreamSubscription? _deviceLimitSubscription;
|
43 |
+
|
44 |
+
// Reference to access video player's buffer manager for simulation updates
|
45 |
+
StreamSubscription? _videoUpdateSubscription;
|
46 |
|
47 |
@override
|
48 |
void initState() {
|
|
|
488 |
video: _videoData,
|
489 |
initialThumbnailUrl: _videoData.thumbnailUrl,
|
490 |
autoPlay: true,
|
491 |
+
onVideoUpdated: (updatedVideo) {
|
492 |
+
debugPrint('VIDEO_SCREEN: Received updated video data');
|
493 |
+
if (updatedVideo.evolvedDescription.isNotEmpty) {
|
494 |
+
debugPrint('VIDEO_SCREEN: Evolved description (${updatedVideo.evolvedDescription.length} chars)');
|
495 |
+
debugPrint('VIDEO_SCREEN: First 100 chars: ${updatedVideo.evolvedDescription.substring(0, math.min(100, updatedVideo.evolvedDescription.length))}...');
|
496 |
+
} else {
|
497 |
+
debugPrint('VIDEO_SCREEN: No evolved description received');
|
498 |
+
}
|
499 |
+
|
500 |
+
setState(() {
|
501 |
+
_videoData = updatedVideo;
|
502 |
+
});
|
503 |
+
},
|
504 |
),
|
505 |
const SizedBox(height: 16),
|
506 |
|
|
|
513 |
}
|
514 |
|
515 |
Widget _buildCollapsibleInfoSection() {
|
516 |
+
// Get settings service to check if debug info should be shown
|
517 |
+
final settingsService = SettingsService();
|
518 |
+
final showDebugInfo = settingsService.showSceneDebugInfo;
|
519 |
+
|
520 |
return ExpansionTile(
|
521 |
initiallyExpanded: false,
|
522 |
tilePadding: EdgeInsets.zero,
|
|
|
547 |
Column(
|
548 |
crossAxisAlignment: CrossAxisAlignment.start,
|
549 |
children: [
|
550 |
+
// Regular Description Section
|
551 |
+
if (!showDebugInfo) ...[
|
552 |
+
const SizedBox(height: 8),
|
553 |
+
Text(
|
554 |
+
_videoData.description,
|
555 |
+
style: const TextStyle(
|
556 |
+
color: TikSlopColors.onSurface,
|
557 |
+
height: 1.5,
|
558 |
+
),
|
559 |
),
|
560 |
+
const SizedBox(height: 8),
|
561 |
+
],
|
562 |
+
|
563 |
+
// Debug Information (when enabled)
|
564 |
+
if (showDebugInfo) ...[
|
565 |
+
const SizedBox(height: 16),
|
566 |
+
const Text(
|
567 |
+
'Initial Description:',
|
568 |
+
style: TextStyle(
|
569 |
+
color: TikSlopColors.primary,
|
570 |
+
fontWeight: FontWeight.bold,
|
571 |
+
fontSize: 14,
|
572 |
+
),
|
573 |
+
),
|
574 |
+
const SizedBox(height: 4),
|
575 |
+
Text(
|
576 |
+
_videoData.description,
|
577 |
+
style: const TextStyle(
|
578 |
+
color: TikSlopColors.onSurface,
|
579 |
+
height: 1.5,
|
580 |
+
fontSize: 13,
|
581 |
+
),
|
582 |
+
),
|
583 |
+
const SizedBox(height: 16),
|
584 |
+
|
585 |
+
// Current Description (Evolved Description) Section
|
586 |
+
const Text(
|
587 |
+
'Current Description:',
|
588 |
+
style: TextStyle(
|
589 |
+
color: TikSlopColors.primary,
|
590 |
+
fontWeight: FontWeight.bold,
|
591 |
+
fontSize: 14,
|
592 |
+
),
|
593 |
+
),
|
594 |
+
const SizedBox(height: 4),
|
595 |
+
Text(
|
596 |
+
_videoData.evolvedDescription.isNotEmpty
|
597 |
+
? _videoData.evolvedDescription
|
598 |
+
: _videoData.description, // If evolved description is empty, show initial
|
599 |
+
style: const TextStyle(
|
600 |
+
color: TikSlopColors.onSurface,
|
601 |
+
height: 1.5,
|
602 |
+
fontSize: 13,
|
603 |
+
),
|
604 |
+
),
|
605 |
+
const SizedBox(height: 16),
|
606 |
+
|
607 |
+
// Condensed History (Last Description) Section
|
608 |
+
const Text(
|
609 |
+
'Last Description:',
|
610 |
+
style: TextStyle(
|
611 |
+
color: TikSlopColors.primary,
|
612 |
+
fontWeight: FontWeight.bold,
|
613 |
+
fontSize: 14,
|
614 |
+
),
|
615 |
+
),
|
616 |
+
const SizedBox(height: 4),
|
617 |
+
Text(
|
618 |
+
_videoData.condensedHistory.isNotEmpty
|
619 |
+
? _videoData.condensedHistory
|
620 |
+
: "No history available yet", // Show message if no history
|
621 |
+
style: const TextStyle(
|
622 |
+
color: TikSlopColors.onSurface,
|
623 |
+
height: 1.5,
|
624 |
+
fontSize: 13,
|
625 |
+
),
|
626 |
+
),
|
627 |
+
const SizedBox(height: 8),
|
628 |
+
],
|
629 |
],
|
630 |
),
|
631 |
],
|
lib/services/clip_queue/clip_queue_manager.dart
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
// lib/services/clip_queue/clip_queue_manager.dart
|
2 |
|
3 |
import 'dart:async';
|
4 |
-
import 'dart:math';
|
5 |
import 'package:tikslop/config/config.dart';
|
6 |
import 'package:flutter/foundation.dart';
|
7 |
import 'package:collection/collection.dart';
|
@@ -10,6 +10,7 @@ import '../../models/video_orientation.dart';
|
|
10 |
import '../../models/chat_message.dart';
|
11 |
import '../websocket_api_service.dart';
|
12 |
import '../chat_service.dart';
|
|
|
13 |
import '../../utils/seed.dart';
|
14 |
import 'clip_states.dart';
|
15 |
import 'video_clip.dart';
|
@@ -65,6 +66,12 @@ class ClipQueueManager {
|
|
65 |
|
66 |
/// Recent chat messages to include in description evolution
|
67 |
final List<ChatMessage> _recentChatMessages = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
|
69 |
/// Constructor
|
70 |
ClipQueueManager({
|
@@ -213,12 +220,25 @@ class ClipQueueManager {
|
|
213 |
// Cancel any existing timer
|
214 |
_descriptionEvolutionTimer?.cancel();
|
215 |
|
216 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
217 |
if (Configuration.instance.simLoopFrequencyInSec <= 0) {
|
|
|
218 |
ClipQueueConstants.logEvent('Simulation disabled (frequency is 0)');
|
219 |
return;
|
220 |
}
|
221 |
|
|
|
|
|
222 |
// Adaptive check interval - less frequent checks to reduce overhead
|
223 |
final checkInterval = max(3, Configuration.instance.simLoopFrequencyInSec ~/ 3);
|
224 |
|
@@ -228,30 +248,41 @@ class ClipQueueManager {
|
|
228 |
_descriptionEvolutionTimer = Timer.periodic(
|
229 |
Duration(seconds: checkInterval),
|
230 |
(timer) async {
|
231 |
-
|
|
|
|
|
|
|
|
|
232 |
|
233 |
// Skip if simulation is paused (due to video playback being paused)
|
234 |
if (_isSimulationPaused) {
|
|
|
235 |
ClipQueueConstants.logEvent('Skipping simulation because it is paused');
|
236 |
return;
|
237 |
}
|
238 |
|
239 |
-
//
|
|
|
240 |
final isGenerating = _activeGenerations.isNotEmpty;
|
241 |
if (isGenerating) {
|
242 |
-
|
243 |
-
|
|
|
244 |
}
|
245 |
|
246 |
// Calculate time since last simulation
|
247 |
final now = DateTime.now();
|
248 |
final duration = now.difference(_lastDescriptionEvolutionTime);
|
|
|
249 |
|
250 |
// If we've waited long enough, simulate the video
|
251 |
if (duration.inSeconds >= Configuration.instance.simLoopFrequencyInSec) {
|
|
|
252 |
ClipQueueConstants.logEvent('Triggering simulation after ${duration.inSeconds} seconds');
|
253 |
await _evolveDescription();
|
254 |
_lastDescriptionEvolutionTime = now;
|
|
|
|
|
255 |
}
|
256 |
},
|
257 |
);
|
@@ -260,13 +291,16 @@ class ClipQueueManager {
|
|
260 |
|
261 |
/// Simulate the video by evolving the description using the LLM
|
262 |
Future<void> _evolveDescription() async {
|
|
|
263 |
if (!_websocketService.isConnected) {
|
|
|
264 |
ClipQueueConstants.logEvent('Cannot simulate video: websocket not connected');
|
265 |
return;
|
266 |
}
|
267 |
|
268 |
int retryCount = 0;
|
269 |
const maxRetries = 2;
|
|
|
270 |
|
271 |
// Function to get chat message string
|
272 |
String getChatMessagesString() {
|
@@ -297,13 +331,25 @@ class ClipQueueManager {
|
|
297 |
);
|
298 |
|
299 |
// Update the video with the evolved description
|
|
|
|
|
|
|
|
|
|
|
|
|
300 |
video = video.copyWith(
|
301 |
-
evolvedDescription:
|
302 |
-
condensedHistory:
|
303 |
);
|
304 |
|
305 |
_evolutionCounter++;
|
|
|
306 |
ClipQueueConstants.logEvent('Video simulated (iteration $_evolutionCounter)');
|
|
|
|
|
|
|
|
|
|
|
307 |
onQueueUpdated?.call();
|
308 |
|
309 |
// Success, exit retry loop
|
@@ -658,6 +704,9 @@ class ClipQueueManager {
|
|
658 |
_websocketService.cancelRequestsForVideo(videoId);
|
659 |
}
|
660 |
|
|
|
|
|
|
|
661 |
// Clear all collections
|
662 |
_clipBuffer.clear();
|
663 |
_clipHistory.clear();
|
|
|
1 |
// lib/services/clip_queue/clip_queue_manager.dart
|
2 |
|
3 |
import 'dart:async';
|
4 |
+
import 'dart:math' show max, min;
|
5 |
import 'package:tikslop/config/config.dart';
|
6 |
import 'package:flutter/foundation.dart';
|
7 |
import 'package:collection/collection.dart';
|
|
|
10 |
import '../../models/chat_message.dart';
|
11 |
import '../websocket_api_service.dart';
|
12 |
import '../chat_service.dart';
|
13 |
+
import '../settings_service.dart';
|
14 |
import '../../utils/seed.dart';
|
15 |
import 'clip_states.dart';
|
16 |
import 'video_clip.dart';
|
|
|
66 |
|
67 |
/// Recent chat messages to include in description evolution
|
68 |
final List<ChatMessage> _recentChatMessages = [];
|
69 |
+
|
70 |
+
/// Stream controller for video updates (evolved descriptions)
|
71 |
+
final StreamController<VideoResult> _videoUpdateController = StreamController<VideoResult>.broadcast();
|
72 |
+
|
73 |
+
/// Stream of video updates for subscribers
|
74 |
+
Stream<VideoResult> get videoUpdateStream => _videoUpdateController.stream;
|
75 |
|
76 |
/// Constructor
|
77 |
ClipQueueManager({
|
|
|
220 |
// Cancel any existing timer
|
221 |
_descriptionEvolutionTimer?.cancel();
|
222 |
|
223 |
+
// Check if simulation is enabled globally in config and from user settings
|
224 |
+
final settingsService = SettingsService();
|
225 |
+
final isSimulationEnabled = Configuration.instance.enableSimLoop && settingsService.enableSimulation;
|
226 |
+
|
227 |
+
// Only start if simulation is enabled and frequency is greater than 0
|
228 |
+
if (!isSimulationEnabled) {
|
229 |
+
debugPrint('SIMULATION: Disabled (enable_sim_loop is false)');
|
230 |
+
ClipQueueConstants.logEvent('Simulation disabled (enable_sim_loop is false)');
|
231 |
+
return;
|
232 |
+
}
|
233 |
+
|
234 |
if (Configuration.instance.simLoopFrequencyInSec <= 0) {
|
235 |
+
debugPrint('SIMULATION: Disabled (frequency is 0)');
|
236 |
ClipQueueConstants.logEvent('Simulation disabled (frequency is 0)');
|
237 |
return;
|
238 |
}
|
239 |
|
240 |
+
debugPrint('SIMULATION: Starting simulation timer with settings: enableSimLoop=${Configuration.instance.enableSimLoop}, userSetting=${settingsService.enableSimulation}, frequency=${Configuration.instance.simLoopFrequencyInSec}s');
|
241 |
+
|
242 |
// Adaptive check interval - less frequent checks to reduce overhead
|
243 |
final checkInterval = max(3, Configuration.instance.simLoopFrequencyInSec ~/ 3);
|
244 |
|
|
|
248 |
_descriptionEvolutionTimer = Timer.periodic(
|
249 |
Duration(seconds: checkInterval),
|
250 |
(timer) async {
|
251 |
+
debugPrint('SIMULATION: Timer check triggered');
|
252 |
+
if (_isDisposed) {
|
253 |
+
debugPrint('SIMULATION: Skipping because manager is disposed');
|
254 |
+
return;
|
255 |
+
}
|
256 |
|
257 |
// Skip if simulation is paused (due to video playback being paused)
|
258 |
if (_isSimulationPaused) {
|
259 |
+
debugPrint('SIMULATION: Skipping because it is paused');
|
260 |
ClipQueueConstants.logEvent('Skipping simulation because it is paused');
|
261 |
return;
|
262 |
}
|
263 |
|
264 |
+
// We previously delayed simulation if clips were being generated,
|
265 |
+
// but since clip generation is constant, we'll now run them in parallel
|
266 |
final isGenerating = _activeGenerations.isNotEmpty;
|
267 |
if (isGenerating) {
|
268 |
+
debugPrint('SIMULATION: Continuing with simulation despite active generations');
|
269 |
+
ClipQueueConstants.logEvent('Running simulation in parallel with active generations');
|
270 |
+
// We no longer return early here
|
271 |
}
|
272 |
|
273 |
// Calculate time since last simulation
|
274 |
final now = DateTime.now();
|
275 |
final duration = now.difference(_lastDescriptionEvolutionTime);
|
276 |
+
debugPrint('SIMULATION: Time since last simulation: ${duration.inSeconds}s (frequency: ${Configuration.instance.simLoopFrequencyInSec}s)');
|
277 |
|
278 |
// If we've waited long enough, simulate the video
|
279 |
if (duration.inSeconds >= Configuration.instance.simLoopFrequencyInSec) {
|
280 |
+
debugPrint('SIMULATION: Triggering simulation after ${duration.inSeconds} seconds');
|
281 |
ClipQueueConstants.logEvent('Triggering simulation after ${duration.inSeconds} seconds');
|
282 |
await _evolveDescription();
|
283 |
_lastDescriptionEvolutionTime = now;
|
284 |
+
} else {
|
285 |
+
debugPrint('SIMULATION: Not enough time elapsed since last simulation');
|
286 |
}
|
287 |
},
|
288 |
);
|
|
|
291 |
|
292 |
/// Simulate the video by evolving the description using the LLM
|
293 |
Future<void> _evolveDescription() async {
|
294 |
+
debugPrint('SIMULATION: Starting description evolution');
|
295 |
if (!_websocketService.isConnected) {
|
296 |
+
debugPrint('SIMULATION: Cannot simulate video - websocket not connected');
|
297 |
ClipQueueConstants.logEvent('Cannot simulate video: websocket not connected');
|
298 |
return;
|
299 |
}
|
300 |
|
301 |
int retryCount = 0;
|
302 |
const maxRetries = 2;
|
303 |
+
debugPrint('SIMULATION: Current video state: title="${video.title}", evolvedDescription length=${video.evolvedDescription.length}, original description length=${video.description.length}');
|
304 |
|
305 |
// Function to get chat message string
|
306 |
String getChatMessagesString() {
|
|
|
331 |
);
|
332 |
|
333 |
// Update the video with the evolved description
|
334 |
+
final newEvolvedDescription = result['evolved_description'] as String;
|
335 |
+
final newCondensedHistory = result['condensed_history'] as String;
|
336 |
+
|
337 |
+
debugPrint('SIMULATION: Received evolved description (${newEvolvedDescription.length} chars)');
|
338 |
+
debugPrint('SIMULATION: First 100 chars: ${newEvolvedDescription.substring(0, min(100, newEvolvedDescription.length))}...');
|
339 |
+
|
340 |
video = video.copyWith(
|
341 |
+
evolvedDescription: newEvolvedDescription,
|
342 |
+
condensedHistory: newCondensedHistory,
|
343 |
);
|
344 |
|
345 |
_evolutionCounter++;
|
346 |
+
debugPrint('SIMULATION: Video simulated (iteration $_evolutionCounter)');
|
347 |
ClipQueueConstants.logEvent('Video simulated (iteration $_evolutionCounter)');
|
348 |
+
|
349 |
+
// Emit the updated video to the stream for subscribers
|
350 |
+
debugPrint('SIMULATION: Emitting updated video to stream');
|
351 |
+
_videoUpdateController.add(video);
|
352 |
+
|
353 |
onQueueUpdated?.call();
|
354 |
|
355 |
// Success, exit retry loop
|
|
|
704 |
_websocketService.cancelRequestsForVideo(videoId);
|
705 |
}
|
706 |
|
707 |
+
// Close stream controller
|
708 |
+
await _videoUpdateController.close();
|
709 |
+
|
710 |
// Clear all collections
|
711 |
_clipBuffer.clear();
|
712 |
_clipHistory.clear();
|
lib/services/settings_service.dart
CHANGED
@@ -7,6 +7,8 @@ class SettingsService {
|
|
7 |
static const String _promptPrefixKey = 'video_prompt_prefix';
|
8 |
static const String _hfApiKeyKey = 'huggingface_api_key';
|
9 |
static const String _negativePromptKey = 'negative_video_prompt';
|
|
|
|
|
10 |
static final SettingsService _instance = SettingsService._internal();
|
11 |
|
12 |
factory SettingsService() => _instance;
|
@@ -41,6 +43,20 @@ class SettingsService {
|
|
41 |
await _prefs.setString(_hfApiKeyKey, apiKey);
|
42 |
_settingsController.add(null);
|
43 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
|
45 |
void dispose() {
|
46 |
_settingsController.close();
|
|
|
7 |
static const String _promptPrefixKey = 'video_prompt_prefix';
|
8 |
static const String _hfApiKeyKey = 'huggingface_api_key';
|
9 |
static const String _negativePromptKey = 'negative_video_prompt';
|
10 |
+
static const String _showSceneDebugInfoKey = 'show_scene_debug_info';
|
11 |
+
static const String _enableSimulationKey = 'enable_simulation';
|
12 |
static final SettingsService _instance = SettingsService._internal();
|
13 |
|
14 |
factory SettingsService() => _instance;
|
|
|
43 |
await _prefs.setString(_hfApiKeyKey, apiKey);
|
44 |
_settingsController.add(null);
|
45 |
}
|
46 |
+
|
47 |
+
bool get showSceneDebugInfo => _prefs.getBool(_showSceneDebugInfoKey) ?? false;
|
48 |
+
|
49 |
+
Future<void> setShowSceneDebugInfo(bool value) async {
|
50 |
+
await _prefs.setBool(_showSceneDebugInfoKey, value);
|
51 |
+
_settingsController.add(null);
|
52 |
+
}
|
53 |
+
|
54 |
+
bool get enableSimulation => _prefs.getBool(_enableSimulationKey) ?? Configuration.instance.enableSimLoop;
|
55 |
+
|
56 |
+
Future<void> setEnableSimulation(bool value) async {
|
57 |
+
await _prefs.setBool(_enableSimulationKey, value);
|
58 |
+
_settingsController.add(null);
|
59 |
+
}
|
60 |
|
61 |
void dispose() {
|
62 |
_settingsController.close();
|
lib/services/websocket_api_service.dart
CHANGED
@@ -1246,6 +1246,8 @@ class WebSocketApiService {
|
|
1246 |
};
|
1247 |
}
|
1248 |
|
|
|
|
|
1249 |
try {
|
1250 |
final response = await _sendRequest(
|
1251 |
WebSocketRequest(
|
@@ -1264,12 +1266,18 @@ class WebSocketApiService {
|
|
1264 |
);
|
1265 |
|
1266 |
if (!response['success']) {
|
|
|
1267 |
throw Exception(response['error'] ?? 'Simulation failed');
|
1268 |
}
|
1269 |
|
|
|
|
|
|
|
|
|
|
|
1270 |
return {
|
1271 |
-
'evolved_description':
|
1272 |
-
'condensed_history':
|
1273 |
};
|
1274 |
} catch (e) {
|
1275 |
debugPrint('WebSocketApiService: Error simulating video: $e');
|
|
|
1246 |
};
|
1247 |
}
|
1248 |
|
1249 |
+
debugPrint('WebSocketApiService: Sending simulation request for video $videoId (evolution #$evolutionCount)');
|
1250 |
+
|
1251 |
try {
|
1252 |
final response = await _sendRequest(
|
1253 |
WebSocketRequest(
|
|
|
1266 |
);
|
1267 |
|
1268 |
if (!response['success']) {
|
1269 |
+
debugPrint('WebSocketApiService: Simulation API returned error: ${response['error']}');
|
1270 |
throw Exception(response['error'] ?? 'Simulation failed');
|
1271 |
}
|
1272 |
|
1273 |
+
final evolvedDescription = response['evolved_description'] as String? ?? currentDescription;
|
1274 |
+
final newHistory = response['condensed_history'] as String? ?? condensedHistory;
|
1275 |
+
|
1276 |
+
debugPrint('WebSocketApiService: Simulation successful, received ${evolvedDescription.length} chars for evolved description');
|
1277 |
+
|
1278 |
return {
|
1279 |
+
'evolved_description': evolvedDescription,
|
1280 |
+
'condensed_history': newHistory
|
1281 |
};
|
1282 |
} catch (e) {
|
1283 |
debugPrint('WebSocketApiService: Error simulating video: $e');
|
lib/widgets/ad_banner.dart
CHANGED
@@ -100,13 +100,13 @@ class _AdBannerState extends State<AdBanner> {
|
|
100 |
onTap: () => _launchURL(_currentAd!['link'] ?? ''),
|
101 |
child: Image.asset(
|
102 |
_currentAd!['image'] ?? '',
|
103 |
-
height:
|
104 |
fit: BoxFit.contain,
|
105 |
errorBuilder: (context, error, stackTrace) {
|
106 |
// If image fails to load, show a placeholder
|
107 |
print('Error loading ad image: $error');
|
108 |
return Container(
|
109 |
-
height:
|
110 |
color: Colors.grey.withOpacity(0.1),
|
111 |
child: const Center(child: Text('Ad')),
|
112 |
);
|
|
|
100 |
onTap: () => _launchURL(_currentAd!['link'] ?? ''),
|
101 |
child: Image.asset(
|
102 |
_currentAd!['image'] ?? '',
|
103 |
+
height: 80,
|
104 |
fit: BoxFit.contain,
|
105 |
errorBuilder: (context, error, stackTrace) {
|
106 |
// If image fails to load, show a placeholder
|
107 |
print('Error loading ad image: $error');
|
108 |
return Container(
|
109 |
+
height: 80,
|
110 |
color: Colors.grey.withOpacity(0.1),
|
111 |
child: const Center(child: Text('Ad')),
|
112 |
);
|
lib/widgets/video_player/video_player_widget.dart
CHANGED
@@ -2,6 +2,7 @@
|
|
2 |
|
3 |
import 'dart:async';
|
4 |
import 'dart:math' as math;
|
|
|
5 |
import 'dart:io' show Platform;
|
6 |
import 'package:tikslop/config/config.dart';
|
7 |
import 'package:flutter/material.dart';
|
@@ -43,6 +44,9 @@ class VideoPlayerWidget extends StatefulWidget {
|
|
43 |
|
44 |
/// Callback when video is loaded
|
45 |
final VoidCallback? onVideoLoaded;
|
|
|
|
|
|
|
46 |
|
47 |
/// Constructor
|
48 |
const VideoPlayerWidget({
|
@@ -52,6 +56,7 @@ class VideoPlayerWidget extends StatefulWidget {
|
|
52 |
this.autoPlay = true,
|
53 |
this.borderRadius = 12,
|
54 |
this.onVideoLoaded,
|
|
|
55 |
});
|
56 |
|
57 |
@override
|
@@ -71,6 +76,9 @@ class _VideoPlayerWidgetState extends State<VideoPlayerWidget> with WidgetsBindi
|
|
71 |
/// Whether playback was happening before going to background
|
72 |
bool _wasPlayingBeforeBackground = false;
|
73 |
|
|
|
|
|
|
|
74 |
/// Current orientation
|
75 |
VideoOrientation _currentOrientation = VideoOrientation.LANDSCAPE;
|
76 |
|
@@ -205,6 +213,29 @@ class _VideoPlayerWidgetState extends State<VideoPlayerWidget> with WidgetsBindi
|
|
205 |
|
206 |
// Update orientation after initialization
|
207 |
await _bufferManager.updateOrientation(_currentOrientation);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
208 |
}
|
209 |
|
210 |
if (!_isDisposed && mounted) {
|
@@ -420,6 +451,9 @@ class _VideoPlayerWidgetState extends State<VideoPlayerWidget> with WidgetsBindi
|
|
420 |
// Unregister the observer
|
421 |
WidgetsBinding.instance.removeObserver(this);
|
422 |
|
|
|
|
|
|
|
423 |
// Dispose controllers and timers
|
424 |
_playbackController.dispose();
|
425 |
_bufferManager.dispose();
|
|
|
2 |
|
3 |
import 'dart:async';
|
4 |
import 'dart:math' as math;
|
5 |
+
import 'dart:math' show min;
|
6 |
import 'dart:io' show Platform;
|
7 |
import 'package:tikslop/config/config.dart';
|
8 |
import 'package:flutter/material.dart';
|
|
|
44 |
|
45 |
/// Callback when video is loaded
|
46 |
final VoidCallback? onVideoLoaded;
|
47 |
+
|
48 |
+
/// Callback when video data is updated (for simulation updates)
|
49 |
+
final Function(VideoResult updatedVideo)? onVideoUpdated;
|
50 |
|
51 |
/// Constructor
|
52 |
const VideoPlayerWidget({
|
|
|
56 |
this.autoPlay = true,
|
57 |
this.borderRadius = 12,
|
58 |
this.onVideoLoaded,
|
59 |
+
this.onVideoUpdated,
|
60 |
});
|
61 |
|
62 |
@override
|
|
|
76 |
/// Whether playback was happening before going to background
|
77 |
bool _wasPlayingBeforeBackground = false;
|
78 |
|
79 |
+
/// Subscription to video update stream
|
80 |
+
StreamSubscription? _videoUpdateSubscription;
|
81 |
+
|
82 |
/// Current orientation
|
83 |
VideoOrientation _currentOrientation = VideoOrientation.LANDSCAPE;
|
84 |
|
|
|
213 |
|
214 |
// Update orientation after initialization
|
215 |
await _bufferManager.updateOrientation(_currentOrientation);
|
216 |
+
|
217 |
+
// Subscribe to video updates
|
218 |
+
_videoUpdateSubscription = _bufferManager.queueManager.videoUpdateStream.listen((updatedVideo) {
|
219 |
+
if (!_isDisposed && mounted) {
|
220 |
+
// Calling setState to refresh UI with updated video data
|
221 |
+
setState(() {
|
222 |
+
// Since VideoResult is immutable, we need to use the updated copy
|
223 |
+
// that comes from the stream, not create a new one
|
224 |
+
// The parent widget should receive this via the onVideoUpdated callback
|
225 |
+
if (widget.onVideoUpdated != null) {
|
226 |
+
debugPrint('PLAYER: Received video update, evolvedDescription length: ${updatedVideo.evolvedDescription.length}');
|
227 |
+
|
228 |
+
if (updatedVideo.evolvedDescription.isNotEmpty) {
|
229 |
+
debugPrint('PLAYER: First 100 chars: ${updatedVideo.evolvedDescription.substring(0, min(100, updatedVideo.evolvedDescription.length))}...');
|
230 |
+
}
|
231 |
+
|
232 |
+
widget.onVideoUpdated!(updatedVideo);
|
233 |
+
} else {
|
234 |
+
debugPrint('PLAYER: No video update callback registered');
|
235 |
+
}
|
236 |
+
});
|
237 |
+
}
|
238 |
+
});
|
239 |
}
|
240 |
|
241 |
if (!_isDisposed && mounted) {
|
|
|
451 |
// Unregister the observer
|
452 |
WidgetsBinding.instance.removeObserver(this);
|
453 |
|
454 |
+
// Cancel the video update subscription
|
455 |
+
_videoUpdateSubscription?.cancel();
|
456 |
+
|
457 |
// Dispose controllers and timers
|
458 |
_playbackController.dispose();
|
459 |
_bufferManager.dispose();
|