mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
lyrics not working bugfix
Download track feature complete support in Android Android sudden solid color screen inno-script updated for permission_handler gensums updated for support for android apk updated description of Spotube in every file changes added to CHANGELOG
This commit is contained in:
parent
0cf5bfea50
commit
45f9d08595
3
.github/workflows/flutter-build.yml
vendored
3
.github/workflows/flutter-build.yml
vendored
@ -29,6 +29,7 @@ jobs:
|
|||||||
- run: make appimage
|
- run: make appimage
|
||||||
# Building Android Application
|
# Building Android Application
|
||||||
- run: flutter build apk
|
- run: flutter build apk
|
||||||
|
- run: mv build/app/outputs/apk/release/app-release.apk build/Spotube-android-arm64-v8a.apk
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: Spotube-Linux-Bundle
|
name: Spotube-Linux-Bundle
|
||||||
@ -36,7 +37,7 @@ jobs:
|
|||||||
build/Spotube-linux-x86_64.deb
|
build/Spotube-linux-x86_64.deb
|
||||||
build/Spotube-linux-x86_64.tar.xz
|
build/Spotube-linux-x86_64.tar.xz
|
||||||
build/Spotube-*-x86_64.AppImage
|
build/Spotube-*-x86_64.AppImage
|
||||||
build/app/outputs/apk/release/app-release.apk
|
build/Spotube-android-arm64-v8a.apk
|
||||||
build_windows:
|
build_windows:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
|
27
CHANGELOG.md
27
CHANGELOG.md
@ -1,3 +1,30 @@
|
|||||||
|
# v2.0.0
|
||||||
|
|
||||||
|
### New
|
||||||
|
- Android Support https://github.com/KRTirtho/spotube/issues/24
|
||||||
|
- Responsive UI (Mobile, Tablet)
|
||||||
|
- Anonymous/Guest Account
|
||||||
|
- Mini floating player
|
||||||
|
- Full page PlayerView for smaller devices
|
||||||
|
- Horizontal CategoryCard Scroll & pagination for quicker access to Playlists
|
||||||
|
- Bottom bar for smaller devices
|
||||||
|
- Collapsed Sidebar for medium sized devices
|
||||||
|
- Persists Volume level
|
||||||
|
- Android NavigationPanel controls (OS media controls of Android)
|
||||||
|
|
||||||
|
### Improved
|
||||||
|
- Search - now scrolls & paginates for Playlists & Albums
|
||||||
|
- Authentication - allows guest accounts making authentication optional
|
||||||
|
- Lyrics - can be fetched without requiring GeniusAccessToken. This makes geniusAccessToken optional
|
||||||
|
- UI snappiness & faster load times
|
||||||
|
- Simpler logic, faster calculations & better caching (flutter_hooks)
|
||||||
|
- shared state management - uses riverpod & hooks combination
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
- Can't play any song in macos https://github.com/KRTirtho/spotube/issues/23
|
||||||
|
- Downloaded tracks can't be played as they're WebAudio (.weba) instead of MP3
|
||||||
|
- delay while changing Playlist/Single tracks
|
||||||
|
|
||||||
# v1.2.0
|
# v1.2.0
|
||||||
|
|
||||||
### New
|
### New
|
||||||
|
31
README.md
31
README.md
@ -27,7 +27,9 @@ Spotube is a [Flutter](https://flutter.dev) based lightweight spotify client. It
|
|||||||
Following are the features that currently spotube offers:
|
Following are the features that currently spotube offers:
|
||||||
|
|
||||||
- Open Source
|
- Open Source
|
||||||
- No telementry, diagnostics or user data collection
|
- Anonymous/Guest Login
|
||||||
|
- Cross platform
|
||||||
|
- No telemetry, diagnostics or user data collection
|
||||||
- Lightweight & resource friendly
|
- Lightweight & resource friendly
|
||||||
- Native performance (Thanks to Flutter+Skia)
|
- Native performance (Thanks to Flutter+Skia)
|
||||||
- Playback control is on user's machine instead of server based
|
- Playback control is on user's machine instead of server based
|
||||||
@ -36,7 +38,7 @@ Following are the features that currently spotube offers:
|
|||||||
- Lyrics
|
- Lyrics
|
||||||
- Downloadable track
|
- Downloadable track
|
||||||
|
|
||||||
<a href="https://www.producthunt.com/posts/spotube?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-spotube" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=327965&theme=dark" alt="Spotube - A lightweight+free Spotify desktop-client made with flutter | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
<a href="https://www.producthunt.com/posts/spotube?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-spotube" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=327965&theme=dark" alt="Spotube - A lightweight+free Spotify crossplatform-client made with flutter | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
@ -44,6 +46,10 @@ I'm always releasing newer versions of binary of the software each 2-3 month wit
|
|||||||
|
|
||||||
All the binaries are located in the [releases](https://github.com/krtirtho/spotube/releases), just download
|
All the binaries are located in the [releases](https://github.com/krtirtho/spotube/releases), just download
|
||||||
|
|
||||||
|
## Android
|
||||||
|
|
||||||
|
Download the [Android app](https://github.com/KRTirtho/spotube/releases/latest/download/Spotube-android-arm64-v8a.apk) & install it on your phone/tablet
|
||||||
|
|
||||||
## Windows
|
## Windows
|
||||||
|
|
||||||
Download the [setup file](https://github.com/KRTirtho/spotube/releases/latest/download/Spotube-windows-x86_64-setup.exe) & follow along the installer
|
Download the [setup file](https://github.com/KRTirtho/spotube/releases/latest/download/Spotube-windows-x86_64-setup.exe) & follow along the installer
|
||||||
@ -98,9 +104,10 @@ Download the [Mac OS Disk Image (.dmg) file](https://github.com/KRTirtho/spotube
|
|||||||
|
|
||||||
|
|
||||||
**I'll/try to upload the package binaries to linux debian/arch/ubuntu/snap/flatpack/redhat/chocolatey/homebrew stores or software centers or repositories**
|
**I'll/try to upload the package binaries to linux debian/arch/ubuntu/snap/flatpack/redhat/chocolatey/homebrew stores or software centers or repositories**
|
||||||
# Configuration
|
|
||||||
|
|
||||||
There are some configurations that needs to be done to start using this software
|
## Optional Configurations
|
||||||
|
<details>
|
||||||
|
<summary>Login with <b>Spotify</b></summary>
|
||||||
|
|
||||||
You need a spotify account & a developer app for
|
You need a spotify account & a developer app for
|
||||||
|
|
||||||
@ -120,8 +127,10 @@ You need a spotify account & a developer app for
|
|||||||
|
|
||||||
- Click on **SHOW CLIENT SECRET** to reveal the **clientSecret**. Then copy the **clientID**, **clientSecret** & paste in the **Spotube's** respective fields
|
- Click on **SHOW CLIENT SECRET** to reveal the **clientSecret**. Then copy the **clientID**, **clientSecret** & paste in the **Spotube's** respective fields
|
||||||

|

|
||||||
|
</details>
|
||||||
|
|
||||||
**Setup Genius Lyrics (Optional)**
|
<details>
|
||||||
|
<summary>Setup <b>Genius Lyrics</b></summary>
|
||||||
|
|
||||||
- Signup/Login into [genius](https://genius.com/signup) for **lyrics**
|
- Signup/Login into [genius](https://genius.com/signup) for **lyrics**
|
||||||
- Go To [Genius Developer Portal](https://genius.com/api-clients/new) for creating an API client
|
- Go To [Genius Developer Portal](https://genius.com/api-clients/new) for creating an API client
|
||||||
@ -132,6 +141,7 @@ You need a spotify account & a developer app for
|
|||||||

|

|
||||||
|
|
||||||
> **Note!**: No personal data or any kind of sensitive information won't be collected from spotify. Don't believe? See the code for yourself
|
> **Note!**: No personal data or any kind of sensitive information won't be collected from spotify. Don't believe? See the code for yourself
|
||||||
|
</details>
|
||||||
|
|
||||||
# TODO:
|
# TODO:
|
||||||
|
|
||||||
@ -140,6 +150,7 @@ You need a spotify account & a developer app for
|
|||||||
- [x] Track download
|
- [x] Track download
|
||||||
- [ ] Support for playing/streaming podcasts/shows
|
- [ ] Support for playing/streaming podcasts/shows
|
||||||
- [x] Artist, User & Album pages
|
- [x] Artist, User & Album pages
|
||||||
|
- [x] Android Support
|
||||||
|
|
||||||
# Building from source
|
# Building from source
|
||||||
|
|
||||||
@ -179,6 +190,16 @@ Bu why? You can learn about it [here](https://dev.to/krtirtho/choosing-open-sour
|
|||||||
- [bitsdojo_window](https://github.com/bitsdojo/bitsdojo_window) - A Flutter package that makes it easy to customize and work with your Flutter desktop app window on Windows, macOS and Linux
|
- [bitsdojo_window](https://github.com/bitsdojo/bitsdojo_window) - A Flutter package that makes it easy to customize and work with your Flutter desktop app window on Windows, macOS and Linux
|
||||||
- [hotkey_manager](https://github.com/leanflutter/hotkey_manager) - A flutter plugin that allow Flutter desktop apps to defines system/inapp wide hotkey
|
- [hotkey_manager](https://github.com/leanflutter/hotkey_manager) - A flutter plugin that allow Flutter desktop apps to defines system/inapp wide hotkey
|
||||||
- [Inno Setup](https://jrsoftware.org/isinfo.php) - Inno Setup is a free installer for Windows programs by Jordan Russell and Martijn Laan
|
- [Inno Setup](https://jrsoftware.org/isinfo.php) - Inno Setup is a free installer for Windows programs by Jordan Russell and Martijn Laan
|
||||||
|
- [collection](https://github.com/dart-lang/collection) - The collection package for Dart contains a number of separate libraries with utility functions and classes that makes working with collections easier
|
||||||
|
- [flutter_riverpod](https://riverpod.dev/) - A Reactive Caching and Data-binding Framework
|
||||||
|
- [flutter_hooks](https://github.com/rrousselGit/flutter_hooks) - React hooks for Flutter. Hooks are a new kind of object that manages a Widget life-cycles. They are used to increase code sharing between widgets and as a complete replacement for StatefulWidget
|
||||||
|
- [hooks_riverpod](https://riverpod.dev/) - Riverpod with hooks
|
||||||
|
- [go_router](https://github.com/flutter/packages/tree/main/packages/go_router) - A declarative router for Flutter based on Navigation 2 supporting deep linking, data-driven routes and more
|
||||||
|
- [palette_generator](https://github.com/flutter/packages/tree/main/packages/palette_generator) - Flutter package for generating palette colors from a source image.
|
||||||
|
- [audio_session](https://github.com/ryanheise/audio_session) - Sets the iOS audio session category and Android audio attributes for your app, and manages your app's audio focus, mixing and ducking behaviour.
|
||||||
|
- [logger](https://github.com/leisim/logger) - Small, easy to use and extensible logger which prints beautiful logs
|
||||||
|
- [flutter_launcher_icons](https://github.com/fluttercommunity/flutter_launcher_icons) - A package which simplifies the task of updating your Flutter app's launcher icon.
|
||||||
|
- [permission_handler](https://github.com/baseflow/flutter-permission-handler) - Permission plugin for Flutter. This plugin provides a cross-platform (iOS, Android) API to request and check permissions.
|
||||||
|
|
||||||
|
|
||||||
# Social handlers
|
# Social handlers
|
||||||
|
@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
|
|||||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion flutter.compileSdkVersion
|
compileSdkVersion 31
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
<queries>
|
<queries>
|
||||||
<!-- If your app opens https URLs -->
|
<!-- If your app opens https URLs -->
|
||||||
@ -12,7 +14,7 @@
|
|||||||
</intent>
|
</intent>
|
||||||
</queries>
|
</queries>
|
||||||
|
|
||||||
<application android:label="Spotube" android:name="${applicationName}" android:icon="@mipmap/ic_launcher" android:usesCleartextTraffic="true">
|
<application android:label="Spotube" android:name="${applicationName}" android:icon="@mipmap/ic_launcher" android:usesCleartextTraffic="true" android:requestLegacyExternalStorage="true">
|
||||||
<activity android:name="com.ryanheise.audioservice.AudioServiceActivity" android:exported="true" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize">
|
<activity android:name="com.ryanheise.audioservice.AudioServiceActivity" android:exported="true" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize">
|
||||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
the Android process has started. This theme is visible to the user
|
the Android process has started. This theme is visible to the user
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
pkgbase = spotube-bin
|
pkgbase = spotube-bin
|
||||||
pkgdesc = A lightweight free Spotify desktop-client which handles playback manually, streams music using Youtube & no Spotify premium account is needed
|
pkgdesc = A lightweight free Spotify crossplatform-client which handles playback manually, streams music using Youtube & no Spotify premium account is needed
|
||||||
pkgver = 1.2.0
|
pkgver = 1.2.0
|
||||||
pkgrel = 2
|
pkgrel = 2
|
||||||
url = https://github.com/KRTirtho/spotube/
|
url = https://github.com/KRTirtho/spotube/
|
||||||
|
@ -3,7 +3,7 @@ pkgname=spotube-bin
|
|||||||
pkgver=1.2.0
|
pkgver=1.2.0
|
||||||
pkgrel=2
|
pkgrel=2
|
||||||
epoch=
|
epoch=
|
||||||
pkgdesc="A lightweight free Spotify desktop-client which handles playback manually, streams music using Youtube & no Spotify premium account is needed"
|
pkgdesc="A lightweight free Spotify crossplatform-client which handles playback manually, streams music using Youtube & no Spotify premium account is needed"
|
||||||
arch=(x86_64)
|
arch=(x86_64)
|
||||||
url="https://github.com/KRTirtho/spotube/"
|
url="https://github.com/KRTirtho/spotube/"
|
||||||
license=('BSD-4-Clause')
|
license=('BSD-4-Clause')
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
<docsUrl>https://github.com/KRTirtho/spotube#readme</docsUrl>
|
<docsUrl>https://github.com/KRTirtho/spotube#readme</docsUrl>
|
||||||
<bugTrackerUrl>https://github.com/KRTirtho/spotube/issues/new</bugTrackerUrl>
|
<bugTrackerUrl>https://github.com/KRTirtho/spotube/issues/new</bugTrackerUrl>
|
||||||
<tags>spotube music audio spotify youtube flutter</tags>
|
<tags>spotube music audio spotify youtube flutter</tags>
|
||||||
<summary>A lightweight free Spotify 🎧 desktop-client 🖥 which handles playback manually, streams music using Youtube & no Spotify premium account is needed 😱</summary>
|
<summary>A lightweight free Spotify 🎧 crossplatform-client 🖥📱 which handles playback manually, streams music using Youtube & no Spotify premium account is needed 😱</summary>
|
||||||
<description>
|
<description>
|
||||||
Spotube is a Flutter based lightweight spotify client. It utilizes the power
|
Spotube is a Flutter based lightweight spotify client. It utilizes the power
|
||||||
of Spotify & Youtube's public API & creates a hazardless, performant & resource
|
of Spotify & Youtube's public API & creates a hazardless, performant & resource
|
||||||
|
@ -7,5 +7,5 @@ Essential: no
|
|||||||
Installed-Size: 24400
|
Installed-Size: 24400
|
||||||
Depends: libkeybinder-3.0-0 (>= 0.3.2)
|
Depends: libkeybinder-3.0-0 (>= 0.3.2)
|
||||||
Maintainer: Kingkor Roy Tirtho <krtirtho@gmail.com>
|
Maintainer: Kingkor Roy Tirtho <krtirtho@gmail.com>
|
||||||
Description: A lightweight free Spotify desktop-client which handles playback manually, streams music using Youtube & no Spotify premium account is needed
|
Description: A lightweight free Spotify crossplatform-client which handles playback manually, streams music using Youtube & no Spotify premium account is needed
|
||||||
Homepage: https://github.com/KRTirtho/spotube
|
Homepage: https://github.com/KRTirtho/spotube
|
||||||
|
@ -15,12 +15,11 @@ class Lyrics extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
Playback playback = ref.watch(playbackProvider);
|
Playback playback = ref.watch(playbackProvider);
|
||||||
UserPreferences userPreferences = ref.watch(userPreferencesProvider);
|
UserPreferences userPreferences = ref.watch(userPreferencesProvider);
|
||||||
var lyrics = useState({});
|
final lyrics = useState({});
|
||||||
|
|
||||||
bool hasToken = userPreferences.geniusAccessToken.isNotEmpty;
|
final lyricsFuture = useMemoized(() {
|
||||||
var lyricsFuture = useMemoized(() {
|
|
||||||
if (playback.currentTrack == null ||
|
if (playback.currentTrack == null ||
|
||||||
!hasToken ||
|
userPreferences.geniusAccessToken.isEmpty ||
|
||||||
(playback.currentTrack?.id != null &&
|
(playback.currentTrack?.id != null &&
|
||||||
playback.currentTrack?.id == lyrics.value["id"])) {
|
playback.currentTrack?.id == lyrics.value["id"])) {
|
||||||
return null;
|
return null;
|
||||||
@ -31,9 +30,9 @@ class Lyrics extends HookConsumerWidget {
|
|||||||
apiKey: userPreferences.geniusAccessToken,
|
apiKey: userPreferences.geniusAccessToken,
|
||||||
optimizeQuery: true,
|
optimizeQuery: true,
|
||||||
);
|
);
|
||||||
}, [playback.currentTrack]);
|
}, [playback.currentTrack, userPreferences.geniusAccessToken]);
|
||||||
|
|
||||||
var lyricsSnapshot = useFuture(lyricsFuture);
|
final lyricsSnapshot = useFuture(lyricsFuture);
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
if (lyricsSnapshot.hasData &&
|
if (lyricsSnapshot.hasData &&
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
@ -40,9 +41,16 @@ class Player extends HookConsumerWidget {
|
|||||||
/// [disposeAllPlayers] method which is throwing
|
/// [disposeAllPlayers] method which is throwing
|
||||||
/// [UnimplementedException] in the [PlatformInterface]
|
/// [UnimplementedException] in the [PlatformInterface]
|
||||||
/// implementation
|
/// implementation
|
||||||
|
if (Platform.isAndroid || Platform.isIOS) {
|
||||||
playback.audioSession
|
playback.audioSession
|
||||||
?.setActive(true)
|
?.setActive(true)
|
||||||
.then((_) => player.setAsset("assets/warmer.mp3"));
|
.then((_) => player.setAsset("assets/warmer.mp3"))
|
||||||
|
.catchError((e) {
|
||||||
|
logger.e("useEffect", e, StackTrace.current);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
player.setAsset("assets/warmer.mp3");
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -64,7 +72,7 @@ class Player extends HookConsumerWidget {
|
|||||||
|
|
||||||
final entryRef = useRef<OverlayEntry?>(null);
|
final entryRef = useRef<OverlayEntry?>(null);
|
||||||
|
|
||||||
disposeOverlay() {
|
void disposeOverlay() {
|
||||||
try {
|
try {
|
||||||
entryRef.value?.remove();
|
entryRef.value?.remove();
|
||||||
entryRef.value = null;
|
entryRef.value = null;
|
||||||
@ -76,25 +84,37 @@ class Player extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
// clearing the overlay-entry as passing the already available
|
|
||||||
// entry will result in splashing while resizing the window
|
|
||||||
if (entryRef.value != null) disposeOverlay();
|
|
||||||
if (breakpoint.isLessThanOrEqualTo(Breakpoints.md)) {
|
|
||||||
entryRef.value = OverlayEntry(
|
|
||||||
opaque: false,
|
|
||||||
builder: (context) => PlayerOverlay(albumArt: albumArt),
|
|
||||||
);
|
|
||||||
// I can't believe useEffect doesn't run Post Frame aka
|
// I can't believe useEffect doesn't run Post Frame aka
|
||||||
// after rendering/painting the UI
|
// after rendering/painting the UI
|
||||||
// `My disappointment is immeasurable and my day is ruined` XD
|
// `My disappointment is immeasurable and my day is ruined` XD
|
||||||
WidgetsBinding.instance?.addPostFrameCallback((time) {
|
WidgetsBinding.instance?.addPostFrameCallback((time) {
|
||||||
|
// clearing the overlay-entry as passing the already available
|
||||||
|
// entry will result in splashing while resizing the window
|
||||||
|
if (breakpoint.isLessThanOrEqualTo(Breakpoints.md) &&
|
||||||
|
entryRef.value == null &&
|
||||||
|
playback.currentTrack != null) {
|
||||||
|
entryRef.value = OverlayEntry(
|
||||||
|
opaque: false,
|
||||||
|
builder: (context) => PlayerOverlay(albumArt: albumArt),
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
Overlay.of(context)?.insert(entryRef.value!);
|
||||||
|
} catch (e) {
|
||||||
|
if (e is AssertionError &&
|
||||||
|
e.message ==
|
||||||
|
'The specified entry is already present in the Overlay.') {
|
||||||
|
disposeOverlay();
|
||||||
Overlay.of(context)?.insert(entryRef.value!);
|
Overlay.of(context)?.insert(entryRef.value!);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
disposeOverlay();
|
||||||
|
}
|
||||||
|
});
|
||||||
return () {
|
return () {
|
||||||
disposeOverlay();
|
disposeOverlay();
|
||||||
};
|
};
|
||||||
}, [breakpoint]);
|
}, [breakpoint, playback.currentTrack]);
|
||||||
|
|
||||||
// returning an empty non spacious Container as the overlay will take
|
// returning an empty non spacious Container as the overlay will take
|
||||||
// place in the global overlay stack aka [_entries]
|
// place in the global overlay stack aka [_entries]
|
||||||
|
@ -66,14 +66,17 @@ class Settings extends HookConsumerWidget {
|
|||||||
? () async {
|
? () async {
|
||||||
SharedPreferences localStorage =
|
SharedPreferences localStorage =
|
||||||
await SharedPreferences.getInstance();
|
await SharedPreferences.getInstance();
|
||||||
|
if (geniusAccessToken.value != null &&
|
||||||
|
geniusAccessToken.value!.isNotEmpty) {
|
||||||
preferences.setGeniusAccessToken(
|
preferences.setGeniusAccessToken(
|
||||||
geniusAccessToken.value ?? "");
|
geniusAccessToken.value!,
|
||||||
|
);
|
||||||
localStorage.setString(
|
localStorage.setString(
|
||||||
LocalStorageKeys.geniusAccessToken,
|
LocalStorageKeys.geniusAccessToken,
|
||||||
geniusAccessToken.value ?? "");
|
geniusAccessToken.value!);
|
||||||
|
}
|
||||||
|
|
||||||
geniusAccessToken.value = null;
|
geniusAccessToken.value = null;
|
||||||
|
|
||||||
textEditingController.text = "";
|
textEditingController.text = "";
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
@ -141,12 +144,12 @@ class Settings extends HookConsumerWidget {
|
|||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
if (auth.isAnonymous)
|
if (auth.isAnonymous)
|
||||||
Wrap(
|
Wrap(
|
||||||
|
spacing: 20,
|
||||||
|
runSpacing: 20,
|
||||||
alignment: WrapAlignment.spaceBetween,
|
alignment: WrapAlignment.spaceBetween,
|
||||||
runAlignment: WrapAlignment.center,
|
|
||||||
crossAxisAlignment: WrapCrossAlignment.center,
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const Text("Login with your Spotify account"),
|
const Text("Login with your Spotify account"),
|
||||||
const SizedBox(width: 20),
|
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
child: Text("Connect with Spotify".toUpperCase()),
|
child: Text("Connect with Spotify".toUpperCase()),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
@ -1,10 +1,23 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
class AnonymousFallback extends StatelessWidget {
|
class AnonymousFallback extends StatelessWidget {
|
||||||
const AnonymousFallback({Key? key}) : super(key: key);
|
const AnonymousFallback({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return const Center(child: Text("You're not logged in"));
|
return Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Text("You're not logged in"),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
ElevatedButton(
|
||||||
|
child: const Text("Login with Spotify"),
|
||||||
|
onPressed: () => GoRouter.of(context).push("/settings"),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import 'package:spotube/helpers/artist-to-string.dart';
|
|||||||
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
|
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
|
||||||
import 'package:path_provider/path_provider.dart' as path_provider;
|
import 'package:path_provider/path_provider.dart' as path_provider;
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
|
||||||
enum TrackStatus { downloading, idle, done }
|
enum TrackStatus { downloading, idle, done }
|
||||||
|
|
||||||
@ -21,16 +22,31 @@ class DownloadTrackButton extends HookWidget {
|
|||||||
|
|
||||||
var _downloadTrack = useCallback(() async {
|
var _downloadTrack = useCallback(() async {
|
||||||
if (track == null) return;
|
if (track == null) return;
|
||||||
|
if ((Platform.isAndroid || Platform.isIOS) &&
|
||||||
|
!await Permission.storage.isGranted &&
|
||||||
|
!await Permission.storage.isPermanentlyDenied) {
|
||||||
|
final status = await Permission.storage.request();
|
||||||
|
if (!status.isGranted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Text("Couldn't download track. Not enough permissions"),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
StreamManifest manifest =
|
StreamManifest manifest =
|
||||||
await yt.videos.streamsClient.getManifest(track?.href);
|
await yt.videos.streamsClient.getManifest(track?.href);
|
||||||
|
|
||||||
var audioStream = yt.videos.streamsClient.get(
|
final audioStream = yt.videos.streamsClient
|
||||||
|
.get(
|
||||||
manifest.audioOnly
|
manifest.audioOnly
|
||||||
.where((audio) => audio.codec.mimeType == "audio/mp4")
|
.where((audio) => audio.codec.mimeType == "audio/mp4")
|
||||||
.withHighestBitrate(),
|
.withHighestBitrate(),
|
||||||
);
|
)
|
||||||
|
.asBroadcastStream();
|
||||||
|
|
||||||
var statusCb = audioStream.listen(
|
final statusCb = audioStream.listen(
|
||||||
(event) {
|
(event) {
|
||||||
if (status.value != TrackStatus.downloading) {
|
if (status.value != TrackStatus.downloading) {
|
||||||
status.value = TrackStatus.downloading;
|
status.value = TrackStatus.downloading;
|
||||||
@ -50,7 +66,10 @@ class DownloadTrackButton extends HookWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
String downloadFolder = path.join(
|
String downloadFolder = path.join(
|
||||||
(await path_provider.getDownloadsDirectory())!.path, "Spotube");
|
Platform.isAndroid
|
||||||
|
? "/storage/emulated/0/Download"
|
||||||
|
: (await path_provider.getDownloadsDirectory())!.path,
|
||||||
|
"Spotube");
|
||||||
String fileName =
|
String fileName =
|
||||||
"${track?.name} - ${artistsToString<Artist>(track?.artists ?? [])}.mp3";
|
"${track?.name} - ${artistsToString<Artist>(track?.artists ?? [])}.mp3";
|
||||||
File outputFile = File(path.join(downloadFolder, fileName));
|
File outputFile = File(path.join(downloadFolder, fileName));
|
||||||
|
@ -88,7 +88,7 @@ Future<List?> searchSong(
|
|||||||
Future<String?> getLyrics(
|
Future<String?> getLyrics(
|
||||||
String title,
|
String title,
|
||||||
String artist, {
|
String artist, {
|
||||||
String apiKey = "",
|
required String apiKey,
|
||||||
bool optimizeQuery = false,
|
bool optimizeQuery = false,
|
||||||
bool authHeader = false,
|
bool authHeader = false,
|
||||||
}) async {
|
}) async {
|
||||||
|
@ -107,8 +107,15 @@ class Playback extends ChangeNotifier {
|
|||||||
_processingStateStreamListener =
|
_processingStateStreamListener =
|
||||||
player.processingStateStream.listen((event) async {
|
player.processingStateStream.listen((event) async {
|
||||||
try {
|
try {
|
||||||
if (event == ProcessingState.completed && _currentTrack?.id != null) {
|
if (event != ProcessingState.completed) return;
|
||||||
|
if (_currentTrack?.id != null) {
|
||||||
movePlaylistPositionBy(1);
|
movePlaylistPositionBy(1);
|
||||||
|
} else {
|
||||||
|
await audioSession?.setActive(false);
|
||||||
|
_isPlaying = false;
|
||||||
|
_duration = null;
|
||||||
|
_callAllDurationListeners(null);
|
||||||
|
notifyListeners();
|
||||||
}
|
}
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
_logger.e("PrecessingStateStreamListener", e, stack);
|
_logger.e("PrecessingStateStreamListener", e, stack);
|
||||||
@ -167,6 +174,7 @@ class Playback extends ChangeNotifier {
|
|||||||
_callAllDurationListeners(null);
|
_callAllDurationListeners(null);
|
||||||
_currentPlaylist = null;
|
_currentPlaylist = null;
|
||||||
_currentTrack = null;
|
_currentTrack = null;
|
||||||
|
_audioSession?.setActive(false);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,7 +204,7 @@ class Playback extends ChangeNotifier {
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
movePlaylistPositionBy(int pos) {
|
void movePlaylistPositionBy(int pos) {
|
||||||
if (_currentTrack != null && _currentPlaylist != null) {
|
if (_currentTrack != null && _currentPlaylist != null) {
|
||||||
int index = _currentPlaylist!.trackIds.indexOf(_currentTrack!.id!) + pos;
|
int index = _currentPlaylist!.trackIds.indexOf(_currentTrack!.id!) + pos;
|
||||||
|
|
||||||
|
@ -4,8 +4,10 @@ import 'package:flutter/cupertino.dart';
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:hotkey_manager/hotkey_manager.dart';
|
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:spotube/helpers/get-random-element.dart';
|
||||||
import 'package:spotube/models/LocalStorageKeys.dart';
|
import 'package:spotube/models/LocalStorageKeys.dart';
|
||||||
import 'package:spotube/models/Logger.dart';
|
import 'package:spotube/models/Logger.dart';
|
||||||
|
import 'package:spotube/models/generated_secrets.dart';
|
||||||
|
|
||||||
class UserPreferences extends ChangeNotifier {
|
class UserPreferences extends ChangeNotifier {
|
||||||
String geniusAccessToken;
|
String geniusAccessToken;
|
||||||
@ -33,15 +35,17 @@ class UserPreferences extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
return HotKey.fromJson(json);
|
return HotKey.fromJson(json);
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
onInit() async {
|
Future<void> onInit() async {
|
||||||
try {
|
try {
|
||||||
SharedPreferences localStorage = await SharedPreferences.getInstance();
|
SharedPreferences localStorage = await SharedPreferences.getInstance();
|
||||||
String? accessToken =
|
String? accessToken =
|
||||||
localStorage.getString(LocalStorageKeys.geniusAccessToken);
|
localStorage.getString(LocalStorageKeys.geniusAccessToken);
|
||||||
|
geniusAccessToken = accessToken != null && accessToken.isNotEmpty
|
||||||
if (accessToken != null) geniusAccessToken = accessToken;
|
? accessToken
|
||||||
|
: getRandomElement(lyricsSecrets);
|
||||||
|
|
||||||
nextTrackHotKey ??= (await _getHotKeyFromLocalStorage(
|
nextTrackHotKey ??= (await _getHotKeyFromLocalStorage(
|
||||||
localStorage,
|
localStorage,
|
||||||
@ -75,12 +79,12 @@ class UserPreferences extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setGeniusAccessToken(String token) {
|
void setGeniusAccessToken(String token) {
|
||||||
geniusAccessToken = token;
|
geniusAccessToken = token;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
setNextTrackHotKey(HotKey? value) {
|
void setNextTrackHotKey(HotKey? value) {
|
||||||
nextTrackHotKey = value;
|
nextTrackHotKey = value;
|
||||||
SharedPreferences.getInstance().then((preferences) {
|
SharedPreferences.getInstance().then((preferences) {
|
||||||
preferences.setString(
|
preferences.setString(
|
||||||
@ -91,7 +95,7 @@ class UserPreferences extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
setPrevTrackHotKey(HotKey? value) {
|
void setPrevTrackHotKey(HotKey? value) {
|
||||||
prevTrackHotKey = value;
|
prevTrackHotKey = value;
|
||||||
SharedPreferences.getInstance().then((preferences) {
|
SharedPreferences.getInstance().then((preferences) {
|
||||||
preferences.setString(
|
preferences.setString(
|
||||||
@ -102,7 +106,7 @@ class UserPreferences extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
setPlayPauseHotKey(HotKey? value) {
|
void setPlayPauseHotKey(HotKey? value) {
|
||||||
playPauseHotKey = value;
|
playPauseHotKey = value;
|
||||||
SharedPreferences.getInstance().then((preferences) {
|
SharedPreferences.getInstance().then((preferences) {
|
||||||
preferences.setString(
|
preferences.setString(
|
||||||
@ -114,5 +118,5 @@ class UserPreferences extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var userPreferencesProvider =
|
final userPreferencesProvider =
|
||||||
ChangeNotifierProvider((_) => UserPreferences(geniusAccessToken: ""));
|
ChangeNotifierProvider((_) => UserPreferences(geniusAccessToken: ""));
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<id>com.github.KRTirtho.Spotube</id>
|
<id>com.github.KRTirtho.Spotube</id>
|
||||||
<name>Spotube</name>
|
<name>Spotube</name>
|
||||||
<summary>
|
<summary>
|
||||||
A lightweight free Spotify desktop-client which handles playback manually, streams music using Youtube & no Spotify premium account is needed
|
A lightweight free Spotify crossplatform-client which handles playback manually, streams music using Youtube & no Spotify premium account is needed
|
||||||
</summary>
|
</summary>
|
||||||
<metadata_license>CC0-1.0</metadata_license>
|
<metadata_license>CC0-1.0</metadata_license>
|
||||||
<project_license>BSD-4-Clause</project_license>
|
<project_license>BSD-4-Clause</project_license>
|
||||||
|
35
pubspec.lock
35
pubspec.lock
@ -506,6 +506,41 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.11.1"
|
version: "1.11.1"
|
||||||
|
permission_handler:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: permission_handler
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "9.2.0"
|
||||||
|
permission_handler_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_android
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "9.0.2+1"
|
||||||
|
permission_handler_apple:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_apple
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "9.0.3"
|
||||||
|
permission_handler_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.7.0"
|
||||||
|
permission_handler_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_windows
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.0"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
name: spotube
|
name: spotube
|
||||||
description: A lightweight free Spotify desktop-client which handles playback manually, streams music using Youtube & no Spotify premium account is needed
|
description: A lightweight free Spotify crossplatform-client which handles playback manually, streams music using Youtube & no Spotify premium account is needed
|
||||||
|
|
||||||
# The following line prevents the package from being accidentally published to
|
# The following line prevents the package from being accidentally published to
|
||||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||||
@ -56,6 +56,7 @@ dependencies:
|
|||||||
audio_session: ^0.1.6+1
|
audio_session: ^0.1.6+1
|
||||||
just_audio_background: ^0.0.1-beta.5
|
just_audio_background: ^0.0.1-beta.5
|
||||||
logger: ^1.1.0
|
logger: ^1.1.0
|
||||||
|
permission_handler: ^9.2.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/bin/env bash
|
#!/bin/env bash
|
||||||
md5sum build/**/*.{AppImage,deb,tar.xz,dmg,exe,nupkg} >build/RELEASE.md5sum
|
md5sum build/**/*.{AppImage,deb,tar.xz,dmg,exe,nupkg,apk} >build/RELEASE.md5sum
|
||||||
sha256sum build/**/*.{AppImage,deb,tar.xz,dmg,exe,nupkg} >build/RELEASE.sha256sum
|
sha256sum build/**/*.{AppImage,deb,tar.xz,dmg,exe,nupkg,apk} >build/RELEASE.sha256sum
|
||||||
sed -i 's|build/Spotube-.*-Bundle/||' build/RELEASE.sha256sum build/RELEASE.md5sum
|
sed -i 's|build/Spotube-.*-Bundle/||' build/RELEASE.sha256sum build/RELEASE.md5sum
|
@ -10,7 +10,7 @@
|
|||||||
[Setup]
|
[Setup]
|
||||||
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
|
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
|
||||||
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
|
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
|
||||||
AppId={{80B901C8-D6FE-494E-8AF7-A2BD440E8644}
|
AppId={80B901C8-D6FE-494E-8AF7-A2BD440E8644}
|
||||||
AppName={#MyAppName}
|
AppName={#MyAppName}
|
||||||
AppVersion={#MyAppVersion}
|
AppVersion={#MyAppVersion}
|
||||||
;AppVerName={#MyAppName} {#MyAppVersion}
|
;AppVerName={#MyAppName} {#MyAppVersion}
|
||||||
@ -43,6 +43,7 @@ Source: "..\build\windows\runner\Release\flutter_windows.dll"; DestDir: "{app}";
|
|||||||
Source: "..\build\windows\runner\Release\hotkey_manager_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "..\build\windows\runner\Release\hotkey_manager_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "..\build\windows\runner\Release\libwinmedia.dll"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "..\build\windows\runner\Release\libwinmedia.dll"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "..\build\windows\runner\Release\libwinmedia_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "..\build\windows\runner\Release\libwinmedia_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
|
Source: "..\build\windows\runner\Release\permission_handler_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "..\build\windows\runner\Release\spotube.exp"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "..\build\windows\runner\Release\spotube.exp"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "..\build\windows\runner\Release\spotube.lib"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "..\build\windows\runner\Release\spotube.lib"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "..\build\windows\runner\Release\url_launcher_windows_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "..\build\windows\runner\Release\url_launcher_windows_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <bitsdojo_window_windows/bitsdojo_window_plugin.h>
|
#include <bitsdojo_window_windows/bitsdojo_window_plugin.h>
|
||||||
#include <hotkey_manager/hotkey_manager_plugin.h>
|
#include <hotkey_manager/hotkey_manager_plugin.h>
|
||||||
#include <libwinmedia/libwinmedia_plugin.h>
|
#include <libwinmedia/libwinmedia_plugin.h>
|
||||||
|
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||||
#include <url_launcher_windows/url_launcher_windows.h>
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
@ -18,6 +19,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
|||||||
registry->GetRegistrarForPlugin("HotkeyManagerPlugin"));
|
registry->GetRegistrarForPlugin("HotkeyManagerPlugin"));
|
||||||
LibwinmediaPluginRegisterWithRegistrar(
|
LibwinmediaPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("LibwinmediaPlugin"));
|
registry->GetRegistrarForPlugin("LibwinmediaPlugin"));
|
||||||
|
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||||
UrlLauncherWindowsRegisterWithRegistrar(
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||||||
bitsdojo_window_windows
|
bitsdojo_window_windows
|
||||||
hotkey_manager
|
hotkey_manager
|
||||||
libwinmedia
|
libwinmedia
|
||||||
|
permission_handler_windows
|
||||||
url_launcher_windows
|
url_launcher_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user