mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-06-08 23:53:57 +02:00
f29177216d
Enable strict-casts, strict-inference, and strict-raw-types in analysis_options.yaml. Add custom_lint with riverpod_lint. Fix all resulting type warnings with explicit type parameters and safer casts. Also improves APK update checker to detect device ABIs for correct variant selection and fixes Deezer artist name parsing edge case.
137 lines
4.0 KiB
Dart
137 lines
4.0 KiB
Dart
import 'dart:async';
|
|
import 'dart:io';
|
|
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
|
import 'package:spotiflac_android/utils/logger.dart';
|
|
|
|
final _log = AppLogger('ShareIntent');
|
|
|
|
class ShareIntentService {
|
|
static final ShareIntentService _instance = ShareIntentService._internal();
|
|
factory ShareIntentService() => _instance;
|
|
ShareIntentService._internal();
|
|
|
|
// Spotify patterns
|
|
static final RegExp _spotifyUriPattern = RegExp(
|
|
r'spotify:(track|album|playlist|artist):[a-zA-Z0-9]+',
|
|
);
|
|
static final RegExp _spotifyUrlPattern = RegExp(
|
|
r'https?://open\.spotify\.com/(track|album|playlist|artist)/[a-zA-Z0-9]+(\?[^\s]*)?',
|
|
);
|
|
|
|
// Deezer patterns
|
|
static final RegExp _deezerUrlPattern = RegExp(
|
|
r'https?://(www\.)?deezer\.com/(track|album|playlist|artist)/\d+(\?[^\s]*)?',
|
|
);
|
|
static final RegExp _deezerShortLinkPattern = RegExp(
|
|
r'https?://deezer\.page\.link/[a-zA-Z0-9]+',
|
|
);
|
|
|
|
// Tidal patterns
|
|
static final RegExp _tidalUrlPattern = RegExp(
|
|
r'https?://(listen\.)?tidal\.com/(track|album|playlist|artist)/[a-zA-Z0-9-]+(\?[^\s]*)?',
|
|
);
|
|
|
|
// YouTube Music patterns
|
|
static final RegExp _ytMusicUrlPattern = RegExp(
|
|
r'https?://music\.youtube\.com/(watch\?v=|playlist\?list=|channel/|browse/)[a-zA-Z0-9_-]+([?&][^\s]*)?',
|
|
);
|
|
|
|
// Standard YouTube patterns (youtu.be short links and www.youtube.com/watch)
|
|
static final RegExp _youtubeUrlPattern = RegExp(
|
|
r'https?://(youtu\.be/[a-zA-Z0-9_-]+|www\.youtube\.com/watch\?v=[a-zA-Z0-9_-]+)([?&][^\s]*)?',
|
|
);
|
|
|
|
final _sharedUrlController = StreamController<String>.broadcast();
|
|
StreamSubscription<List<SharedMediaFile>>? _mediaSubscription;
|
|
bool _initialized = false;
|
|
String? _pendingUrl;
|
|
|
|
Stream<String> get sharedUrlStream => _sharedUrlController.stream;
|
|
|
|
String? consumePendingUrl() {
|
|
final url = _pendingUrl;
|
|
_pendingUrl = null;
|
|
return url;
|
|
}
|
|
|
|
Future<void> initialize() async {
|
|
if (_initialized) return;
|
|
_initialized = true;
|
|
|
|
if (!Platform.isAndroid && !Platform.isIOS) {
|
|
_log.i('Share intent is not supported on this platform');
|
|
return;
|
|
}
|
|
|
|
_mediaSubscription = ReceiveSharingIntent.instance.getMediaStream().listen(
|
|
_handleSharedMedia,
|
|
onError: (Object err) => _log.e('Error: $err'),
|
|
);
|
|
|
|
final initialMedia = await ReceiveSharingIntent.instance.getInitialMedia();
|
|
if (initialMedia.isNotEmpty) {
|
|
_handleSharedMedia(initialMedia, isInitial: true);
|
|
ReceiveSharingIntent.instance.reset();
|
|
}
|
|
}
|
|
|
|
void _handleSharedMedia(
|
|
List<SharedMediaFile> files, {
|
|
bool isInitial = false,
|
|
}) {
|
|
for (final file in files) {
|
|
final textsToCheck = [file.path, if (file.message != null) file.message!];
|
|
|
|
for (final textToCheck in textsToCheck) {
|
|
final url = _extractMusicUrl(textToCheck);
|
|
if (url != null) {
|
|
_log.i('Received music URL: $url (initial: $isInitial)');
|
|
if (isInitial) {
|
|
_pendingUrl = url;
|
|
}
|
|
_sharedUrlController.add(url);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
String? _extractMusicUrl(String text) {
|
|
if (text.isEmpty) return null;
|
|
|
|
final uriMatch = _spotifyUriPattern.firstMatch(text);
|
|
if (uriMatch != null) {
|
|
return uriMatch.group(0);
|
|
}
|
|
|
|
final patterns = [
|
|
_spotifyUrlPattern,
|
|
_deezerUrlPattern,
|
|
_deezerShortLinkPattern,
|
|
_tidalUrlPattern,
|
|
_ytMusicUrlPattern,
|
|
_youtubeUrlPattern,
|
|
];
|
|
|
|
for (final pattern in patterns) {
|
|
final match = pattern.firstMatch(text);
|
|
if (match != null) {
|
|
final fullUrl = match.group(0)!;
|
|
// Keep query params for YouTube URLs (needed for ?v=, ?list=, etc.)
|
|
if (pattern == _ytMusicUrlPattern || pattern == _youtubeUrlPattern) {
|
|
return fullUrl;
|
|
}
|
|
final queryIndex = fullUrl.indexOf('?');
|
|
return queryIndex > 0 ? fullUrl.substring(0, queryIndex) : fullUrl;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
void dispose() {
|
|
_mediaSubscription?.cancel();
|
|
_sharedUrlController.close();
|
|
}
|
|
}
|