Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.mac.sample
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ BUILD_ENV=local
BARK_VARIANT=cpu
# ACE-Step: "cuda" for Linux/Windows with NVIDIA GPU, "cpu" for Mac
ACESTEP_VARIANT=cpu
# ACE-Step LLM: "true" to enable built-in lyrics LLM, "false" to disable (saves memory)
ACESTEP_INIT_LLM=false
# YuLan-Mini: "cuda" for NVIDIA GPU, "cpu" for Mac
YULAN_VARIANT=cpu
# Uncomment the next line on GPU machines to enable NVIDIA device passthrough:
Expand Down
2 changes: 2 additions & 0 deletions .env.windows.sample
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ BUILD_ENV=local
BARK_VARIANT=cuda
# ACE-Step: "cuda" for Linux/Windows with NVIDIA GPU, "cpu" for Mac
ACESTEP_VARIANT=cuda
# ACE-Step LLM: "true" to enable built-in lyrics LLM, "false" to disable (saves VRAM)
ACESTEP_INIT_LLM=true
# YuLan-Mini: "cuda" for NVIDIA GPU, "cpu" for Mac
YULAN_VARIANT=cuda
# Uncomment the next line on GPU machines to enable NVIDIA device passthrough:
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ services:
ACESTEP_CONFIG_PATH: acestep-v15-turbo
ACESTEP_LM_MODEL_PATH: acestep-5Hz-lm-0.6B
ACESTEP_LM_BACKEND: pt
ACESTEP_INIT_LLM: "false"
ACESTEP_INIT_LLM: "${ACESTEP_INIT_LLM:-false}"
ACESTEP_DEVICE: ${ACESTEP_VARIANT:-cpu}
ACESTEP_DOWNLOAD_SOURCE: huggingface
ACESTEP_API_HOST: 0.0.0.0
Expand Down
4 changes: 2 additions & 2 deletions packages/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -641,10 +641,10 @@ packages:
dependency: transitive
description:
name: meta
sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
url: "https://pub.dev"
source: hosted
version: "1.18.0"
version: "1.17.0"
mime:
dependency: transitive
description:
Expand Down
73 changes: 31 additions & 42 deletions packages/studio_backend/bin/server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@ Future<void> main(List<String> args) async {
await dependencySetup(database);

// Generate and persist the server's external user ID on first start.
final externalUserId =
await di.get<SettingsRepository>().ensureExternalUserId();

final externalUserId = await di
.get<SettingsRepository>()
.ensureExternalUserId();
logger.d(message: 'External user ID: $externalUserId');

final port = int.parse(Platform.environment['PORT'] ?? '80');
Expand All @@ -53,7 +55,6 @@ Future<void> main(List<String> args) async {
..mount('/v1/users', di.get<UserService>().router.call)
..mount('/v1/health', di.get<HealthService>().router.call);


if (di.isRegistered<AudioService>()) {
rootRouter.mount('/v1/audio', di.get<AudioService>().router.call);
}
Expand All @@ -64,40 +65,22 @@ Future<void> main(List<String> args) async {

if (di.isRegistered<TrainingProxyService>()) {
final trainingProxy = di.get<TrainingProxyService>();
rootRouter.mount(
'/v1/dataset',
trainingProxy.datasetRouter.call,
);
rootRouter.mount(
'/v1/training',
trainingProxy.trainingRouter.call,
);
rootRouter.mount('/v1/dataset', trainingProxy.datasetRouter.call);
rootRouter.mount('/v1/training', trainingProxy.trainingRouter.call);
}

rootRouter.mount(
'/v1/settings',
di.get<SettingsService>().router.call,
);
rootRouter.mount('/v1/settings', di.get<SettingsService>().router.call);

rootRouter.mount(
'/v1/server-backends',
di.get<ServerBackendService>().router.call,
);

rootRouter.mount(
'/v1/peers',
di.get<PeerService>().router.call,
);
rootRouter.mount('/v1/peers', di.get<PeerService>().router.call);

rootRouter.mount(
'/v1/logs',
di.get<LogService>().router.call,
);
rootRouter.mount('/v1/logs', di.get<LogService>().router.call);

rootRouter.mount(
'/v1/browse',
di.get<BrowseService>().router.call,
);
rootRouter.mount('/v1/browse', di.get<BrowseService>().router.call);

rootRouter.mount(
'/v1/workspaces',
Expand All @@ -113,21 +96,27 @@ Future<void> main(List<String> args) async {
.addMiddleware(_ignoreFavicon())
.addMiddleware(_logUnhandledErrors())
.addMiddleware(logRequests())
.addMiddleware(corsMiddleware(
allowedOrigins: Platform.environment['CORS_ALLOWED_ORIGINS']
?.split(',')
.map((s) => s.trim())
.where((s) => s.isNotEmpty)
.toList(),
))
.addMiddleware(signatureVerificationMiddleware(
peerRepository: di.get<PeerRepository>(),
settingsRepository: di.get<SettingsRepository>(),
))
.addMiddleware(forwardingMiddleware(
serverBackendRepository: di.get<ServerBackendRepository>(),
settingsRepository: di.get<SettingsRepository>(),
))
.addMiddleware(
corsMiddleware(
allowedOrigins: Platform.environment['CORS_ALLOWED_ORIGINS']
?.split(',')
.map((s) => s.trim())
.where((s) => s.isNotEmpty)
.toList(),
),
)
.addMiddleware(
signatureVerificationMiddleware(
peerRepository: di.get<PeerRepository>(),
settingsRepository: di.get<SettingsRepository>(),
),
)
.addMiddleware(
forwardingMiddleware(
serverBackendRepository: di.get<ServerBackendRepository>(),
settingsRepository: di.get<SettingsRepository>(),
),
)
.addMiddleware(userAutoCreateMiddleware(di.get<UserRepository>()))
.addHandler(rootRouter.call);

Expand Down
74 changes: 44 additions & 30 deletions packages/studio_ui/lib/widgets/genre_autocomplete.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class _GenreAutocompleteState extends State<GenreAutocomplete> {
final _focusNode = FocusNode();
List<MapEntry<String, MajorGenre>> _filteredResults = [];
bool _showOverlay = false;
bool _isPointerOverOverlay = false;
final _layerLink = LayerLink();
OverlayEntry? _overlayEntry;
int _highlightedIndex = 0;
Expand Down Expand Up @@ -90,7 +91,7 @@ class _GenreAutocompleteState extends State<GenreAutocomplete> {
setState(() {}); // rebuild for border color
if (!_focusNode.hasFocus) {
Future.delayed(const Duration(milliseconds: 100), () {
if (mounted && !_focusNode.hasFocus) {
if (mounted && !_focusNode.hasFocus && !_isPointerOverOverlay) {
_removeOverlay();
}
});
Expand Down Expand Up @@ -204,29 +205,33 @@ class _GenreAutocompleteState extends State<GenreAutocomplete> {
link: _layerLink,
showWhenUnlinked: false,
offset: Offset(0, size.height + 4),
child: Material(
elevation: 0,
color: AppColors.surfaceHigh,
borderRadius: BorderRadius.circular(8),
child: Container(
constraints: const BoxConstraints(maxHeight: 200),
decoration: BoxDecoration(
border: Border.all(color: AppColors.border),
borderRadius: BorderRadius.circular(8),
),
child: ListView.builder(
padding: const EdgeInsets.symmetric(vertical: 4),
shrinkWrap: true,
itemCount: _filteredResults.length,
itemBuilder: (context, index) {
final genre = _filteredResults[index];
final isHighlighted = index == _highlightedIndex;
return _GenreOptionTile(
genre: genre,
highlighted: isHighlighted,
onTap: () => _selectGenre(genre),
);
},
child: MouseRegion(
onEnter: (_) => _isPointerOverOverlay = true,
onExit: (_) => _isPointerOverOverlay = false,
child: Material(
elevation: 0,
color: AppColors.surfaceHigh,
borderRadius: BorderRadius.circular(8),
child: Container(
constraints: const BoxConstraints(maxHeight: 200),
decoration: BoxDecoration(
border: Border.all(color: AppColors.border),
borderRadius: BorderRadius.circular(8),
),
child: ListView.builder(
padding: const EdgeInsets.symmetric(vertical: 4),
shrinkWrap: true,
itemCount: _filteredResults.length,
itemBuilder: (context, index) {
final genre = _filteredResults[index];
final isHighlighted = index == _highlightedIndex;
return _GenreOptionTile(
genre: genre,
highlighted: isHighlighted,
onTap: () => _selectGenre(genre),
);
},
),
),
),
),
Expand Down Expand Up @@ -355,7 +360,7 @@ class _GenreAutocompleteState extends State<GenreAutocomplete> {
}
}

class _GenreOptionTile extends StatelessWidget {
class _GenreOptionTile extends StatefulWidget {
const _GenreOptionTile({
required this.genre,
required this.highlighted,
Expand All @@ -366,16 +371,25 @@ class _GenreOptionTile extends StatelessWidget {
final bool highlighted;
final VoidCallback onTap;

@override
State<_GenreOptionTile> createState() => _GenreOptionTileState();
}

class _GenreOptionTileState extends State<_GenreOptionTile> {
bool _hovered = false;

@override
Widget build(BuildContext context) {
final color = genre.value.color;
final color = widget.genre.value.color;
return GestureDetector(
onTap: onTap,
onTap: widget.onTap,
child: MouseRegion(
cursor: SystemMouseCursors.click,
onEnter: (_) => setState(() => _hovered = true),
onExit: (_) => setState(() => _hovered = false),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
color: highlighted ? Colors.white10 : Colors.transparent,
color: (_hovered || widget.highlighted) ? Colors.white10 : Colors.transparent,
child: Row(
children: [
Container(
Expand All @@ -389,7 +403,7 @@ class _GenreOptionTile extends StatelessWidget {
const SizedBox(width: 8),
Expanded(
child: Text(
genre.key,
widget.genre.key,
style: const TextStyle(
color: AppColors.text,
fontSize: 12,
Expand All @@ -399,7 +413,7 @@ class _GenreOptionTile extends StatelessWidget {
),
const SizedBox(width: 8),
Text(
genre.value.name,
widget.genre.value.name,
style: TextStyle(
color: color.withValues(alpha: 0.6),
fontSize: 10,
Expand Down