File size: 8,109 Bytes
5acd9c3
 
 
 
 
 
 
 
 
 
 
 
 
 
5ca5f4b
5acd9c3
 
 
 
 
 
 
5ca5f4b
5acd9c3
 
 
 
 
 
5ca5f4b
5acd9c3
 
 
 
 
 
 
 
 
 
2e813e6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5acd9c3
2e813e6
 
 
 
 
 
 
 
 
 
 
7ab27ea
 
 
 
 
 
 
 
 
 
 
2e813e6
 
7ab27ea
 
2e813e6
7ab27ea
 
2e813e6
7ab27ea
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2e813e6
 
 
 
5acd9c3
2e813e6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5acd9c3
2e813e6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5acd9c3
2e813e6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5acd9c3
 
 
2e813e6
 
 
 
 
 
 
 
 
5acd9c3
2e813e6
 
 
5acd9c3
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
import 'package:flutter/material.dart';
import '../services/settings_service.dart';
import '../services/websocket_api_service.dart';
import '../theme/colors.dart';

class SettingsScreen extends StatefulWidget {
  const SettingsScreen({super.key});

  @override
  State<SettingsScreen> createState() => _SettingsScreenState();
}

class _SettingsScreenState extends State<SettingsScreen> {
  final _promptController = TextEditingController();
  final _negativePromptController = TextEditingController();
  final _hfApiKeyController = TextEditingController();
  final _settingsService = SettingsService();

  @override
  void initState() {
    super.initState();
    _promptController.text = _settingsService.videoPromptPrefix;
    _negativePromptController.text = _settingsService.negativeVideoPrompt;
    _hfApiKeyController.text = _settingsService.huggingfaceApiKey;
  }

  @override
  void dispose() {
    _promptController.dispose();
    _negativePromptController.dispose();
    _hfApiKeyController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Settings'),
      ),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          // API Configuration Card
          Card(
            child: Padding(
              padding: const EdgeInsets.all(16),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  const Text(
                    'API Configuration',
                    style: TextStyle(
                      color: AiTubeColors.onBackground,
                      fontSize: 20,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(height: 16),
                  TextField(
                    controller: _hfApiKeyController,
                    decoration: const InputDecoration(
                      labelText: 'Connect using your Hugging Face API Key (optional)',
                      helperText: 'Hugging Face members enjoy a higher-resolution rendering.',
                      helperMaxLines: 2,
                    ),
                    obscureText: true,
                    onChanged: (value) async {
                      await _settingsService.setHuggingfaceApiKey(value);
                      
                      // Show a snackbar to indicate the API key was saved
                      if (context.mounted) {
                        ScaffoldMessenger.of(context).showSnackBar(
                          const SnackBar(
                            content: Text('API Key saved. Reconnecting...'),
                            duration: Duration(seconds: 2),
                          ),
                        );
                      }
                      
                      // Reinitialize the websocket connection when the API key changes
                      final websocket = WebSocketApiService();
                      try {
                        // First dispose the current connection
                        await websocket.dispose();
                        
                        // Then create a new connection with the new API key
                        await websocket.connect();
                        
                        // Finally, initialize the connection completely
                        await websocket.initialize();
                        
                        // Show success message
                        if (context.mounted) {
                          ScaffoldMessenger.of(context).showSnackBar(
                            const SnackBar(
                              content: Text('Connected successfully with new API key'),
                              backgroundColor: Colors.green,
                            ),
                          );
                        }
                      } catch (e) {
                        // Show error message if connection fails
                        if (context.mounted) {
                          ScaffoldMessenger.of(context).showSnackBar(
                            SnackBar(
                              content: Text('Failed to connect: $e'),
                              backgroundColor: Colors.red,
                            ),
                          );
                        }
                      }
                    },
                  ),
                ],
              ),
            ),
          ),
          const SizedBox(height: 16),
          // Video Prompt Prefix Card
          Card(
            child: Padding(
              padding: const EdgeInsets.all(16),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  const Text(
                    'Video Generation',
                    style: TextStyle(
                      color: AiTubeColors.onBackground,
                      fontSize: 20,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(height: 16),
                  TextField(
                    controller: _promptController,
                    decoration: const InputDecoration(
                      labelText: 'Video Prompt Prefix',
                      helperText: 'Text to prepend to all video generation prompts',
                      helperMaxLines: 2,
                    ),
                    onChanged: (value) {
                      _settingsService.setVideoPromptPrefix(value);
                    },
                  ),
                  const SizedBox(height: 16),
                  TextField(
                    controller: _negativePromptController,
                    decoration: const InputDecoration(
                      labelText: 'Negative Prompt',
                      helperText: 'Content to avoid in the output generation',
                      helperMaxLines: 2,
                    ),
                    onChanged: (value) {
                      _settingsService.setNegativeVideoPrompt(value);
                    },
                  ),
                ],
              ),
            ),
          ),
          const SizedBox(height: 16),
          // Custom Video Model Card
          Card(
            child: Padding(
              padding: const EdgeInsets.all(16),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  const Text(
                    'Custom Video Model',
                    style: TextStyle(
                      color: AiTubeColors.onBackground,
                      fontSize: 20,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(height: 16),
                  DropdownButtonFormField<String>(
                    decoration: const InputDecoration(
                      labelText: 'Video Generation Model',
                    ),
                    value: 'ltx-video-0.9.6',
                    onChanged: null, // Disabled
                    items: const [
                      DropdownMenuItem(
                        value: 'ltx-video-0.9.6',
                        child: Text('LTX-Video 0.9.6 (base model)'),
                      ),
                    ],
                  ),
                  const SizedBox(height: 8),
                  const Text(
                    'Interested in using custom Hugging Face models? If you trained a public and distilled LoRA model based on LTX-Video 0.9.6 (remember, it has to be distilled), it can be integrated into AiTube2. Please open a thread in the Community forum and I\'ll see for a way to allow for custom models.',
                    style: TextStyle(
                      fontSize: 12,
                      color: Colors.grey,
                    ),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }

}