mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-04-11 14:22:36 +02:00
Resolve API (api.zarz.moe): - Refactor songlink.go: Spotify URLs use resolve API, non-Spotify uses SongLink API - Add SongLink fallback when resolve API fails for Spotify (two-layer resilience) - Remove dead code: page parser, XOR-obfuscated keys, legacy helpers Multi-artist tag fix (#288): - Add RewriteSplitArtistTags() in Go to rewrite ARTIST/ALBUMARTIST as split Vorbis comments - Wire method channel handler in Android (MainActivity.kt) and iOS (AppDelegate.swift) - Add PlatformBridge.rewriteSplitArtistTags() in Dart - Call native FLAC rewriter after FFmpeg embed when split_vorbis mode is active - Extract deezerTrackArtistDisplay() helper to use Contributors in album/playlist tracks Code cleanup: - Remove unused imports, dead code, and redundant comments across Go and Dart - Fix build: remove stale getQobuzDebugKey() reference in deezer_download.go
131 lines
3.7 KiB
Dart
131 lines
3.7 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();
|
|
|
|
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]*)?',
|
|
);
|
|
|
|
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]+',
|
|
);
|
|
|
|
static final RegExp _tidalUrlPattern = RegExp(
|
|
r'https?://(listen\.)?tidal\.com/(track|album|playlist|artist)/[a-zA-Z0-9-]+(\?[^\s]*)?',
|
|
);
|
|
|
|
static final RegExp _ytMusicUrlPattern = RegExp(
|
|
r'https?://music\.youtube\.com/(watch\?v=|playlist\?list=|channel/|browse/)[a-zA-Z0-9_-]+([?&][^\s]*)?',
|
|
);
|
|
|
|
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)!;
|
|
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();
|
|
}
|
|
}
|