mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-07-07 21:28:05 +02:00
refactor: extract YouTube download to ytmusic extension and fix UI issues
Remove built-in YouTube/Cobalt download pipeline from Go backend and Dart frontend. YouTube downloading now requires the ytmusic-spotiflac extension (with download_provider capability). Go backend: - Delete youtube.go (745 lines) and youtube_quality_test.go - Remove DownloadFromYouTube, IsYouTubeURLExport, ExtractYouTubeVideoIDExport from exports.go - Remove YouTube routing from DownloadTrack and DownloadByStrategy Dart frontend: - Remove YouTube from built-in services, bitrate settings, quality UI - Remove youtubeOpusBitrate/youtubeMp3Bitrate from settings model - Add migration 7: default service youtube -> tidal - Remove YouTube l10n keys from all 14 arb files and regenerate - Update _determineOutputExt to handle opus_/mp3_ quality strings - Add SAF opus/mp3 metadata embedding in unified branch - Fix TweenSequence assertion crash (t outside 0.0-1.0) - Fix store URL TextField styling consistency Extension changes (gitignored, in extension/YT-Music-SpotiFLAC/): - Add download_provider type, qualityOptions, network permissions - Implement checkAvailability and download via SpotubeDL/Cobalt
This commit is contained in:
@@ -748,7 +748,7 @@ class _DownloadSuccessOverlayState extends State<DownloadSuccessOverlay>
|
||||
_flashAnimation = TweenSequence<double>([
|
||||
TweenSequenceItem(tween: Tween(begin: 0.0, end: 0.15), weight: 30),
|
||||
TweenSequenceItem(tween: Tween(begin: 0.15, end: 0.0), weight: 70),
|
||||
]).animate(CurvedAnimation(parent: _controller, curve: Curves.easeOut));
|
||||
]).animate(_controller);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -816,7 +816,7 @@ class _AnimatedBadgeState extends State<AnimatedBadge>
|
||||
_scaleAnimation = TweenSequence<double>([
|
||||
TweenSequenceItem(tween: Tween(begin: 1.0, end: 1.3), weight: 40),
|
||||
TweenSequenceItem(tween: Tween(begin: 1.3, end: 1.0), weight: 60),
|
||||
]).animate(CurvedAnimation(parent: _controller, curve: Curves.easeOutBack));
|
||||
]).animate(_controller);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -77,24 +77,6 @@ const _builtInServices = [
|
||||
),
|
||||
],
|
||||
),
|
||||
BuiltInService(
|
||||
id: 'youtube',
|
||||
label: 'YouTube',
|
||||
qualityOptions: [
|
||||
QualityOption(
|
||||
id: 'opus_320',
|
||||
label: 'Opus 320kbps',
|
||||
description: 'Best quality lossy (~10MB per track)',
|
||||
),
|
||||
QualityOption(
|
||||
id: 'mp3_320',
|
||||
label: 'MP3 320kbps',
|
||||
description: 'Best compatibility (~10MB per track)',
|
||||
),
|
||||
],
|
||||
isDisabled: false,
|
||||
disabledReason: null,
|
||||
),
|
||||
];
|
||||
|
||||
class DownloadServicePicker extends ConsumerStatefulWidget {
|
||||
@@ -148,9 +130,6 @@ class DownloadServicePicker extends ConsumerStatefulWidget {
|
||||
}
|
||||
|
||||
class _DownloadServicePickerState extends ConsumerState<DownloadServicePicker> {
|
||||
static const List<int> _youtubeOpusSupportedBitrates = [128, 256, 320];
|
||||
static const List<int> _youtubeMp3SupportedBitrates = [128, 256, 320];
|
||||
|
||||
late String _selectedService;
|
||||
|
||||
@override
|
||||
@@ -167,30 +146,6 @@ class _DownloadServicePickerState extends ConsumerState<DownloadServicePicker> {
|
||||
|
||||
/// Get quality options for the selected service
|
||||
List<QualityOption> _getQualityOptions() {
|
||||
final settings = ref.read(settingsProvider);
|
||||
if (_selectedService == 'youtube') {
|
||||
final opusBitrate = _nearestSupportedBitrate(
|
||||
settings.youtubeOpusBitrate,
|
||||
_youtubeOpusSupportedBitrates,
|
||||
);
|
||||
final mp3Bitrate = _nearestSupportedBitrate(
|
||||
settings.youtubeMp3Bitrate,
|
||||
_youtubeMp3SupportedBitrates,
|
||||
);
|
||||
return [
|
||||
QualityOption(
|
||||
id: 'opus_$opusBitrate',
|
||||
label: 'Opus ${opusBitrate}kbps',
|
||||
description: 'Configured from YouTube settings',
|
||||
),
|
||||
QualityOption(
|
||||
id: 'mp3_$mp3Bitrate',
|
||||
label: 'MP3 ${mp3Bitrate}kbps',
|
||||
description: 'Configured from YouTube settings',
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
final builtIn = _builtInServices
|
||||
.where((s) => s.id == _selectedService)
|
||||
.firstOrNull;
|
||||
@@ -215,22 +170,6 @@ class _DownloadServicePickerState extends ConsumerState<DownloadServicePicker> {
|
||||
];
|
||||
}
|
||||
|
||||
int _nearestSupportedBitrate(int value, List<int> supported) {
|
||||
var nearest = supported.first;
|
||||
var nearestDistance = (value - nearest).abs();
|
||||
|
||||
for (final option in supported.skip(1)) {
|
||||
final distance = (value - option).abs();
|
||||
if (distance < nearestDistance ||
|
||||
(distance == nearestDistance && option > nearest)) {
|
||||
nearest = option;
|
||||
nearestDistance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
return nearest;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
@@ -324,9 +263,7 @@ class _DownloadServicePickerState extends ConsumerState<DownloadServicePicker> {
|
||||
),
|
||||
),
|
||||
|
||||
if (_builtInServices.any(
|
||||
(s) => s.id == _selectedService && s.id != 'youtube',
|
||||
))
|
||||
if (_builtInServices.any((s) => s.id == _selectedService))
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 0, 24, 12),
|
||||
child: Text(
|
||||
@@ -338,18 +275,6 @@ class _DownloadServicePickerState extends ConsumerState<DownloadServicePicker> {
|
||||
),
|
||||
),
|
||||
|
||||
if (_selectedService == 'youtube')
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 0, 24, 12),
|
||||
child: Text(
|
||||
context.l10n.youtubeQualityNote,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
for (final quality in qualityOptions)
|
||||
_QualityOption(
|
||||
title: quality.label,
|
||||
|
||||
Reference in New Issue
Block a user