feat(plugins): filter plugins by abilities in plugins page and show abilities as badge

This commit is contained in:
Kingkor Roy Tirtho 2025-10-23 08:57:45 +06:00
parent 439de5d7f7
commit f6d9d64b7d
41 changed files with 270 additions and 118 deletions

View File

@ -135,7 +135,7 @@ abstract class SpotubeIcons {
static const list = FeatherIcons.list;
static const device = FeatherIcons.smartphone;
static const engine = FeatherIcons.server;
static const extensions = FeatherIcons.package;
static const extensions = Icons.extension_rounded;
static const message = FeatherIcons.send;
static const upload = FeatherIcons.uploadCloud;
static const plugin = Icons.extension_outlined;

View File

@ -452,14 +452,14 @@
"disclaimer": "Disclaimer",
"third_party_plugin_dmca_notice": "The Spotube team does not hold any responsibility (including legal) for any \"Third-party\" plugins.\nPlease use them at your own risk. For any bugs/issues, please report them to the plugin repository.\n\nIf any \"Third-party\" plugin is breaking ToS/DMCA of any service/legal entity, please ask the \"Third-party\" plugin author or the hosting platform .e.g GitHub/Codeberg to take action. Above listed (\"Third-party\" labelled) are all public/community maintained plugins. We're not curating them, so we cannot take any action on them.\n\n",
"input_does_not_match_format": "Input doesn't match the required format",
"metadata_provider_plugins": "Metadata Provider Plugins",
"plugins": "Plugins",
"paste_plugin_download_url": "Paste download url or GitHub/Codeberg repo url or direct link to .smplug file",
"download_and_install_plugin_from_url": "Download and install plugin from url",
"failed_to_add_plugin_error": "Failed to add plugin: {error}",
"upload_plugin_from_file": "Upload plugin from file",
"installed": "Installed",
"available_plugins": "Available plugins",
"configure_your_own_metadata_plugin": "Configure your own playlist/album/artist/feed metadata provider",
"configure_plugins": "Configure your own metadata provider and audio source plugins",
"audio_scrobblers": "Audio Scrobblers",
"scrobbling": "Scrobbling",
"source": "Source: ",

View File

@ -2871,11 +2871,11 @@ abstract class AppLocalizations {
/// **'Input doesn\'t match the required format'**
String get input_does_not_match_format;
/// No description provided for @metadata_provider_plugins.
/// No description provided for @plugins.
///
/// In en, this message translates to:
/// **'Metadata Provider Plugins'**
String get metadata_provider_plugins;
/// **'Plugins'**
String get plugins;
/// No description provided for @paste_plugin_download_url.
///
@ -2913,11 +2913,11 @@ abstract class AppLocalizations {
/// **'Available plugins'**
String get available_plugins;
/// No description provided for @configure_your_own_metadata_plugin.
/// No description provided for @configure_plugins.
///
/// In en, this message translates to:
/// **'Configure your own playlist/album/artist/feed metadata provider'**
String get configure_your_own_metadata_plugin;
/// **'Configure your own metadata provider and audio source plugins'**
String get configure_plugins;
/// No description provided for @audio_scrobblers.
///

View File

@ -1504,7 +1504,7 @@ class AppLocalizationsAr extends AppLocalizations {
'المدخل لا يتوافق مع التنسيق المطلوب';
@override
String get metadata_provider_plugins => 'إضافات مزود البيانات';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1529,8 +1529,8 @@ class AppLocalizationsAr extends AppLocalizations {
String get available_plugins => 'الإضافات المتوفّرة';
@override
String get configure_your_own_metadata_plugin =>
'تهيئة مزوّد بيانات للقائمة/الألبوم/الفنان/المصدر خاص بك';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'أجهزة تتبع الصوت';

View File

@ -1505,7 +1505,7 @@ class AppLocalizationsBn extends AppLocalizations {
'ইনপুট প্রয়োজনীয় ফরম্যাটের সাথে মেলে না';
@override
String get metadata_provider_plugins => 'মেটাডেটা প্রদানকারী প্লাগইনসমূহ';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1530,8 +1530,8 @@ class AppLocalizationsBn extends AppLocalizations {
String get available_plugins => 'উপলব্ধ প্লাগইনগুলো';
@override
String get configure_your_own_metadata_plugin =>
'নিজস্ব প্লেলিস্ট/অ্যালবাম/শিল্পী/ফিড মেটাডেটা প্রদানকারী কনফিগার করুন';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'অডিও স্ক্রোব্বলার্স';

View File

@ -1514,8 +1514,7 @@ class AppLocalizationsCa extends AppLocalizations {
'Lentrada no coincideix amb el format requerit';
@override
String get metadata_provider_plugins =>
'Complements de proveïdor de metadades';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1540,8 +1539,8 @@ class AppLocalizationsCa extends AppLocalizations {
String get available_plugins => 'Complements disponibles';
@override
String get configure_your_own_metadata_plugin =>
'Configura el teu propi proveïdor de metadades per llistes/reproduccions àlbum/artista/flux';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'Scrobblers dàudio';

View File

@ -1505,7 +1505,7 @@ class AppLocalizationsCs extends AppLocalizations {
'Vstup neodpovídá požadovanému formátu';
@override
String get metadata_provider_plugins => 'Pluginy poskytovatelů metadat';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1530,8 +1530,8 @@ class AppLocalizationsCs extends AppLocalizations {
String get available_plugins => 'Dostupné pluginy';
@override
String get configure_your_own_metadata_plugin =>
'Nakonfigurujte si vlastního poskytovatele metadat pro playlist/album/umělec/fid';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'Audio scrobblers';

View File

@ -1517,7 +1517,7 @@ class AppLocalizationsDe extends AppLocalizations {
'Eingabe entspricht nicht dem geforderten Format';
@override
String get metadata_provider_plugins => 'Plugins für Metadatenanbieter';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1542,8 +1542,8 @@ class AppLocalizationsDe extends AppLocalizations {
String get available_plugins => 'Verfügbare Plugins';
@override
String get configure_your_own_metadata_plugin =>
'Eigenen Anbieter für Playlist-/Album-/Künstler-/Feed-Metadaten konfigurieren';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'Audio-Scrobbler';

View File

@ -1503,7 +1503,7 @@ class AppLocalizationsEn extends AppLocalizations {
'Input doesn\'t match the required format';
@override
String get metadata_provider_plugins => 'Metadata Provider Plugins';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1528,8 +1528,8 @@ class AppLocalizationsEn extends AppLocalizations {
String get available_plugins => 'Available plugins';
@override
String get configure_your_own_metadata_plugin =>
'Configure your own playlist/album/artist/feed metadata provider';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'Audio Scrobblers';

View File

@ -1517,8 +1517,7 @@ class AppLocalizationsEs extends AppLocalizations {
'La entrada no coincide con el formato requerido';
@override
String get metadata_provider_plugins =>
'Complementos de proveedor de metadatos';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1543,8 +1542,8 @@ class AppLocalizationsEs extends AppLocalizations {
String get available_plugins => 'Complementos disponibles';
@override
String get configure_your_own_metadata_plugin =>
'Configura tu propio proveedor de metadatos para listas/álbum/artista/feeds';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'Scrobblers de audio';

View File

@ -1515,7 +1515,7 @@ class AppLocalizationsEu extends AppLocalizations {
'Sarrera ezin da beharrezko formatutik desberdina izan';
@override
String get metadata_provider_plugins => 'Metadaten hornitzailearen pluginak';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1540,8 +1540,8 @@ class AppLocalizationsEu extends AppLocalizations {
String get available_plugins => 'Eskaintzen diren pluginak';
@override
String get configure_your_own_metadata_plugin =>
'Konfiguratu zureko playlists-/album-/artista-/feed-metadaten hornitzailea';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'Audio scrobbler-ak';

View File

@ -1503,7 +1503,7 @@ class AppLocalizationsFa extends AppLocalizations {
'ورودی با قالب مورد نیاز تطابق ندارد';
@override
String get metadata_provider_plugins => 'افزونه‌های ارائه‌دهندهٔ متادیتا';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1528,8 +1528,8 @@ class AppLocalizationsFa extends AppLocalizations {
String get available_plugins => 'افزونه‌های موجود';
@override
String get configure_your_own_metadata_plugin =>
'پیکربندی ارائه‌دهندهٔ متادیتا برای پلی‌لیست/آلبوم/هنرمند/فید به‌صورت سفارشی';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'اسکراب‌بلرهای صوتی';

View File

@ -1503,7 +1503,7 @@ class AppLocalizationsFi extends AppLocalizations {
String get input_does_not_match_format => 'Syöte ei vastaa vaadittua muotoa';
@override
String get metadata_provider_plugins => 'Metatietojen tarjoajan lisäosat';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1528,8 +1528,8 @@ class AppLocalizationsFi extends AppLocalizations {
String get available_plugins => 'Saatavilla olevat lisäosat';
@override
String get configure_your_own_metadata_plugin =>
'Määritä oma soittolistan/albumin/artistin/syötteen metatietojen tarjoaja';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'Äänen scrobblerit';

View File

@ -1521,8 +1521,7 @@ class AppLocalizationsFr extends AppLocalizations {
'L\'entrée ne correspond pas au format requis';
@override
String get metadata_provider_plugins =>
'Plugins de fournisseur de métadonnées';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1548,8 +1547,8 @@ class AppLocalizationsFr extends AppLocalizations {
String get available_plugins => 'Plugins disponibles';
@override
String get configure_your_own_metadata_plugin =>
'Configurer votre propre fournisseur de métadonnées de playlist/album/artiste/flux';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'Scrobblers audio';

View File

@ -1509,7 +1509,7 @@ class AppLocalizationsHi extends AppLocalizations {
'इनपुट आवश्यक प्रारूप से मेल नहीं खाता है';
@override
String get metadata_provider_plugins => 'मेटाडेटा प्रदाता प्लगइन';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1534,8 +1534,8 @@ class AppLocalizationsHi extends AppLocalizations {
String get available_plugins => 'उपलब्ध प्लगइन';
@override
String get configure_your_own_metadata_plugin =>
'अपनी खुद की प्लेलिस्ट/एल्बम/कलाकार/फ़ीड मेटाडेटा प्रदाता कॉन्फ़िगर करें';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'ऑडियो स्क्रॉबलर्स';

View File

@ -1511,7 +1511,7 @@ class AppLocalizationsId extends AppLocalizations {
'Masukan tidak cocok dengan format yang diperlukan';
@override
String get metadata_provider_plugins => 'Plugin Penyedia Metadata';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1536,8 +1536,8 @@ class AppLocalizationsId extends AppLocalizations {
String get available_plugins => 'Plugin yang tersedia';
@override
String get configure_your_own_metadata_plugin =>
'Konfigurasi penyedia metadata playlist/album/artis/feed Anda sendiri';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'Scrobblers Audio';

View File

@ -1510,7 +1510,7 @@ class AppLocalizationsIt extends AppLocalizations {
'L\'input non corrisponde al formato richiesto';
@override
String get metadata_provider_plugins => 'Plugin del provider di metadati';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1535,8 +1535,8 @@ class AppLocalizationsIt extends AppLocalizations {
String get available_plugins => 'Plugin disponibili';
@override
String get configure_your_own_metadata_plugin =>
'Configura il tuo provider di metadati per playlist/album/artista/feed';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'Scrobbler audio';

View File

@ -1474,7 +1474,7 @@ class AppLocalizationsJa extends AppLocalizations {
String get input_does_not_match_format => '入力が必須フォーマットと一致しません';
@override
String get metadata_provider_plugins => 'メタデータプロバイダープラグイン';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1499,8 +1499,8 @@ class AppLocalizationsJa extends AppLocalizations {
String get available_plugins => '利用可能なプラグイン';
@override
String get configure_your_own_metadata_plugin =>
'独自のプレイリスト/アルバム/アーティスト/フィードのメタデータプロバイダーを構成';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'オーディオスクロッブラー';

View File

@ -1511,8 +1511,7 @@ class AppLocalizationsKa extends AppLocalizations {
'შეყვანა არ ემთხვევა საჭირო ფორმატს';
@override
String get metadata_provider_plugins =>
'მეტამონაცემების პროვაიდერების პლაგინები';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1537,8 +1536,8 @@ class AppLocalizationsKa extends AppLocalizations {
String get available_plugins => 'ხელმისაწვდომი პლაგინები';
@override
String get configure_your_own_metadata_plugin =>
'დააყენეთ თქვენი საკუთარი პლეილისტის/ალბომის/არტისტის/ფიდის მეტამონაცემების პროვაიდერი';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'აუდიო სქრობლერები';

View File

@ -1479,7 +1479,7 @@ class AppLocalizationsKo extends AppLocalizations {
String get input_does_not_match_format => '입력이 필요한 형식과 일치하지 않습니다';
@override
String get metadata_provider_plugins => '메타데이터 제공자 플러그인';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1503,8 +1503,8 @@ class AppLocalizationsKo extends AppLocalizations {
String get available_plugins => '사용 가능한 플러그인';
@override
String get configure_your_own_metadata_plugin =>
'자신만의 플레이리스트/앨범/아티스트/피드 메타데이터 제공자 구성';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => '오디오 스크로블러';

View File

@ -1515,7 +1515,7 @@ class AppLocalizationsNe extends AppLocalizations {
String get input_does_not_match_format => 'इनपुट आवश्यक ढाँचासँग मेल खाँदैन';
@override
String get metadata_provider_plugins => 'मेटाडेटा प्रदायक प्लगइनहरू';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1540,8 +1540,8 @@ class AppLocalizationsNe extends AppLocalizations {
String get available_plugins => 'उपलब्ध प्लगइनहरू';
@override
String get configure_your_own_metadata_plugin =>
'तपाईंको आफ्नै प्लेलिस्ट/एल्बम/कलाकार/फिड मेटाडेटा प्रदायक कन्फिगर गर्नुहोस्';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'अडियो स्क्रब्बलरहरू';

View File

@ -1509,7 +1509,7 @@ class AppLocalizationsNl extends AppLocalizations {
'Invoer komt niet overeen met het vereiste formaat';
@override
String get metadata_provider_plugins => 'Metadata-aanbieder Plugins';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1534,8 +1534,8 @@ class AppLocalizationsNl extends AppLocalizations {
String get available_plugins => 'Beschikbare plugins';
@override
String get configure_your_own_metadata_plugin =>
'Configureer uw eigen metadata-aanbieder voor afspeellijst/album/artiest/feed';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'Audioscrobblers';

View File

@ -1511,7 +1511,7 @@ class AppLocalizationsPl extends AppLocalizations {
'Wprowadzony tekst nie pasuje do wymaganego formatu';
@override
String get metadata_provider_plugins => 'Wtyczki dostawców metadanych';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1536,8 +1536,8 @@ class AppLocalizationsPl extends AppLocalizations {
String get available_plugins => 'Dostępne wtyczki';
@override
String get configure_your_own_metadata_plugin =>
'Skonfiguruj własnego dostawcę metadanych dla playlisty/albumu/artysty/kanału';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'Scrobblery audio';

View File

@ -1508,7 +1508,7 @@ class AppLocalizationsPt extends AppLocalizations {
'A entrada não corresponde ao formato exigido';
@override
String get metadata_provider_plugins => 'Plugins do provedor de metadados';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1533,8 +1533,8 @@ class AppLocalizationsPt extends AppLocalizations {
String get available_plugins => 'Plugins disponíveis';
@override
String get configure_your_own_metadata_plugin =>
'Configure seu próprio provedor de metadados de playlist/álbum/artista/feed';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'Scrobblers de áudio';

View File

@ -1511,7 +1511,7 @@ class AppLocalizationsRu extends AppLocalizations {
'Введенные данные не соответствуют требуемому формату';
@override
String get metadata_provider_plugins => 'Плагины поставщика метаданных';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1536,8 +1536,8 @@ class AppLocalizationsRu extends AppLocalizations {
String get available_plugins => 'Доступные плагины';
@override
String get configure_your_own_metadata_plugin =>
'Настройте свой собственный поставщик метаданных для плейлиста/альбома/артиста/ленты';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'Аудио скробблеры';

View File

@ -1517,7 +1517,7 @@ class AppLocalizationsTa extends AppLocalizations {
'உள்ளீடு தேவையான வடிவத்துடன் பொருந்தவில்லை';
@override
String get metadata_provider_plugins => 'மெட்டாடேட்டா வழங்குநர் பிளகின்கள்';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1542,8 +1542,8 @@ class AppLocalizationsTa extends AppLocalizations {
String get available_plugins => 'கிடைக்கக்கூடிய பிளகின்கள்';
@override
String get configure_your_own_metadata_plugin =>
'உங்கள் சொந்த பிளேலிஸ்ட்/ஆல்பம்/கலைஞர்/ஊட்ட மெட்டாடேட்டா வழங்குநரை உள்ளமைக்கவும்';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'ஆடியோ ஸ்க்ரோப்ளர்கள்';

View File

@ -1500,7 +1500,7 @@ class AppLocalizationsTh extends AppLocalizations {
String get input_does_not_match_format => 'อินพุตไม่ตรงกับรูปแบบที่ต้องการ';
@override
String get metadata_provider_plugins => 'ปลั๊กอินผู้ให้บริการเมตาดาต้า';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1525,8 +1525,8 @@ class AppLocalizationsTh extends AppLocalizations {
String get available_plugins => 'ปลั๊กอินที่มีอยู่';
@override
String get configure_your_own_metadata_plugin =>
'กำหนดค่าผู้ให้บริการเมตาดาต้าเพลย์ลิสต์/อัลบั้ม/ศิลปิน/ฟีดของคุณเอง';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'เครื่อง scrobbler เสียง';

View File

@ -1518,7 +1518,7 @@ class AppLocalizationsTl extends AppLocalizations {
'Ang input ay hindi tumutugma sa kinakailangang format';
@override
String get metadata_provider_plugins => 'Mga Plugin ng Metadata Provider';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1543,8 +1543,8 @@ class AppLocalizationsTl extends AppLocalizations {
String get available_plugins => 'Mga available na plugin';
@override
String get configure_your_own_metadata_plugin =>
'I-configure ang iyong sariling playlist/album/artist/feed metadata provider';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'Mga Audio Scrobbler';

View File

@ -1511,7 +1511,7 @@ class AppLocalizationsTr extends AppLocalizations {
String get input_does_not_match_format => 'Girdi, gerekli biçimle eşleşmiyor';
@override
String get metadata_provider_plugins => 'Meta Veri Sağlayıcısı Eklentileri';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1536,8 +1536,8 @@ class AppLocalizationsTr extends AppLocalizations {
String get available_plugins => 'Mevcut eklentiler';
@override
String get configure_your_own_metadata_plugin =>
'Kendi çalma listenizi/albümünüzü/sanatçınızı/akış meta veri sağlayıcınızı yapılandırın';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'Ses Scrobbler\'lar';

View File

@ -1507,7 +1507,7 @@ class AppLocalizationsUk extends AppLocalizations {
'Введені дані не відповідають необхідному формату';
@override
String get metadata_provider_plugins => 'Плагіни провайдера метаданих';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1532,8 +1532,8 @@ class AppLocalizationsUk extends AppLocalizations {
String get available_plugins => 'Доступні плагіни';
@override
String get configure_your_own_metadata_plugin =>
'Налаштуйте свій власний провайдер метаданих для плейлиста/альбому/виконавця/стрічки';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'Аудіо скробблери';

View File

@ -1513,7 +1513,7 @@ class AppLocalizationsVi extends AppLocalizations {
'Đầu vào không khớp với định dạng yêu cầu';
@override
String get metadata_provider_plugins => 'Plugin Nhà cung cấp siêu dữ liệu';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1538,8 +1538,8 @@ class AppLocalizationsVi extends AppLocalizations {
String get available_plugins => 'Các plugin có sẵn';
@override
String get configure_your_own_metadata_plugin =>
'Cấu hình nhà cung cấp siêu dữ liệu danh sách phát/album/nghệ sĩ/nguồn cấp dữ liệu của riêng bạn';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => 'Bộ scrobbler âm thanh';

View File

@ -1469,7 +1469,7 @@ class AppLocalizationsZh extends AppLocalizations {
String get input_does_not_match_format => '输入与所需格式不匹配';
@override
String get metadata_provider_plugins => '元数据提供者插件';
String get plugins => 'Plugins';
@override
String get paste_plugin_download_url =>
@ -1493,7 +1493,8 @@ class AppLocalizationsZh extends AppLocalizations {
String get available_plugins => '可用插件';
@override
String get configure_your_own_metadata_plugin => '配置您自己的播放列表/专辑/艺人/订阅元数据提供者';
String get configure_plugins =>
'Configure your own metadata provider and audio source plugins';
@override
String get audio_scrobblers => '音频 Scrobblers';
@ -2976,9 +2977,6 @@ class AppLocalizationsZhTw extends AppLocalizationsZh {
@override
String get input_does_not_match_format => '輸入不符合所需格式';
@override
String get metadata_provider_plugins => '中繼資料供應商外掛程式';
@override
String get paste_plugin_download_url =>
'貼上下載網址、GitHub/Codeberg 儲存庫網址或 .smplug 檔案的直接連結';
@ -3000,9 +2998,6 @@ class AppLocalizationsZhTw extends AppLocalizationsZh {
@override
String get available_plugins => '可用的外掛程式';
@override
String get configure_your_own_metadata_plugin => '設定您自己的播放清單/專輯/藝人/動態中繼資料供應商';
@override
String get audio_scrobblers => '音訊 Scrobblers';

View File

@ -6539,6 +6539,7 @@ mixin _$MetadataPluginRepository {
String get owner => throw _privateConstructorUsedError;
String get description => throw _privateConstructorUsedError;
String get repoUrl => throw _privateConstructorUsedError;
List<String> get topics => throw _privateConstructorUsedError;
/// Serializes this MetadataPluginRepository to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@ -6556,7 +6557,12 @@ abstract class $MetadataPluginRepositoryCopyWith<$Res> {
$Res Function(MetadataPluginRepository) then) =
_$MetadataPluginRepositoryCopyWithImpl<$Res, MetadataPluginRepository>;
@useResult
$Res call({String name, String owner, String description, String repoUrl});
$Res call(
{String name,
String owner,
String description,
String repoUrl,
List<String> topics});
}
/// @nodoc
@ -6579,6 +6585,7 @@ class _$MetadataPluginRepositoryCopyWithImpl<$Res,
Object? owner = null,
Object? description = null,
Object? repoUrl = null,
Object? topics = null,
}) {
return _then(_value.copyWith(
name: null == name
@ -6597,6 +6604,10 @@ class _$MetadataPluginRepositoryCopyWithImpl<$Res,
? _value.repoUrl
: repoUrl // ignore: cast_nullable_to_non_nullable
as String,
topics: null == topics
? _value.topics
: topics // ignore: cast_nullable_to_non_nullable
as List<String>,
) as $Val);
}
}
@ -6610,7 +6621,12 @@ abstract class _$$MetadataPluginRepositoryImplCopyWith<$Res>
__$$MetadataPluginRepositoryImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String name, String owner, String description, String repoUrl});
$Res call(
{String name,
String owner,
String description,
String repoUrl,
List<String> topics});
}
/// @nodoc
@ -6632,6 +6648,7 @@ class __$$MetadataPluginRepositoryImplCopyWithImpl<$Res>
Object? owner = null,
Object? description = null,
Object? repoUrl = null,
Object? topics = null,
}) {
return _then(_$MetadataPluginRepositoryImpl(
name: null == name
@ -6650,6 +6667,10 @@ class __$$MetadataPluginRepositoryImplCopyWithImpl<$Res>
? _value.repoUrl
: repoUrl // ignore: cast_nullable_to_non_nullable
as String,
topics: null == topics
? _value._topics
: topics // ignore: cast_nullable_to_non_nullable
as List<String>,
));
}
}
@ -6661,7 +6682,9 @@ class _$MetadataPluginRepositoryImpl implements _MetadataPluginRepository {
{required this.name,
required this.owner,
required this.description,
required this.repoUrl});
required this.repoUrl,
required final List<String> topics})
: _topics = topics;
factory _$MetadataPluginRepositoryImpl.fromJson(Map<String, dynamic> json) =>
_$$MetadataPluginRepositoryImplFromJson(json);
@ -6674,10 +6697,17 @@ class _$MetadataPluginRepositoryImpl implements _MetadataPluginRepository {
final String description;
@override
final String repoUrl;
final List<String> _topics;
@override
List<String> get topics {
if (_topics is EqualUnmodifiableListView) return _topics;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_topics);
}
@override
String toString() {
return 'MetadataPluginRepository(name: $name, owner: $owner, description: $description, repoUrl: $repoUrl)';
return 'MetadataPluginRepository(name: $name, owner: $owner, description: $description, repoUrl: $repoUrl, topics: $topics)';
}
@override
@ -6689,13 +6719,14 @@ class _$MetadataPluginRepositoryImpl implements _MetadataPluginRepository {
(identical(other.owner, owner) || other.owner == owner) &&
(identical(other.description, description) ||
other.description == description) &&
(identical(other.repoUrl, repoUrl) || other.repoUrl == repoUrl));
(identical(other.repoUrl, repoUrl) || other.repoUrl == repoUrl) &&
const DeepCollectionEquality().equals(other._topics, _topics));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(runtimeType, name, owner, description, repoUrl);
int get hashCode => Object.hash(runtimeType, name, owner, description,
repoUrl, const DeepCollectionEquality().hash(_topics));
/// Create a copy of MetadataPluginRepository
/// with the given fields replaced by the non-null parameter values.
@ -6719,7 +6750,8 @@ abstract class _MetadataPluginRepository implements MetadataPluginRepository {
{required final String name,
required final String owner,
required final String description,
required final String repoUrl}) = _$MetadataPluginRepositoryImpl;
required final String repoUrl,
required final List<String> topics}) = _$MetadataPluginRepositoryImpl;
factory _MetadataPluginRepository.fromJson(Map<String, dynamic> json) =
_$MetadataPluginRepositoryImpl.fromJson;
@ -6732,6 +6764,8 @@ abstract class _MetadataPluginRepository implements MetadataPluginRepository {
String get description;
@override
String get repoUrl;
@override
List<String> get topics;
/// Create a copy of MetadataPluginRepository
/// with the given fields replaced by the non-null parameter values.

View File

@ -603,6 +603,8 @@ _$MetadataPluginRepositoryImpl _$$MetadataPluginRepositoryImplFromJson(
owner: json['owner'] as String,
description: json['description'] as String,
repoUrl: json['repoUrl'] as String,
topics:
(json['topics'] as List<dynamic>).map((e) => e as String).toList(),
);
Map<String, dynamic> _$$MetadataPluginRepositoryImplToJson(
@ -612,4 +614,5 @@ Map<String, dynamic> _$$MetadataPluginRepositoryImplToJson(
'owner': instance.owner,
'description': instance.description,
'repoUrl': instance.repoUrl,
'topics': instance.topics,
};

View File

@ -7,6 +7,7 @@ class MetadataPluginRepository with _$MetadataPluginRepository {
required String owner,
required String description,
required String repoUrl,
required List<String> topics,
}) = _MetadataPluginRepository;
factory MetadataPluginRepository.fromJson(Map<String, dynamic> json) =>

View File

@ -5,6 +5,7 @@ import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/markdown/markdown.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/models/metadata/metadata.dart';
import 'package:spotube/modules/metadata_plugins/plugin_repository.dart';
import 'package:spotube/modules/metadata_plugins/plugin_update_available_dialog.dart';
import 'package:spotube/provider/metadata_plugin/core/auth.dart';
import 'package:spotube/provider/metadata_plugin/core/support.dart';
@ -12,6 +13,11 @@ import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart';
import 'package:spotube/provider/metadata_plugin/updater/update_checker.dart';
import 'package:url_launcher/url_launcher.dart';
final validAbilities = {
PluginAbilities.metadata: ("Metadata", SpotubeIcons.album),
PluginAbilities.audioSource: ("Audio Source", SpotubeIcons.music),
};
class MetadataInstalledPluginItem extends HookConsumerWidget {
final PluginConfiguration plugin;
final bool isDefault;
@ -79,6 +85,18 @@ class MetadataInstalledPluginItem extends HookConsumerWidget {
spacing: 8,
children: [
Text(plugin.description),
Wrap(
spacing: 8,
runSpacing: 8,
children: [
for (final ability in plugin.abilities)
if (validAbilities.keys.contains(ability))
SecondaryBadge(
leading: Icon(validAbilities[ability]!.$2),
child: Text(validAbilities[ability]!.$1),
),
],
),
if (repoUrl != null)
Wrap(
spacing: 8,

View File

@ -11,6 +11,11 @@ import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:change_case/change_case.dart';
final validTopics = {
"spotube-metadata-plugin": ("Metadata", SpotubeIcons.album),
"spotube-audio-source-plugin": ("Audio Source", SpotubeIcons.music),
};
class MetadataPluginRepositoryItem extends HookConsumerWidget {
final MetadataPluginRepository pluginRepo;
const MetadataPluginRepositoryItem({
@ -208,6 +213,12 @@ class MetadataPluginRepositoryItem extends HookConsumerWidget {
),
),
],
for (final topic in pluginRepo.topics)
if (validTopics.keys.contains(topic))
SecondaryBadge(
leading: Icon(validTopics[topic]!.$2),
child: Text(validTopics[topic]!.$1),
),
SecondaryBadge(
leading: host == "github.com"
? const Icon(SpotubeIcons.github)

View File

@ -30,6 +30,7 @@ class SettingsMetadataProviderPage extends HookConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final tabState = useState<int>(0);
final formKey = useMemoized(() => GlobalKey<FormBuilderState>(), []);
final plugins = ref.watch(metadataPluginsProvider);
@ -49,11 +50,30 @@ class SettingsMetadataProviderPage extends HookConsumerWidget {
final pluginRepos = pluginReposSnapshot.asData?.value.items ?? [];
if (installedPluginIds.isEmpty) return pluginRepos;
return pluginRepos
final availablePlugins = pluginRepos
.whereNot((repo) => installedPluginIds.contains(repo.repoUrl))
.toList();
if (tabState.value != 0) {
// metadata only plugins
return availablePlugins
.where(
(d) => d.topics.contains(
tabState.value == 1
? "spotube-metadata-plugin"
: "spotube-audio-source-plugin",
),
)
.toList();
}
return availablePlugins; // all plugins
},
[plugins.asData?.value.plugins, pluginReposSnapshot.asData?.value],
[
plugins.asData?.value.plugins,
pluginReposSnapshot.asData?.value,
tabState.value,
],
);
return SafeArea(
@ -61,7 +81,7 @@ class SettingsMetadataProviderPage extends HookConsumerWidget {
child: Scaffold(
headers: [
TitleBar(
title: Text(context.l10n.metadata_provider_plugins),
title: Text(context.l10n.plugins),
)
],
child: Padding(
@ -193,6 +213,20 @@ class SettingsMetadataProviderPage extends HookConsumerWidget {
),
),
const SliverGap(12),
SliverToBoxAdapter(
child: TabList(
index: tabState.value,
onChanged: (value) {
tabState.value = value;
},
children: const [
TabItem(child: Text("All")),
TabItem(child: Text("Metadata")),
TabItem(child: Text("Audio Source")),
],
),
),
const SliverGap(12),
if (plugins.asData?.value.plugins.isNotEmpty ?? false)
SliverToBoxAdapter(
child: Row(
@ -249,6 +283,7 @@ class SettingsMetadataProviderPage extends HookConsumerWidget {
description: "Loading...",
repoUrl: "",
owner: "",
topics: [],
),
),
);

View File

@ -21,8 +21,8 @@ class SettingsAccountSection extends HookConsumerWidget {
children: [
ListTile(
leading: const Icon(SpotubeIcons.extensions),
title: Text(context.l10n.metadata_provider_plugins),
subtitle: Text(context.l10n.configure_your_own_metadata_plugin),
title: Text(context.l10n.plugins),
subtitle: Text(context.l10n.configure_plugins),
onTap: () {
context.pushRoute(const SettingsMetadataProviderRoute());
},

View File

@ -49,6 +49,7 @@ class MetadataPluginRepositoriesNotifier
owner: repo["owner"]["login"] ?? "",
description: repo["description"] ?? "",
repoUrl: repo["html_url"] ?? "",
topics: repo["topics"].cast<String>() ?? [],
);
}).toList();

View File

@ -1,101 +1,135 @@
{
"ar": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"bn": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"ca": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"cs": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"de": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"es": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"eu": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"fa": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"fi": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"fr": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"hi": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"id": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"it": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"ja": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"ka": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"ko": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"ne": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
@ -103,72 +137,96 @@
"nl": [
"audio_source",
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"pl": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"pt": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"ru": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"ta": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"th": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"tl": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"tr": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"uk": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"vi": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"zh": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"
],
"zh_TW": [
"plugins",
"configure_plugins",
"source",
"uncompressed",
"dab_music_source_description"