Merge branch 'master' into master
@ -1,4 +1,4 @@
|
||||
{
|
||||
"flutterSdkVersion": "3.16.0",
|
||||
"flutterSdkVersion": "3.19.1",
|
||||
"flavors": {}
|
||||
}
|
||||
13
.github/workflows/spotube-release-binary.yml
vendored
@ -26,7 +26,7 @@ on:
|
||||
default: true
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: '3.16.3'
|
||||
FLUTTER_VERSION: '3.19.1'
|
||||
|
||||
jobs:
|
||||
windows:
|
||||
@ -181,6 +181,7 @@ jobs:
|
||||
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: ${{ inputs.channel == 'stable' }}
|
||||
with:
|
||||
if-no-files-found: error
|
||||
name: Spotube-Release-Binaries
|
||||
@ -188,6 +189,16 @@ jobs:
|
||||
dist/Spotube-linux-x86_64.deb
|
||||
dist/Spotube-linux-x86_64.rpm
|
||||
dist/spotube-linux-${{ env.BUILD_VERSION }}-x86_64.tar.xz
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: ${{ inputs.channel == 'nightly' }}
|
||||
with:
|
||||
if-no-files-found: error
|
||||
name: Spotube-Release-Binaries
|
||||
path: |
|
||||
dist/Spotube-linux-x86_64.deb
|
||||
dist/Spotube-linux-x86_64.rpm
|
||||
dist/spotube-linux-nightly-x86_64.tar.xz
|
||||
|
||||
- name: Debug With SSH When fails
|
||||
if: ${{ failure() && inputs.debug && inputs.channel == 'nightly' }}
|
||||
|
||||
32
CHANGELOG.md
@ -2,6 +2,38 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
## [3.5.0](https://github.com/krtirtho/spotube/compare/v3.4.1...v3.5.0) (2024-03-08)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add endless playback support [#285](https://github.com/krtirtho/spotube/issues/285) ([9dfd49c](https://github.com/krtirtho/spotube/commit/9dfd49ca04f0e915e333e205b17ac70456873f6e))
|
||||
* add getting started page ([96a2a1f](https://github.com/krtirtho/spotube/commit/96a2a1f5a622cb3c580041417d5023e37fa69716))
|
||||
* Add iOS background play support ([#1166](https://github.com/krtirtho/spotube/issues/1166)) ([095587e](https://github.com/krtirtho/spotube/commit/095587ee84f7d867c69fcf4b09ed608d63478e1e))
|
||||
* add songlink based track matching for youtube and open song link button ([9095a8c](https://github.com/krtirtho/spotube/commit/9095a8c8f849e42daabb7efcc20085cfb863c974))
|
||||
* **playlist:** show confirmation before deleting user playlist [#1222](https://github.com/krtirtho/spotube/issues/1222) ([9f92440](https://github.com/krtirtho/spotube/commit/9f9244062a39759aa0ce28d2d5f7c8fa53d73003))
|
||||
* Sort by Duration ([#1238](https://github.com/krtirtho/spotube/issues/1238)) ([6f8271f](https://github.com/krtirtho/spotube/commit/6f8271f5e9394cb4053e41dd222aa2844c34d609))
|
||||
* start radio support ([4defeef](https://github.com/krtirtho/spotube/commit/4defeefe7e5947aa00a2afb2a06577ec141cdc52))
|
||||
* **translations:** add Korean translation ([#1275](https://github.com/krtirtho/spotube/issues/1275)) ([fdea930](https://github.com/krtirtho/spotube/commit/fdea9307bbfb8f3f62cfb795bfb3ca58c38c33d9))
|
||||
* **translations:** Added Vietnamese ([#1135](https://github.com/krtirtho/spotube/issues/1135)) ([019ba86](https://github.com/krtirtho/spotube/commit/019ba865e20a8b54ea3490c01e47158eaf3a4c8d))
|
||||
* **windows:** Install Visual C++ 2015-2022 Redistributable if missing when installing ([ba69496](https://github.com/krtirtho/spotube/commit/ba69496dcc9a1b7f6ea4e104e71764a854d27f1f))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* album images are small in certain places ([ca76a39](https://github.com/krtirtho/spotube/commit/ca76a39910b1a5af91aa7882a0d33c9d71db58a2))
|
||||
* album, artist page not loading [#1282](https://github.com/krtirtho/spotube/issues/1282) ([a9a1d4c](https://github.com/krtirtho/spotube/commit/a9a1d4c9dc24aaf3181dc4090d1822ebfe755991))
|
||||
* **android:** audio issue when screen is off and broadcast audio session id ([#1221](https://github.com/krtirtho/spotube/issues/1221) & [#1247](https://github.com/krtirtho/spotube/issues/1247)) ([17105a6](https://github.com/krtirtho/spotube/commit/17105a640bf5107bd5d333b9b4d097c14a3949a2)), closes [KRTirtho/spotube#571](https://github.com/KRTirtho/spotube/issues/571)
|
||||
* **android:** only ask battery optimization once [#1252](https://github.com/krtirtho/spotube/issues/1252) ([e516afb](https://github.com/krtirtho/spotube/commit/e516afb185f616471822ea745495a3d1d1281bd3))
|
||||
* **android:** pressing back button in any other tab other than home exits the app ([c3289a0](https://github.com/krtirtho/spotube/commit/c3289a0ba4e7de094a15246677ffcb940504ebde))
|
||||
* **android:** system back button in player page exits the app ([3294f65](https://github.com/krtirtho/spotube/commit/3294f657fe8a03b18d9be8974968b6508465963d))
|
||||
* cleanTitle removing feat and ft from words instead of whole words ([8612345](https://github.com/krtirtho/spotube/commit/86123456f2ff577921cf62cffca180427dfe1dd5))
|
||||
* friends list not scrollable with mouse drag ([ab08c82](https://github.com/krtirtho/spotube/commit/ab08c82c8dd501263049f3adcbd48907ba13e3a9))
|
||||
* no draggable scrollbar in playlist/album page [#1158](https://github.com/krtirtho/spotube/issues/1158) ([6f71e52](https://github.com/krtirtho/spotube/commit/6f71e52ea8a5712d2c3527f2a524af9fbb718bef))
|
||||
* non-banger songs breaking the queue if sources not found ([90f7c53](https://github.com/krtirtho/spotube/commit/90f7c531cdc8640afdbabf5a0592159715ea1e6f))
|
||||
* track loading when not found in Youtube ([e964f61](https://github.com/krtirtho/spotube/commit/e964f61d38cb303e3d3fd60c866414f57207181c))
|
||||
* **translations:** Update app_nl.arb ([#1168](https://github.com/krtirtho/spotube/issues/1168)) ([8167963](https://github.com/krtirtho/spotube/commit/8167963212eeb5dfb0b4fb2eadf81d466659a9f1))
|
||||
|
||||
## [3.4.1](https://personal.github.com/krtirtho/spotube/compare/v3.4.0...v3.4.1) (2024-01-27)
|
||||
|
||||
|
||||
|
||||
1
Makefile
@ -28,6 +28,7 @@ publishaur:
|
||||
|
||||
innoinstall:
|
||||
powershell curl -o build\installer.exe http://files.jrsoftware.org/is/6/innosetup-${INNO_VERSION}.exe
|
||||
powershell git clone https://github.com/DomGries/InnoDependencyInstaller.git build\inno-depend
|
||||
powershell build\installer.exe /verysilent /allusers /dir=build\iscc
|
||||
|
||||
inno:
|
||||
|
||||
14
README.md
@ -13,6 +13,8 @@ Btw it's not just another Electron app 😉
|
||||
<a href="https://patreon.com/krtirtho"><img alt="Support me on Patron" height="56" src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/donate/patreon-singular_vector.svg"></a>
|
||||
<a href="https://www.buymeacoffee.com/krtirtho"><img alt="Buy me a Coffee" height="56" src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/donate/buymeacoffee-singular_vector.svg"></a>
|
||||
|
||||
[](https://news.ycombinator.com/item?id=39066136)
|
||||
|
||||
<a href="https://opencollective.com/spotube"><img src="https://opencollective.com/spotube/donate/button.png?color=blue" alt="Donate to our Open Collective" height="45"></a>
|
||||
|
||||
---
|
||||
@ -202,6 +204,7 @@ If you are concerned, you can [read the reason of choosing this license](https:/
|
||||
1. [Piped](https://piped-docs.kavin.rocks/) - Piped is a privacy friendly alternative YouTube frontend, which is efficient and scalable by design.
|
||||
1. [YouTube](https://youtube.com/) - YouTube is an American online video-sharing platform headquartered in San Bruno, California. Three former PayPal employees—Chad Hurley, Steve Chen, and Jawed Karim—created the service in February 2005
|
||||
1. [JioSaavn](https://www.jiosaavn.com) - JioSaavn is an Indian online music streaming service and a digital distributor of Bollywood, English and other regional Indian music across the world. Since it was founded in 2007 as Saavn, the company has acquired rights to over 5 crore (50 million) music tracks in 15 languages
|
||||
1. [SongLink](https://song.link) - SongLink is a free smart link service that helps you share music with your audience. It's a one-stop-shop for creating smart links for music, podcasts, and other audio content
|
||||
1. [Linux](https://www.linux.org) - Linux is a family of open-source Unix-like operating systems based on the Linux kernel, an operating system kernel first released on September 17, 1991, by Linus Torvalds. Linux is typically packaged in a Linux distribution
|
||||
1. [AUR](https://aur.archlinux.org) - AUR stands for Arch User Repository. It is a community-driven repository for Arch-based Linux distributions users
|
||||
1. [Flatpak](https://flatpak.org) - Flatpak is a utility for software deployment and package management for Linux
|
||||
@ -240,7 +243,7 @@ If you are concerned, you can [read the reason of choosing this license](https:/
|
||||
1. [flutter_hooks](https://github.com/rrousselGit/flutter_hooks) - A flutter implementation of React hooks. It adds a new kind of widget with enhanced code reuse.
|
||||
1. [flutter_inappwebview](https://inappwebview.dev/) - A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window.
|
||||
1. [flutter_native_splash](https://pub.dev/packages/flutter_native_splash) - Customize Flutter's default white native splash screen with background color and splash image. Supports dark mode, full screen, and more.
|
||||
1. [flutter_riverpod](https://riverpod.dev) - A simple way to access state from anywhere in your application while robust and testable.
|
||||
1. [flutter_riverpod](https://riverpod.dev) - A reactive caching and data-binding framework. Riverpod makes working with asynchronous code a breeze.
|
||||
1. [flutter_secure_storage](https://pub.dev/packages/flutter_secure_storage) - Flutter Secure Storage provides API to store data in secure storage. Keychain is used in iOS, KeyStore based solution is used in Android.
|
||||
1. [flutter_svg](https://pub.dev/packages/flutter_svg) - An SVG rendering and widget library for Flutter, which allows painting and displaying Scalable Vector Graphics 1.1 files.
|
||||
1. [form_validator](https://github.com/TheMisir/form-validator) - Simplest form validation library for flutter's form field widgets
|
||||
@ -249,7 +252,7 @@ If you are concerned, you can [read the reason of choosing this license](https:/
|
||||
1. [google_fonts](https://pub.dev/packages/google_fonts) - A Flutter package to use fonts from fonts.google.com. Supports HTTP fetching, caching, and asset bundling.
|
||||
1. [hive](https://github.com/hivedb/hive/tree/master/hive) - Lightweight and blazing fast key-value database written in pure Dart. Strongly encrypted using AES-256.
|
||||
1. [hive_flutter](https://github.com/hivedb/hive/tree/master/hive_flutter) - Extension for Hive. Makes it easier to use Hive in Flutter apps.
|
||||
1. [hooks_riverpod](https://riverpod.dev) - A simple way to access state from anywhere in your application while robust and testable.
|
||||
1. [hooks_riverpod](https://riverpod.dev) - A reactive caching and data-binding framework. Riverpod makes working with asynchronous code a breeze.
|
||||
1. [html](https://pub.dev/packages/html) - APIs for parsing and manipulating HTML content outside the browser.
|
||||
1. [http](https://pub.dev/packages/http) - A composable, multi-platform, Future-based API for HTTP requests.
|
||||
1. [image_picker](https://pub.dev/packages/image_picker) - Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera.
|
||||
@ -266,14 +269,12 @@ If you are concerned, you can [read the reason of choosing this license](https:/
|
||||
1. [path](https://pub.dev/packages/path) - A string-based path manipulation library. All of the path operations you know and love, with solid support for Windows, POSIX (Linux and Mac OS X), and the web.
|
||||
1. [path_provider](https://pub.dev/packages/path_provider) - Flutter plugin for getting commonly used locations on host platform file systems, such as the temp and app data directories.
|
||||
1. [permission_handler](https://pub.dev/packages/permission_handler) - Permission plugin for Flutter. This plugin provides a cross-platform (iOS, Android) API to request and check permissions.
|
||||
1. [piped_client](https://github.com/KRTirtho/piped_client) - API Client for piped.video
|
||||
1. [popover](https://github.com/minikin/popover) - A popover is a transient view that appears above other content onscreen when you tap a control or in an area.
|
||||
1. [scroll_to_index](https://github.com/quire-io/scroll-to-index) - Scroll to a specific child of any scrollable widget in Flutter
|
||||
1. [sidebarx](https://github.com/Frezyx/sidebarx) - flutter multiplatform navigation sidebar / side navigationbar / drawer widget
|
||||
1. [shared_preferences](https://pub.dev/packages/shared_preferences) - Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android.
|
||||
1. [skeleton_text](https://github.com/101Loop/Skeleton-Text) - A package that provides an easy way to add skeleton text loading animation in Flutter project. This project is a part of 101Loop community.
|
||||
1. [smtc_windows](https://github.com/KRTirtho/smtc_windows) - Windows `SystemMediaTransportControls` implementation for Flutter giving access to Windows OS Media Control applet.
|
||||
1. [spotify](https://github.com/rinukkusu/spotify-dart) - An incomplete dart library for interfacing with the Spotify Web API.
|
||||
1. [stroke_text](https://github.com/MohamedAbd0/stroke_text) - A Simple Flutter plugin for applying stroke (border) style to a text widget
|
||||
1. [system_theme](https://pub.dev/packages/system_theme) - A plugin to get the current system theme info. Supports Android, Web, Windows, Linux and macOS
|
||||
1. [titlebar_buttons](https://github.com/gtk-flutter/titlebar_buttons) - A package which provides most of the titlebar buttons from windows, linux and macos.
|
||||
@ -296,6 +297,9 @@ If you are concerned, you can [read the reason of choosing this license](https:/
|
||||
1. [app_links](https://github.com/llfbandit/app_links) - Android App Links, Deep Links, iOs Universal Links and Custom URL schemes handler for Flutter (desktop included).
|
||||
1. [win32_registry](https://win32.pub) - A package that provides a friendly Dart API for accessing the Windows Registry.
|
||||
1. [flutter_sharing_intent](https://github.com/bhagat-techind/flutter_sharing_intent.git) - A flutter plugin that allow flutter apps to receive photos, videos, text, urls or any other file types from another app.
|
||||
1. [flutter_broadcasts](https://pub.dev/packages/flutter_broadcasts) - A plugin for sending and receiving broadcasts with Android intents and iOS notifications.
|
||||
1. [freezed_annotation](https://pub.dev/packages/freezed_annotation) - Annotations for the freezed code-generator. This package does nothing without freezed too.
|
||||
1. [spotify](https://github.com/rinukkusu/spotify-dart) - An incomplete dart library for interfacing with the Spotify Web API.
|
||||
1. [build_runner](https://pub.dev/packages/build_runner) - A build system for Dart code generation and modular compilation.
|
||||
1. [envied_generator](https://github.com/petercinibulk/envied) - Generator for the Envied package. See https://pub.dev/packages/envied.
|
||||
1. [flutter_distributor](https://distributor.leanflutter.org) - A complete tool for packaging and publishing your Flutter apps.
|
||||
@ -306,7 +310,9 @@ If you are concerned, you can [read the reason of choosing this license](https:/
|
||||
1. [json_serializable](https://pub.dev/packages/json_serializable) - Automatically generate code for converting to and from JSON by annotating Dart classes.
|
||||
1. [pub_api_client](https://github.com/leoafarias/pub_api_client) - An API Client for Pub to interact with public package information.
|
||||
1. [pubspec_parse](https://pub.dev/packages/pubspec_parse) - Simple package for parsing pubspec.yaml files with a type-safe API and rich error reporting.
|
||||
1. [freezed](https://pub.dev/packages/freezed) - Code generation for immutable classes that has a simple syntax/API without compromising on the features.
|
||||
1. [flutter_desktop_tools](https://github.com/KRTirtho/flutter_desktop_tools) - Essential collection of tools for flutter desktop app development
|
||||
1. [piped_client](https://github.com/KRTirtho/piped_client) - API Client for piped.video
|
||||
1. [scrobblenaut](https://github.com/Nebulino/Scrobblenaut) - A deadly simple LastFM API Wrapper for Dart. So deadly simple that it's gonna hit the mark.
|
||||
1. [window_size](https://github.com/google/flutter-desktop-embedding.git) - Allows resizing and repositioning the window containing Flutter.
|
||||
1. [draggable_scrollbar](https://github.com/fluttercommunity/flutter-draggable-scrollbar) - A scrollbar that can be dragged for quickly navigation through a vertical list. Additional option is showing label next to scrollthumb with information about current item.
|
||||
|
||||
@ -31,4 +31,6 @@ linter:
|
||||
analyzer:
|
||||
enable-experiment:
|
||||
- records
|
||||
- patterns
|
||||
- patterns
|
||||
errors:
|
||||
invalid_annotation_target: ignore
|
||||
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
@ -5,6 +5,10 @@
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
Flutter draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
<item name="android:forceDarkAllowed">false</item>
|
||||
<item name="android:windowFullscreen">false</item>
|
||||
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
|
||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
|
||||
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 26 KiB |
BIN
assets/logos/songlink-transparent.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
assets/logos/songlink.png
Normal file
|
After Width: | Height: | Size: 86 KiB |
26
bin/translated_messages.dart
Normal file
@ -0,0 +1,26 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
void main(List<String> args) async {
|
||||
final translatedFile =
|
||||
jsonDecode(await File('tm.json').readAsString()) as Map<String, dynamic>;
|
||||
|
||||
for (final MapEntry(:key, :value) in translatedFile.entries) {
|
||||
print('Updating locale: $key');
|
||||
final file = File('lib/l10n/app_$key.arb');
|
||||
|
||||
final fileContent =
|
||||
jsonDecode(await file.readAsString()) as Map<String, dynamic>;
|
||||
|
||||
final newContent = {
|
||||
...fileContent,
|
||||
...value,
|
||||
};
|
||||
|
||||
await file.writeAsString(
|
||||
const JsonEncoder.withIndent(' ').convert(newContent),
|
||||
);
|
||||
|
||||
print('✅ Updated locale: $key');
|
||||
}
|
||||
}
|
||||
@ -21,6 +21,6 @@
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>11.0</string>
|
||||
<string>12.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
# Uncomment this line to define a global platform for your project
|
||||
# platform :ios, '11.0'
|
||||
# platform :ios, '12.0'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||
|
||||
@ -196,7 +196,7 @@ SPEC CHECKSUMS:
|
||||
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
|
||||
file_picker: 15fd9539e4eb735dc54bae8c0534a7a9511a03de
|
||||
file_selector_ios: 8c25d700d625e1dcdd6599f2d927072f2254647b
|
||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||
flutter_inappwebview: acd4fc0f012cefd09015000c241137d82f01ba62
|
||||
flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
|
||||
flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83
|
||||
@ -221,6 +221,6 @@ SPEC CHECKSUMS:
|
||||
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
|
||||
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
|
||||
|
||||
PODFILE CHECKSUM: e36c7ad9836dfd8d22934c7680185432a658e28f
|
||||
PODFILE CHECKSUM: 5129d2e80ab0dfc533f262cedf032011b1dfe4fd
|
||||
|
||||
COCOAPODS: 1.14.3
|
||||
COCOAPODS: 1.15.2
|
||||
|
||||
@ -406,7 +406,7 @@
|
||||
97C146E61CF9000F007C117D /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1430;
|
||||
LastUpgradeCheck = 1510;
|
||||
ORGANIZATIONNAME = "";
|
||||
TargetAttributes = {
|
||||
97C146ED1CF9000F007C117D = {
|
||||
@ -1056,6 +1056,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "stable-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -1078,6 +1079,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "stable-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -1099,6 +1101,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "stable-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -1198,6 +1201,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "stable-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -1294,6 +1298,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "stable-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -1387,6 +1392,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "stable-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -1408,6 +1414,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "dev-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -1430,6 +1437,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "dev-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -1452,6 +1460,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "dev-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -1473,6 +1482,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "dev-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -1494,6 +1504,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "dev-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -1515,6 +1526,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "dev-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -1614,6 +1626,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "stable-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -1636,6 +1649,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "dev-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -1732,6 +1746,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "stable-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -1753,6 +1768,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "dev-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -1846,6 +1862,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "stable-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -1867,6 +1884,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "dev-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -1888,6 +1906,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "nightly-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -1910,6 +1929,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "nightly-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -1932,6 +1952,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "nightly-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -1954,6 +1975,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "nightly-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -1975,6 +1997,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "nightly-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -1996,6 +2019,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "nightly-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -2017,6 +2041,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "nightly-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -2038,6 +2063,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "nightly-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -2059,6 +2085,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "nightly-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -2158,6 +2185,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "stable-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -2180,6 +2208,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "dev-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -2202,6 +2231,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "nightly-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -2298,6 +2328,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "stable-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -2319,6 +2350,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "dev-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -2340,6 +2372,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "nightly-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -2433,6 +2466,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "stable-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -2454,6 +2488,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "dev-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -2475,6 +2510,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 88NVGSJ5N3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "nightly-Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1430"
|
||||
LastUpgradeVersion = "1510"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 31 KiB |
@ -1,66 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Spotube</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>spotube</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
<key>NSAllowsArbitraryLoadsForMedia</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<false/>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>This app require access to the photo library</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>This app require access to the device camera</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>This app does not require access to the device microphone</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
<dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true />
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Spotube</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>spotube</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true />
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true />
|
||||
<key>NSAllowsArbitraryLoadsForMedia</key>
|
||||
<true />
|
||||
</dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>This app require access to the device camera</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>This app does not require access to the device microphone</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>This app require access to the photo library</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true />
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>audio</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<false />
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<true />
|
||||
</dict>
|
||||
</plist>
|
||||
@ -41,6 +41,10 @@
|
||||
<string>This app require access to the photo library</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>audio</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
|
||||
@ -1,66 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Spotube</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>spotube</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Spotube</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>spotube</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<key>NSAllowsArbitraryLoadsForMedia</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
<key>NSAllowsArbitraryLoadsForMedia</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<false/>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>This app require access to the photo library</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>This app require access to the device camera</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>This app does not require access to the device microphone</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
</dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>This app require access to the device camera</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>This app does not require access to the device microphone</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>This app require access to the photo library</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>audio</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<false/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@ -1,66 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Spotube</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>spotube</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Spotube</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>spotube</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<key>NSAllowsArbitraryLoadsForMedia</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
<key>NSAllowsArbitraryLoadsForMedia</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<false/>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>This app require access to the photo library</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>This app require access to the device camera</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>This app does not require access to the device microphone</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
</dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>This app require access to the device camera</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>This app does not require access to the device microphone</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>This app require access to the photo library</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>audio</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<false/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@ -9,6 +9,21 @@
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class $AssetsLogosGen {
|
||||
const $AssetsLogosGen();
|
||||
|
||||
/// File path: assets/logos/songlink-transparent.png
|
||||
AssetGenImage get songlinkTransparent =>
|
||||
const AssetGenImage('assets/logos/songlink-transparent.png');
|
||||
|
||||
/// File path: assets/logos/songlink.png
|
||||
AssetGenImage get songlink =>
|
||||
const AssetGenImage('assets/logos/songlink.png');
|
||||
|
||||
/// List of all assets
|
||||
List<AssetGenImage> get values => [songlinkTransparent, songlink];
|
||||
}
|
||||
|
||||
class $AssetsTutorialGen {
|
||||
const $AssetsTutorialGen();
|
||||
|
||||
@ -37,6 +52,7 @@ class Assets {
|
||||
static const AssetGenImage jiosaavn = AssetGenImage('assets/jiosaavn.png');
|
||||
static const AssetGenImage likedTracks =
|
||||
AssetGenImage('assets/liked-tracks.jpg');
|
||||
static const $AssetsLogosGen logos = $AssetsLogosGen();
|
||||
static const AssetGenImage placeholder =
|
||||
AssetGenImage('assets/placeholder.png');
|
||||
static const AssetGenImage spotubeHeroBanner =
|
||||
|
||||
@ -4,8 +4,8 @@ import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:spotube/components/player/player_controls.dart';
|
||||
import 'package:spotube/collections/routes.dart';
|
||||
import 'package:spotube/components/player/player_controls.dart';
|
||||
import 'package:spotube/models/logger.dart';
|
||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||
@ -64,6 +64,7 @@ class HomeTabIntent extends Intent {
|
||||
class HomeTabAction extends Action<HomeTabIntent> {
|
||||
@override
|
||||
invoke(intent) {
|
||||
final router = intent.ref.read(routerProvider);
|
||||
switch (intent.tab) {
|
||||
case HomeTabs.browse:
|
||||
router.go("/");
|
||||
|
||||
@ -6,6 +6,11 @@ class ISOLanguageName {
|
||||
required this.name,
|
||||
required this.nativeName,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "$name ($nativeName)";
|
||||
}
|
||||
}
|
||||
|
||||
// Uncomment the languages as we add support for them
|
||||
@ -348,10 +353,10 @@ abstract class LanguageLocals {
|
||||
// name: "Kongo",
|
||||
// nativeName: "KiKongo",
|
||||
// ),
|
||||
// "ko": const ISOLanguageName(
|
||||
// name: "Korean",
|
||||
// nativeName: "한국어 (韓國語), 조선말 (朝鮮語)",
|
||||
// ),
|
||||
"ko": const ISOLanguageName(
|
||||
name: "Korean",
|
||||
nativeName: "한국어 (韓國語), 조선말 (朝鮮語)",
|
||||
),
|
||||
// "ku": const ISOLanguageName(
|
||||
// name: "Kurdish",
|
||||
// nativeName: "Kurdî, كوردی",
|
||||
|
||||
@ -2,8 +2,10 @@ import 'package:catcher_2/catcher_2.dart';
|
||||
import 'package:flutter/foundation.dart' hide Category;
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:spotify/spotify.dart' hide Search;
|
||||
import 'package:spotube/pages/album/album.dart';
|
||||
import 'package:spotube/pages/getting_started/getting_started.dart';
|
||||
import 'package:spotube/pages/home/genres/genre_playlists.dart';
|
||||
import 'package:spotube/pages/home/genres/genres.dart';
|
||||
import 'package:spotube/pages/home/home.dart';
|
||||
@ -18,6 +20,8 @@ import 'package:spotube/pages/settings/blacklist.dart';
|
||||
import 'package:spotube/pages/settings/about.dart';
|
||||
import 'package:spotube/pages/settings/logs.dart';
|
||||
import 'package:spotube/pages/track/track.dart';
|
||||
import 'package:spotube/provider/authentication_provider.dart';
|
||||
import 'package:spotube/services/kv_store/kv_store.dart';
|
||||
import 'package:spotube/utils/platform.dart';
|
||||
import 'package:spotube/components/shared/spotube_page_route.dart';
|
||||
import 'package:spotube/pages/artist/artist.dart';
|
||||
@ -31,157 +35,180 @@ import 'package:spotube/pages/mobile_login/mobile_login.dart';
|
||||
|
||||
final rootNavigatorKey = Catcher2.navigatorKey;
|
||||
final shellRouteNavigatorKey = GlobalKey<NavigatorState>();
|
||||
final router = GoRouter(
|
||||
navigatorKey: rootNavigatorKey,
|
||||
routes: [
|
||||
ShellRoute(
|
||||
navigatorKey: shellRouteNavigatorKey,
|
||||
builder: (context, state, child) => RootApp(child: child),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: "/",
|
||||
pageBuilder: (context, state) => const SpotubePage(child: HomePage()),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: "genres",
|
||||
pageBuilder: (context, state) =>
|
||||
const SpotubePage(child: GenrePage()),
|
||||
),
|
||||
GoRoute(
|
||||
path: "genre/:categoryId",
|
||||
pageBuilder: (context, state) => SpotubePage(
|
||||
child: GenrePlaylistsPage(
|
||||
category: state.extra as Category,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
GoRoute(
|
||||
path: "/search",
|
||||
name: "Search",
|
||||
pageBuilder: (context, state) =>
|
||||
const SpotubePage(child: SearchPage()),
|
||||
),
|
||||
GoRoute(
|
||||
path: "/library",
|
||||
name: "Library",
|
||||
final routerProvider = Provider((ref) {
|
||||
return GoRouter(
|
||||
navigatorKey: rootNavigatorKey,
|
||||
routes: [
|
||||
ShellRoute(
|
||||
navigatorKey: shellRouteNavigatorKey,
|
||||
builder: (context, state, child) => RootApp(child: child),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: "/",
|
||||
redirect: (context, state) async {
|
||||
final authNotifier =
|
||||
ref.read(AuthenticationNotifier.provider.notifier);
|
||||
final json = await authNotifier.box.get(authNotifier.cacheKey);
|
||||
|
||||
if (json?["cookie"] == null &&
|
||||
!KVStoreService.doneGettingStarted) {
|
||||
return "/getting-started";
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
pageBuilder: (context, state) =>
|
||||
const SpotubePage(child: LibraryPage()),
|
||||
const SpotubePage(child: HomePage()),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: "generate",
|
||||
pageBuilder: (context, state) =>
|
||||
const SpotubePage(child: PlaylistGeneratorPage()),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: "result",
|
||||
pageBuilder: (context, state) => SpotubePage(
|
||||
child: PlaylistGenerateResultPage(
|
||||
state:
|
||||
state.extra as PlaylistGenerateResultRouteState,
|
||||
),
|
||||
),
|
||||
),
|
||||
]),
|
||||
]),
|
||||
GoRoute(
|
||||
path: "/lyrics",
|
||||
name: "Lyrics",
|
||||
pageBuilder: (context, state) =>
|
||||
const SpotubePage(child: LyricsPage()),
|
||||
),
|
||||
GoRoute(
|
||||
path: "/settings",
|
||||
pageBuilder: (context, state) => const SpotubePage(
|
||||
child: SettingsPage(),
|
||||
),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: "blacklist",
|
||||
pageBuilder: (context, state) => SpotubeSlidePage(
|
||||
child: const BlackListPage(),
|
||||
path: "genres",
|
||||
pageBuilder: (context, state) =>
|
||||
const SpotubePage(child: GenrePage()),
|
||||
),
|
||||
),
|
||||
if (!kIsWeb)
|
||||
GoRoute(
|
||||
path: "logs",
|
||||
pageBuilder: (context, state) => SpotubeSlidePage(
|
||||
child: const LogsPage(),
|
||||
path: "genre/:categoryId",
|
||||
pageBuilder: (context, state) => SpotubePage(
|
||||
child: GenrePlaylistsPage(
|
||||
category: state.extra as Category,
|
||||
),
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: "about",
|
||||
pageBuilder: (context, state) => SpotubeSlidePage(
|
||||
child: const AboutSpotube(),
|
||||
),
|
||||
],
|
||||
),
|
||||
GoRoute(
|
||||
path: "/search",
|
||||
name: "Search",
|
||||
pageBuilder: (context, state) =>
|
||||
const SpotubePage(child: SearchPage()),
|
||||
),
|
||||
GoRoute(
|
||||
path: "/library",
|
||||
name: "Library",
|
||||
pageBuilder: (context, state) =>
|
||||
const SpotubePage(child: LibraryPage()),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: "generate",
|
||||
pageBuilder: (context, state) =>
|
||||
const SpotubePage(child: PlaylistGeneratorPage()),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: "result",
|
||||
pageBuilder: (context, state) => SpotubePage(
|
||||
child: PlaylistGenerateResultPage(
|
||||
state:
|
||||
state.extra as PlaylistGenerateResultRouteState,
|
||||
),
|
||||
),
|
||||
),
|
||||
]),
|
||||
]),
|
||||
GoRoute(
|
||||
path: "/lyrics",
|
||||
name: "Lyrics",
|
||||
pageBuilder: (context, state) =>
|
||||
const SpotubePage(child: LyricsPage()),
|
||||
),
|
||||
GoRoute(
|
||||
path: "/settings",
|
||||
pageBuilder: (context, state) => const SpotubePage(
|
||||
child: SettingsPage(),
|
||||
),
|
||||
],
|
||||
),
|
||||
GoRoute(
|
||||
path: "/album/:id",
|
||||
pageBuilder: (context, state) {
|
||||
assert(state.extra is AlbumSimple);
|
||||
return SpotubePage(
|
||||
child: AlbumPage(album: state.extra as AlbumSimple),
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: "/artist/:id",
|
||||
pageBuilder: (context, state) {
|
||||
assert(state.pathParameters["id"] != null);
|
||||
return SpotubePage(child: ArtistPage(state.pathParameters["id"]!));
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: "/playlist/:id",
|
||||
pageBuilder: (context, state) {
|
||||
assert(state.extra is PlaylistSimple);
|
||||
return SpotubePage(
|
||||
child: state.pathParameters["id"] == "user-liked-tracks"
|
||||
? LikedPlaylistPage(playlist: state.extra as PlaylistSimple)
|
||||
: PlaylistPage(playlist: state.extra as PlaylistSimple),
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: "/track/:id",
|
||||
pageBuilder: (context, state) {
|
||||
final id = state.pathParameters["id"]!;
|
||||
return SpotubePage(
|
||||
child: TrackPage(trackId: id),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
GoRoute(
|
||||
path: "/mini-player",
|
||||
parentNavigatorKey: rootNavigatorKey,
|
||||
pageBuilder: (context, state) => SpotubePage(
|
||||
child: MiniLyricsPage(prevSize: state.extra as Size),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: "blacklist",
|
||||
pageBuilder: (context, state) => SpotubeSlidePage(
|
||||
child: const BlackListPage(),
|
||||
),
|
||||
),
|
||||
if (!kIsWeb)
|
||||
GoRoute(
|
||||
path: "logs",
|
||||
pageBuilder: (context, state) => SpotubeSlidePage(
|
||||
child: const LogsPage(),
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: "about",
|
||||
pageBuilder: (context, state) => SpotubeSlidePage(
|
||||
child: const AboutSpotube(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
GoRoute(
|
||||
path: "/album/:id",
|
||||
pageBuilder: (context, state) {
|
||||
assert(state.extra is AlbumSimple);
|
||||
return SpotubePage(
|
||||
child: AlbumPage(album: state.extra as AlbumSimple),
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: "/artist/:id",
|
||||
pageBuilder: (context, state) {
|
||||
assert(state.pathParameters["id"] != null);
|
||||
return SpotubePage(
|
||||
child: ArtistPage(state.pathParameters["id"]!));
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: "/playlist/:id",
|
||||
pageBuilder: (context, state) {
|
||||
assert(state.extra is PlaylistSimple);
|
||||
return SpotubePage(
|
||||
child: state.pathParameters["id"] == "user-liked-tracks"
|
||||
? LikedPlaylistPage(playlist: state.extra as PlaylistSimple)
|
||||
: PlaylistPage(playlist: state.extra as PlaylistSimple),
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: "/track/:id",
|
||||
pageBuilder: (context, state) {
|
||||
final id = state.pathParameters["id"]!;
|
||||
return SpotubePage(
|
||||
child: TrackPage(trackId: id),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: "/login",
|
||||
parentNavigatorKey: rootNavigatorKey,
|
||||
pageBuilder: (context, state) => SpotubePage(
|
||||
child: kIsMobile ? const WebViewLogin() : const DesktopLoginPage(),
|
||||
GoRoute(
|
||||
path: "/mini-player",
|
||||
parentNavigatorKey: rootNavigatorKey,
|
||||
pageBuilder: (context, state) => SpotubePage(
|
||||
child: MiniLyricsPage(prevSize: state.extra as Size),
|
||||
),
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: "/login-tutorial",
|
||||
parentNavigatorKey: rootNavigatorKey,
|
||||
pageBuilder: (context, state) => const SpotubePage(
|
||||
child: LoginTutorial(),
|
||||
GoRoute(
|
||||
path: "/getting-started",
|
||||
parentNavigatorKey: rootNavigatorKey,
|
||||
pageBuilder: (context, state) => const SpotubePage(
|
||||
child: GettingStarting(),
|
||||
),
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: "/lastfm-login",
|
||||
parentNavigatorKey: rootNavigatorKey,
|
||||
pageBuilder: (context, state) =>
|
||||
const SpotubePage(child: LastFMLoginPage()),
|
||||
),
|
||||
],
|
||||
);
|
||||
GoRoute(
|
||||
path: "/login",
|
||||
parentNavigatorKey: rootNavigatorKey,
|
||||
pageBuilder: (context, state) => SpotubePage(
|
||||
child: kIsMobile ? const WebViewLogin() : const DesktopLoginPage(),
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: "/login-tutorial",
|
||||
parentNavigatorKey: rootNavigatorKey,
|
||||
pageBuilder: (context, state) => const SpotubePage(
|
||||
child: LoginTutorial(),
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: "/lastfm-login",
|
||||
parentNavigatorKey: rootNavigatorKey,
|
||||
pageBuilder: (context, state) =>
|
||||
const SpotubePage(child: LastFMLoginPage()),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
@ -111,4 +111,8 @@ abstract class SpotubeIcons {
|
||||
static const wikipedia = SimpleIcons.wikipedia;
|
||||
static const discord = SimpleIcons.discord;
|
||||
static const youtube = SimpleIcons.youtube;
|
||||
static const radio = FeatherIcons.radio;
|
||||
static const github = SimpleIcons.github;
|
||||
static const openCollective = SimpleIcons.opencollective;
|
||||
static const anonymous = FeatherIcons.user;
|
||||
}
|
||||
|
||||
@ -21,8 +21,8 @@ class AlbumCard extends HookConsumerWidget {
|
||||
final AlbumSimple album;
|
||||
const AlbumCard(
|
||||
this.album, {
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
|
||||
31
lib/components/getting_started/blur_card.dart
Normal file
@ -0,0 +1,31 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
class BlurCard extends HookConsumerWidget {
|
||||
final Widget child;
|
||||
const BlurCard({super.key, required this.child});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.all(16.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
constraints: const BoxConstraints(maxWidth: 400),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 20, sigmaY: 20),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,6 @@
|
||||
import 'dart:ffi';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:skeletonizer/skeletonizer.dart';
|
||||
@ -69,22 +72,27 @@ class HomePageFriendsSection extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
for (final group in friendGroup)
|
||||
Row(
|
||||
children: [
|
||||
for (final friend in group)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: FriendItem(friend: friend),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
child: ScrollConfiguration(
|
||||
behavior: ScrollConfiguration.of(context).copyWith(
|
||||
dragDevices: PointerDeviceKind.values.toSet(),
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
for (final group in friendGroup)
|
||||
Row(
|
||||
children: [
|
||||
for (final friend in group)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: FriendItem(friend: friend),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@ -50,10 +50,11 @@ enum SortBy {
|
||||
none,
|
||||
ascending,
|
||||
descending,
|
||||
artist,
|
||||
album,
|
||||
newest,
|
||||
oldest,
|
||||
duration,
|
||||
artist,
|
||||
album,
|
||||
}
|
||||
|
||||
final localTracksProvider = FutureProvider<List<LocalTrack>>((ref) async {
|
||||
|
||||
@ -24,7 +24,9 @@ import 'package:spotube/models/local_track.dart';
|
||||
import 'package:spotube/pages/lyrics/lyrics.dart';
|
||||
import 'package:spotube/provider/authentication_provider.dart';
|
||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
||||
import 'package:spotube/services/sourced_track/sources/youtube.dart';
|
||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
class PlayerView extends HookConsumerWidget {
|
||||
final PanelController panelController;
|
||||
@ -94,10 +96,10 @@ class PlayerView extends HookConsumerWidget {
|
||||
|
||||
final topPadding = MediaQueryData.fromView(View.of(context)).padding.top;
|
||||
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
onPopInvoked: (didPop) async {
|
||||
panelController.close();
|
||||
return WillPopScope(
|
||||
onWillPop: () async {
|
||||
await panelController.close();
|
||||
return false;
|
||||
},
|
||||
child: IconTheme(
|
||||
data: theme.iconTheme.copyWith(color: bodyTextColor),
|
||||
@ -137,11 +139,31 @@ class PlayerView extends HookConsumerWidget {
|
||||
onPressed: panelController.close,
|
||||
),
|
||||
actions: [
|
||||
if (currentTrack is YoutubeSourcedTrack)
|
||||
TextButton.icon(
|
||||
icon: Assets.logos.songlinkTransparent.image(
|
||||
width: 20,
|
||||
height: 20,
|
||||
color: bodyTextColor,
|
||||
),
|
||||
label: Text(context.l10n.song_link),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: bodyTextColor,
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
onPressed: () {
|
||||
final url =
|
||||
"https://song.link/s/${currentTrack.id}";
|
||||
|
||||
launchUrlString(url);
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(SpotubeIcons.info, size: 18),
|
||||
tooltip: context.l10n.details,
|
||||
style: IconButton.styleFrom(
|
||||
foregroundColor: bodyTextColor),
|
||||
foregroundColor: bodyTextColor,
|
||||
),
|
||||
onPressed: currentTrack == null
|
||||
? null
|
||||
: () {
|
||||
|
||||
@ -15,8 +15,6 @@ class InterScrollbar extends HookWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
if (DesktopTools.platform.isDesktop) return child;
|
||||
|
||||
return DraggableScrollbar.semicircle(
|
||||
|
||||
@ -48,6 +48,11 @@ class SortTracksDropdown extends StatelessWidget {
|
||||
enabled: value != SortBy.oldest,
|
||||
title: Text(context.l10n.sort_oldest),
|
||||
),
|
||||
PopSheetEntry(
|
||||
value: SortBy.duration,
|
||||
enabled: value != SortBy.duration,
|
||||
title: Text(context.l10n.sort_duration),
|
||||
),
|
||||
PopSheetEntry(
|
||||
value: SortBy.artist,
|
||||
enabled: value != SortBy.artist,
|
||||
|
||||
@ -1,15 +1,18 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fl_query/fl_query.dart';
|
||||
import 'package:flutter/material.dart' hide Page;
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/collections/assets.gen.dart';
|
||||
import 'package:spotube/collections/spotube_icons.dart';
|
||||
import 'package:spotube/components/library/user_local_tracks.dart';
|
||||
import 'package:spotube/components/shared/adaptive/adaptive_pop_sheet_list.dart';
|
||||
import 'package:spotube/components/shared/dialogs/playlist_add_track_dialog.dart';
|
||||
import 'package:spotube/components/shared/dialogs/prompt_dialog.dart';
|
||||
import 'package:spotube/components/shared/dialogs/track_details_dialog.dart';
|
||||
import 'package:spotube/components/shared/heart_button.dart';
|
||||
import 'package:spotube/components/shared/image/universal_image.dart';
|
||||
@ -20,12 +23,16 @@ import 'package:spotube/provider/authentication_provider.dart';
|
||||
import 'package:spotube/provider/blacklist_provider.dart';
|
||||
import 'package:spotube/provider/download_manager_provider.dart';
|
||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
||||
import 'package:spotube/provider/spotify_provider.dart';
|
||||
import 'package:spotube/services/mutations/mutations.dart';
|
||||
import 'package:spotube/services/queries/search.dart';
|
||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
enum TrackOptionValue {
|
||||
album,
|
||||
share,
|
||||
songlink,
|
||||
addToPlaylist,
|
||||
addToQueue,
|
||||
removeFromPlaylist,
|
||||
@ -36,6 +43,7 @@ enum TrackOptionValue {
|
||||
favorite,
|
||||
details,
|
||||
download,
|
||||
startRadio,
|
||||
}
|
||||
|
||||
class TrackOptions extends HookConsumerWidget {
|
||||
@ -82,11 +90,85 @@ class TrackOptions extends HookConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
void actionStartRadio(
|
||||
BuildContext context,
|
||||
WidgetRef ref,
|
||||
Track track,
|
||||
) async {
|
||||
final playback = ref.read(ProxyPlaylistNotifier.notifier);
|
||||
final playlist = ref.read(ProxyPlaylistNotifier.provider);
|
||||
final spotify = ref.read(spotifyProvider);
|
||||
final query = "${track.name} Radio";
|
||||
final pages = await QueryClient.of(context)
|
||||
.fetchInfiniteQueryJob<List<Page>, dynamic, int, SearchParams>(
|
||||
job: SearchQueries.queryJob(query),
|
||||
args: (
|
||||
spotify: spotify,
|
||||
searchType: SearchType.playlist,
|
||||
query: query,
|
||||
),
|
||||
) ??
|
||||
[];
|
||||
|
||||
final radios = pages
|
||||
.expand((e) => e.items?.toList() ?? <PlaylistSimple>[])
|
||||
.toList()
|
||||
.cast<PlaylistSimple>();
|
||||
|
||||
final artists = track.artists!.map((e) => e.name);
|
||||
|
||||
final radio = radios.firstWhere(
|
||||
(e) {
|
||||
final validPlaylists =
|
||||
artists.where((a) => e.description!.contains(a!));
|
||||
return e.name == "${track.name} Radio" &&
|
||||
(validPlaylists.length >= 2 ||
|
||||
validPlaylists.length == artists.length) &&
|
||||
e.owner?.displayName == "Spotify";
|
||||
},
|
||||
orElse: () => radios.first,
|
||||
);
|
||||
|
||||
bool replaceQueue = false;
|
||||
|
||||
if (context.mounted && playlist.tracks.isNotEmpty) {
|
||||
replaceQueue = await showPromptDialog(
|
||||
context: context,
|
||||
title: context.l10n.how_to_start_radio,
|
||||
message: context.l10n.replace_queue_question,
|
||||
okText: context.l10n.replace,
|
||||
cancelText: context.l10n.add_to_queue,
|
||||
);
|
||||
}
|
||||
|
||||
if (replaceQueue || playlist.tracks.isEmpty) {
|
||||
await playback.stop();
|
||||
await playback.load([track], autoPlay: true);
|
||||
|
||||
// we don't have to add those tracks as useEndlessPlayback will do it for us
|
||||
return;
|
||||
} else {
|
||||
await playback.addTrack(track);
|
||||
}
|
||||
|
||||
final tracks =
|
||||
await spotify.playlists.getTracksByPlaylistId(radio.id!).all();
|
||||
|
||||
await playback.addTracks(
|
||||
tracks.toList()
|
||||
..removeWhere((e) {
|
||||
final isDuplicate = playlist.tracks.any((t) => t.id == e.id);
|
||||
return e.id == track.id || isDuplicate;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||
final mediaQuery = MediaQuery.of(context);
|
||||
final router = GoRouter.of(context);
|
||||
final ThemeData(:colorScheme) = Theme.of(context);
|
||||
|
||||
final playlist = ref.watch(ProxyPlaylistNotifier.provider);
|
||||
final playback = ref.watch(ProxyPlaylistNotifier.notifier);
|
||||
@ -198,6 +280,10 @@ class TrackOptions extends HookConsumerWidget {
|
||||
case TrackOptionValue.share:
|
||||
actionShare(context, track);
|
||||
break;
|
||||
case TrackOptionValue.songlink:
|
||||
final url = "https://song.link/s/${track.id}";
|
||||
await launchUrlString(url);
|
||||
break;
|
||||
case TrackOptionValue.details:
|
||||
showDialog(
|
||||
context: context,
|
||||
@ -207,6 +293,9 @@ class TrackOptions extends HookConsumerWidget {
|
||||
case TrackOptionValue.download:
|
||||
await downloadManager.addToQueue(track);
|
||||
break;
|
||||
case TrackOptionValue.startRadio:
|
||||
actionStartRadio(context, ref, track);
|
||||
break;
|
||||
}
|
||||
},
|
||||
icon: icon ?? const Icon(SpotubeIcons.moreHorizontal),
|
||||
@ -287,12 +376,18 @@ class TrackOptions extends HookConsumerWidget {
|
||||
: context.l10n.save_as_favorite,
|
||||
),
|
||||
),
|
||||
if (auth != null)
|
||||
if (auth != null) ...[
|
||||
PopSheetEntry(
|
||||
value: TrackOptionValue.startRadio,
|
||||
leading: const Icon(SpotubeIcons.radio),
|
||||
title: Text(context.l10n.start_a_radio),
|
||||
),
|
||||
PopSheetEntry(
|
||||
value: TrackOptionValue.addToPlaylist,
|
||||
leading: const Icon(SpotubeIcons.playlistAdd),
|
||||
title: Text(context.l10n.add_to_playlist),
|
||||
),
|
||||
],
|
||||
if (userPlaylist && auth != null)
|
||||
PopSheetEntry(
|
||||
value: TrackOptionValue.removeFromPlaylist,
|
||||
@ -331,6 +426,15 @@ class TrackOptions extends HookConsumerWidget {
|
||||
leading: const Icon(SpotubeIcons.share),
|
||||
title: Text(context.l10n.share),
|
||||
),
|
||||
PopSheetEntry(
|
||||
value: TrackOptionValue.songlink,
|
||||
leading: Assets.logos.songlinkTransparent.image(
|
||||
width: 22,
|
||||
height: 22,
|
||||
color: colorScheme.onSurface.withOpacity(0.5),
|
||||
),
|
||||
title: Text(context.l10n.song_link),
|
||||
),
|
||||
PopSheetEntry(
|
||||
value: TrackOptionValue.details,
|
||||
leading: const Icon(SpotubeIcons.info),
|
||||
|
||||
@ -70,9 +70,9 @@ class TrackViewHeaderActions extends HookConsumerWidget {
|
||||
tooltip: props.isLiked
|
||||
? context.l10n.remove_from_favorites
|
||||
: context.l10n.save_as_favorite,
|
||||
onPressed: () {
|
||||
props.onHeart?.call();
|
||||
if (isUserPlaylist) {
|
||||
onPressed: () async {
|
||||
final shouldPop = await props.onHeart?.call();
|
||||
if (isUserPlaylist && shouldPop == true && context.mounted) {
|
||||
context.pop();
|
||||
}
|
||||
},
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:sliver_tools/sliver_tools.dart';
|
||||
import 'package:spotube/components/shared/inter_scrollbar/inter_scrollbar.dart';
|
||||
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
||||
import 'package:spotube/components/shared/tracks_view/sections/header/flexible_header.dart';
|
||||
import 'package:spotube/components/shared/tracks_view/sections/body/track_view_body.dart';
|
||||
@ -13,6 +15,7 @@ class TrackView extends HookConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final props = InheritedTrackView.of(context);
|
||||
final controller = useScrollController();
|
||||
|
||||
return Scaffold(
|
||||
appBar: DesktopTools.platform.isDesktop
|
||||
@ -29,14 +32,18 @@ class TrackView extends HookConsumerWidget {
|
||||
extendBodyBehindAppBar: true,
|
||||
body: RefreshIndicator(
|
||||
onRefresh: props.pagination.onRefresh,
|
||||
child: const CustomScrollView(
|
||||
slivers: [
|
||||
TrackViewFlexHeader(),
|
||||
SliverAnimatedSwitcher(
|
||||
duration: Duration(milliseconds: 500),
|
||||
child: TrackViewBodySection(),
|
||||
),
|
||||
],
|
||||
child: InterScrollbar(
|
||||
controller: controller,
|
||||
child: CustomScrollView(
|
||||
controller: controller,
|
||||
slivers: const [
|
||||
TrackViewFlexHeader(),
|
||||
SliverAnimatedSwitcher(
|
||||
duration: Duration(milliseconds: 500),
|
||||
child: TrackViewBodySection(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:fl_query/fl_query.dart';
|
||||
import 'package:flutter/material.dart' hide Page;
|
||||
import 'package:spotify/spotify.dart';
|
||||
@ -62,7 +64,7 @@ class InheritedTrackView extends InheritedWidget {
|
||||
final String shareUrl;
|
||||
|
||||
// events
|
||||
final VoidCallback? onHeart; // if null heart button will hidden
|
||||
final FutureOr<bool?> Function()? onHeart; // if null heart button will hidden
|
||||
|
||||
const InheritedTrackView({
|
||||
super.key,
|
||||
|
||||
@ -9,3 +9,9 @@ extension UnescapeHtml on String {
|
||||
extension NullableUnescapeHtml on String? {
|
||||
String? unescapeHtml() => this == null ? null : htmlEscape.convert(this!);
|
||||
}
|
||||
|
||||
extension StringExtension on String {
|
||||
String capitalize() {
|
||||
return "${this[0].toUpperCase()}${substring(1)}";
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,6 +19,8 @@ void useDeepLinking(WidgetRef ref) {
|
||||
final spotify = ref.watch(spotifyProvider);
|
||||
final queryClient = useQueryClient();
|
||||
|
||||
final router = ref.watch(routerProvider);
|
||||
|
||||
useEffect(() {
|
||||
void uriListener(List<SharedFile> files) async {
|
||||
for (final file in files) {
|
||||
|
||||
@ -1,47 +1,21 @@
|
||||
import 'package:disable_battery_optimization/disable_battery_optimization.dart';
|
||||
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:spotube/hooks/utils/use_async_effect.dart';
|
||||
import 'package:spotube/services/kv_store/kv_store.dart';
|
||||
|
||||
bool _asked = false;
|
||||
void useDisableBatteryOptimizations() {
|
||||
useAsyncEffect(() async {
|
||||
if (!DesktopTools.platform.isAndroid || _asked) return;
|
||||
final localStorage = await SharedPreferences.getInstance();
|
||||
if (!DesktopTools.platform.isAndroid ||
|
||||
KVStoreService.askedForBatteryOptimization) return;
|
||||
|
||||
final rawIsBatteryOptimizationDisabled =
|
||||
localStorage.getBool("isBatteryOptimizationDisabled");
|
||||
final isBatteryOptimizationDisabled =
|
||||
await DisableBatteryOptimization.isBatteryOptimizationDisabled;
|
||||
if (rawIsBatteryOptimizationDisabled != false &&
|
||||
isBatteryOptimizationDisabled == false) {
|
||||
final hasDisabled = await DisableBatteryOptimization
|
||||
.showDisableBatteryOptimizationSettings();
|
||||
await DisableBatteryOptimization.showDisableBatteryOptimizationSettings();
|
||||
|
||||
localStorage.setBool(
|
||||
"isBatteryOptimizationDisabled",
|
||||
hasDisabled == true,
|
||||
);
|
||||
}
|
||||
await DisableBatteryOptimization
|
||||
.showDisableManufacturerBatteryOptimizationSettings(
|
||||
"Your device has additional battery optimization",
|
||||
"Follow the steps and disable the optimizations to allow smooth functioning of this app",
|
||||
);
|
||||
|
||||
final rawIsManBatteryOptimizationDisabled =
|
||||
localStorage.getBool("isManufacturerBatteryOptimizationDisabled");
|
||||
final isManBatteryOptimizationDisabled = await DisableBatteryOptimization
|
||||
.isManufacturerBatteryOptimizationDisabled;
|
||||
|
||||
if (rawIsManBatteryOptimizationDisabled != false &&
|
||||
isManBatteryOptimizationDisabled == false) {
|
||||
final hasDisabled = await DisableBatteryOptimization
|
||||
.showDisableManufacturerBatteryOptimizationSettings(
|
||||
"Your device has additional battery optimization",
|
||||
"Follow the steps and disable the optimizations to allow smooth functioning of this app",
|
||||
);
|
||||
|
||||
localStorage.setBool(
|
||||
"isManufacturerBatteryOptimizationDisabled",
|
||||
hasDisabled == true,
|
||||
);
|
||||
}
|
||||
_asked = true;
|
||||
await KVStoreService.setAskedForBatteryOptimization(true);
|
||||
}, null, []);
|
||||
}
|
||||
|
||||
103
lib/hooks/configurators/use_endless_playback.dart
Normal file
@ -0,0 +1,103 @@
|
||||
import 'package:catcher_2/catcher_2.dart';
|
||||
import 'package:fl_query_hooks/fl_query_hooks.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/provider/authentication_provider.dart';
|
||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
||||
import 'package:spotube/provider/spotify_provider.dart';
|
||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||
import 'package:spotube/services/queries/search.dart';
|
||||
|
||||
void useEndlessPlayback(WidgetRef ref) {
|
||||
final auth = ref.watch(AuthenticationNotifier.provider);
|
||||
final playback = ref.watch(ProxyPlaylistNotifier.notifier);
|
||||
final playlist = ref.watch(ProxyPlaylistNotifier.provider);
|
||||
final spotify = ref.watch(spotifyProvider);
|
||||
final endlessPlayback =
|
||||
ref.watch(userPreferencesProvider.select((s) => s.endlessPlayback));
|
||||
|
||||
final queryClient = useQueryClient();
|
||||
|
||||
useEffect(
|
||||
() {
|
||||
if (!endlessPlayback || auth == null) return null;
|
||||
|
||||
void listener(int index) async {
|
||||
try {
|
||||
final playlist = ref.read(ProxyPlaylistNotifier.provider);
|
||||
if (index != playlist.tracks.length - 1) return;
|
||||
|
||||
final track = playlist.tracks.last;
|
||||
|
||||
final query = "${track.name} Radio";
|
||||
final pages = await queryClient.fetchInfiniteQueryJob<List<Page>,
|
||||
dynamic, int, SearchParams>(
|
||||
job: SearchQueries.queryJob(query),
|
||||
args: (
|
||||
spotify: spotify,
|
||||
searchType: SearchType.playlist,
|
||||
query: query
|
||||
),
|
||||
) ??
|
||||
[];
|
||||
|
||||
final radios = pages
|
||||
.expand((e) => e.items?.toList() ?? <PlaylistSimple>[])
|
||||
.toList()
|
||||
.cast<PlaylistSimple>();
|
||||
|
||||
final artists = track.artists!.map((e) => e.name);
|
||||
|
||||
final radio = radios.firstWhere(
|
||||
(e) {
|
||||
final validPlaylists =
|
||||
artists.where((a) => e.description!.contains(a!));
|
||||
return e.name == "${track.name} Radio" &&
|
||||
(validPlaylists.length >= 2 ||
|
||||
validPlaylists.length == artists.length) &&
|
||||
e.owner?.displayName != "Spotify";
|
||||
},
|
||||
orElse: () => radios.first,
|
||||
);
|
||||
|
||||
final tracks =
|
||||
await spotify.playlists.getTracksByPlaylistId(radio.id!).all();
|
||||
|
||||
await playback.addTracks(
|
||||
tracks.toList()
|
||||
..removeWhere((e) {
|
||||
final playlist = ref.read(ProxyPlaylistNotifier.provider);
|
||||
final isDuplicate = playlist.tracks.any((t) => t.id == e.id);
|
||||
return e.id == track.id || isDuplicate;
|
||||
}),
|
||||
);
|
||||
} catch (e, stack) {
|
||||
Catcher2.reportCheckedError(e, stack);
|
||||
}
|
||||
}
|
||||
|
||||
// Sometimes user can change settings for which the currentIndexChanged
|
||||
// might not be called. So we need to check if the current track is the
|
||||
// last track and if it is then we need to call the listener manually.
|
||||
if (playlist.active == playlist.tracks.length - 1 &&
|
||||
audioPlayer.isPlaying) {
|
||||
listener(playlist.active!);
|
||||
}
|
||||
|
||||
final subscription =
|
||||
audioPlayer.currentIndexChangedStream.listen(listener);
|
||||
|
||||
return subscription.cancel;
|
||||
},
|
||||
[
|
||||
spotify,
|
||||
playback,
|
||||
queryClient,
|
||||
playlist.tracks,
|
||||
endlessPlayback,
|
||||
auth,
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -286,5 +286,32 @@
|
||||
"step_3_steps": "انسخ قيمة الكوكي \"sp_dc\"",
|
||||
"step_4_steps": "الصق قيمة \"sp_dc\" المنسوخة",
|
||||
"friends": "أصدقاء",
|
||||
"no_lyrics_available": "عذرًا، تعذر العثور على كلمات الأغنية لهذه العنصر"
|
||||
"no_lyrics_available": "عذرًا، تعذر العثور على كلمات الأغنية لهذه العنصر",
|
||||
"sort_duration": "ترتيب حسب المدة",
|
||||
"start_a_radio": "بدء راديو",
|
||||
"how_to_start_radio": "كيف تريد بدء الراديو؟",
|
||||
"replace_queue_question": "هل تريد استبدال قائمة التشغيل الحالية أم إضافة إليها؟",
|
||||
"endless_playback": "تشغيل بلا نهاية",
|
||||
"delete_playlist": "حذف قائمة التشغيل",
|
||||
"delete_playlist_confirmation": "هل أنت متأكد أنك تريد حذف هذه قائمة التشغيل؟",
|
||||
"local_tracks": "المسارات المحلية",
|
||||
"song_link": "رابط الأغنية",
|
||||
"skip_this_nonsense": "تخطي هذه الهراء",
|
||||
"freedom_of_music": "“حرية الموسيقى”",
|
||||
"freedom_of_music_palm": "“حرية الموسيقى في متناول يدك”",
|
||||
"get_started": "لنبدأ",
|
||||
"youtube_source_description": "موصى به ويعمل بشكل أفضل.",
|
||||
"piped_source_description": "تشعر بالحرية؟ نفس يوتيوب ولكن أكثر حرية.",
|
||||
"jiosaavn_source_description": "الأفضل لمنطقة جنوب آسيا.",
|
||||
"highest_quality": "أعلى جودة: {quality}",
|
||||
"select_audio_source": "اختر مصدر الصوت",
|
||||
"endless_playback_description": "إلحاق الأغاني الجديدة تلقائيًا\nإلى نهاية قائمة التشغيل",
|
||||
"choose_your_region": "اختر منطقتك",
|
||||
"choose_your_region_description": "سيساعدك هذا في عرض المحتوى المناسب\nلموقعك.",
|
||||
"choose_your_language": "اختر لغتك",
|
||||
"help_project_grow": "ساعد في نمو هذا المشروع",
|
||||
"help_project_grow_description": "Spotube هو مشروع مفتوح المصدر. يمكنك مساعدة هذا المشروع في النمو عن طريق المساهمة في المشروع، أو الإبلاغ عن الأخطاء، أو اقتراح ميزات جديدة.",
|
||||
"contribute_on_github": "المساهمة على GitHub",
|
||||
"donate_on_open_collective": "التبرع على Open Collective",
|
||||
"browse_anonymously": "تصفح بشكل مجهول"
|
||||
}
|
||||
@ -286,5 +286,32 @@
|
||||
"step_3_steps": "কুকি \"sp_dc\" এর মানটি কপি করুন",
|
||||
"step_4_steps": "কপি করা \"sp_dc\" মানটি পেস্ট করুন",
|
||||
"friends": "বন্ধু",
|
||||
"no_lyrics_available": "দুঃখিত, এই ট্র্যাকের জন্য কথা খুঁজে পাওয়া গেলনা"
|
||||
"no_lyrics_available": "দুঃখিত, এই ট্র্যাকের জন্য কথা খুঁজে পাওয়া গেলনা",
|
||||
"sort_duration": "দৈর্ঘ্য অনুযায়ী বাছাই করুন",
|
||||
"start_a_radio": "রেডিও শুরু করুন",
|
||||
"how_to_start_radio": "রেডিও কিভাবে শুরু করতে চান?",
|
||||
"replace_queue_question": "আপনি বর্তমান কিউটি প্রতিস্থাপন করতে চান কিনা বা এর সাথে যুক্ত করতে চান?",
|
||||
"endless_playback": "অবিরাম প্রচার",
|
||||
"delete_playlist": "প্লেলিস্ট মুছুন",
|
||||
"delete_playlist_confirmation": "আপনি কি নিশ্চিত যে আপনি এই প্লেলিস্টটি মুছতে চান?",
|
||||
"local_tracks": "স্থানীয় ট্র্যাক",
|
||||
"song_link": "গানের লিংক",
|
||||
"skip_this_nonsense": "এই বাকবাস পালান",
|
||||
"freedom_of_music": "“সংগীতের স্বাধীনতা”",
|
||||
"freedom_of_music_palm": "“তোমার হাতের কাছে সংগীতের স্বাধীনতা”",
|
||||
"get_started": "শুরু করা যাক",
|
||||
"youtube_source_description": "প্রস্তাবিত এবং সেরা কাজ করে।",
|
||||
"piped_source_description": "মন খারাপ? ইউটিউবের মতো আবার ফ্রি।",
|
||||
"jiosaavn_source_description": "দক্ষিণ এশিয়ান অঞ্চলের জন্য সেরা।",
|
||||
"highest_quality": "সর্বোচ্চ গুণগতি: {quality}",
|
||||
"select_audio_source": "অডিও উৎস নির্বাচন করুন",
|
||||
"endless_playback_description": "নতুন গান নিজে নিজে প্লেলিস্টের শেষে\nসংযুক্ত করুন",
|
||||
"choose_your_region": "আপনার অঞ্চল নির্বাচন করুন",
|
||||
"choose_your_region_description": "এটি স্পটুবে আপনাকে আপনার অবস্থানের জন্য ঠিক কন্টেন্ট দেখানোর সাহায্য করবে।",
|
||||
"choose_your_language": "আপনার ভাষা নির্বাচন করুন",
|
||||
"help_project_grow": "এই প্রকল্পের বৃদ্ধি করুন",
|
||||
"help_project_grow_description": "স্পটুব একটি ওপেন সোর্স প্রকল্প। আপনি প্রকল্পে অবদান রাখেন, বাগ রিপোর্ট করেন, বা নতুন বৈশিষ্ট্যগুলি সুপারিশ করেন।",
|
||||
"contribute_on_github": "গিটহাবে অবদান রাখুন",
|
||||
"donate_on_open_collective": "ওপেন কলেক্টিভে অনুদান করুন",
|
||||
"browse_anonymously": "অজানে ব্রাউজ করুন"
|
||||
}
|
||||
@ -286,5 +286,32 @@
|
||||
"step_3_steps": "Copia el valor de la cookie \"sp_dc\"",
|
||||
"step_4_steps": "Pega el valor copiado de \"sp_dc\"",
|
||||
"friends": "Amics",
|
||||
"no_lyrics_available": "Ho sentim, no es poden trobar les lletres d'aquesta pista"
|
||||
"no_lyrics_available": "Ho sentim, no es poden trobar les lletres d'aquesta pista",
|
||||
"sort_duration": "Ordenar per Durada",
|
||||
"start_a_radio": "Inicia una ràdio",
|
||||
"how_to_start_radio": "Com vols començar la ràdio?",
|
||||
"replace_queue_question": "Voleu substituir la cua actual o afegir-hi?",
|
||||
"endless_playback": "Reproducció infinita",
|
||||
"delete_playlist": "Suprimeix la llista de reproducció",
|
||||
"delete_playlist_confirmation": "Esteu segur que voleu suprimir aquesta llista de reproducció?",
|
||||
"local_tracks": "Pistes locals",
|
||||
"song_link": "Enllaç de la cançó",
|
||||
"skip_this_nonsense": "Omet aquesta tonteria",
|
||||
"freedom_of_music": "“Llibertat de la música”",
|
||||
"freedom_of_music_palm": "“Llibertat de la música a la palma de la mà”",
|
||||
"get_started": "Comencem",
|
||||
"youtube_source_description": "Recomanat i funciona millor.",
|
||||
"piped_source_description": "Et sents lliure? El mateix que YouTube però més lliure.",
|
||||
"jiosaavn_source_description": "El millor per a la regió del sud d'Àsia.",
|
||||
"highest_quality": "Qualitat més alta: {quality}",
|
||||
"select_audio_source": "Seleccioneu la font d'àudio",
|
||||
"endless_playback_description": "Afegiu automàticament noves cançons\nal final de la cua",
|
||||
"choose_your_region": "Trieu la vostra regió",
|
||||
"choose_your_region_description": "Això ajudarà a Spotube a mostrar-vos el contingut adequat\nper a la vostra ubicació.",
|
||||
"choose_your_language": "Trieu el vostre idioma",
|
||||
"help_project_grow": "Ajuda a fer créixer aquest projecte",
|
||||
"help_project_grow_description": "Spotube és un projecte de codi obert. Podeu ajudar a fer créixer aquest projecte contribuint al projecte, informant d'errors o suggerint noves funcionalitats.",
|
||||
"contribute_on_github": "Contribueix a GitHub",
|
||||
"donate_on_open_collective": "Fes una donació a Open Collective",
|
||||
"browse_anonymously": "Navega de manera anònima"
|
||||
}
|
||||
@ -286,5 +286,32 @@
|
||||
"step_3_steps": "Kopiere den Wert des Cookies \"sp_dc\"",
|
||||
"step_4_steps": "Füge den kopierten Wert von \"sp_dc\" ein",
|
||||
"friends": "Freunde",
|
||||
"no_lyrics_available": "Entschuldigung, Texte für diesen Track konnten nicht gefunden werden"
|
||||
"no_lyrics_available": "Entschuldigung, Texte für diesen Track konnten nicht gefunden werden",
|
||||
"sort_duration": "Nach Dauer sortieren",
|
||||
"start_a_radio": "Radio starten",
|
||||
"how_to_start_radio": "Wie möchten Sie das Radio starten?",
|
||||
"replace_queue_question": "Möchten Sie die aktuelle Wiedergabeliste ersetzen oder hinzufügen?",
|
||||
"endless_playback": "Endlose Wiedergabe",
|
||||
"delete_playlist": "Wiedergabeliste löschen",
|
||||
"delete_playlist_confirmation": "Sind Sie sicher, dass Sie diese Wiedergabeliste löschen möchten?",
|
||||
"local_tracks": "Lokale Titel",
|
||||
"song_link": "Lied-Link",
|
||||
"skip_this_nonsense": "Diesen Unsinn überspringen",
|
||||
"freedom_of_music": "“Freiheit der Musik”",
|
||||
"freedom_of_music_palm": "“Freiheit der Musik in Ihrer Handfläche”",
|
||||
"get_started": "Lass uns anfangen",
|
||||
"youtube_source_description": "Empfohlen und funktioniert am besten.",
|
||||
"piped_source_description": "Fühlen Sie sich frei? Wie YouTube, aber viel freier.",
|
||||
"jiosaavn_source_description": "Am besten für die südasiatische Region.",
|
||||
"highest_quality": "Höchste Qualität: {quality}",
|
||||
"select_audio_source": "Audioquelle auswählen",
|
||||
"endless_playback_description": "Neue Lieder automatisch\nam Ende der Wiedergabeliste hinzufügen",
|
||||
"choose_your_region": "Wählen Sie Ihre Region",
|
||||
"choose_your_region_description": "Dies wird Spotube helfen, Ihnen den richtigen Inhalt\nfür Ihren Standort anzuzeigen.",
|
||||
"choose_your_language": "Wählen Sie Ihre Sprache",
|
||||
"help_project_grow": "Helfen Sie diesem Projekt zu wachsen",
|
||||
"help_project_grow_description": "Spotube ist ein Open-Source-Projekt. Sie können diesem Projekt helfen, indem Sie zum Projekt beitragen, Fehler melden oder neue Funktionen vorschlagen.",
|
||||
"contribute_on_github": "Auf GitHub beitragen",
|
||||
"donate_on_open_collective": "Auf Open Collective spenden",
|
||||
"browse_anonymously": "Anonym durchsuchen"
|
||||
}
|
||||
@ -41,6 +41,7 @@
|
||||
"sort_z_a": "Sort by Z-A",
|
||||
"sort_artist": "Sort by Artist",
|
||||
"sort_album": "Sort by Album",
|
||||
"sort_duration": "Sort by Duration",
|
||||
"sort_tracks": "Sort Tracks",
|
||||
"currently_downloading": "Currently Downloading ({tracks_length})",
|
||||
"cancel_all": "Cancel All",
|
||||
@ -286,5 +287,31 @@
|
||||
"genres": "Genres",
|
||||
"explore_genres": "Explore Genres",
|
||||
"friends": "Friends",
|
||||
"no_lyrics_available": "Sorry, unable find lyrics for this track"
|
||||
"no_lyrics_available": "Sorry, unable find lyrics for this track",
|
||||
"start_a_radio": "Start a Radio",
|
||||
"how_to_start_radio": "How do you want to start the radio?",
|
||||
"replace_queue_question": "Do you want to replace the current queue or append to it?",
|
||||
"endless_playback": "Endless Playback",
|
||||
"delete_playlist": "Delete Playlist",
|
||||
"delete_playlist_confirmation": "Are you sure you want to delete this playlist?",
|
||||
"local_tracks": "Local Tracks",
|
||||
"song_link": "Song Link",
|
||||
"skip_this_nonsense": "Skip this nonsense",
|
||||
"freedom_of_music": "“Freedom of Music”",
|
||||
"freedom_of_music_palm": "“Freedom of Music in the palm of your hand”",
|
||||
"get_started": "Let's get started",
|
||||
"youtube_source_description": "Recommended and works best.",
|
||||
"piped_source_description": "Feeling free? Same as YouTube but a lot free.",
|
||||
"jiosaavn_source_description": "Best for South Asian region.",
|
||||
"highest_quality": "Highest Quality: {quality}",
|
||||
"select_audio_source": "Select Audio Source",
|
||||
"endless_playback_description": "Automatically append new songs\nto the end of the queue",
|
||||
"choose_your_region": "Choose your region",
|
||||
"choose_your_region_description": "This will help Spotube show you the right content\nfor your location.",
|
||||
"choose_your_language": "Choose your language",
|
||||
"help_project_grow": "Help this project grow",
|
||||
"help_project_grow_description": "Spotube is an open-source project. You can help this project grow by contributing to the project, reporting bugs, or suggesting new features.",
|
||||
"contribute_on_github": "Contribute on GitHub",
|
||||
"donate_on_open_collective": "Donate on Open Collective",
|
||||
"browse_anonymously": "Browse Anonymously"
|
||||
}
|
||||
@ -286,5 +286,32 @@
|
||||
"step_3_steps": "Copia el valor de la cookie \"sp_dc\"",
|
||||
"step_4_steps": "Pega el valor copiado de \"sp_dc\"",
|
||||
"friends": "Amigos",
|
||||
"no_lyrics_available": "Lo siento, no se pueden encontrar las letras de esta pista"
|
||||
"no_lyrics_available": "Lo siento, no se pueden encontrar las letras de esta pista",
|
||||
"sort_duration": "Ordenar por Duración",
|
||||
"start_a_radio": "Iniciar una Radio",
|
||||
"how_to_start_radio": "¿Cómo quieres iniciar la radio?",
|
||||
"replace_queue_question": "¿Quieres reemplazar la lista de reproducción actual o añadir a ella?",
|
||||
"endless_playback": "Reproducción Infinita",
|
||||
"delete_playlist": "Eliminar Lista de Reproducción",
|
||||
"delete_playlist_confirmation": "¿Estás seguro de que quieres eliminar esta lista de reproducción?",
|
||||
"local_tracks": "Pistas Locales",
|
||||
"song_link": "Enlace de la Canción",
|
||||
"skip_this_nonsense": "Saltar esta tontería",
|
||||
"freedom_of_music": "“Libertad de la Música”",
|
||||
"freedom_of_music_palm": "“Libertad de la Música en la palma de tu mano”",
|
||||
"get_started": "Empecemos",
|
||||
"youtube_source_description": "Recomendado y funciona mejor.",
|
||||
"piped_source_description": "¿Te sientes libre? Igual que YouTube pero más libre.",
|
||||
"jiosaavn_source_description": "Lo mejor para la región del sur de Asia.",
|
||||
"highest_quality": "Mayor Calidad: {quality}",
|
||||
"select_audio_source": "Seleccionar Fuente de Audio",
|
||||
"endless_playback_description": "Añadir automáticamente nuevas canciones\nal final de la cola de reproducción",
|
||||
"choose_your_region": "Elige tu región",
|
||||
"choose_your_region_description": "Esto ayudará a Spotube a mostrarte el contenido adecuado\npara tu ubicación.",
|
||||
"choose_your_language": "Elige tu idioma",
|
||||
"help_project_grow": "Ayuda a que este proyecto crezca",
|
||||
"help_project_grow_description": "Spotube es un proyecto de código abierto. Puedes ayudar a que este proyecto crezca contribuyendo al proyecto, informando errores o sugiriendo nuevas funciones.",
|
||||
"contribute_on_github": "Contribuir en GitHub",
|
||||
"donate_on_open_collective": "Donar en Open Collective",
|
||||
"browse_anonymously": "Navegar Anónimamente"
|
||||
}
|
||||
@ -286,5 +286,32 @@
|
||||
"step_3_steps": "مقدار کوکی \"sp_dc\" را کپی کنید",
|
||||
"step_4_steps": "مقدار کپی شده \"sp_dc\" را الصاق کنید",
|
||||
"friends": "دوستان",
|
||||
"no_lyrics_available": "متاسفیم، قادر به یافتن متن این قطعه نیستیم"
|
||||
"no_lyrics_available": "متاسفیم، قادر به یافتن متن این قطعه نیستیم",
|
||||
"sort_duration": "مرتب کردن بر اساس مدت زمان",
|
||||
"start_a_radio": "شروع یک رادیو",
|
||||
"how_to_start_radio": "چگونه میخواهید رادیو را شروع کنید؟",
|
||||
"replace_queue_question": "آیا میخواهید لیست پخش فعلی را جایگزین کنید یا به آن اضافه کنید؟",
|
||||
"endless_playback": "پخش بیپایان",
|
||||
"delete_playlist": "حذف لیست پخش",
|
||||
"delete_playlist_confirmation": "آیا مطمئن هستید که میخواهید این لیست پخش را حذف کنید؟",
|
||||
"local_tracks": "موسیقیهای محلی",
|
||||
"song_link": "پیوند آهنگ",
|
||||
"skip_this_nonsense": "این احمقانه را بگذرانید",
|
||||
"freedom_of_music": "“آزادی موسیقی”",
|
||||
"freedom_of_music_palm": "“آزادی موسیقی در دستان شما”",
|
||||
"get_started": "بیایید شروع کنیم",
|
||||
"youtube_source_description": "پیشنهاد شده و بهترین عمل میکند.",
|
||||
"piped_source_description": "احساس آزادی میکنید؟ مانند یوتیوب اما بیشتر آزاد.",
|
||||
"jiosaavn_source_description": "بهترین برای منطقه جنوب آسیا.",
|
||||
"highest_quality": "بالاترین کیفیت: {quality}",
|
||||
"select_audio_source": "انتخاب منبع صوتی",
|
||||
"endless_playback_description": "خودکار اضافه کردن آهنگهای جدید\nبه انتهای صف",
|
||||
"choose_your_region": "منطقه خود را انتخاب کنید",
|
||||
"choose_your_region_description": "این به Spotube کمک میکند تا محتوای مناسبی را برای موقعیت شما نشان دهد.",
|
||||
"choose_your_language": "زبان خود را انتخاب کنید",
|
||||
"help_project_grow": "کمک به رشد این پروژه",
|
||||
"help_project_grow_description": "Spotube یک پروژه متن باز است. شما میتوانید با به پروژه کمک کردن، گزارش دادن اشکالات یا پیشنهاد ویژگیهای جدید، به این پروژه کمک کنید.",
|
||||
"contribute_on_github": "مشارکت در GitHub",
|
||||
"donate_on_open_collective": "کمک مالی در Open Collective",
|
||||
"browse_anonymously": "مرور به صورت ناشناس"
|
||||
}
|
||||
@ -286,5 +286,32 @@
|
||||
"step_3_steps": "Copiez la valeur du cookie \"sp_dc\"",
|
||||
"step_4_steps": "Collez la valeur copiée de \"sp_dc\"",
|
||||
"friends": "Amis",
|
||||
"no_lyrics_available": "Désolé, impossible de trouver les paroles de cette piste"
|
||||
"no_lyrics_available": "Désolé, impossible de trouver les paroles de cette piste",
|
||||
"sort_duration": "Trier par durée",
|
||||
"start_a_radio": "Démarrer une radio",
|
||||
"how_to_start_radio": "Comment voulez-vous démarrer la radio ?",
|
||||
"replace_queue_question": "Voulez-vous remplacer la file d'attente actuelle ou y ajouter ?",
|
||||
"endless_playback": "Lecture sans fin",
|
||||
"delete_playlist": "Supprimer la playlist",
|
||||
"delete_playlist_confirmation": "Êtes-vous sûr de vouloir supprimer cette playlist ?",
|
||||
"local_tracks": "Titres locaux",
|
||||
"song_link": "Lien de la chanson",
|
||||
"skip_this_nonsense": "Passer cette absurdité",
|
||||
"freedom_of_music": "“Liberté de la musique”",
|
||||
"freedom_of_music_palm": "“Liberté de la musique dans la paume de votre main”",
|
||||
"get_started": "Commençons",
|
||||
"youtube_source_description": "Recommandé et fonctionne mieux.",
|
||||
"piped_source_description": "Vous vous sentez libre ? Comme YouTube mais beaucoup plus gratuit.",
|
||||
"jiosaavn_source_description": "Le meilleur pour la région d'Asie du Sud.",
|
||||
"highest_quality": "Meilleure qualité : {quality}",
|
||||
"select_audio_source": "Sélectionner la source audio",
|
||||
"endless_playback_description": "Ajouter automatiquement de nouvelles chansons à la fin de la file d'attente",
|
||||
"choose_your_region": "Choisissez votre région",
|
||||
"choose_your_region_description": "Cela aidera Spotube à vous montrer le bon contenu pour votre emplacement.",
|
||||
"choose_your_language": "Choisissez votre langue",
|
||||
"help_project_grow": "Aidez ce projet à grandir",
|
||||
"help_project_grow_description": "Spotube est un projet open-source. Vous pouvez aider ce projet à grandir en contribuant au projet, en signalant des bugs ou en suggérant de nouvelles fonctionnalités.",
|
||||
"contribute_on_github": "Contribuer sur GitHub",
|
||||
"donate_on_open_collective": "Faire un don sur Open Collective",
|
||||
"browse_anonymously": "Naviguer anonymement"
|
||||
}
|
||||
@ -286,5 +286,32 @@
|
||||
"step_3_steps": "\"sp_dc\" कुकी का मूल्य कॉपी करें",
|
||||
"step_4_steps": "कॉपी किए गए \"sp_dc\" मूल्य को पेस्ट करें",
|
||||
"friends": "दोस्त",
|
||||
"no_lyrics_available": "क्षमा करें, इस ट्रैक के लिए गाने नहीं मिल सके"
|
||||
"no_lyrics_available": "क्षमा करें, इस ट्रैक के लिए गाने नहीं मिल सके",
|
||||
"sort_duration": "समय के आधार पर क्रमबद्ध करें",
|
||||
"start_a_radio": "रेडियो शुरू करें",
|
||||
"how_to_start_radio": "रेडियो कैसे शुरू करना चाहते हैं?",
|
||||
"replace_queue_question": "क्या आप वर्तमान कतार को बदलना चाहते हैं या इसे जोड़ना चाहते हैं?",
|
||||
"endless_playback": "अंतहीन प्लेबैक",
|
||||
"delete_playlist": "प्लेलिस्ट हटाएं",
|
||||
"delete_playlist_confirmation": "क्या आप वाकई इस प्लेलिस्ट को हटाना चाहते हैं?",
|
||||
"local_tracks": "स्थानीय ट्रैक्स",
|
||||
"song_link": "गाने का लिंक",
|
||||
"skip_this_nonsense": "इस माया को छोड़ें",
|
||||
"freedom_of_music": "“संगीत की स्वतंत्रता”",
|
||||
"freedom_of_music_palm": "“हाथ में संगीत की स्वतंत्रता”",
|
||||
"get_started": "आइए शुरू करें",
|
||||
"youtube_source_description": "सिफारिश किया गया और सबसे अच्छा काम करता है।",
|
||||
"piped_source_description": "मुफ्त महसूस कर रहे हैं? YouTube के समान लेकिन काफी अधिक मुफ्त।",
|
||||
"jiosaavn_source_description": "दक्षिण एशियाई क्षेत्र के लिए सर्वोत्तम।",
|
||||
"highest_quality": "सर्वोत्तम गुणवत्ता: {quality}",
|
||||
"select_audio_source": "ऑडियो स्रोत चुनें",
|
||||
"endless_playback_description": "क्रमबद्ध कतार के अंत में नए गाने स्वचालित रूप से जोड़ें",
|
||||
"choose_your_region": "अपना क्षेत्र चुनें",
|
||||
"choose_your_region_description": "यह Spotube को आपके स्थान के लिए सही सामग्री दिखाने में मदद करेगा।",
|
||||
"choose_your_language": "अपनी भाषा चुनें",
|
||||
"help_project_grow": "इस परियोजना को बढ़ावा दें",
|
||||
"help_project_grow_description": "Spotube एक ओपन सोर्स परियोजना है। आप इस परियोजना को योगदान देकर, बग रिपोर्ट करके या नई विशेषताओं का सुझाव देकर इस परियोजना को बढ़ा सकते हैं।",
|
||||
"contribute_on_github": "GitHub पर योगदान करें",
|
||||
"donate_on_open_collective": "ओपन कलेक्टिव पर दान करें",
|
||||
"browse_anonymously": "बिना नाम के ब्राउज़ करें"
|
||||
}
|
||||
@ -287,5 +287,32 @@
|
||||
"step_3_steps": "Copia il valore del cookie \"sp_dc\"",
|
||||
"step_4_steps": "Incolla il valore copiato di \"sp_dc\"",
|
||||
"friends": "Amici",
|
||||
"no_lyrics_available": "Spiacente, impossibile trovare il testo di questa traccia"
|
||||
"no_lyrics_available": "Spiacente, impossibile trovare il testo di questa traccia",
|
||||
"sort_duration": "Ordina per Durata",
|
||||
"start_a_radio": "Avvia una Radio",
|
||||
"how_to_start_radio": "Come vuoi avviare la radio?",
|
||||
"replace_queue_question": "Vuoi sostituire la coda attuale o aggiungerla?",
|
||||
"endless_playback": "Riproduzione Infinita",
|
||||
"delete_playlist": "Elimina Playlist",
|
||||
"delete_playlist_confirmation": "Sei sicuro di voler eliminare questa playlist?",
|
||||
"local_tracks": "Tracce Locali",
|
||||
"song_link": "Link della Canzone",
|
||||
"skip_this_nonsense": "Salta questa sciocchezza",
|
||||
"freedom_of_music": "“Libertà della Musica”",
|
||||
"freedom_of_music_palm": "“Libertà della Musica nel palmo della tua mano”",
|
||||
"get_started": "Cominciamo",
|
||||
"youtube_source_description": "Consigliato e funziona meglio.",
|
||||
"piped_source_description": "Ti senti libero? Come YouTube ma molto più gratuito.",
|
||||
"jiosaavn_source_description": "Il migliore per la regione dell'Asia meridionale.",
|
||||
"highest_quality": "Massima Qualità: {quality}",
|
||||
"select_audio_source": "Seleziona Sorgente Audio",
|
||||
"endless_playback_description": "Aggiungi automaticamente nuove canzoni alla fine della coda",
|
||||
"choose_your_region": "Scegli la tua regione",
|
||||
"choose_your_region_description": "Questo aiuterà Spotube a mostrarti il contenuto giusto per la tua posizione.",
|
||||
"choose_your_language": "Scegli la tua lingua",
|
||||
"help_project_grow": "Aiuta questo progetto a crescere",
|
||||
"help_project_grow_description": "Spotube è un progetto open-source. Puoi aiutare questo progetto a crescere contribuendo al progetto, segnalando bug o suggerendo nuove funzionalità.",
|
||||
"contribute_on_github": "Contribuisci su GitHub",
|
||||
"donate_on_open_collective": "Dona su Open Collective",
|
||||
"browse_anonymously": "Naviga in modo anonimo"
|
||||
}
|
||||
@ -286,5 +286,32 @@
|
||||
"step_3_steps": "\"sp_dc\" Cookieの値をコピー",
|
||||
"step_4_steps": "コピーした\"sp_dc\"の値を貼り付け",
|
||||
"friends": "友達",
|
||||
"no_lyrics_available": "申し訳ありませんが、このトラックの歌詞を見つけることができません"
|
||||
"no_lyrics_available": "申し訳ありませんが、このトラックの歌詞を見つけることができません",
|
||||
"sort_duration": "時間で並べ替え",
|
||||
"start_a_radio": "ラジオを開始",
|
||||
"how_to_start_radio": "ラジオをどのように開始しますか?",
|
||||
"replace_queue_question": "現在のキューを置き換えるか、追加しますか?",
|
||||
"endless_playback": "エンドレス再生",
|
||||
"delete_playlist": "プレイリストを削除",
|
||||
"delete_playlist_confirmation": "このプレイリストを削除してもよろしいですか?",
|
||||
"local_tracks": "ローカルトラック",
|
||||
"song_link": "曲のリンク",
|
||||
"skip_this_nonsense": "この愚かなことをスキップ",
|
||||
"freedom_of_music": "“音楽の自由”",
|
||||
"freedom_of_music_palm": "“手のひらの中の音楽の自由”",
|
||||
"get_started": "さあ始めましょう",
|
||||
"youtube_source_description": "推奨され、最適に機能します。",
|
||||
"piped_source_description": "自由に感じますか? YouTubeと同じですが、はるかに無料です。",
|
||||
"jiosaavn_source_description": "南アジア地域向けの最適です。",
|
||||
"highest_quality": "最高品質:{quality}",
|
||||
"select_audio_source": "オーディオソースを選択",
|
||||
"endless_playback_description": "新しい曲をキューの最後に自動的に追加",
|
||||
"choose_your_region": "地域を選択",
|
||||
"choose_your_region_description": "これにより、Spotubeがあなたの場所に適したコンテンツを表示できます。",
|
||||
"choose_your_language": "言語を選択してください",
|
||||
"help_project_grow": "このプロジェクトの成長を支援する",
|
||||
"help_project_grow_description": "Spotubeはオープンソースプロジェクトです。プロジェクトに貢献したり、バグを報告したり、新しい機能を提案することで、このプロジェクトの成長に貢献できます。",
|
||||
"contribute_on_github": "GitHubで貢献する",
|
||||
"donate_on_open_collective": "Open Collectiveで寄付する",
|
||||
"browse_anonymously": "匿名で閲覧する"
|
||||
}
|
||||
318
lib/l10n/app_ko.arb
Normal file
@ -0,0 +1,318 @@
|
||||
{
|
||||
"guest": "게스트",
|
||||
"browse": "찾아보기",
|
||||
"search": "검색",
|
||||
"library": "라이브러리",
|
||||
"lyrics": "가사",
|
||||
"settings": "설정",
|
||||
"genre_categories_filter": "카테고리 혹은 장르별로 불러오기",
|
||||
"genre": "장르",
|
||||
"personalized": "맞춤 추천",
|
||||
"featured": "인기",
|
||||
"new_releases": "신곡",
|
||||
"songs": "노래",
|
||||
"playing_track": "{track} 을 재생",
|
||||
"queue_clear_alert": "현재 재생 대기열을 없앱니다。{track_length} 곡이 제거됩니다。\n계속 진행할까요?",
|
||||
"load_more": "더 불러오기",
|
||||
"playlists": "플레이리스트",
|
||||
"artists": "아티스트",
|
||||
"albums": "앨범",
|
||||
"tracks": "곡",
|
||||
"downloads": "다운로드한 곡",
|
||||
"filter_playlists": "플레이리스트를 필터링",
|
||||
"liked_tracks": "좋아하는 곡",
|
||||
"liked_tracks_description": "좋아요를 남긴 곡들",
|
||||
"create_playlist": "플레이리스트 생성",
|
||||
"create_a_playlist": "플레이리스트를 생성",
|
||||
"create": "생성",
|
||||
"cancel": "취소",
|
||||
"playlist_name": "플레이리스트명",
|
||||
"name_of_playlist": "플레이리스트의 이름",
|
||||
"description": "설명",
|
||||
"public": "공개",
|
||||
"collaborative": "공유 플레이리스트",
|
||||
"search_local_tracks": "기기에 저장된 곡을 검색하기",
|
||||
"play": "재생",
|
||||
"delete": "삭제",
|
||||
"none": "없음",
|
||||
"sort_a_z": "A-Z 순으로 정렬",
|
||||
"sort_z_a": "Z-A 순으로 정렬",
|
||||
"sort_artist": "아티스트 순으로 정렬",
|
||||
"sort_album": "앨범 순으로 정렬",
|
||||
"sort_tracks": "곡명 순으로 정렬",
|
||||
"currently_downloading": "현재 ({tracks_length}) 곡 다운로드 중",
|
||||
"cancel_all": "모두 취소",
|
||||
"filter_artist": "아티스트 필터링",
|
||||
"followers": "{followers} 팔로워",
|
||||
"add_artist_to_blacklist": "이 아티스트를 블랙리스트에 추가",
|
||||
"top_tracks": "인기곡",
|
||||
"fans_also_like": "애청자들이 좋아하는 곡",
|
||||
"loading": "불러오는 중...",
|
||||
"artist": "아티스트",
|
||||
"blacklisted": "블랙리스트",
|
||||
"following": "팔로우 중",
|
||||
"follow": "팔로우하기",
|
||||
"artist_url_copied": "아티스트의 URL 주소를 클립보드에 복사함",
|
||||
"added_to_queue": "{tracks} 곡을 대기열에 추가함",
|
||||
"filter_albums": "앨범 필터링",
|
||||
"synced": "동기화됨",
|
||||
"plain": "그대로",
|
||||
"shuffle": "셔플",
|
||||
"search_tracks": "곡 검색하기",
|
||||
"released": "공개일",
|
||||
"error": "에러",
|
||||
"title": "타이틀",
|
||||
"time": "길이",
|
||||
"more_actions": "다른 작업",
|
||||
"download_count": "({count}) 곡 다운로드",
|
||||
"add_count_to_playlist": "플레이리스트에 ({count}) 곡을 추가",
|
||||
"add_count_to_queue": "대기열에 ({count}) 곡을 추가",
|
||||
"play_count_next": "이 다음에 ({count}) 곡을 재생",
|
||||
"album": "앨범",
|
||||
"copied_to_clipboard": "{data} 를 클립보드에 복사함",
|
||||
"add_to_following_playlists": "{track} 을 이 플레이리스트에 추가",
|
||||
"add": "추가",
|
||||
"added_track_to_queue": "대기열에 {track} 을 추가함",
|
||||
"add_to_queue": "대기열에 추가",
|
||||
"track_will_play_next": "{track} 을 이 다음에 재생",
|
||||
"play_next": "이 다음에 재생",
|
||||
"removed_track_from_queue": "대기열에서 {track} 를 제거함",
|
||||
"remove_from_queue": "대기열에서 제거",
|
||||
"remove_from_favorites": "즐겨찾기에서 제거",
|
||||
"save_as_favorite": "즐겨찾기에 추가",
|
||||
"add_to_playlist": "플레이리스트에 추가",
|
||||
"remove_from_playlist": "플레이리스트에서 제거",
|
||||
"add_to_blacklist": "블랙리스트에 추가",
|
||||
"remove_from_blacklist": "블랙리스트에서 제거",
|
||||
"share": "공유",
|
||||
"mini_player": "미니 플레이어",
|
||||
"slide_to_seek": "앞뒤로 슬라이드하여 탐색",
|
||||
"shuffle_playlist": "플레이리스트를 섞기",
|
||||
"unshuffle_playlist": "플레이리스트를 섞지 않기",
|
||||
"previous_track": "이전 곡",
|
||||
"next_track": "다음 곡",
|
||||
"pause_playback": "일시정지",
|
||||
"resume_playback": "재개",
|
||||
"loop_track": "반복 재생",
|
||||
"repeat_playlist": "플레이리스트 반복",
|
||||
"queue": "재생 대기열",
|
||||
"alternative_track_sources": "대체가능한 음악 서버",
|
||||
"download_track": "곡 다운로드",
|
||||
"tracks_in_queue": "대기열에 {tracks} 곡이 있음",
|
||||
"clear_all": "모두 제거",
|
||||
"show_hide_ui_on_hover": "마우스를 올리면 UI를 표시/숨김",
|
||||
"always_on_top": "항상 위에 표시",
|
||||
"exit_mini_player": "미니 플레이어 닫기",
|
||||
"download_location": "다운로드 경로",
|
||||
"account": "계정",
|
||||
"login_with_spotify": "Spotify 계정으로 로그인",
|
||||
"connect_with_spotify": "Spotify에 연결",
|
||||
"logout": "로그아웃",
|
||||
"logout_of_this_account": "이 계정에서 로그아웃",
|
||||
"language_region": "언어 & 지역",
|
||||
"language": "언어",
|
||||
"system_default": "시스템 기본설정",
|
||||
"market_place_region": "마켓플레이스 지역",
|
||||
"recommendation_country": "추천 국가",
|
||||
"appearance": "디자인",
|
||||
"layout_mode": "레이아웃 모드",
|
||||
"override_layout_settings": "반응형 레이아웃 모드 설정 덮어씌우기",
|
||||
"adaptive": "적응형",
|
||||
"compact": "컴팩트",
|
||||
"extended": "확장",
|
||||
"theme": "테마",
|
||||
"dark": "다크",
|
||||
"light": "라이트",
|
||||
"system": "시스템과 동일",
|
||||
"accent_color": "보조색",
|
||||
"sync_album_color": "앨범 색상",
|
||||
"sync_album_color_description": "앨범아트의 주요 색상을 보조색으로 사용",
|
||||
"playback": "재생",
|
||||
"audio_quality": "음질",
|
||||
"high": "높음",
|
||||
"low": "낮음",
|
||||
"pre_download_play": "재생할 곡을 미리 다운로드",
|
||||
"pre_download_play_description": "스트리밍 방식을 쓰는 대신 파일 단위로 다운로드 받고 재생 (인터넷 대역폭이 높은 환경에서 추천)",
|
||||
"skip_non_music": "음악이 아닌 부분을 스킵 (SponsorBlock)",
|
||||
"blacklist_description": "블랙리스트에 추가된 곡과 아티스트",
|
||||
"wait_for_download_to_finish": "현재 진행중인 다운로드가 끝날 때까지 기다려주세요",
|
||||
"desktop": "데스크톱",
|
||||
"close_behavior": "닫을 때의 동작",
|
||||
"close": "닫기",
|
||||
"minimize_to_tray": "트레이로 최소화",
|
||||
"show_tray_icon": "시스템 트레이 아이콘 표시",
|
||||
"about": "앱 정보",
|
||||
"u_love_spotube": "Spotube... 사랑하시죠?",
|
||||
"check_for_updates": "업데이트 확인",
|
||||
"about_spotube": "Spotube에 관해",
|
||||
"blacklist": "블랙리스트",
|
||||
"please_sponsor": "후원해주시면 감사하겠습니다.",
|
||||
"spotube_description": "Spotube는, 경량에 크로스플랫폼인데다 무료이기까지한 스포티파이 클라이언트입니다",
|
||||
"version": "버전",
|
||||
"build_number": "빌드 번호",
|
||||
"founder": "창시자",
|
||||
"repository": "리포지토리",
|
||||
"bug_issues": "버그 및 이슈",
|
||||
"made_with": "❤️을 담아 방글라데시에서 만듦",
|
||||
"kingkor_roy_tirtho": "Kingkor Roy Tirtho",
|
||||
"copyright": "© 2021-{current_year} Kingkor Roy Tirtho",
|
||||
"license": "라이선스",
|
||||
"add_spotify_credentials": "먼저 Spotify의 로그인정보를 추가하기",
|
||||
"credentials_will_not_be_shared_disclaimer": "걱정마세요. 개인정보를 수집하거나 공유하지 않습니다.",
|
||||
"know_how_to_login": "어떻게 하는건지 모르겠나요?",
|
||||
"follow_step_by_step_guide": "사용법 확인하기",
|
||||
"spotify_cookie": "Spotify {name} Cookies",
|
||||
"cookie_name_cookie": "{name} Cookies",
|
||||
"fill_in_all_fields": "모든 필드에 정보를 입력해주세요",
|
||||
"submit": "제출",
|
||||
"exit": "종료",
|
||||
"previous": "이전으로",
|
||||
"next": "다음으로",
|
||||
"done": "완료",
|
||||
"step_1": "1단계",
|
||||
"first_go_to": "가장 먼저 먼저 들어갈 곳은 ",
|
||||
"login_if_not_logged_in": "그리고 로그인을 하지 않았다면 로그인해주세요",
|
||||
"step_2": "2단계",
|
||||
"step_2_steps": "1. 로그인에 성공하면、F12나 마우스 우클릭 > 검사(Inspect)을 눌러 브라우저의 개발자 도구(devtools)를 열어주세요.\n2. 애플리케이션 (Application) 탭 (Chrome, Edge, Brave 등) 또는 스토리지 탭 (Firefox, Palemoon 등)을 열어주세요.\n3. 쿠키 (Cookies) 섹션으로 들어가서, https://accounts.spotify.com 서브섹션으로 들어가주세요.",
|
||||
"step_3": "3단계",
|
||||
"success_emoji": "성공🥳",
|
||||
"success_message": "성공적으로 스포티파이 게정으로 로그인했습니다. 잘했어요!",
|
||||
"step_4": "4단계",
|
||||
"something_went_wrong": "알 수 없는 이유로 동작에 실패했습니다.",
|
||||
"piped_instance": "Piped 서버의 인스턴스",
|
||||
"piped_description": "곡 탐색에 사용할 Piped 서버 인스턴스",
|
||||
"piped_warning": "몇몇 서버는 제대로 동작하지 않을 수 있습니다. 본인 책임 하에 이용해주세요.",
|
||||
"generate_playlist": "플레이리스트 생성",
|
||||
"track_exists": "곡 {track} 은 이미 리스트에 있습니다",
|
||||
"replace_downloaded_tracks": "다운로드한 모든 곡을 교체",
|
||||
"skip_download_tracks": "다운로드가 끝난 곡을 모두 건너뛰기",
|
||||
"do_you_want_to_replace": "현재 곡을 교체하시겠습니까?",
|
||||
"replace": "교체",
|
||||
"skip": "건너뛰기",
|
||||
"select_up_to_count_type": "{type}을 {count}개까지 선택",
|
||||
"select_genres": "장르 선택",
|
||||
"add_genres": "장르 추가",
|
||||
"country": "국가",
|
||||
"number_of_tracks_generate": "생성할 곡 수",
|
||||
"acousticness": "반주 구간 (Acousticness)",
|
||||
"danceability": "흥겨운 정도 (Danceability)",
|
||||
"energy": "에너지 (Energy)",
|
||||
"instrumentalness": "기악성 (Instrumentalness)",
|
||||
"liveness": "생동감 (Liveness)",
|
||||
"loudness": "라우드니스 (Loudness)",
|
||||
"speechiness": "회화성 (Speechniss)",
|
||||
"valence": "감정가 (Valence)",
|
||||
"popularity": "인기도 (Popularity)",
|
||||
"key": "조성 (키)",
|
||||
"duration": "길이 (초)",
|
||||
"tempo": "템포 (BPM)",
|
||||
"mode": "장조",
|
||||
"time_signature": "박자",
|
||||
"short": "짧음",
|
||||
"medium": "중간",
|
||||
"long": "긺",
|
||||
"min": "최소",
|
||||
"max": "최대",
|
||||
"target": "목표",
|
||||
"moderate": "보통",
|
||||
"deselect_all": "모두 선택해제",
|
||||
"select_all": "모두 선택",
|
||||
"are_you_sure": "괜찮겠습니까?",
|
||||
"generating_playlist": "커스텀 플레이리스트를 생성하는 중...",
|
||||
"selected_count_tracks": "{count} 곡이 선택되었습니다.",
|
||||
"download_warning": "모든 트랙을 대량으로 다운로드하는 것은 명백한 불법 복제이며 음악 창작 사회에 피해를 입히는 행위입니다. 이 점을 알아주셨으면 합니다. 항상 아티스트의 노력을 존중하고 응원해 주세요.",
|
||||
"download_ip_ban_warning": "참고로, 평소보다 과도한 다운로드 요청으로 인해 YouTube에서 IP가 차단될 수 있습니다. IP 차단은 해당 IP 기기에서 최소 2~3개월 동안 (로그인한 상태에서도) YouTube를 사용할 수 없음을 의미합니다. 그리고 이런 일이 발생하더라도 스포튜브는 어떠한 책임도 지지 않습니다.",
|
||||
"by_clicking_accept_terms": "'동의'를 클릭하면 다음 약관에 동의하는 것입니다:",
|
||||
"download_agreement_1": "알고 있습니다. 전 나쁜 사람입니다.",
|
||||
"download_agreement_2": "제가 할 수 있는 모든 곳에서 아티스트를 지원할 것이며, 저는 그들의 작품을 살 돈이 없기 때문에 이렇게 하는 것뿐입니다.",
|
||||
"download_agreement_3": "본인은 YouTube에서 내 IP가 차단될 수 있음을 완전히 알고 있으며, 현재 내 행동으로 인해 발생하는 사고에 대해 Spotube 또는 그 소유자/기여자에게 책임을 묻지 않습니다.",
|
||||
"decline": "거절",
|
||||
"accept": "동의",
|
||||
"details": "상세",
|
||||
"youtube": "YouTube",
|
||||
"channel": "채널",
|
||||
"likes": "좋아요",
|
||||
"dislikes": "싫어요",
|
||||
"views": "조회수",
|
||||
"streamUrl": "스트림 URL",
|
||||
"stop": "중지",
|
||||
"sort_newest": "최근에 추가된 순으로 정렬",
|
||||
"sort_oldest": "예전에 추가된 순으로 정렬",
|
||||
"sleep_timer": "취침 타이머",
|
||||
"mins": "{minutes} 분",
|
||||
"hours": "{hours} 시간",
|
||||
"hour": "{hours} 시간",
|
||||
"custom_hours": "시간 설정",
|
||||
"logs": "로그",
|
||||
"developers": "개발",
|
||||
"not_logged_in": "로그인하지 않았습니다",
|
||||
"search_mode": "검색 모드",
|
||||
"audio_source": "오디오 출처",
|
||||
"ok": "알겠습니다",
|
||||
"failed_to_encrypt": "암호화에 실패했습니다",
|
||||
"encryption_failed_warning": "Spotube는 암호화를 사용하여 데이터를 안전하게 저장합니다. 하지만 그렇게 하지 못했습니다. 따라서 안전하지 않은 저장소로 대체됩니다.\n리눅스를 사용하는 경우, 비밀 서비스(gnome-keyring, kde-wallet, keepassxc 등)가 설치되어 있는지 확인하세요.",
|
||||
"querying_info": "정보를 얻는 중...",
|
||||
"piped_api_down": "Piped API가 응답하지 않습니다",
|
||||
"piped_down_error_instructions": "Piped 인스턴스 {pipedInstance}가 현재 다운되었습니다.\n\n인스턴스를 변경하거나 'API 유형'을 공식 YouTube API로 변경하세요.\n\n변경 후 앱을 다시 시작해야 합니다.",
|
||||
"you_are_offline": "현재 오프라인입니다",
|
||||
"connection_restored": "인터넷에 다시 연결되었습니다",
|
||||
"use_system_title_bar": "시스템 타이틀바를 사용",
|
||||
"update_playlist": "플레이리스트를 업데이트",
|
||||
"update": "업데이트",
|
||||
"crunching_results": "결과를 처리하는 중...",
|
||||
"search_to_get_results": "결과를 얻으려면 검색해주세요",
|
||||
"use_amoled_mode": "AMOLED모드를 사용",
|
||||
"pitch_dark_theme": "검정색 기반의 어두운 테마",
|
||||
"normalize_audio": "오디오 노멀라이즈",
|
||||
"change_cover": "커버 변경",
|
||||
"add_cover": "커버 추가",
|
||||
"restore_defaults": "기본값으로 복원",
|
||||
"download_music_codec": "다운로드 음악 코덱",
|
||||
"streaming_music_codec": "스트리밍 음악 코덱",
|
||||
"login_with_lastfm": "Last.fm에 로그인",
|
||||
"connect": "연결",
|
||||
"disconnect_lastfm": "Last.fm에서 연결 해제",
|
||||
"disconnect": "연결 해제",
|
||||
"username": "사용자명",
|
||||
"password": "비밀번호",
|
||||
"login": "로그인",
|
||||
"login_with_your_lastfm": "내 Last.fm 계정으로로그인",
|
||||
"scrobble_to_lastfm": "Scrobble to Last.fm",
|
||||
"go_to_album": "앨범으로 이동",
|
||||
"discord_rich_presence": "Discord Rich Presence",
|
||||
"browse_all": "모두 탐색",
|
||||
"genres": "장르",
|
||||
"explore_genres": "장르 탐색",
|
||||
"step_3_steps": "\"sp_dc\" 쿠키의 값을 복사",
|
||||
"step_4_steps": "복사한 \"sp_dc\"값을 붙여넣기",
|
||||
"friends": "친구",
|
||||
"no_lyrics_available": "죄송하지만 이 곡의 가사를 찾지 못했습니다",
|
||||
"@@locale": "ko",
|
||||
"sort_duration": "시간순 정렬",
|
||||
"start_a_radio": "라디오 시작",
|
||||
"how_to_start_radio": "라디오를 어떻게 시작하시겠습니까?",
|
||||
"replace_queue_question": "현재 큐를 대체하시겠습니까 아니면 추가하시겠습니까?",
|
||||
"endless_playback": "끝없는 재생",
|
||||
"delete_playlist": "재생 목록 삭제",
|
||||
"delete_playlist_confirmation": "이 재생 목록을 삭제하시겠습니까?",
|
||||
"local_tracks": "로컬 트랙",
|
||||
"song_link": "곡 링크",
|
||||
"skip_this_nonsense": "이 허튼소리 건너뛰기",
|
||||
"freedom_of_music": "“음악의 자유”",
|
||||
"freedom_of_music_palm": "“손바닥 안의 음악의 자유”",
|
||||
"get_started": "시작합시다",
|
||||
"youtube_source_description": "추천되며 가장 잘 작동합니다.",
|
||||
"piped_source_description": "자유로운 기분이 듭니까? YouTube와 같지만 훨씬 더 무료합니다.",
|
||||
"jiosaavn_source_description": "남아시아 지역에 최적입니다.",
|
||||
"highest_quality": "최고 품질: {quality}",
|
||||
"select_audio_source": "오디오 소스 선택",
|
||||
"endless_playback_description": "자동으로 새로운 노래를 대기열의 끝에 추가",
|
||||
"choose_your_region": "지역 선택",
|
||||
"choose_your_region_description": "이것은 Spotube가 위치에 맞는 콘텐츠를 표시하는 데 도움이 됩니다.",
|
||||
"choose_your_language": "언어 선택",
|
||||
"help_project_grow": "이 프로젝트 성장에 도움을 주세요",
|
||||
"help_project_grow_description": "Spotube는 오픈 소스 프로젝트입니다. 프로젝트에 기여하거나 버그를 보고하거나 새로운 기능을 제안하여이 프로젝트의 성장에 도움을 줄 수 있습니다.",
|
||||
"contribute_on_github": "GitHub에서 기여하기",
|
||||
"donate_on_open_collective": "Open Collective에 기부하기",
|
||||
"browse_anonymously": "익명으로 둘러보기"
|
||||
}
|
||||
@ -286,5 +286,32 @@
|
||||
"genres": "शैलीहरू",
|
||||
"explore_genres": "शैलीहरू अन्वेषण गर्नुहोस्",
|
||||
"friends": "साथीहरू",
|
||||
"no_lyrics_available": "क्षमा गर्दैछौं, यस ट्र्याकका लागि गीतका शब्दहरू फेला परेन"
|
||||
"no_lyrics_available": "क्षमा गर्दैछौं, यस ट्र्याकका लागि गीतका शब्दहरू फेला परेन",
|
||||
"sort_duration": "अवधिको अनुसार क्रमबद्ध गर्नुहोस्",
|
||||
"start_a_radio": "रेडियो सुरु गर्नुहोस्",
|
||||
"how_to_start_radio": "तपाईं रेडियो कसरी सुरु गर्न चाहानुहुन्छ?",
|
||||
"replace_queue_question": "के तपाईं वर्तमान कताक्ष कोट बदल्न चाहानुहुन्छ वा यसलाई थप्नुहुन्छ?",
|
||||
"endless_playback": "अनन्त प्लेब्याक",
|
||||
"delete_playlist": "प्लेलिस्ट मेटाउनुहोस्",
|
||||
"delete_playlist_confirmation": "के तपाईं यो प्लेलिस्ट मेटाउन निश्चित हुनुहुन्छ?",
|
||||
"local_tracks": "स्थानिय ट्र्याकहरू",
|
||||
"song_link": "गीत लिंक",
|
||||
"skip_this_nonsense": "यस अबश्यकता छोड्नुहोस्",
|
||||
"freedom_of_music": "“संगीतको स्वतन्त्रता”",
|
||||
"freedom_of_music_palm": "“तपाईंको हातमा संगीतको स्वतन्त्रता”",
|
||||
"get_started": "आइयाँ प्रारम्भ गरौं",
|
||||
"youtube_source_description": "सिफारिस गरिएको र बेस्ट काम गर्दछ।",
|
||||
"piped_source_description": "मुक्त सुस्त? YouTube जस्तै तर धेरै मुक्त।",
|
||||
"jiosaavn_source_description": "दक्षिण एशियाली क्षेत्रको लागि सर्वोत्तम।",
|
||||
"highest_quality": "उच्चतम गुणस्तर: {quality}",
|
||||
"select_audio_source": "आडियो स्रोत चयन गर्नुहोस्",
|
||||
"endless_playback_description": "नयाँ गीतहरूलाई स्वचालित रूपमा कताक्षको अन्तमा जोड्नुहोस्",
|
||||
"choose_your_region": "तपाईंको क्षेत्र छनौट गर्नुहोस्",
|
||||
"choose_your_region_description": "यो Spotubeलाई तपाईंको स्थानका लागि सहि सामग्री देखाउने मद्दत गर्नेछ।",
|
||||
"choose_your_language": "तपाईंको भाषा छनौट गर्नुहोस्",
|
||||
"help_project_grow": "यस परियोजनामा वृद्धि गराउनुहोस्",
|
||||
"help_project_grow_description": "Spotube एक खुला स्रोतको परियोजना हो। तपाईं परियोजनामा योगदान गरेर, त्रुटिहरू सूचिकै, वा नयाँ सुविधाहरू सुझाव दिएर यस परियोजनामा वृद्धि गर्न सक्नुहुन्छ।",
|
||||
"contribute_on_github": "GitHubमा योगदान गर्नुहोस्",
|
||||
"donate_on_open_collective": "खुला संगठनमा दान गर्नुहोस्",
|
||||
"browse_anonymously": "अनामित रूपमा ब्राउज़ गर्नुहोस्"
|
||||
}
|
||||
@ -1,107 +1,107 @@
|
||||
{
|
||||
"guest": "Gast",
|
||||
"browse": "Bladeren",
|
||||
"search": "Zoek op",
|
||||
"search": "Zoeken",
|
||||
"library": "Bibliotheek",
|
||||
"lyrics": "Liedteksten",
|
||||
"lyrics": "Teksten",
|
||||
"settings": "Instellingen",
|
||||
"genre_categories_filter": "Categorieën of genres filteren...",
|
||||
"genre_categories_filter": "Categorieën of genres filteren…",
|
||||
"genre": "Genre",
|
||||
"personalized": "Gepersonaliseerd",
|
||||
"featured": "Aanbevolen",
|
||||
"new_releases": "Nieuwe uitgaves",
|
||||
"songs": "Liedjes",
|
||||
"playing_track": "{track} afspelen",
|
||||
"queue_clear_alert": "Dit zal de huidige wachtrij wissen. {track_length} tracks worden verwijderd\nWilt u doorgaan?",
|
||||
"queue_clear_alert": "Dit zal de huidige wachtrij wissen. {track_length} nummers worden verwijderd\nWil je doorgaan?",
|
||||
"load_more": "Meer laden",
|
||||
"playlists": "Afspeellijsten",
|
||||
"artists": "Kunstenaars",
|
||||
"artists": "Artiesten",
|
||||
"albums": "Albums",
|
||||
"tracks": "Nummers",
|
||||
"downloads": "Downloads",
|
||||
"filter_playlists": "Filter uw afspeellijsten...",
|
||||
"filter_playlists": "Afspeellijsten filteren…",
|
||||
"liked_tracks": "Geliefde tracks",
|
||||
"liked_tracks_description": "Al je favoriete nummers",
|
||||
"create_playlist": "Afspeellijst maken",
|
||||
"create_a_playlist": "Een afspeellijst maken",
|
||||
"create_playlist": "Afspeellijst aanmaken",
|
||||
"create_a_playlist": "Een afspeellijst aanmaken",
|
||||
"update_playlist": "Afspeellijst bijwerken",
|
||||
"create": "Maak",
|
||||
"create": "Aanmaken",
|
||||
"cancel": "Annuleren",
|
||||
"update": "Bijwerken",
|
||||
"playlist_name": "Afspeellijstnaam",
|
||||
"playlist_name": "Naam afspeellijst",
|
||||
"name_of_playlist": "Naam van de afspeellijst",
|
||||
"description": "Beschrijving",
|
||||
"public": "Openbaar",
|
||||
"collaborative": "Samenwerkend",
|
||||
"search_local_tracks": "Lokale nummers zoeken...",
|
||||
"play": "Speel",
|
||||
"search_local_tracks": "Lokale nummers zoeken…",
|
||||
"play": "Afspelen",
|
||||
"delete": "Wissen",
|
||||
"none": "Geen",
|
||||
"sort_a_z": "Sorteren op A-Z",
|
||||
"sort_z_a": "Sorteren op Z-A",
|
||||
"sort_artist": "Sorteren op kunstenaar",
|
||||
"sort_artist": "Sorteren op artiest",
|
||||
"sort_album": "Sorteren op album",
|
||||
"sort_tracks": "Nummers sorteren",
|
||||
"currently_downloading": "Momenteel aan het downloaden ({tracks_length})",
|
||||
"cancel_all": "Alle annuleren",
|
||||
"filter_artist": "Kunstenaars filteren...",
|
||||
"filter_artist": "Artiesten filteren…",
|
||||
"followers": "{followers} volgers",
|
||||
"add_artist_to_blacklist": "Kunstenaar toevoegen aan zwarte lijst",
|
||||
"add_artist_to_blacklist": "Artiest toevoegen aan zwarte lijst",
|
||||
"top_tracks": "Topsporen",
|
||||
"fans_also_like": "Liefhebbers willen ook",
|
||||
"loading": "Aan het laden...",
|
||||
"artist": "Kunstenaar",
|
||||
"blacklisted": "Op de zwarte lijst",
|
||||
"following": "Op volg",
|
||||
"loading": "Laden…",
|
||||
"artist": "Artiest",
|
||||
"blacklisted": "Zwarte lijst",
|
||||
"following": "Volgen",
|
||||
"follow": "Volgen",
|
||||
"artist_url_copied": "URL artiest gekopieerd naar klembord",
|
||||
"added_to_queue": "{tracks} tracks toegevoegd aan wachtrij",
|
||||
"filter_albums": "Albums filteren...",
|
||||
"added_to_queue": "{tracks} nummers toegevoegd aan wachtrij",
|
||||
"filter_albums": "Albums filteren…",
|
||||
"synced": "Gesynchroniseerd",
|
||||
"plain": "Eenvoudig",
|
||||
"shuffle": "Schuifelen",
|
||||
"search_tracks": "Zoek nummers...",
|
||||
"released": "Vrijgegeven",
|
||||
"shuffle": "Willekeurig",
|
||||
"search_tracks": "Nummers zoeken…",
|
||||
"released": "Uitgegeven",
|
||||
"error": "Fout {error}",
|
||||
"title": "Titel",
|
||||
"time": "Tijd",
|
||||
"more_actions": "Meer acties",
|
||||
"download_count": "({count}) downloads",
|
||||
"add_count_to_playlist": "Voeg ({count}) toe aan afspeellijst",
|
||||
"add_count_to_queue": "Voeg ({count}) toe aan wachtrij",
|
||||
"play_count_next": "Speel ({count}) volgende",
|
||||
"add_count_to_playlist": "({count}) aan afspeellijst toevoegen",
|
||||
"add_count_to_queue": "({count}) aan wachtrij toevoegen",
|
||||
"play_count_next": "Volgende ({count}) afspelen",
|
||||
"album": "Album",
|
||||
"copied_to_clipboard": "{data} naar klembord gekopieerd",
|
||||
"add_to_following_playlists": "Voeg {track} toe aan volgende afspeellijsten",
|
||||
"add_to_following_playlists": "{track} aan volgende afspeellijsten toevoegen",
|
||||
"add": "Toevoegen",
|
||||
"added_track_to_queue": "{track} toegevoegd aan wachtrij",
|
||||
"added_track_to_queue": "{track} aan wachtrij toegevoegd",
|
||||
"add_to_queue": "Toevoegen aan wachtrij",
|
||||
"track_will_play_next": "{track} zal hierna spelen",
|
||||
"track_will_play_next": "{track} wordt hierna afgespeeld",
|
||||
"play_next": "Volgende afspelen",
|
||||
"removed_track_from_queue": "{track} uit wachtrij verwijderd",
|
||||
"remove_from_queue": "Verwijderen uit wachtrij",
|
||||
"remove_from_favorites": "Verwijderen uit favorieten",
|
||||
"removed_track_from_queue": "{track} van wachtrij verwijderd",
|
||||
"remove_from_queue": "Van wachtrij verwijderen",
|
||||
"remove_from_favorites": "Van favorieten verwijderen",
|
||||
"save_as_favorite": "Opslaan als favoriet",
|
||||
"add_to_playlist": "Toevoegen aan afspeellijst",
|
||||
"remove_from_playlist": "Verwijderen uit afspeellijst",
|
||||
"add_to_blacklist": "Toevoegen aan zwarte lijst",
|
||||
"remove_from_blacklist": "Verwijderen uit zwarte lijst",
|
||||
"add_to_playlist": "Aan afspeellijst toevoegen",
|
||||
"remove_from_playlist": "Van afspeellijst verwijderen",
|
||||
"add_to_blacklist": "Aan zwarte lijst toevoegen",
|
||||
"remove_from_blacklist": "Van zwarte lijst verwijderen",
|
||||
"share": "Delen",
|
||||
"mini_player": "Minispeler",
|
||||
"slide_to_seek": "Schuif om vooruit of achteruit te zoeken",
|
||||
"slide_to_seek": "Schuiven om vooruit of achteruit te zoeken",
|
||||
"shuffle_playlist": "Afspeellijst schuifelen",
|
||||
"unshuffle_playlist": "Afspeellijst onschuifelen",
|
||||
"previous_track": "Vorige nummer",
|
||||
"next_track": "Volgende nummer",
|
||||
"pause_playback": "Weergave pauzeren",
|
||||
"resume_playback": "Weergave hervatten",
|
||||
"loop_track": "Nummer loopen",
|
||||
"pause_playback": "Afspelen pauzeren",
|
||||
"resume_playback": "Afspelen hervatten",
|
||||
"loop_track": "Nummer herhalen",
|
||||
"repeat_playlist": "Afspeellijst herhalen",
|
||||
"queue": "Wachtrij",
|
||||
"alternative_track_sources": "Alternatieve nummerbronnen",
|
||||
"download_track": "Nummer downloaden",
|
||||
"tracks_in_queue": "{tracks} tracks in wachtrij",
|
||||
"clear_all": "Wis alles",
|
||||
"tracks_in_queue": "{tracks} nummers in wachtrij",
|
||||
"clear_all": "Alles wissen",
|
||||
"show_hide_ui_on_hover": "UI tonen/verbergen bij zweven",
|
||||
"always_on_top": "Altijd bovenaan",
|
||||
"exit_mini_player": "Minispeler afsluiten",
|
||||
@ -111,7 +111,7 @@
|
||||
"connect_with_spotify": "Verbinden met Spotify",
|
||||
"logout": "Afmelden",
|
||||
"logout_of_this_account": "Afmelden van dit account",
|
||||
"language_region": "Taal & Regio",
|
||||
"language_region": "Taal & regio",
|
||||
"language": "Taal",
|
||||
"system_default": "Systeemstandaard",
|
||||
"market_place_region": "Marktplaats-regio",
|
||||
@ -119,76 +119,78 @@
|
||||
"appearance": "Uiterlijk",
|
||||
"layout_mode": "Opmaakmodus",
|
||||
"override_layout_settings": "Instellingen voor responsieve opmaakmodus opheffen",
|
||||
"adaptive": "Aanpassingsgericht",
|
||||
"adaptive": "Adaptief",
|
||||
"compact": "Compact",
|
||||
"extended": "Uitgebreide",
|
||||
"extended": "Uitgebreid",
|
||||
"theme": "Thema",
|
||||
"dark": "Donker",
|
||||
"light": "Licht",
|
||||
"system": "Systeem",
|
||||
"accent_color": "Accentkleur",
|
||||
"sync_album_color": "Albumkleur synchroniseren",
|
||||
"sync_album_color_description": "Gebruikt de overheersende kleur van het albumartikel als accentkleur",
|
||||
"sync_album_color_description": "Gebruikt de overheersende kleur van het album als accentkleur",
|
||||
"playback": "Weergave",
|
||||
"audio_quality": "Audiokwaliteit",
|
||||
"high": "Hoog",
|
||||
"low": "Laag",
|
||||
"pre_download_play": "Vooraf downloaden en spelen",
|
||||
"pre_download_play": "Vooraf downloaden en afspelen",
|
||||
"pre_download_play_description": "In plaats van audio te streamen, kun je bytes downloaden en afspelen (aanbevolen voor gebruikers met een hogere bandbreedte)",
|
||||
"skip_non_music": "Niet-muzieksegmenten overslaan (SponsorBlock)",
|
||||
"blacklist_description": "Nummers en artiesten op de zwarte lijst",
|
||||
"wait_for_download_to_finish": "Wacht tot de huidige download is voltooid",
|
||||
"desktop": "Bureaublad",
|
||||
"close_behavior": "Sluitgedrag",
|
||||
"close": "Sluit af",
|
||||
"minimize_to_tray": "Minimaliseren naar lade",
|
||||
"close": "Afsluiten",
|
||||
"minimize_to_tray": "Minimaliseren naar systeemvak",
|
||||
"show_tray_icon": "Systeemvakpictogram tonen",
|
||||
"about": "Over",
|
||||
"u_love_spotube": "We weten dat jullie van Spotube houden",
|
||||
"u_love_spotube": "We weten dat je van Spotube houd",
|
||||
"check_for_updates": "Controleren op updates",
|
||||
"about_spotube": "Over Spotube",
|
||||
"blacklist": "Zwarte lijst",
|
||||
"please_sponsor": "Sponsor/Doneer a.u.b.",
|
||||
"spotube_description": "Spotube, een lichtgewicht, cross-platform, vrij-voor-alles Spotify-client",
|
||||
"version": "Versie",
|
||||
"build_number": "Beeldnummer",
|
||||
"founder": "Stichter",
|
||||
"build_number": "Bouwnummer",
|
||||
"founder": "Grondlegger",
|
||||
"repository": "Opslagplaats",
|
||||
"bug_issues": "Bug+problemen",
|
||||
"made_with": "Gemaakt met ❤️ in Bangladesh🇧🇩",
|
||||
"made_with": "Met ❤️ gemaakt in Bangladesh🇧🇩",
|
||||
"kingkor_roy_tirtho": "Kingkor Roy Tirtho",
|
||||
"copyright": "© 2021-{current_year} Kingkor Roy Tirtho",
|
||||
"license": "Licentie",
|
||||
"add_spotify_credentials": "Voeg je spotify-referenties toe om te beginnen",
|
||||
"credentials_will_not_be_shared_disclaimer": "Maakt u geen zorgen, uw gegevens worden niet verzameld of gedeeld met anderen.",
|
||||
"know_how_to_login": "Weet u niet hoe u dit moet doen?",
|
||||
"follow_step_by_step_guide": "Volg de stap voor stap gids",
|
||||
"add_spotify_credentials": "Voeg om te beginnen je spotify-aanmeldgegevens toe",
|
||||
"credentials_will_not_be_shared_disclaimer": "Maak je geen zorgen, je gegevens worden niet verzameld of gedeeld met anderen.",
|
||||
"know_how_to_login": "Weet je niet hoe je dit moet doen?",
|
||||
"follow_step_by_step_guide": "Volg de stapsgewijze handleiding",
|
||||
"spotify_cookie": "Spotify {name} Cookie",
|
||||
"cookie_name_cookie": "{name} Cookie",
|
||||
"fill_in_all_fields": "Vul alle velden in a.u.b.",
|
||||
"submit": "Verzenden",
|
||||
"exit": "Ga weg",
|
||||
"exit": "Afronden",
|
||||
"previous": "Vorige",
|
||||
"next": "Volgende",
|
||||
"done": "Klaar",
|
||||
"step_1": "Stap 1",
|
||||
"first_go_to": "Ga eerst naar",
|
||||
"login_if_not_logged_in": "en Inloggen/Aanmelden als u niet bent ingelogd",
|
||||
"login_if_not_logged_in": "en Inloggen/Aanmelden als je niet bent ingelogd",
|
||||
"step_2": "Stap 2",
|
||||
"step_2_steps": "1. Zodra je bent aangemeld, druk je op F12 of klik je met de rechtermuisknop > Inspect om de Browser devtools te openen.\n2. Ga vervolgens naar het tabblad \"Toepassing\" (Chrome, Edge, Brave enz..) of naar het tabblad \"Opslag\" (Firefox, Palemoon enz..).\n3. Ga naar de sectie \"Cookies\" en vervolgens naar de subsectie \"https://accounts.spotify.com\".",
|
||||
"step_3": "Stap 3",
|
||||
"step_3_steps": "De waarde van cookie \"sp_dc\" kopiëren",
|
||||
"success_emoji": "Succes🥳",
|
||||
"success_message": "Je bent nu succesvol ingelogd met je Spotify account. Goed gedaan, maat!",
|
||||
"success_message": "Je bent nu ingelogd met je Spotify account. Goed gedaan!",
|
||||
"step_4": "Stap 4",
|
||||
"step_4_steps": "De gekopieerde waarde \"sp_dc\" plakken",
|
||||
"something_went_wrong": "Er ging iets mis",
|
||||
"piped_instance": "Piped-serverinstantie",
|
||||
"piped_description": "De Piped-serverinstantie die moet worden gebruikt voor het matchen van sporen",
|
||||
"piped_description": "De Piped-serverinstantie die moet worden gebruikt voor overeenkomstige nummers",
|
||||
"piped_warning": "Sommige werken misschien niet goed. Dus gebruik ze op eigen risico",
|
||||
"generate_playlist": "Afspeellijst genereren",
|
||||
"track_exists": "Nummer {track} bestaat al",
|
||||
"replace_downloaded_tracks": "Alle gedownloade nummers vervangen",
|
||||
"skip_download_tracks": "Downloaden van alle gedownloade nummers overslaan",
|
||||
"do_you_want_to_replace": "Wil je de bestaande nummer vervangen?",
|
||||
"do_you_want_to_replace": "Wil je het bestaande nummer vervangen?",
|
||||
"replace": "Vervangen",
|
||||
"skip": "Overslaan",
|
||||
"select_up_to_count_type": "Selecteer tot {count} {type}",
|
||||
@ -196,13 +198,13 @@
|
||||
"add_genres": "Genres toevoegen",
|
||||
"country": "Land",
|
||||
"number_of_tracks_generate": "Aantal nummers om te genereren",
|
||||
"acousticness": "Akoesticiteit",
|
||||
"acousticness": "Akoestiek",
|
||||
"danceability": "Dansbaarheid",
|
||||
"energy": "Energie",
|
||||
"instrumentalness": "Instrumentaliteit",
|
||||
"liveness": "Levendigheid",
|
||||
"loudness": "Luidheid",
|
||||
"speechiness": "Sprakeligheid",
|
||||
"speechiness": "Spraak",
|
||||
"valence": "Valentie",
|
||||
"popularity": "Populariteit",
|
||||
"key": "Sleutel",
|
||||
@ -217,16 +219,16 @@
|
||||
"max": "Max",
|
||||
"target": "Doel",
|
||||
"moderate": "Matig",
|
||||
"deselect_all": "Alles deselecteren",
|
||||
"deselect_all": "Selectie opheffen",
|
||||
"select_all": "Alles selecteren",
|
||||
"are_you_sure": "Weet je het zeker?",
|
||||
"generating_playlist": "Je aangepaste afspeellijst genereren...",
|
||||
"generating_playlist": "Aangepaste afspeellijst genereren…",
|
||||
"selected_count_tracks": "{count} nummers geselecteerd",
|
||||
"download_warning": "Als je alle Tracks in bulk downloadt, ben je duidelijk bezig met muziekpiraterij en breng je schade toe aan de creatieve muziekmaatschappij. Ik hoop dat je je hiervan bewust bent. Probeer altijd het harde werk van artiesten te respecteren en te steunen.",
|
||||
"download_ip_ban_warning": "BTW, je IP-adres kan worden geblokkeerd op YouTube als gevolg van buitensporige downloadverzoeken dan normaal. IP blokkering betekent dat je YouTube niet kunt gebruiken (zelfs als je ingelogd bent) voor tenminste 2-3 maanden vanaf dat IP apparaat. Spotube is niet verantwoordelijk als dit ooit gebeurt.",
|
||||
"download_warning": "Als je alle nummers in bulk downloadt, ben je duidelijk bezig met muziekpiraterij en breng je schade toe aan de creatieve muziekmaatschappij. Ik hoop dat je je hiervan bewust bent. Probeer altijd het harde werk van artiesten te respecteren en te steunen.",
|
||||
"download_ip_ban_warning": "BTW, je IP-adres kan worden geblokkeerd op YouTube als gevolg van buitensporige downloadverzoeken. IP-blokkering betekent dat je YouTube niet kunt gebruiken (zelfs als je ingelogd bent) voor tenminste 2-3 maanden vanaf dat IP-apparaat. Spotube is niet verantwoordelijk als dit ooit gebeurt.",
|
||||
"by_clicking_accept_terms": "Door op 'accepteren' te klikken ga je akkoord met de volgende voorwaarden:",
|
||||
"download_agreement_1": "Ik weet dat ik muziek illegaal verveel. Ik ben en crimineel.",
|
||||
"download_agreement_2": "Ik steun de kunstenaar waar ik kan en ik doe dit alleen omdat ik geen geld heb om hun kunst te kopen.",
|
||||
"download_agreement_1": "Ik weet dat ik muziek illegaal donload. Ik ben slecht.",
|
||||
"download_agreement_2": "Ik steun de artiest waar ik kan en ik doe dit alleen omdat ik geen geld heb om hun kunst te kopen.",
|
||||
"download_agreement_3": "Ik ben me er volledig van bewust dat mijn IP geblokkeerd kan worden op YouTube & ik houd Spotube of zijn eigenaars/contributeurs niet verantwoordelijk voor ongelukken die veroorzaakt worden door mijn huidige actie.",
|
||||
"decline": "Weigeren",
|
||||
"accept": "Accepteren",
|
||||
@ -247,45 +249,70 @@
|
||||
"custom_hours": "Aangepaste uren",
|
||||
"logs": "Logboeken",
|
||||
"developers": "Ontwikkelaars",
|
||||
"not_logged_in": "U bent niet aangemeld",
|
||||
"not_logged_in": "Je bent niet aangemeld",
|
||||
"search_mode": "Zoekmodus",
|
||||
"youtube_api_type": "API-type",
|
||||
"ok": "Oké",
|
||||
"failed_to_encrypt": "Versleuteling mislukt",
|
||||
"encryption_failed_warning": "Spotube gebruikt encryptie om je gegevens veilig op te slaan. Maar dat is niet gelukt. Dus zal het terugvallen op onveilige opslag.\nAls je linux gebruikt, zorg er dan voor dat je een geheim-dienst (gnome-keyring, kde-wallet, keepassxc etc) hebt geïnstalleerd.",
|
||||
"querying_info": "Info opvragen...",
|
||||
"encryption_failed_warning": "Spotube gebruikt versleuteling om je gegevens veilig op te slaan. Maar dat is niet gelukt. Dus zal het terugvallen op onveilige opslag.\nAls je linux gebruikt, zorg er dan voor dat je een geheim-dienst (gnome-keyring, kde-wallet, keepassxc etc) hebt geïnstalleerd.",
|
||||
"querying_info": "Info opvragen…",
|
||||
"piped_api_down": "Piped API is uit",
|
||||
"piped_down_error_instructions": "De Piped-instantie {pipedInstance} is momenteel uitgevallen\n\nVerander de instantie of verander het 'API-type' naar de officiële YouTube API.\n\nZorg ervoor dat u de app herstart na de wijziging",
|
||||
"you_are_offline": "U bent momenteel offline",
|
||||
"connection_restored": "Uw internetverbinding is hersteld",
|
||||
"you_are_offline": "Je bent momenteel offline",
|
||||
"connection_restored": "Je internetverbinding is hersteld",
|
||||
"use_system_title_bar": "Systeemtitelbalk gebruiken",
|
||||
"crunching_results": "Resultaten kraken...",
|
||||
"search_to_get_results": "Zoek om resultaten te krijgen",
|
||||
"crunching_results": "Resultaten verwerken…",
|
||||
"search_to_get_results": "Zoeken naar resultaten",
|
||||
"use_amoled_mode": "Pikzwart donkerthema",
|
||||
"pitch_dark_theme": "AMOLED-modus",
|
||||
"normalize_audio": "Audio normaliseren",
|
||||
"change_cover": "Dekking wijzigen",
|
||||
"add_cover": "Dekking toevoegen",
|
||||
"change_cover": "Hoes aanpassen",
|
||||
"add_cover": "Hoes toevoegen",
|
||||
"restore_defaults": "Standaardwaarden herstellen",
|
||||
"download_music_codec": "Muziek-codec downloaden",
|
||||
"streaming_music_codec": "Muziek-codec streamen",
|
||||
"login_with_lastfm": "Aanmelden met Last.fm",
|
||||
"download_music_codec": "Download-codec",
|
||||
"streaming_music_codec": "Streaming-codec",
|
||||
"login_with_lastfm": "Inloggen met Last.fm",
|
||||
"connect": "Verbinden",
|
||||
"disconnect_lastfm": "Last.fm verbreken",
|
||||
"disconnect": "Ontkoppelen",
|
||||
"disconnect": "Verbeken",
|
||||
"username": "Gebruikersnaam",
|
||||
"password": "Wachtwoord",
|
||||
"login": "Inloggen",
|
||||
"login_with_your_lastfm": "Inloggen met uw Last.fm account",
|
||||
"scrobble_to_lastfm": "Scrobbel naar Last.fm",
|
||||
"audio_source": "Audiobron",
|
||||
"login_with_your_lastfm": "Inloggen met je Last.fm account",
|
||||
"scrobble_to_lastfm": "Scrobbelen naar Last.fm",
|
||||
"go_to_album": "Ga naar album",
|
||||
"discord_rich_presence": "Discord Rich Presence",
|
||||
"browse_all": "Alles bekijken",
|
||||
"browse_all": "Alles doorbladeren",
|
||||
"genres": "Genres",
|
||||
"explore_genres": "Verken genres",
|
||||
"step_3_steps": "Kopieer de waarde van de \"sp_dc\"-cookie",
|
||||
"step_4_steps": "Plak de gekopieerde waarde van \"sp_dc\"",
|
||||
"explore_genres": "Genres verkennen",
|
||||
"friends": "Vrienden",
|
||||
"no_lyrics_available": "Sorry, kan geen teksten vinden voor deze track"
|
||||
"no_lyrics_available": "Sorry, geen teksten gevonden voor dit nummer",
|
||||
"sort_duration": "Sorteer op Duur",
|
||||
"audio_source": "Audiobron",
|
||||
"start_a_radio": "Start een Radio",
|
||||
"how_to_start_radio": "Hoe wilt u de radio starten?",
|
||||
"replace_queue_question": "Wilt u de huidige wachtrij vervangen of eraan toevoegen?",
|
||||
"endless_playback": "Eindeloze Afspelen",
|
||||
"delete_playlist": "Verwijder Afspeellijst",
|
||||
"delete_playlist_confirmation": "Weet u zeker dat u deze afspeellijst wilt verwijderen?",
|
||||
"local_tracks": "Lokale Nummers",
|
||||
"song_link": "Nummer Link",
|
||||
"skip_this_nonsense": "Sla deze onzin over",
|
||||
"freedom_of_music": "“Vrijheid van Muziek”",
|
||||
"freedom_of_music_palm": "“Vrijheid van Muziek in de palm van je hand”",
|
||||
"get_started": "Laten we beginnen",
|
||||
"youtube_source_description": "Aanbevolen en werkt het beste.",
|
||||
"piped_source_description": "Voel je vrij? Hetzelfde als YouTube maar veel gratis.",
|
||||
"jiosaavn_source_description": "Het beste voor de Zuid-Aziatische regio.",
|
||||
"highest_quality": "Hoogste Kwaliteit: {quality}",
|
||||
"select_audio_source": "Selecteer Audiobron",
|
||||
"endless_playback_description": "Voeg automatisch nieuwe nummers toe aan het einde van de wachtrij",
|
||||
"choose_your_region": "Kies uw regio",
|
||||
"choose_your_region_description": "Dit zal Spotube helpen om de juiste inhoud voor uw locatie te tonen.",
|
||||
"choose_your_language": "Kies uw taal",
|
||||
"help_project_grow": "Help dit project groeien",
|
||||
"help_project_grow_description": "Spotube is een open-source project. U kunt dit project helpen groeien door bij te dragen aan het project, bugs te melden of nieuwe functies voor te stellen.",
|
||||
"contribute_on_github": "Bijdragen op GitHub",
|
||||
"donate_on_open_collective": "Doneren op Open Collective",
|
||||
"browse_anonymously": "Anoniem Bladeren"
|
||||
}
|
||||
@ -286,5 +286,32 @@
|
||||
"step_3_steps": "Skopiuj wartość ciasteczka \"sp_dc\"",
|
||||
"step_4_steps": "Wklej skopiowaną wartość \"sp_dc\"",
|
||||
"friends": "Przyjaciele",
|
||||
"no_lyrics_available": "Przepraszamy, nie można znaleźć tekstu dla tego utworu"
|
||||
"no_lyrics_available": "Przepraszamy, nie można znaleźć tekstu dla tego utworu",
|
||||
"sort_duration": "Sortuj według Czasu Trwania",
|
||||
"start_a_radio": "Uruchom radio",
|
||||
"how_to_start_radio": "Jak chcesz uruchomić radio?",
|
||||
"replace_queue_question": "Czy chcesz zastąpić bieżącą kolejkę czy dodać do niej?",
|
||||
"endless_playback": "Nieskończona Odtwarzanie",
|
||||
"delete_playlist": "Usuń Playlistę",
|
||||
"delete_playlist_confirmation": "Czy na pewno chcesz usunąć tę listę odtwarzania?",
|
||||
"local_tracks": "Lokalne Utwory",
|
||||
"song_link": "Link do Utworu",
|
||||
"skip_this_nonsense": "Pomiń tę bzdurę",
|
||||
"freedom_of_music": "“Wolność Muzyki”",
|
||||
"freedom_of_music_palm": "“Wolność Muzyki w Twojej dłoni”",
|
||||
"get_started": "Zacznijmy",
|
||||
"youtube_source_description": "Polecane i działa najlepiej.",
|
||||
"piped_source_description": "Czujesz się wolny? To samo co YouTube, ale dużo za darmo.",
|
||||
"jiosaavn_source_description": "Najlepszy dla regionu Azji Południowej.",
|
||||
"highest_quality": "Najwyższa Jakość: {quality}",
|
||||
"select_audio_source": "Wybierz Źródło Audio",
|
||||
"endless_playback_description": "Automatycznie dodaj nowe utwory na koniec kolejki",
|
||||
"choose_your_region": "Wybierz swoją region",
|
||||
"choose_your_region_description": "To pomoże Spotube pokazać Ci odpowiednią treść dla Twojej lokalizacji.",
|
||||
"choose_your_language": "Wybierz swój język",
|
||||
"help_project_grow": "Pomóż temu projektowi rosnąć",
|
||||
"help_project_grow_description": "Spotube to projekt open-source. Możesz pomóc temu projektowi rosnąć, przyczyniając się do projektu, zgłaszając błędy lub sugerując nowe funkcje.",
|
||||
"contribute_on_github": "Przyczyniaj się na GitHubie",
|
||||
"donate_on_open_collective": "Dotuj na Open Collective",
|
||||
"browse_anonymously": "Przeglądaj Anonimowo"
|
||||
}
|
||||
@ -286,5 +286,32 @@
|
||||
"step_3_steps": "Copie o valor do cookie \"sp_dc\"",
|
||||
"step_4_steps": "Cole o valor copiado de \"sp_dc\"",
|
||||
"friends": "Amigos",
|
||||
"no_lyrics_available": "Desculpe, não foi possível encontrar a letra desta faixa"
|
||||
"no_lyrics_available": "Desculpe, não foi possível encontrar a letra desta faixa",
|
||||
"sort_duration": "Ordenar por Duração",
|
||||
"start_a_radio": "Iniciar uma Rádio",
|
||||
"how_to_start_radio": "Como você deseja iniciar a rádio?",
|
||||
"replace_queue_question": "Você deseja substituir a fila atual ou acrescentar a ela?",
|
||||
"endless_playback": "Reprodução sem fim",
|
||||
"delete_playlist": "Excluir Lista de Reprodução",
|
||||
"delete_playlist_confirmation": "Tem certeza de que deseja excluir esta lista de reprodução?",
|
||||
"local_tracks": "Faixas Locais",
|
||||
"song_link": "Link da Música",
|
||||
"skip_this_nonsense": "Pular essa bobagem",
|
||||
"freedom_of_music": "“Liberdade da Música”",
|
||||
"freedom_of_music_palm": "“Liberdade da Música na palma da sua mão”",
|
||||
"get_started": "Vamos começar",
|
||||
"youtube_source_description": "Recomendado e funciona melhor.",
|
||||
"piped_source_description": "Sentindo-se livre? Igual ao YouTube, mas muito mais grátis.",
|
||||
"jiosaavn_source_description": "Melhor para a região da Ásia do Sul.",
|
||||
"highest_quality": "Melhor Qualidade: {quality}",
|
||||
"select_audio_source": "Selecionar Fonte de Áudio",
|
||||
"endless_playback_description": "Adicionar automaticamente novas músicas\nao final da fila",
|
||||
"choose_your_region": "Escolha sua região",
|
||||
"choose_your_region_description": "Isso ajudará o Spotube a mostrar o conteúdo certo\npara sua localização.",
|
||||
"choose_your_language": "Escolha seu idioma",
|
||||
"help_project_grow": "Ajude este projeto a crescer",
|
||||
"help_project_grow_description": "Spotube é um projeto de código aberto. Você pode ajudar este projeto a crescer contribuindo para o projeto, relatando bugs ou sugerindo novos recursos.",
|
||||
"contribute_on_github": "Contribuir no GitHub",
|
||||
"donate_on_open_collective": "Doar no Open Collective",
|
||||
"browse_anonymously": "Navegar Anonimamente"
|
||||
}
|
||||
@ -286,5 +286,32 @@
|
||||
"step_3_steps": "Скопируйте значение файла cookie \"sp_dc\"",
|
||||
"step_4_steps": "Вставьте скопированное значение \"sp_dc\"",
|
||||
"friends": "Друзья",
|
||||
"no_lyrics_available": "Извините, не удается найти текст для этого трека"
|
||||
"no_lyrics_available": "Извините, не удается найти текст для этого трека",
|
||||
"sort_duration": "Сортировка по Длительности",
|
||||
"start_a_radio": "Запустить радио",
|
||||
"how_to_start_radio": "Как вы хотите запустить радио?",
|
||||
"replace_queue_question": "Хотите заменить текущую очередь или добавить к ней?",
|
||||
"endless_playback": "Бесконечное воспроизведение",
|
||||
"delete_playlist": "Удалить плейлист",
|
||||
"delete_playlist_confirmation": "Вы уверены, что хотите удалить этот плейлист?",
|
||||
"local_tracks": "Локальные треки",
|
||||
"song_link": "Ссылка на песню",
|
||||
"skip_this_nonsense": "Пропустить этот бред",
|
||||
"freedom_of_music": "“Свобода музыки”",
|
||||
"freedom_of_music_palm": "“Свобода музыки в вашей ладони”",
|
||||
"get_started": "Начнем",
|
||||
"youtube_source_description": "Рекомендуется и лучше всего работает.",
|
||||
"piped_source_description": "Чувствуете себя свободно? То же самое, что и YouTube, но намного бесплатно.",
|
||||
"jiosaavn_source_description": "Лучший для Южно-Азиатского региона.",
|
||||
"highest_quality": "Наивысшее качество: {quality}",
|
||||
"select_audio_source": "Выберите аудиоисточник",
|
||||
"endless_playback_description": "Автоматически добавляйте новые песни\nв конец очереди",
|
||||
"choose_your_region": "Выберите ваш регион",
|
||||
"choose_your_region_description": "Это поможет Spotube показать вам правильный контент\nдля вашего местоположения.",
|
||||
"choose_your_language": "Выберите ваш язык",
|
||||
"help_project_grow": "Помогите этому проекту расти",
|
||||
"help_project_grow_description": "Spotube - это проект с открытым исходным кодом. Вы можете помочь этому проекту развиваться, внося вклад в проект, сообщая ошибках или предлагая новые функции.",
|
||||
"contribute_on_github": "Внести вклад на GitHub",
|
||||
"donate_on_open_collective": "Пожертвовать на Open Collective",
|
||||
"browse_anonymously": "Анонимно просматривать"
|
||||
}
|
||||
@ -254,7 +254,7 @@
|
||||
"audio_source": "Ses Kaynağı",
|
||||
"ok": "Tamam",
|
||||
"failed_to_encrypt": "Şifrelenemedi",
|
||||
"encryption_failed_warning": "Spotube, verilerinizi güvenli bir şekilde saklamak için şifreleme kullanır. Ama başaramadı. Bu yüzden güvensiz depolamaya geri dönecek\nLinux kullanıyorsanız, lütfen herhangi bir gizli servisin (gnome - anahtarlık, kde - cüzdan, keepassxc vb.) yüklü olduğundan emin olun",
|
||||
"encryption_failed_warning": "Spotube, verilerinizi güvenli bir şekilde saklamak için şifreleme kullanır. Ama başarısız oldu. Bu yüzden güvensiz depolamaya geri dönecek\nLinux kullanıyorsanız, lütfen herhangi bir gizli servisin (gnome - anahtarlık, kde - cüzdan, keepassxc vb.) yüklü olduğundan emin olun",
|
||||
"querying_info": "Bilgi sorgulanıyor...",
|
||||
"piped_api_down": "Borulu API kapalı",
|
||||
"piped_down_error_instructions": "Borulu örnek {pipedInstance} şu anda kapalı\n\nÖrneği değiştirin veya 'API türünü' resmi YouTube API'si olarak değiştirin\n\nDeğişiklikten sonra uygulamayı yeniden başlattığınızdan emin olun",
|
||||
@ -286,5 +286,5 @@
|
||||
"genres": "Türler",
|
||||
"explore_genres": "Türleri Keşfet",
|
||||
"friends": "Arkadaşlar",
|
||||
"no_lyrics_available": "Üzgünüz, bu parçanın sözleri bulunamıyor"
|
||||
"no_lyrics_available": "Üzgünüz, bu parça için şarkı sözleri bulunamıyor",
|
||||
}
|
||||
@ -286,5 +286,32 @@
|
||||
"step_3_steps": "Скопіюйте значення cookie \"sp_dc\"",
|
||||
"step_4_steps": "Вставте скопійоване значення \"sp_dc\"",
|
||||
"friends": "Друзі",
|
||||
"no_lyrics_available": "Вибачте, не вдалося знайти текст для цього треку"
|
||||
"no_lyrics_available": "Вибачте, не вдалося знайти текст для цього треку",
|
||||
"sort_duration": "Сортувати за тривалістю",
|
||||
"start_a_radio": "Запустити радіо",
|
||||
"how_to_start_radio": "Як ви хочете запустити радіо?",
|
||||
"replace_queue_question": "Ви хочете замінити поточну чергу чи додати до неї?",
|
||||
"endless_playback": "Безкінечне відтворення",
|
||||
"delete_playlist": "Видалити плейлист",
|
||||
"delete_playlist_confirmation": "Ви впевнені, що хочете видалити цей плейлист?",
|
||||
"local_tracks": "Місцеві треки",
|
||||
"song_link": "Посилання на пісню",
|
||||
"skip_this_nonsense": "Пропустити цей бред",
|
||||
"freedom_of_music": "“Свобода музики”",
|
||||
"freedom_of_music_palm": "“Свобода музики у вашій долоні”",
|
||||
"get_started": "Давайте почнемо",
|
||||
"youtube_source_description": "Рекомендовано та працює краще за все.",
|
||||
"piped_source_description": "Чи почуваєте себе вільно? Те саме, що і на YouTube, але набагато безкоштовно.",
|
||||
"jiosaavn_source_description": "Найкраще для регіону Південної Азії.",
|
||||
"highest_quality": "Найвища якість: {quality}",
|
||||
"select_audio_source": "Виберіть джерело аудіо",
|
||||
"endless_playback_description": "Автоматично додавати нові пісні\nв кінець черги",
|
||||
"choose_your_region": "Виберіть ваш регіон",
|
||||
"choose_your_region_description": "Це допоможе Spotube показати вам правильний контент\nдля вашого місцезнаходження.",
|
||||
"choose_your_language": "Виберіть свою мову",
|
||||
"help_project_grow": "Допоможіть цьому проекту рости",
|
||||
"help_project_grow_description": "Spotube - це проект з відкритим кодом. Ви можете допомогти цьому проекту зростати, вносячи свій внесок у проект, повідомляючи про помилки або пропонуючи нові функції.",
|
||||
"contribute_on_github": "Долучайтесь на GitHub",
|
||||
"donate_on_open_collective": "Пожертвуйте на Open Collective",
|
||||
"browse_anonymously": "Анонімно переглядати"
|
||||
}
|
||||
@ -284,5 +284,32 @@
|
||||
"discord_rich_presence": "Hiển thị trạng thái Discord",
|
||||
"browse_all": "Duyệt tất cả",
|
||||
"genres": "Thể loại",
|
||||
"explore_genres": "Khám phá Thể loại"
|
||||
}
|
||||
"explore_genres": "Khám phá Thể loại",
|
||||
"sort_duration": "Sắp xếp theo Thời lượng",
|
||||
"start_a_radio": "Bắt đầu Một Đài phát thanh",
|
||||
"how_to_start_radio": "Bạn muốn bắt đầu đài phát thanh như thế nào?",
|
||||
"replace_queue_question": "Bạn muốn thay thế hàng đợi hiện tại hay thêm vào?",
|
||||
"endless_playback": "Phát không giới hạn",
|
||||
"delete_playlist": "Xóa Danh sách phát",
|
||||
"delete_playlist_confirmation": "Bạn có chắc chắn muốn xóa danh sách phát này không?",
|
||||
"local_tracks": "Bài hát Địa phương",
|
||||
"song_link": "Liên kết Bài hát",
|
||||
"skip_this_nonsense": "Bỏ qua bớt rối này",
|
||||
"freedom_of_music": "“Sự Tự do của Âm nhạc”",
|
||||
"freedom_of_music_palm": "“Sự Tự do của Âm nhạc trong lòng bàn tay của bạn”",
|
||||
"get_started": "Bắt đầu thôi",
|
||||
"youtube_source_description": "Được đề xuất và hoạt động tốt nhất.",
|
||||
"piped_source_description": "Cảm thấy tự do? Giống như YouTube nhưng miễn phí hơn rất nhiều.",
|
||||
"jiosaavn_source_description": "Tốt nhất cho khu vực Nam Á.",
|
||||
"highest_quality": "Chất lượng Tốt nhất: {quality}",
|
||||
"select_audio_source": "Chọn Nguồn Âm thanh",
|
||||
"endless_playback_description": "Tự động thêm các bài hát mới\nvào cuối hàng đợi",
|
||||
"choose_your_region": "Chọn khu vực của bạn",
|
||||
"choose_your_region_description": "Điều này sẽ giúp Spotube hiển thị nội dung phù hợp cho vị trí của bạn.",
|
||||
"choose_your_language": "Chọn ngôn ngữ của bạn",
|
||||
"help_project_grow": "Hãy giúp dự án này phát triển",
|
||||
"help_project_grow_description": "Spotube là một dự án mã nguồn mở. Bạn có thể giúp dự án này phát triển bằng cách đóng góp vào dự án, báo cáo lỗi hoặc đề xuất tính năng mới.",
|
||||
"contribute_on_github": "Đóng góp trên GitHub",
|
||||
"donate_on_open_collective": "Quyên góp trên Open Collective",
|
||||
"browse_anonymously": "Duyệt Anonymously"
|
||||
}
|
||||
@ -286,5 +286,32 @@
|
||||
"step_3_steps": "复制\"sp_dc\" Cookie的值",
|
||||
"step_4_steps": "粘贴复制的\"sp_dc\"值",
|
||||
"friends": "朋友",
|
||||
"no_lyrics_available": "抱歉,无法找到此曲的歌词"
|
||||
"no_lyrics_available": "抱歉,无法找到此曲的歌词",
|
||||
"sort_duration": "按时长排序",
|
||||
"start_a_radio": "开始收听电台",
|
||||
"how_to_start_radio": "您想如何开始收听电台?",
|
||||
"replace_queue_question": "您想要替换当前队列还是追加到队列?",
|
||||
"endless_playback": "无尽播放",
|
||||
"delete_playlist": "删除播放列表",
|
||||
"delete_playlist_confirmation": "您确定要删除此播放列表吗?",
|
||||
"local_tracks": "本地音轨",
|
||||
"song_link": "歌曲链接",
|
||||
"skip_this_nonsense": "跳过此无聊内容",
|
||||
"freedom_of_music": "“音乐的自由”",
|
||||
"freedom_of_music_palm": "“音乐的自由掌握在您手中”",
|
||||
"get_started": "让我们开始吧",
|
||||
"youtube_source_description": "推荐并且效果最佳。",
|
||||
"piped_source_description": "感觉自由?与YouTube一样但更自由。",
|
||||
"jiosaavn_source_description": "最适合南亚地区。",
|
||||
"highest_quality": "最高音质:{quality}",
|
||||
"select_audio_source": "选择音频源",
|
||||
"endless_playback_description": "自动将新歌曲添加到队列的末尾",
|
||||
"choose_your_region": "选择您的地区",
|
||||
"choose_your_region_description": "这将帮助Spotube为您的位置显示正确的内容。",
|
||||
"choose_your_language": "选择您的语言",
|
||||
"help_project_grow": "帮助这个项目成长",
|
||||
"help_project_grow_description": "Spotube是一个开源项目。您可以通过为项目做出贡献、报告错误或建议新功能来帮助该项目成长。",
|
||||
"contribute_on_github": "在GitHub上做出贡献",
|
||||
"donate_on_open_collective": "在Open Collective上捐款",
|
||||
"browse_anonymously": "匿名浏览"
|
||||
}
|
||||
@ -8,8 +8,9 @@
|
||||
/// yuri-val@github => Ukrainian
|
||||
/// energywave@github, ncvescera@github, OpenCode@github => Italian
|
||||
/// mdksec@github => Turkish
|
||||
/// SecularSteve@github => Dutch
|
||||
/// Stephan-P@github, SecularSteve@github => Dutch
|
||||
/// doannc2212@github => Vietnamese
|
||||
/// sappho192@github => Korean
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class L10n {
|
||||
@ -26,6 +27,7 @@ class L10n {
|
||||
const Locale('hi', 'IN'),
|
||||
const Locale('it', 'IT'),
|
||||
const Locale('ja', 'JP'),
|
||||
const Locale('ko', 'KR'),
|
||||
const Locale('nl', 'NL'),
|
||||
const Locale('pl', 'PL'),
|
||||
const Locale('pt', 'PT'),
|
||||
|
||||
@ -29,6 +29,7 @@ import 'package:spotube/provider/user_preferences/user_preferences_provider.dart
|
||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||
import 'package:spotube/services/cli/cli.dart';
|
||||
import 'package:spotube/services/connectivity_adapter.dart';
|
||||
import 'package:spotube/services/kv_store/kv_store.dart';
|
||||
import 'package:spotube/themes/theme.dart';
|
||||
import 'package:spotube/utils/persisted_state_notifier.dart';
|
||||
import 'package:system_theme/system_theme.dart';
|
||||
@ -68,6 +69,8 @@ Future<void> main(List<String> rawArgs) async {
|
||||
DiscordRPC.initialize();
|
||||
}
|
||||
|
||||
await KVStoreService.initialize();
|
||||
|
||||
final hiveCacheDir =
|
||||
kIsWeb ? null : (await getApplicationSupportDirectory()).path;
|
||||
|
||||
@ -184,6 +187,7 @@ class SpotubeState extends ConsumerState<Spotube> {
|
||||
final locale = ref.watch(userPreferencesProvider.select((s) => s.locale));
|
||||
final paletteColor =
|
||||
ref.watch(paletteProvider.select((s) => s?.dominantColor?.color));
|
||||
final router = ref.watch(routerProvider);
|
||||
|
||||
useDisableBatteryOptimizations();
|
||||
useInitSysTray(ref);
|
||||
|
||||
@ -15,9 +15,9 @@ import 'package:spotube/utils/type_conversion_utils.dart';
|
||||
class AlbumPage extends HookConsumerWidget {
|
||||
final AlbumSimple album;
|
||||
const AlbumPage({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.album,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
@ -69,8 +69,9 @@ class AlbumPage extends HookConsumerWidget {
|
||||
shareUrl: album.externalUrls!.spotify!,
|
||||
isLiked: isLiked,
|
||||
onHeart: albumIsSaved.hasData
|
||||
? () {
|
||||
toggleAlbumLike.mutate(isLiked);
|
||||
? () async {
|
||||
await toggleAlbumLike.mutate(isLiked);
|
||||
return null;
|
||||
}
|
||||
: null,
|
||||
child: const TrackView(),
|
||||
|
||||
104
lib/pages/getting_started/getting_started.dart
Normal file
@ -0,0 +1,104 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:spotube/collections/assets.gen.dart';
|
||||
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
||||
import 'package:spotube/extensions/context.dart';
|
||||
import 'package:spotube/pages/getting_started/sections/greeting.dart';
|
||||
import 'package:spotube/pages/getting_started/sections/playback.dart';
|
||||
import 'package:spotube/pages/getting_started/sections/region.dart';
|
||||
import 'package:spotube/pages/getting_started/sections/support.dart';
|
||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||
import 'package:spotube/themes/theme.dart';
|
||||
|
||||
class GettingStarting extends HookConsumerWidget {
|
||||
const GettingStarting({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final preferences = ref.watch(userPreferencesProvider);
|
||||
final themeData = theme(
|
||||
preferences.accentColorScheme,
|
||||
Brightness.dark,
|
||||
preferences.amoledDarkTheme,
|
||||
);
|
||||
final pageController = usePageController();
|
||||
|
||||
final onNext = useCallback(() {
|
||||
pageController.nextPage(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
}, [pageController]);
|
||||
|
||||
final onPrevious = useCallback(() {
|
||||
pageController.previousPage(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
}, [pageController]);
|
||||
|
||||
return Theme(
|
||||
data: themeData,
|
||||
child: Scaffold(
|
||||
appBar: PageWindowTitleBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
actions: [
|
||||
ListenableBuilder(
|
||||
listenable: pageController,
|
||||
builder: (context, _) {
|
||||
return AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: pageController.hasClients &&
|
||||
(pageController.page == 0 || pageController.page == 3)
|
||||
? const SizedBox()
|
||||
: TextButton(
|
||||
onPressed: () {
|
||||
pageController.animateToPage(
|
||||
3,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
context.l10n.skip_this_nonsense,
|
||||
style: TextStyle(
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: themeData.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
extendBodyBehindAppBar: true,
|
||||
body: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: Assets.bengaliPatternsBg.provider(),
|
||||
fit: BoxFit.cover,
|
||||
colorFilter: const ColorFilter.mode(
|
||||
Colors.black38,
|
||||
BlendMode.srcOver,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: PageView(
|
||||
controller: pageController,
|
||||
children: [
|
||||
GettingStartedPageGreetingSection(onNext: onNext),
|
||||
GettingStartedPageLanguageRegionSection(onNext: onNext),
|
||||
GettingStartedPagePlaybackSection(
|
||||
onNext: onNext,
|
||||
onPrevious: onPrevious,
|
||||
),
|
||||
const GettingStartedScreenSupportSection(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
55
lib/pages/getting_started/sections/greeting.dart
Normal file
@ -0,0 +1,55 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:spotube/collections/assets.gen.dart';
|
||||
import 'package:spotube/collections/spotube_icons.dart';
|
||||
import 'package:spotube/components/getting_started/blur_card.dart';
|
||||
import 'package:spotube/extensions/context.dart';
|
||||
import 'package:spotube/utils/platform.dart';
|
||||
|
||||
class GettingStartedPageGreetingSection extends HookConsumerWidget {
|
||||
final VoidCallback onNext;
|
||||
const GettingStartedPageGreetingSection({super.key, required this.onNext});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final ThemeData(:textTheme) = Theme.of(context);
|
||||
|
||||
return Center(
|
||||
child: BlurCard(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Assets.spotubeLogoPng.image(height: 200),
|
||||
const Gap(24),
|
||||
Text(
|
||||
"Spotube",
|
||||
style:
|
||||
textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const Gap(4),
|
||||
Text(
|
||||
kIsMobile
|
||||
? context.l10n.freedom_of_music_palm
|
||||
: context.l10n.freedom_of_music,
|
||||
textAlign: TextAlign.center,
|
||||
style: textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w300,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
const Gap(84),
|
||||
Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: FilledButton.icon(
|
||||
onPressed: onNext,
|
||||
icon: const Icon(SpotubeIcons.angleRight),
|
||||
label: Text(context.l10n.get_started),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
162
lib/pages/getting_started/sections/playback.dart
Normal file
@ -0,0 +1,162 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:spotube/collections/assets.gen.dart';
|
||||
import 'package:spotube/collections/spotube_icons.dart';
|
||||
import 'package:spotube/components/getting_started/blur_card.dart';
|
||||
import 'package:spotube/extensions/context.dart';
|
||||
import 'package:spotube/extensions/string.dart';
|
||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||
import 'package:spotube/provider/user_preferences/user_preferences_state.dart';
|
||||
|
||||
final audioSourceToIconMap = {
|
||||
AudioSource.youtube: const Icon(
|
||||
SpotubeIcons.youtube,
|
||||
color: Colors.red,
|
||||
size: 30,
|
||||
),
|
||||
AudioSource.piped: const Icon(SpotubeIcons.piped, size: 30),
|
||||
AudioSource.jiosaavn: Assets.jiosaavn.image(width: 48, height: 48),
|
||||
};
|
||||
|
||||
class GettingStartedPagePlaybackSection extends HookConsumerWidget {
|
||||
final VoidCallback onNext;
|
||||
final VoidCallback onPrevious;
|
||||
|
||||
const GettingStartedPagePlaybackSection({
|
||||
super.key,
|
||||
required this.onNext,
|
||||
required this.onPrevious,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final ThemeData(:textTheme, :colorScheme, :dividerColor) =
|
||||
Theme.of(context);
|
||||
final preferences = ref.watch(userPreferencesProvider);
|
||||
final preferencesNotifier = ref.read(userPreferencesProvider.notifier);
|
||||
|
||||
final audioSourceToDescription = useMemoized(
|
||||
() => {
|
||||
AudioSource.youtube: "${context.l10n.youtube_source_description}\n"
|
||||
"${context.l10n.highest_quality("148kbps mp4, 128kbps opus")}",
|
||||
AudioSource.piped: context.l10n.piped_source_description,
|
||||
AudioSource.jiosaavn:
|
||||
"${context.l10n.jiosaavn_source_description}\n"
|
||||
"${context.l10n.highest_quality("320kbps mp")}",
|
||||
},
|
||||
[]);
|
||||
|
||||
return Center(
|
||||
child: BlurCard(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const Icon(SpotubeIcons.album, size: 16),
|
||||
const Gap(8),
|
||||
Text(context.l10n.playback, style: textTheme.titleMedium),
|
||||
],
|
||||
),
|
||||
const Gap(16),
|
||||
ListTile(
|
||||
title: Text(
|
||||
context.l10n.select_audio_source,
|
||||
style: textTheme.titleMedium,
|
||||
),
|
||||
),
|
||||
const Gap(16),
|
||||
ToggleButtons(
|
||||
isSelected: [
|
||||
for (final source in AudioSource.values)
|
||||
preferences.audioSource == source,
|
||||
],
|
||||
onPressed: (index) {
|
||||
preferencesNotifier.setAudioSource(AudioSource.values[index]);
|
||||
},
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
children: [
|
||||
for (final source in AudioSource.values)
|
||||
SizedBox.square(
|
||||
dimension: 84,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
audioSourceToIconMap[source]!,
|
||||
const Gap(8),
|
||||
Text(
|
||||
source.name.capitalize(),
|
||||
style: textTheme.bodySmall!.copyWith(
|
||||
color: preferences.audioSource == source
|
||||
? colorScheme.primary
|
||||
: null,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
ListTile(
|
||||
title: Align(
|
||||
alignment: switch (preferences.audioSource) {
|
||||
AudioSource.youtube => Alignment.centerLeft,
|
||||
AudioSource.piped => Alignment.center,
|
||||
AudioSource.jiosaavn => Alignment.centerRight,
|
||||
},
|
||||
child: Text(
|
||||
audioSourceToDescription[preferences.audioSource]!,
|
||||
style: textTheme.bodySmall?.copyWith(
|
||||
color: dividerColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Gap(16),
|
||||
ListTile(
|
||||
title: Text(context.l10n.endless_playback),
|
||||
subtitle: Text(
|
||||
context.l10n.endless_playback_description,
|
||||
style: textTheme.bodySmall?.copyWith(
|
||||
color: dividerColor,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
preferencesNotifier
|
||||
.setEndlessPlayback(!preferences.endlessPlayback);
|
||||
},
|
||||
trailing: Switch(
|
||||
value: preferences.endlessPlayback,
|
||||
onChanged: (value) {
|
||||
preferencesNotifier.setEndlessPlayback(value);
|
||||
},
|
||||
),
|
||||
),
|
||||
const Gap(34),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
FilledButton.icon(
|
||||
icon: const Icon(SpotubeIcons.angleLeft),
|
||||
label: Text(context.l10n.previous),
|
||||
onPressed: onPrevious,
|
||||
),
|
||||
Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: FilledButton.icon(
|
||||
icon: const Icon(SpotubeIcons.angleRight),
|
||||
label: Text(context.l10n.next),
|
||||
onPressed: onNext,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
129
lib/pages/getting_started/sections/region.dart
Normal file
@ -0,0 +1,129 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:spotube/collections/language_codes.dart';
|
||||
import 'package:spotube/collections/spotify_markets.dart';
|
||||
import 'package:spotube/collections/spotube_icons.dart';
|
||||
import 'package:spotube/components/getting_started/blur_card.dart';
|
||||
import 'package:spotube/extensions/context.dart';
|
||||
import 'package:spotube/l10n/l10n.dart';
|
||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||
|
||||
class GettingStartedPageLanguageRegionSection extends HookConsumerWidget {
|
||||
final void Function() onNext;
|
||||
const GettingStartedPageLanguageRegionSection(
|
||||
{super.key, required this.onNext});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final ThemeData(:textTheme, :dividerColor) = Theme.of(context);
|
||||
final preferences = ref.watch(userPreferencesProvider);
|
||||
|
||||
return SafeArea(
|
||||
child: Center(
|
||||
child: BlurCard(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const Icon(
|
||||
SpotubeIcons.language,
|
||||
size: 16,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
context.l10n.language_region,
|
||||
style: textTheme.titleMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
const Gap(48),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
context.l10n.choose_your_region,
|
||||
style: textTheme.titleSmall,
|
||||
),
|
||||
Text(
|
||||
context.l10n.choose_your_region_description,
|
||||
style: textTheme.bodySmall?.copyWith(
|
||||
color: dividerColor,
|
||||
),
|
||||
),
|
||||
const Gap(16),
|
||||
DropdownMenu(
|
||||
initialSelection: preferences.recommendationMarket,
|
||||
onSelected: (value) {
|
||||
if (value == null) return;
|
||||
ref
|
||||
.read(userPreferencesProvider.notifier)
|
||||
.setRecommendationMarket(value);
|
||||
},
|
||||
hintText: preferences.recommendationMarket.name,
|
||||
label: Text(context.l10n.market_place_region),
|
||||
inputDecorationTheme:
|
||||
const InputDecorationTheme(isDense: true),
|
||||
dropdownMenuEntries: [
|
||||
for (final market in spotifyMarkets)
|
||||
DropdownMenuEntry(
|
||||
value: market.$1,
|
||||
label: market.$2,
|
||||
),
|
||||
],
|
||||
),
|
||||
const Gap(36),
|
||||
Text(
|
||||
context.l10n.choose_your_language,
|
||||
style: textTheme.titleSmall,
|
||||
),
|
||||
const Gap(16),
|
||||
DropdownMenu(
|
||||
initialSelection: preferences.locale,
|
||||
onSelected: (locale) {
|
||||
if (locale == null) return;
|
||||
ref
|
||||
.read(userPreferencesProvider.notifier)
|
||||
.setLocale(locale);
|
||||
},
|
||||
hintText: context.l10n.system_default,
|
||||
label: Text(context.l10n.language),
|
||||
inputDecorationTheme:
|
||||
const InputDecorationTheme(isDense: true),
|
||||
dropdownMenuEntries: [
|
||||
DropdownMenuEntry(
|
||||
value: const Locale("system", "system"),
|
||||
label: context.l10n.system_default,
|
||||
),
|
||||
for (final locale in L10n.all)
|
||||
DropdownMenuEntry(
|
||||
value: locale,
|
||||
label: LanguageLocals.getDisplayLanguage(
|
||||
locale.languageCode)
|
||||
.toString(),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const Gap(48),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: FilledButton.icon(
|
||||
icon: const Icon(SpotubeIcons.angleRight),
|
||||
label: Text(context.l10n.next),
|
||||
onPressed: onNext,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
134
lib/pages/getting_started/sections/support.dart
Normal file
@ -0,0 +1,134 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:spotube/collections/spotube_icons.dart';
|
||||
import 'package:spotube/components/getting_started/blur_card.dart';
|
||||
import 'package:spotube/extensions/context.dart';
|
||||
import 'package:spotube/services/kv_store/kv_store.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
class GettingStartedScreenSupportSection extends HookConsumerWidget {
|
||||
const GettingStartedScreenSupportSection({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final ThemeData(:textTheme, :colorScheme) = Theme.of(context);
|
||||
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
BlurCard(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const Icon(SpotubeIcons.heartFilled, color: Colors.pink),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
context.l10n.help_project_grow,
|
||||
style:
|
||||
textTheme.titleMedium?.copyWith(color: Colors.pink),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Gap(16),
|
||||
Text(context.l10n.help_project_grow_description),
|
||||
const Gap(16),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
FilledButton.icon(
|
||||
icon: const Icon(SpotubeIcons.github),
|
||||
label: Text(context.l10n.contribute_on_github),
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: Colors.black,
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
onPressed: () async {
|
||||
await launchUrlString(
|
||||
"https://github.com/KRTirtho/spotube",
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
),
|
||||
const Gap(16),
|
||||
FilledButton.icon(
|
||||
icon: const Icon(SpotubeIcons.openCollective),
|
||||
label: Text(context.l10n.donate_on_open_collective),
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: const Color(0xff4cb7f6),
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
onPressed: () async {
|
||||
await launchUrlString(
|
||||
"https://opencollective.com/spotube",
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Gap(48),
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 250),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
colorScheme.primary,
|
||||
colorScheme.secondary,
|
||||
],
|
||||
),
|
||||
),
|
||||
child: TextButton.icon(
|
||||
icon: const Icon(SpotubeIcons.anonymous),
|
||||
label: Text(context.l10n.browse_anonymously),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
onPressed: () async {
|
||||
await KVStoreService.setDoneGettingStarted(true);
|
||||
if (context.mounted) {
|
||||
context.go("/");
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
const Gap(16),
|
||||
FilledButton.icon(
|
||||
icon: const Icon(SpotubeIcons.spotify),
|
||||
label: Text(context.l10n.connect_with_spotify),
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: const Color(0xff1db954),
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
onPressed: () async {
|
||||
await KVStoreService.setDoneGettingStarted(true);
|
||||
if (context.mounted) {
|
||||
context.push("/login");
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -18,8 +18,7 @@ import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
|
||||
|
||||
class GenrePlaylistsPage extends HookConsumerWidget {
|
||||
final Category category;
|
||||
const GenrePlaylistsPage({Key? key, required this.category})
|
||||
: super(key: key);
|
||||
const GenrePlaylistsPage({super.key, required this.category});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
@ -51,123 +50,118 @@ class GenrePlaylistsPage extends HookConsumerWidget {
|
||||
)
|
||||
: null,
|
||||
extendBodyBehindAppBar: true,
|
||||
body: CustomScrollView(
|
||||
controller: scrollController,
|
||||
slivers: [
|
||||
SliverAppBar(
|
||||
automaticallyImplyLeading: DesktopTools.platform.isMobile,
|
||||
expandedHeight: mediaQuery.mdAndDown ? 200 : 150,
|
||||
pinned: true,
|
||||
floating: false,
|
||||
title: const Text(""),
|
||||
backgroundColor: Colors.brown.withOpacity(0.7),
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
stretchModes: const [
|
||||
StretchMode.zoomBackground,
|
||||
StretchMode.blurBackground,
|
||||
],
|
||||
background: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: UniversalImage.imageProvider(
|
||||
category.icons!.first.url!,
|
||||
),
|
||||
fit: BoxFit.cover,
|
||||
body: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: UniversalImage.imageProvider(category.icons!.first.url!),
|
||||
alignment: Alignment.topCenter,
|
||||
fit: BoxFit.cover,
|
||||
colorFilter: ColorFilter.mode(
|
||||
Colors.black.withOpacity(0.5),
|
||||
BlendMode.darken,
|
||||
),
|
||||
repeat: ImageRepeat.noRepeat,
|
||||
matchTextDirection: true,
|
||||
),
|
||||
),
|
||||
child: CustomScrollView(
|
||||
controller: scrollController,
|
||||
slivers: [
|
||||
SliverAppBar(
|
||||
automaticallyImplyLeading: DesktopTools.platform.isMobile,
|
||||
expandedHeight: mediaQuery.mdAndDown ? 200 : 150,
|
||||
title: const Text(""),
|
||||
backgroundColor: Colors.transparent,
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
centerTitle: DesktopTools.platform.isDesktop,
|
||||
title: Text(
|
||||
category.name!,
|
||||
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
|
||||
color: Colors.white,
|
||||
letterSpacing: 3,
|
||||
shadows: [
|
||||
const Shadow(
|
||||
offset: Offset(-1.5, -1.5),
|
||||
color: Colors.black54,
|
||||
),
|
||||
const Shadow(
|
||||
offset: Offset(1.5, -1.5),
|
||||
color: Colors.black54,
|
||||
),
|
||||
const Shadow(
|
||||
offset: Offset(1.5, 1.5),
|
||||
color: Colors.black54,
|
||||
),
|
||||
const Shadow(
|
||||
offset: Offset(-1.5, 1.5),
|
||||
color: Colors.black54,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
|
||||
child: const ColoredBox(color: Colors.transparent),
|
||||
),
|
||||
collapseMode: CollapseMode.parallax,
|
||||
),
|
||||
centerTitle: DesktopTools.platform.isDesktop,
|
||||
title: Text(
|
||||
category.name!,
|
||||
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
|
||||
color: Colors.white,
|
||||
letterSpacing: 3,
|
||||
shadows: [
|
||||
const Shadow(
|
||||
offset: Offset(-1.5, -1.5),
|
||||
color: Colors.black54,
|
||||
),
|
||||
const Shadow(
|
||||
offset: Offset(1.5, -1.5),
|
||||
color: Colors.black54,
|
||||
),
|
||||
const Shadow(
|
||||
offset: Offset(1.5, 1.5),
|
||||
color: Colors.black54,
|
||||
),
|
||||
const Shadow(
|
||||
offset: Offset(-1.5, 1.5),
|
||||
color: Colors.black54,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
collapseMode: CollapseMode.parallax,
|
||||
),
|
||||
),
|
||||
const SliverGap(20),
|
||||
SliverSafeArea(
|
||||
top: false,
|
||||
sliver: SliverPadding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: mediaQuery.mdAndDown ? 12 : 24,
|
||||
),
|
||||
sliver: playlists.isEmpty
|
||||
? Skeletonizer.sliver(
|
||||
child: SliverToBoxAdapter(
|
||||
child: Wrap(
|
||||
spacing: 12,
|
||||
runSpacing: 12,
|
||||
children: List.generate(
|
||||
6,
|
||||
(index) => PlaylistCard(FakeData.playlist),
|
||||
const SliverGap(20),
|
||||
SliverSafeArea(
|
||||
top: false,
|
||||
sliver: SliverPadding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: mediaQuery.mdAndDown ? 12 : 24,
|
||||
),
|
||||
sliver: playlists.isEmpty
|
||||
? Skeletonizer.sliver(
|
||||
child: SliverToBoxAdapter(
|
||||
child: Wrap(
|
||||
spacing: 12,
|
||||
runSpacing: 12,
|
||||
children: List.generate(
|
||||
6,
|
||||
(index) => PlaylistCard(FakeData.playlist),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: SliverGrid.builder(
|
||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 190,
|
||||
mainAxisExtent: mediaQuery.mdAndDown ? 225 : 250,
|
||||
crossAxisSpacing: 12,
|
||||
mainAxisSpacing: 12,
|
||||
),
|
||||
itemCount: playlists.length + 1,
|
||||
itemBuilder: (context, index) {
|
||||
final playlist = playlists.elementAtOrNull(index);
|
||||
)
|
||||
: SliverGrid.builder(
|
||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 190,
|
||||
mainAxisExtent: mediaQuery.mdAndDown ? 225 : 250,
|
||||
crossAxisSpacing: 12,
|
||||
mainAxisSpacing: 12,
|
||||
),
|
||||
itemCount: playlists.length + 1,
|
||||
itemBuilder: (context, index) {
|
||||
final playlist = playlists.elementAtOrNull(index);
|
||||
|
||||
if (playlist == null) {
|
||||
if (!playlistsQuery.hasNextPage) {
|
||||
return const SizedBox.shrink();
|
||||
if (playlist == null) {
|
||||
if (!playlistsQuery.hasNextPage) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return Skeletonizer(
|
||||
enabled: true,
|
||||
child: Waypoint(
|
||||
controller: scrollController,
|
||||
isGrid: true,
|
||||
onTouchEdge: () async {
|
||||
if (playlistsQuery.hasNextPage) {
|
||||
await playlistsQuery.fetchNext();
|
||||
}
|
||||
},
|
||||
child: PlaylistCard(FakeData.playlist),
|
||||
),
|
||||
);
|
||||
}
|
||||
return Skeletonizer(
|
||||
enabled: true,
|
||||
child: Waypoint(
|
||||
controller: scrollController,
|
||||
isGrid: true,
|
||||
onTouchEdge: () async {
|
||||
if (playlistsQuery.hasNextPage) {
|
||||
await playlistsQuery.fetchNext();
|
||||
}
|
||||
},
|
||||
child: PlaylistCard(FakeData.playlist),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Skeleton.keep(
|
||||
child: PlaylistCard(playlist),
|
||||
);
|
||||
},
|
||||
),
|
||||
return Skeleton.keep(
|
||||
child: PlaylistCard(playlist),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SliverGap(20),
|
||||
],
|
||||
const SliverGap(20),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ import 'package:spotube/provider/user_preferences/user_preferences_provider.dart
|
||||
import 'package:spotube/services/queries/queries.dart';
|
||||
|
||||
class GenrePage extends HookConsumerWidget {
|
||||
const GenrePage({Key? key}) : super(key: key);
|
||||
const GenrePage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
|
||||
@ -27,7 +27,7 @@ class LibraryPage extends HookConsumerWidget {
|
||||
leading: ThemedButtonsTabBar(
|
||||
tabs: [
|
||||
Tab(text: " ${context.l10n.playlists} "),
|
||||
Tab(text: " ${context.l10n.tracks} "),
|
||||
Tab(text: " ${context.l10n.local_tracks} "),
|
||||
Tab(
|
||||
child: Badge(
|
||||
isLabelVisible: downloadingCount > 0,
|
||||
|
||||
@ -2,8 +2,11 @@ import 'package:flutter/material.dart' hide Page;
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/components/shared/dialogs/prompt_dialog.dart';
|
||||
import 'package:spotube/components/shared/tracks_view/sections/body/use_is_user_playlist.dart';
|
||||
import 'package:spotube/components/shared/tracks_view/track_view.dart';
|
||||
import 'package:spotube/components/shared/tracks_view/track_view_props.dart';
|
||||
import 'package:spotube/extensions/context.dart';
|
||||
import 'package:spotube/extensions/infinite_query.dart';
|
||||
import 'package:spotube/provider/spotify_provider.dart';
|
||||
import 'package:spotube/services/mutations/mutations.dart';
|
||||
@ -45,6 +48,8 @@ class PlaylistPage extends HookConsumerWidget {
|
||||
],
|
||||
);
|
||||
|
||||
final isUserPlaylist = useIsUserPlaylist(ref, playlist.id!);
|
||||
|
||||
return InheritedTrackView(
|
||||
collectionId: playlist.id!,
|
||||
image: TypeConversionUtils.image_X_UrlString(
|
||||
@ -72,9 +77,20 @@ class PlaylistPage extends HookConsumerWidget {
|
||||
shareUrl: playlist.externalUrls?.spotify ?? "",
|
||||
onHeart: () async {
|
||||
if (!isLikedQuery.hasData || togglePlaylistLike.isMutating) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
await togglePlaylistLike.mutate(isLikedQuery.data!);
|
||||
final confirmed = isUserPlaylist
|
||||
? await showPromptDialog(
|
||||
context: context,
|
||||
title: context.l10n.delete_playlist,
|
||||
message: context.l10n.delete_playlist_confirmation,
|
||||
)
|
||||
: true;
|
||||
if (confirmed) {
|
||||
await togglePlaylistLike.mutate(isLikedQuery.data!);
|
||||
return isUserPlaylist;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
child: const TrackView(),
|
||||
);
|
||||
|
||||
@ -15,6 +15,7 @@ import 'package:spotube/components/root/bottom_player.dart';
|
||||
import 'package:spotube/components/root/sidebar.dart';
|
||||
import 'package:spotube/components/root/spotube_navigation_bar.dart';
|
||||
import 'package:spotube/extensions/context.dart';
|
||||
import 'package:spotube/hooks/configurators/use_endless_playback.dart';
|
||||
import 'package:spotube/hooks/configurators/use_update_checker.dart';
|
||||
import 'package:spotube/provider/download_manager_provider.dart';
|
||||
import 'package:spotube/utils/persisted_state_notifier.dart';
|
||||
@ -134,6 +135,8 @@ class RootApp extends HookConsumerWidget {
|
||||
// checks for latest version of the application
|
||||
useUpdateChecker(ref);
|
||||
|
||||
useEndlessPlayback(ref);
|
||||
|
||||
final backgroundColor = Theme.of(context).scaffoldBackgroundColor;
|
||||
|
||||
useEffect(() {
|
||||
@ -159,38 +162,47 @@ class RootApp extends HookConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
body: Sidebar(
|
||||
selectedIndex: rootPaths[location],
|
||||
onSelectedIndexChanged: onSelectIndexChanged,
|
||||
child: child,
|
||||
),
|
||||
extendBody: true,
|
||||
drawerScrimColor: Colors.transparent,
|
||||
endDrawer: DesktopTools.platform.isDesktop
|
||||
? Container(
|
||||
constraints: const BoxConstraints(maxWidth: 800),
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: theme.brightness == Brightness.light
|
||||
? null
|
||||
: kElevationToShadow[8],
|
||||
),
|
||||
margin: const EdgeInsets.only(
|
||||
top: 40,
|
||||
bottom: 100,
|
||||
),
|
||||
child: const PlayerQueue(floating: true),
|
||||
)
|
||||
: null,
|
||||
bottomNavigationBar: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
BottomPlayer(),
|
||||
SpotubeNavigationBar(
|
||||
selectedIndex: rootPaths[location],
|
||||
onSelectedIndexChanged: onSelectIndexChanged,
|
||||
),
|
||||
],
|
||||
return WillPopScope(
|
||||
onWillPop: () async {
|
||||
if (rootPaths[location] != 0) {
|
||||
onSelectIndexChanged(0);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
child: Scaffold(
|
||||
body: Sidebar(
|
||||
selectedIndex: rootPaths[location],
|
||||
onSelectedIndexChanged: onSelectIndexChanged,
|
||||
child: child,
|
||||
),
|
||||
extendBody: true,
|
||||
drawerScrimColor: Colors.transparent,
|
||||
endDrawer: DesktopTools.platform.isDesktop
|
||||
? Container(
|
||||
constraints: const BoxConstraints(maxWidth: 800),
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: theme.brightness == Brightness.light
|
||||
? null
|
||||
: kElevationToShadow[8],
|
||||
),
|
||||
margin: const EdgeInsets.only(
|
||||
top: 40,
|
||||
bottom: 100,
|
||||
),
|
||||
child: const PlayerQueue(floating: true),
|
||||
)
|
||||
: null,
|
||||
bottomNavigationBar: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
BottomPlayer(),
|
||||
SpotubeNavigationBar(
|
||||
selectedIndex: rootPaths[location],
|
||||
onSelectedIndexChanged: onSelectIndexChanged,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:spotube/collections/spotube_icons.dart';
|
||||
import 'package:spotube/components/settings/color_scheme_picker_dialog.dart';
|
||||
@ -10,7 +11,11 @@ import 'package:spotube/provider/user_preferences/user_preferences_provider.dart
|
||||
import 'package:spotube/provider/user_preferences/user_preferences_state.dart';
|
||||
|
||||
class SettingsAppearanceSection extends HookConsumerWidget {
|
||||
const SettingsAppearanceSection({Key? key}) : super(key: key);
|
||||
final bool isGettingStarted;
|
||||
const SettingsAppearanceSection({
|
||||
Key? key,
|
||||
this.isGettingStarted = false,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
@ -24,87 +29,101 @@ class SettingsAppearanceSection extends HookConsumerWidget {
|
||||
});
|
||||
}, []);
|
||||
|
||||
final children = [
|
||||
AdaptiveSelectTile<LayoutMode>(
|
||||
secondary: const Icon(SpotubeIcons.dashboard),
|
||||
title: Text(context.l10n.layout_mode),
|
||||
subtitle: Text(context.l10n.override_layout_settings),
|
||||
value: preferences.layoutMode,
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
preferencesNotifier.setLayoutMode(value);
|
||||
}
|
||||
},
|
||||
options: [
|
||||
DropdownMenuItem(
|
||||
value: LayoutMode.adaptive,
|
||||
child: Text(context.l10n.adaptive),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: LayoutMode.compact,
|
||||
child: Text(context.l10n.compact),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: LayoutMode.extended,
|
||||
child: Text(context.l10n.extended),
|
||||
),
|
||||
],
|
||||
),
|
||||
AdaptiveSelectTile<ThemeMode>(
|
||||
secondary: const Icon(SpotubeIcons.darkMode),
|
||||
title: Text(context.l10n.theme),
|
||||
value: preferences.themeMode,
|
||||
options: [
|
||||
DropdownMenuItem(
|
||||
value: ThemeMode.dark,
|
||||
child: Text(context.l10n.dark),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: ThemeMode.light,
|
||||
child: Text(context.l10n.light),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: ThemeMode.system,
|
||||
child: Text(context.l10n.system),
|
||||
),
|
||||
],
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
preferencesNotifier.setThemeMode(value);
|
||||
}
|
||||
},
|
||||
),
|
||||
SwitchListTile(
|
||||
secondary: const Icon(SpotubeIcons.amoled),
|
||||
title: Text(context.l10n.use_amoled_mode),
|
||||
subtitle: Text(context.l10n.pitch_dark_theme),
|
||||
value: preferences.amoledDarkTheme,
|
||||
onChanged: preferencesNotifier.setAmoledDarkTheme,
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(SpotubeIcons.palette),
|
||||
title: Text(context.l10n.accent_color),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 15,
|
||||
vertical: 5,
|
||||
),
|
||||
trailing: ColorTile.compact(
|
||||
color: preferences.accentColorScheme,
|
||||
onPressed: pickColorScheme(),
|
||||
isActive: true,
|
||||
),
|
||||
onTap: pickColorScheme(),
|
||||
),
|
||||
SwitchListTile(
|
||||
secondary: const Icon(SpotubeIcons.colorSync),
|
||||
title: Text(context.l10n.sync_album_color),
|
||||
subtitle: Text(context.l10n.sync_album_color_description),
|
||||
value: preferences.albumColorSync,
|
||||
onChanged: preferencesNotifier.setAlbumColorSync,
|
||||
),
|
||||
];
|
||||
|
||||
if (isGettingStarted) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
for (final child in children) ...[
|
||||
child,
|
||||
const Gap(16),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
return SectionCardWithHeading(
|
||||
heading: context.l10n.appearance,
|
||||
children: [
|
||||
AdaptiveSelectTile<LayoutMode>(
|
||||
secondary: const Icon(SpotubeIcons.dashboard),
|
||||
title: Text(context.l10n.layout_mode),
|
||||
subtitle: Text(context.l10n.override_layout_settings),
|
||||
value: preferences.layoutMode,
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
preferencesNotifier.setLayoutMode(value);
|
||||
}
|
||||
},
|
||||
options: [
|
||||
DropdownMenuItem(
|
||||
value: LayoutMode.adaptive,
|
||||
child: Text(context.l10n.adaptive),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: LayoutMode.compact,
|
||||
child: Text(context.l10n.compact),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: LayoutMode.extended,
|
||||
child: Text(context.l10n.extended),
|
||||
),
|
||||
],
|
||||
),
|
||||
AdaptiveSelectTile<ThemeMode>(
|
||||
secondary: const Icon(SpotubeIcons.darkMode),
|
||||
title: Text(context.l10n.theme),
|
||||
value: preferences.themeMode,
|
||||
options: [
|
||||
DropdownMenuItem(
|
||||
value: ThemeMode.dark,
|
||||
child: Text(context.l10n.dark),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: ThemeMode.light,
|
||||
child: Text(context.l10n.light),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: ThemeMode.system,
|
||||
child: Text(context.l10n.system),
|
||||
),
|
||||
],
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
preferencesNotifier.setThemeMode(value);
|
||||
}
|
||||
},
|
||||
),
|
||||
SwitchListTile(
|
||||
secondary: const Icon(SpotubeIcons.amoled),
|
||||
title: Text(context.l10n.use_amoled_mode),
|
||||
subtitle: Text(context.l10n.pitch_dark_theme),
|
||||
value: preferences.amoledDarkTheme,
|
||||
onChanged: preferencesNotifier.setAmoledDarkTheme,
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(SpotubeIcons.palette),
|
||||
title: Text(context.l10n.accent_color),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 15,
|
||||
vertical: 5,
|
||||
),
|
||||
trailing: ColorTile.compact(
|
||||
color: preferences.accentColorScheme,
|
||||
onPressed: pickColorScheme(),
|
||||
isActive: true,
|
||||
),
|
||||
onTap: pickColorScheme(),
|
||||
),
|
||||
SwitchListTile(
|
||||
secondary: const Icon(SpotubeIcons.colorSync),
|
||||
title: Text(context.l10n.sync_album_color),
|
||||
subtitle: Text(context.l10n.sync_album_color_description),
|
||||
value: preferences.albumColorSync,
|
||||
onChanged: preferencesNotifier.setAlbumColorSync,
|
||||
),
|
||||
],
|
||||
children: children,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ import 'package:spotube/l10n/l10n.dart';
|
||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||
|
||||
class SettingsLanguageRegionSection extends HookConsumerWidget {
|
||||
const SettingsLanguageRegionSection({Key? key}) : super(key: key);
|
||||
const SettingsLanguageRegionSection({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
|
||||
@ -221,6 +221,12 @@ class SettingsPlaybackSection extends HookConsumerWidget {
|
||||
preferencesNotifier.setDownloadMusicCodec(value);
|
||||
},
|
||||
),
|
||||
SwitchListTile(
|
||||
secondary: const Icon(SpotubeIcons.repeat),
|
||||
title: Text(context.l10n.endless_playback),
|
||||
value: preferences.endlessPlayback,
|
||||
onChanged: preferencesNotifier.setEndlessPlayback,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@ -144,8 +144,7 @@ class ProxyPlaylistNotifier extends PersistedStateNotifier<ProxyPlaylist>
|
||||
}
|
||||
} catch (e, stackTrace) {
|
||||
// Removing tracks that were not found to avoid queue interruption
|
||||
// TODO: Add a flag to enable/disable skip not found tracks
|
||||
if (e is TrackNotFoundException) {
|
||||
if (e is TrackNotFoundError) {
|
||||
final oldTrack =
|
||||
mapSourcesToTracks([audioPlayer.nextSource!]).firstOrNull;
|
||||
await removeTrack(oldTrack!.id!);
|
||||
|
||||
@ -123,6 +123,10 @@ class UserPreferencesNotifier extends PersistedStateNotifier<UserPreferences> {
|
||||
audioPlayer.setAudioNormalization(normalize);
|
||||
}
|
||||
|
||||
void setEndlessPlayback(bool endless) {
|
||||
state = state.copyWith(endlessPlayback: endless);
|
||||
}
|
||||
|
||||
Future<String> _getDefaultDownloadDirectory() async {
|
||||
if (kIsAndroid) return "/storage/emulated/0/Download/Spotube";
|
||||
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/components/settings/color_scheme_picker_dialog.dart';
|
||||
import 'package:spotube/services/sourced_track/enums.dart';
|
||||
|
||||
part 'user_preferences_state.g.dart';
|
||||
part 'user_preferences_state.freezed.dart';
|
||||
|
||||
@JsonEnum()
|
||||
enum LayoutMode {
|
||||
@ -53,40 +54,48 @@ enum SearchMode {
|
||||
}
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
final class UserPreferences {
|
||||
@JsonKey(
|
||||
defaultValue: SourceQualities.high,
|
||||
unknownEnumValue: SourceQualities.high,
|
||||
)
|
||||
final SourceQualities audioQuality;
|
||||
@freezed
|
||||
class UserPreferences with _$UserPreferences {
|
||||
const factory UserPreferences({
|
||||
@Default(SourceQualities.high) SourceQualities audioQuality,
|
||||
@Default(true) bool albumColorSync,
|
||||
@Default(false) bool amoledDarkTheme,
|
||||
@Default(true) bool checkUpdate,
|
||||
@Default(false) bool normalizeAudio,
|
||||
@Default(true) bool showSystemTrayIcon,
|
||||
@Default(false) bool skipNonMusic,
|
||||
@Default(false) bool systemTitleBar,
|
||||
@Default(CloseBehavior.minimizeToTray) CloseBehavior closeBehavior,
|
||||
@Default(SpotubeColor(0xFF2196F3, name: "Blue"))
|
||||
@JsonKey(
|
||||
fromJson: UserPreferences._accentColorSchemeFromJson,
|
||||
toJson: UserPreferences._accentColorSchemeToJson,
|
||||
readValue: UserPreferences._accentColorSchemeReadValue,
|
||||
)
|
||||
SpotubeColor accentColorScheme,
|
||||
@Default(LayoutMode.adaptive) LayoutMode layoutMode,
|
||||
@Default(Locale("system", "system"))
|
||||
@JsonKey(
|
||||
fromJson: UserPreferences._localeFromJson,
|
||||
toJson: UserPreferences._localeToJson,
|
||||
readValue: UserPreferences._localeReadValue,
|
||||
)
|
||||
Locale locale,
|
||||
@Default(Market.US) Market recommendationMarket,
|
||||
@Default(SearchMode.youtube) SearchMode searchMode,
|
||||
@Default("") String downloadLocation,
|
||||
@Default("https://pipedapi.kavin.rocks") String pipedInstance,
|
||||
@Default(ThemeMode.system) ThemeMode themeMode,
|
||||
@Default(AudioSource.youtube) AudioSource audioSource,
|
||||
@Default(SourceCodecs.weba) SourceCodecs streamMusicCodec,
|
||||
@Default(SourceCodecs.m4a) SourceCodecs downloadMusicCodec,
|
||||
@Default(true) bool discordPresence,
|
||||
@Default(true) bool endlessPlayback,
|
||||
}) = _UserPreferences;
|
||||
factory UserPreferences.fromJson(Map<String, dynamic> json) =>
|
||||
_$UserPreferencesFromJson(json);
|
||||
|
||||
@JsonKey(defaultValue: true)
|
||||
final bool albumColorSync;
|
||||
|
||||
@JsonKey(defaultValue: false)
|
||||
final bool amoledDarkTheme;
|
||||
|
||||
@JsonKey(defaultValue: true)
|
||||
final bool checkUpdate;
|
||||
|
||||
@JsonKey(defaultValue: false)
|
||||
final bool normalizeAudio;
|
||||
|
||||
@JsonKey(defaultValue: true)
|
||||
final bool showSystemTrayIcon;
|
||||
|
||||
@JsonKey(defaultValue: true)
|
||||
final bool skipNonMusic;
|
||||
|
||||
@JsonKey(defaultValue: false)
|
||||
final bool systemTitleBar;
|
||||
|
||||
@JsonKey(
|
||||
defaultValue: CloseBehavior.minimizeToTray,
|
||||
unknownEnumValue: CloseBehavior.minimizeToTray,
|
||||
)
|
||||
final CloseBehavior closeBehavior;
|
||||
factory UserPreferences.withDefaults() => UserPreferences.fromJson({});
|
||||
|
||||
static SpotubeColor _accentColorSchemeFromJson(Map<String, dynamic> json) {
|
||||
return SpotubeColor.fromString(json["color"]);
|
||||
@ -105,23 +114,6 @@ final class UserPreferences {
|
||||
return {"color": color.toString()};
|
||||
}
|
||||
|
||||
static SpotubeColor _defaultAccentColorScheme() =>
|
||||
const SpotubeColor(0xFF2196F3, name: "Blue");
|
||||
|
||||
@JsonKey(
|
||||
defaultValue: UserPreferences._defaultAccentColorScheme,
|
||||
fromJson: UserPreferences._accentColorSchemeFromJson,
|
||||
toJson: UserPreferences._accentColorSchemeToJson,
|
||||
readValue: UserPreferences._accentColorSchemeReadValue,
|
||||
)
|
||||
final SpotubeColor accentColorScheme;
|
||||
|
||||
@JsonKey(
|
||||
defaultValue: LayoutMode.adaptive,
|
||||
unknownEnumValue: LayoutMode.adaptive,
|
||||
)
|
||||
final LayoutMode layoutMode;
|
||||
|
||||
static Locale _localeFromJson(Map<String, dynamic> json) {
|
||||
return Locale(json["languageCode"], json["countryCode"]);
|
||||
}
|
||||
@ -145,144 +137,4 @@ final class UserPreferences {
|
||||
|
||||
return json[key] as Map<String, dynamic>?;
|
||||
}
|
||||
|
||||
static Locale _defaultLocaleValue() => const Locale("system", "system");
|
||||
|
||||
@JsonKey(
|
||||
defaultValue: UserPreferences._defaultLocaleValue,
|
||||
toJson: UserPreferences._localeToJson,
|
||||
fromJson: UserPreferences._localeFromJson,
|
||||
readValue: UserPreferences._localeReadValue,
|
||||
)
|
||||
final Locale locale;
|
||||
|
||||
@JsonKey(
|
||||
defaultValue: Market.US,
|
||||
unknownEnumValue: Market.US,
|
||||
)
|
||||
final Market recommendationMarket;
|
||||
|
||||
@JsonKey(
|
||||
defaultValue: SearchMode.youtube,
|
||||
unknownEnumValue: SearchMode.youtube,
|
||||
)
|
||||
final SearchMode searchMode;
|
||||
|
||||
@JsonKey(defaultValue: "")
|
||||
final String downloadLocation;
|
||||
|
||||
@JsonKey(defaultValue: "https://pipedapi.kavin.rocks")
|
||||
final String pipedInstance;
|
||||
|
||||
@JsonKey(
|
||||
defaultValue: ThemeMode.system,
|
||||
unknownEnumValue: ThemeMode.system,
|
||||
)
|
||||
final ThemeMode themeMode;
|
||||
|
||||
@JsonKey(
|
||||
defaultValue: AudioSource.youtube,
|
||||
unknownEnumValue: AudioSource.youtube,
|
||||
)
|
||||
final AudioSource audioSource;
|
||||
|
||||
@JsonKey(
|
||||
defaultValue: SourceCodecs.weba,
|
||||
unknownEnumValue: SourceCodecs.weba,
|
||||
)
|
||||
final SourceCodecs streamMusicCodec;
|
||||
|
||||
@JsonKey(
|
||||
defaultValue: SourceCodecs.m4a,
|
||||
unknownEnumValue: SourceCodecs.m4a,
|
||||
)
|
||||
final SourceCodecs downloadMusicCodec;
|
||||
|
||||
@JsonKey(defaultValue: true)
|
||||
final bool discordPresence;
|
||||
|
||||
UserPreferences({
|
||||
required this.audioQuality,
|
||||
required this.albumColorSync,
|
||||
required this.amoledDarkTheme,
|
||||
required this.checkUpdate,
|
||||
required this.normalizeAudio,
|
||||
required this.showSystemTrayIcon,
|
||||
required this.skipNonMusic,
|
||||
required this.systemTitleBar,
|
||||
required this.closeBehavior,
|
||||
required this.accentColorScheme,
|
||||
required this.layoutMode,
|
||||
required this.locale,
|
||||
required this.recommendationMarket,
|
||||
required this.searchMode,
|
||||
required this.downloadLocation,
|
||||
required this.pipedInstance,
|
||||
required this.themeMode,
|
||||
required this.audioSource,
|
||||
required this.streamMusicCodec,
|
||||
required this.downloadMusicCodec,
|
||||
required this.discordPresence,
|
||||
});
|
||||
|
||||
factory UserPreferences.withDefaults() {
|
||||
return UserPreferences.fromJson({});
|
||||
}
|
||||
|
||||
factory UserPreferences.fromJson(Map<String, dynamic> json) {
|
||||
return _$UserPreferencesFromJson(json);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$UserPreferencesToJson(this);
|
||||
}
|
||||
|
||||
UserPreferences copyWith({
|
||||
ThemeMode? themeMode,
|
||||
SpotubeColor? accentColorScheme,
|
||||
bool? albumColorSync,
|
||||
bool? checkUpdate,
|
||||
SourceQualities? audioQuality,
|
||||
String? downloadLocation,
|
||||
LayoutMode? layoutMode,
|
||||
CloseBehavior? closeBehavior,
|
||||
bool? showSystemTrayIcon,
|
||||
Locale? locale,
|
||||
String? pipedInstance,
|
||||
SearchMode? searchMode,
|
||||
bool? skipNonMusic,
|
||||
AudioSource? audioSource,
|
||||
Market? recommendationMarket,
|
||||
bool? saveTrackLyrics,
|
||||
bool? amoledDarkTheme,
|
||||
bool? normalizeAudio,
|
||||
SourceCodecs? downloadMusicCodec,
|
||||
SourceCodecs? streamMusicCodec,
|
||||
bool? systemTitleBar,
|
||||
bool? discordPresence,
|
||||
}) {
|
||||
return UserPreferences(
|
||||
themeMode: themeMode ?? this.themeMode,
|
||||
accentColorScheme: accentColorScheme ?? this.accentColorScheme,
|
||||
albumColorSync: albumColorSync ?? this.albumColorSync,
|
||||
checkUpdate: checkUpdate ?? this.checkUpdate,
|
||||
audioQuality: audioQuality ?? this.audioQuality,
|
||||
downloadLocation: downloadLocation ?? this.downloadLocation,
|
||||
layoutMode: layoutMode ?? this.layoutMode,
|
||||
closeBehavior: closeBehavior ?? this.closeBehavior,
|
||||
showSystemTrayIcon: showSystemTrayIcon ?? this.showSystemTrayIcon,
|
||||
locale: locale ?? this.locale,
|
||||
pipedInstance: pipedInstance ?? this.pipedInstance,
|
||||
searchMode: searchMode ?? this.searchMode,
|
||||
skipNonMusic: skipNonMusic ?? this.skipNonMusic,
|
||||
audioSource: audioSource ?? this.audioSource,
|
||||
recommendationMarket: recommendationMarket ?? this.recommendationMarket,
|
||||
amoledDarkTheme: amoledDarkTheme ?? this.amoledDarkTheme,
|
||||
downloadMusicCodec: downloadMusicCodec ?? this.downloadMusicCodec,
|
||||
normalizeAudio: normalizeAudio ?? this.normalizeAudio,
|
||||
streamMusicCodec: streamMusicCodec ?? this.streamMusicCodec,
|
||||
systemTitleBar: systemTitleBar ?? this.systemTitleBar,
|
||||
discordPresence: discordPresence ?? this.discordPresence,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,697 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'user_preferences_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
|
||||
|
||||
UserPreferences _$UserPreferencesFromJson(Map<String, dynamic> json) {
|
||||
return _UserPreferences.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$UserPreferences {
|
||||
SourceQualities get audioQuality => throw _privateConstructorUsedError;
|
||||
bool get albumColorSync => throw _privateConstructorUsedError;
|
||||
bool get amoledDarkTheme => throw _privateConstructorUsedError;
|
||||
bool get checkUpdate => throw _privateConstructorUsedError;
|
||||
bool get normalizeAudio => throw _privateConstructorUsedError;
|
||||
bool get showSystemTrayIcon => throw _privateConstructorUsedError;
|
||||
bool get skipNonMusic => throw _privateConstructorUsedError;
|
||||
bool get systemTitleBar => throw _privateConstructorUsedError;
|
||||
CloseBehavior get closeBehavior => throw _privateConstructorUsedError;
|
||||
@JsonKey(
|
||||
fromJson: UserPreferences._accentColorSchemeFromJson,
|
||||
toJson: UserPreferences._accentColorSchemeToJson,
|
||||
readValue: UserPreferences._accentColorSchemeReadValue)
|
||||
SpotubeColor get accentColorScheme => throw _privateConstructorUsedError;
|
||||
LayoutMode get layoutMode => throw _privateConstructorUsedError;
|
||||
@JsonKey(
|
||||
fromJson: UserPreferences._localeFromJson,
|
||||
toJson: UserPreferences._localeToJson,
|
||||
readValue: UserPreferences._localeReadValue)
|
||||
Locale get locale => throw _privateConstructorUsedError;
|
||||
Market get recommendationMarket => throw _privateConstructorUsedError;
|
||||
SearchMode get searchMode => throw _privateConstructorUsedError;
|
||||
String get downloadLocation => throw _privateConstructorUsedError;
|
||||
String get pipedInstance => throw _privateConstructorUsedError;
|
||||
ThemeMode get themeMode => throw _privateConstructorUsedError;
|
||||
AudioSource get audioSource => throw _privateConstructorUsedError;
|
||||
SourceCodecs get streamMusicCodec => throw _privateConstructorUsedError;
|
||||
SourceCodecs get downloadMusicCodec => throw _privateConstructorUsedError;
|
||||
bool get discordPresence => throw _privateConstructorUsedError;
|
||||
bool get endlessPlayback => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$UserPreferencesCopyWith<UserPreferences> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $UserPreferencesCopyWith<$Res> {
|
||||
factory $UserPreferencesCopyWith(
|
||||
UserPreferences value, $Res Function(UserPreferences) then) =
|
||||
_$UserPreferencesCopyWithImpl<$Res, UserPreferences>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{SourceQualities audioQuality,
|
||||
bool albumColorSync,
|
||||
bool amoledDarkTheme,
|
||||
bool checkUpdate,
|
||||
bool normalizeAudio,
|
||||
bool showSystemTrayIcon,
|
||||
bool skipNonMusic,
|
||||
bool systemTitleBar,
|
||||
CloseBehavior closeBehavior,
|
||||
@JsonKey(
|
||||
fromJson: UserPreferences._accentColorSchemeFromJson,
|
||||
toJson: UserPreferences._accentColorSchemeToJson,
|
||||
readValue: UserPreferences._accentColorSchemeReadValue)
|
||||
SpotubeColor accentColorScheme,
|
||||
LayoutMode layoutMode,
|
||||
@JsonKey(
|
||||
fromJson: UserPreferences._localeFromJson,
|
||||
toJson: UserPreferences._localeToJson,
|
||||
readValue: UserPreferences._localeReadValue)
|
||||
Locale locale,
|
||||
Market recommendationMarket,
|
||||
SearchMode searchMode,
|
||||
String downloadLocation,
|
||||
String pipedInstance,
|
||||
ThemeMode themeMode,
|
||||
AudioSource audioSource,
|
||||
SourceCodecs streamMusicCodec,
|
||||
SourceCodecs downloadMusicCodec,
|
||||
bool discordPresence,
|
||||
bool endlessPlayback});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$UserPreferencesCopyWithImpl<$Res, $Val extends UserPreferences>
|
||||
implements $UserPreferencesCopyWith<$Res> {
|
||||
_$UserPreferencesCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? audioQuality = null,
|
||||
Object? albumColorSync = null,
|
||||
Object? amoledDarkTheme = null,
|
||||
Object? checkUpdate = null,
|
||||
Object? normalizeAudio = null,
|
||||
Object? showSystemTrayIcon = null,
|
||||
Object? skipNonMusic = null,
|
||||
Object? systemTitleBar = null,
|
||||
Object? closeBehavior = null,
|
||||
Object? accentColorScheme = null,
|
||||
Object? layoutMode = null,
|
||||
Object? locale = null,
|
||||
Object? recommendationMarket = null,
|
||||
Object? searchMode = null,
|
||||
Object? downloadLocation = null,
|
||||
Object? pipedInstance = null,
|
||||
Object? themeMode = null,
|
||||
Object? audioSource = null,
|
||||
Object? streamMusicCodec = null,
|
||||
Object? downloadMusicCodec = null,
|
||||
Object? discordPresence = null,
|
||||
Object? endlessPlayback = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
audioQuality: null == audioQuality
|
||||
? _value.audioQuality
|
||||
: audioQuality // ignore: cast_nullable_to_non_nullable
|
||||
as SourceQualities,
|
||||
albumColorSync: null == albumColorSync
|
||||
? _value.albumColorSync
|
||||
: albumColorSync // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
amoledDarkTheme: null == amoledDarkTheme
|
||||
? _value.amoledDarkTheme
|
||||
: amoledDarkTheme // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
checkUpdate: null == checkUpdate
|
||||
? _value.checkUpdate
|
||||
: checkUpdate // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
normalizeAudio: null == normalizeAudio
|
||||
? _value.normalizeAudio
|
||||
: normalizeAudio // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
showSystemTrayIcon: null == showSystemTrayIcon
|
||||
? _value.showSystemTrayIcon
|
||||
: showSystemTrayIcon // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
skipNonMusic: null == skipNonMusic
|
||||
? _value.skipNonMusic
|
||||
: skipNonMusic // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
systemTitleBar: null == systemTitleBar
|
||||
? _value.systemTitleBar
|
||||
: systemTitleBar // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
closeBehavior: null == closeBehavior
|
||||
? _value.closeBehavior
|
||||
: closeBehavior // ignore: cast_nullable_to_non_nullable
|
||||
as CloseBehavior,
|
||||
accentColorScheme: null == accentColorScheme
|
||||
? _value.accentColorScheme
|
||||
: accentColorScheme // ignore: cast_nullable_to_non_nullable
|
||||
as SpotubeColor,
|
||||
layoutMode: null == layoutMode
|
||||
? _value.layoutMode
|
||||
: layoutMode // ignore: cast_nullable_to_non_nullable
|
||||
as LayoutMode,
|
||||
locale: null == locale
|
||||
? _value.locale
|
||||
: locale // ignore: cast_nullable_to_non_nullable
|
||||
as Locale,
|
||||
recommendationMarket: null == recommendationMarket
|
||||
? _value.recommendationMarket
|
||||
: recommendationMarket // ignore: cast_nullable_to_non_nullable
|
||||
as Market,
|
||||
searchMode: null == searchMode
|
||||
? _value.searchMode
|
||||
: searchMode // ignore: cast_nullable_to_non_nullable
|
||||
as SearchMode,
|
||||
downloadLocation: null == downloadLocation
|
||||
? _value.downloadLocation
|
||||
: downloadLocation // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
pipedInstance: null == pipedInstance
|
||||
? _value.pipedInstance
|
||||
: pipedInstance // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
themeMode: null == themeMode
|
||||
? _value.themeMode
|
||||
: themeMode // ignore: cast_nullable_to_non_nullable
|
||||
as ThemeMode,
|
||||
audioSource: null == audioSource
|
||||
? _value.audioSource
|
||||
: audioSource // ignore: cast_nullable_to_non_nullable
|
||||
as AudioSource,
|
||||
streamMusicCodec: null == streamMusicCodec
|
||||
? _value.streamMusicCodec
|
||||
: streamMusicCodec // ignore: cast_nullable_to_non_nullable
|
||||
as SourceCodecs,
|
||||
downloadMusicCodec: null == downloadMusicCodec
|
||||
? _value.downloadMusicCodec
|
||||
: downloadMusicCodec // ignore: cast_nullable_to_non_nullable
|
||||
as SourceCodecs,
|
||||
discordPresence: null == discordPresence
|
||||
? _value.discordPresence
|
||||
: discordPresence // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
endlessPlayback: null == endlessPlayback
|
||||
? _value.endlessPlayback
|
||||
: endlessPlayback // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$UserPreferencesImplCopyWith<$Res>
|
||||
implements $UserPreferencesCopyWith<$Res> {
|
||||
factory _$$UserPreferencesImplCopyWith(_$UserPreferencesImpl value,
|
||||
$Res Function(_$UserPreferencesImpl) then) =
|
||||
__$$UserPreferencesImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{SourceQualities audioQuality,
|
||||
bool albumColorSync,
|
||||
bool amoledDarkTheme,
|
||||
bool checkUpdate,
|
||||
bool normalizeAudio,
|
||||
bool showSystemTrayIcon,
|
||||
bool skipNonMusic,
|
||||
bool systemTitleBar,
|
||||
CloseBehavior closeBehavior,
|
||||
@JsonKey(
|
||||
fromJson: UserPreferences._accentColorSchemeFromJson,
|
||||
toJson: UserPreferences._accentColorSchemeToJson,
|
||||
readValue: UserPreferences._accentColorSchemeReadValue)
|
||||
SpotubeColor accentColorScheme,
|
||||
LayoutMode layoutMode,
|
||||
@JsonKey(
|
||||
fromJson: UserPreferences._localeFromJson,
|
||||
toJson: UserPreferences._localeToJson,
|
||||
readValue: UserPreferences._localeReadValue)
|
||||
Locale locale,
|
||||
Market recommendationMarket,
|
||||
SearchMode searchMode,
|
||||
String downloadLocation,
|
||||
String pipedInstance,
|
||||
ThemeMode themeMode,
|
||||
AudioSource audioSource,
|
||||
SourceCodecs streamMusicCodec,
|
||||
SourceCodecs downloadMusicCodec,
|
||||
bool discordPresence,
|
||||
bool endlessPlayback});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$UserPreferencesImplCopyWithImpl<$Res>
|
||||
extends _$UserPreferencesCopyWithImpl<$Res, _$UserPreferencesImpl>
|
||||
implements _$$UserPreferencesImplCopyWith<$Res> {
|
||||
__$$UserPreferencesImplCopyWithImpl(
|
||||
_$UserPreferencesImpl _value, $Res Function(_$UserPreferencesImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? audioQuality = null,
|
||||
Object? albumColorSync = null,
|
||||
Object? amoledDarkTheme = null,
|
||||
Object? checkUpdate = null,
|
||||
Object? normalizeAudio = null,
|
||||
Object? showSystemTrayIcon = null,
|
||||
Object? skipNonMusic = null,
|
||||
Object? systemTitleBar = null,
|
||||
Object? closeBehavior = null,
|
||||
Object? accentColorScheme = null,
|
||||
Object? layoutMode = null,
|
||||
Object? locale = null,
|
||||
Object? recommendationMarket = null,
|
||||
Object? searchMode = null,
|
||||
Object? downloadLocation = null,
|
||||
Object? pipedInstance = null,
|
||||
Object? themeMode = null,
|
||||
Object? audioSource = null,
|
||||
Object? streamMusicCodec = null,
|
||||
Object? downloadMusicCodec = null,
|
||||
Object? discordPresence = null,
|
||||
Object? endlessPlayback = null,
|
||||
}) {
|
||||
return _then(_$UserPreferencesImpl(
|
||||
audioQuality: null == audioQuality
|
||||
? _value.audioQuality
|
||||
: audioQuality // ignore: cast_nullable_to_non_nullable
|
||||
as SourceQualities,
|
||||
albumColorSync: null == albumColorSync
|
||||
? _value.albumColorSync
|
||||
: albumColorSync // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
amoledDarkTheme: null == amoledDarkTheme
|
||||
? _value.amoledDarkTheme
|
||||
: amoledDarkTheme // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
checkUpdate: null == checkUpdate
|
||||
? _value.checkUpdate
|
||||
: checkUpdate // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
normalizeAudio: null == normalizeAudio
|
||||
? _value.normalizeAudio
|
||||
: normalizeAudio // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
showSystemTrayIcon: null == showSystemTrayIcon
|
||||
? _value.showSystemTrayIcon
|
||||
: showSystemTrayIcon // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
skipNonMusic: null == skipNonMusic
|
||||
? _value.skipNonMusic
|
||||
: skipNonMusic // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
systemTitleBar: null == systemTitleBar
|
||||
? _value.systemTitleBar
|
||||
: systemTitleBar // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
closeBehavior: null == closeBehavior
|
||||
? _value.closeBehavior
|
||||
: closeBehavior // ignore: cast_nullable_to_non_nullable
|
||||
as CloseBehavior,
|
||||
accentColorScheme: null == accentColorScheme
|
||||
? _value.accentColorScheme
|
||||
: accentColorScheme // ignore: cast_nullable_to_non_nullable
|
||||
as SpotubeColor,
|
||||
layoutMode: null == layoutMode
|
||||
? _value.layoutMode
|
||||
: layoutMode // ignore: cast_nullable_to_non_nullable
|
||||
as LayoutMode,
|
||||
locale: null == locale
|
||||
? _value.locale
|
||||
: locale // ignore: cast_nullable_to_non_nullable
|
||||
as Locale,
|
||||
recommendationMarket: null == recommendationMarket
|
||||
? _value.recommendationMarket
|
||||
: recommendationMarket // ignore: cast_nullable_to_non_nullable
|
||||
as Market,
|
||||
searchMode: null == searchMode
|
||||
? _value.searchMode
|
||||
: searchMode // ignore: cast_nullable_to_non_nullable
|
||||
as SearchMode,
|
||||
downloadLocation: null == downloadLocation
|
||||
? _value.downloadLocation
|
||||
: downloadLocation // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
pipedInstance: null == pipedInstance
|
||||
? _value.pipedInstance
|
||||
: pipedInstance // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
themeMode: null == themeMode
|
||||
? _value.themeMode
|
||||
: themeMode // ignore: cast_nullable_to_non_nullable
|
||||
as ThemeMode,
|
||||
audioSource: null == audioSource
|
||||
? _value.audioSource
|
||||
: audioSource // ignore: cast_nullable_to_non_nullable
|
||||
as AudioSource,
|
||||
streamMusicCodec: null == streamMusicCodec
|
||||
? _value.streamMusicCodec
|
||||
: streamMusicCodec // ignore: cast_nullable_to_non_nullable
|
||||
as SourceCodecs,
|
||||
downloadMusicCodec: null == downloadMusicCodec
|
||||
? _value.downloadMusicCodec
|
||||
: downloadMusicCodec // ignore: cast_nullable_to_non_nullable
|
||||
as SourceCodecs,
|
||||
discordPresence: null == discordPresence
|
||||
? _value.discordPresence
|
||||
: discordPresence // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
endlessPlayback: null == endlessPlayback
|
||||
? _value.endlessPlayback
|
||||
: endlessPlayback // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$UserPreferencesImpl implements _UserPreferences {
|
||||
const _$UserPreferencesImpl(
|
||||
{this.audioQuality = SourceQualities.high,
|
||||
this.albumColorSync = true,
|
||||
this.amoledDarkTheme = false,
|
||||
this.checkUpdate = true,
|
||||
this.normalizeAudio = false,
|
||||
this.showSystemTrayIcon = true,
|
||||
this.skipNonMusic = false,
|
||||
this.systemTitleBar = false,
|
||||
this.closeBehavior = CloseBehavior.minimizeToTray,
|
||||
@JsonKey(
|
||||
fromJson: UserPreferences._accentColorSchemeFromJson,
|
||||
toJson: UserPreferences._accentColorSchemeToJson,
|
||||
readValue: UserPreferences._accentColorSchemeReadValue)
|
||||
this.accentColorScheme = const SpotubeColor(0xFF2196F3, name: "Blue"),
|
||||
this.layoutMode = LayoutMode.adaptive,
|
||||
@JsonKey(
|
||||
fromJson: UserPreferences._localeFromJson,
|
||||
toJson: UserPreferences._localeToJson,
|
||||
readValue: UserPreferences._localeReadValue)
|
||||
this.locale = const Locale("system", "system"),
|
||||
this.recommendationMarket = Market.US,
|
||||
this.searchMode = SearchMode.youtube,
|
||||
this.downloadLocation = "",
|
||||
this.pipedInstance = "https://pipedapi.kavin.rocks",
|
||||
this.themeMode = ThemeMode.system,
|
||||
this.audioSource = AudioSource.youtube,
|
||||
this.streamMusicCodec = SourceCodecs.weba,
|
||||
this.downloadMusicCodec = SourceCodecs.m4a,
|
||||
this.discordPresence = true,
|
||||
this.endlessPlayback = true});
|
||||
|
||||
factory _$UserPreferencesImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$UserPreferencesImplFromJson(json);
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final SourceQualities audioQuality;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool albumColorSync;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool amoledDarkTheme;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool checkUpdate;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool normalizeAudio;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool showSystemTrayIcon;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool skipNonMusic;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool systemTitleBar;
|
||||
@override
|
||||
@JsonKey()
|
||||
final CloseBehavior closeBehavior;
|
||||
@override
|
||||
@JsonKey(
|
||||
fromJson: UserPreferences._accentColorSchemeFromJson,
|
||||
toJson: UserPreferences._accentColorSchemeToJson,
|
||||
readValue: UserPreferences._accentColorSchemeReadValue)
|
||||
final SpotubeColor accentColorScheme;
|
||||
@override
|
||||
@JsonKey()
|
||||
final LayoutMode layoutMode;
|
||||
@override
|
||||
@JsonKey(
|
||||
fromJson: UserPreferences._localeFromJson,
|
||||
toJson: UserPreferences._localeToJson,
|
||||
readValue: UserPreferences._localeReadValue)
|
||||
final Locale locale;
|
||||
@override
|
||||
@JsonKey()
|
||||
final Market recommendationMarket;
|
||||
@override
|
||||
@JsonKey()
|
||||
final SearchMode searchMode;
|
||||
@override
|
||||
@JsonKey()
|
||||
final String downloadLocation;
|
||||
@override
|
||||
@JsonKey()
|
||||
final String pipedInstance;
|
||||
@override
|
||||
@JsonKey()
|
||||
final ThemeMode themeMode;
|
||||
@override
|
||||
@JsonKey()
|
||||
final AudioSource audioSource;
|
||||
@override
|
||||
@JsonKey()
|
||||
final SourceCodecs streamMusicCodec;
|
||||
@override
|
||||
@JsonKey()
|
||||
final SourceCodecs downloadMusicCodec;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool discordPresence;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool endlessPlayback;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'UserPreferences(audioQuality: $audioQuality, albumColorSync: $albumColorSync, amoledDarkTheme: $amoledDarkTheme, checkUpdate: $checkUpdate, normalizeAudio: $normalizeAudio, showSystemTrayIcon: $showSystemTrayIcon, skipNonMusic: $skipNonMusic, systemTitleBar: $systemTitleBar, closeBehavior: $closeBehavior, accentColorScheme: $accentColorScheme, layoutMode: $layoutMode, locale: $locale, recommendationMarket: $recommendationMarket, searchMode: $searchMode, downloadLocation: $downloadLocation, pipedInstance: $pipedInstance, themeMode: $themeMode, audioSource: $audioSource, streamMusicCodec: $streamMusicCodec, downloadMusicCodec: $downloadMusicCodec, discordPresence: $discordPresence, endlessPlayback: $endlessPlayback)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$UserPreferencesImpl &&
|
||||
(identical(other.audioQuality, audioQuality) ||
|
||||
other.audioQuality == audioQuality) &&
|
||||
(identical(other.albumColorSync, albumColorSync) ||
|
||||
other.albumColorSync == albumColorSync) &&
|
||||
(identical(other.amoledDarkTheme, amoledDarkTheme) ||
|
||||
other.amoledDarkTheme == amoledDarkTheme) &&
|
||||
(identical(other.checkUpdate, checkUpdate) ||
|
||||
other.checkUpdate == checkUpdate) &&
|
||||
(identical(other.normalizeAudio, normalizeAudio) ||
|
||||
other.normalizeAudio == normalizeAudio) &&
|
||||
(identical(other.showSystemTrayIcon, showSystemTrayIcon) ||
|
||||
other.showSystemTrayIcon == showSystemTrayIcon) &&
|
||||
(identical(other.skipNonMusic, skipNonMusic) ||
|
||||
other.skipNonMusic == skipNonMusic) &&
|
||||
(identical(other.systemTitleBar, systemTitleBar) ||
|
||||
other.systemTitleBar == systemTitleBar) &&
|
||||
(identical(other.closeBehavior, closeBehavior) ||
|
||||
other.closeBehavior == closeBehavior) &&
|
||||
(identical(other.accentColorScheme, accentColorScheme) ||
|
||||
other.accentColorScheme == accentColorScheme) &&
|
||||
(identical(other.layoutMode, layoutMode) ||
|
||||
other.layoutMode == layoutMode) &&
|
||||
(identical(other.locale, locale) || other.locale == locale) &&
|
||||
(identical(other.recommendationMarket, recommendationMarket) ||
|
||||
other.recommendationMarket == recommendationMarket) &&
|
||||
(identical(other.searchMode, searchMode) ||
|
||||
other.searchMode == searchMode) &&
|
||||
(identical(other.downloadLocation, downloadLocation) ||
|
||||
other.downloadLocation == downloadLocation) &&
|
||||
(identical(other.pipedInstance, pipedInstance) ||
|
||||
other.pipedInstance == pipedInstance) &&
|
||||
(identical(other.themeMode, themeMode) ||
|
||||
other.themeMode == themeMode) &&
|
||||
(identical(other.audioSource, audioSource) ||
|
||||
other.audioSource == audioSource) &&
|
||||
(identical(other.streamMusicCodec, streamMusicCodec) ||
|
||||
other.streamMusicCodec == streamMusicCodec) &&
|
||||
(identical(other.downloadMusicCodec, downloadMusicCodec) ||
|
||||
other.downloadMusicCodec == downloadMusicCodec) &&
|
||||
(identical(other.discordPresence, discordPresence) ||
|
||||
other.discordPresence == discordPresence) &&
|
||||
(identical(other.endlessPlayback, endlessPlayback) ||
|
||||
other.endlessPlayback == endlessPlayback));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hashAll([
|
||||
runtimeType,
|
||||
audioQuality,
|
||||
albumColorSync,
|
||||
amoledDarkTheme,
|
||||
checkUpdate,
|
||||
normalizeAudio,
|
||||
showSystemTrayIcon,
|
||||
skipNonMusic,
|
||||
systemTitleBar,
|
||||
closeBehavior,
|
||||
accentColorScheme,
|
||||
layoutMode,
|
||||
locale,
|
||||
recommendationMarket,
|
||||
searchMode,
|
||||
downloadLocation,
|
||||
pipedInstance,
|
||||
themeMode,
|
||||
audioSource,
|
||||
streamMusicCodec,
|
||||
downloadMusicCodec,
|
||||
discordPresence,
|
||||
endlessPlayback
|
||||
]);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$UserPreferencesImplCopyWith<_$UserPreferencesImpl> get copyWith =>
|
||||
__$$UserPreferencesImplCopyWithImpl<_$UserPreferencesImpl>(
|
||||
this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$UserPreferencesImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _UserPreferences implements UserPreferences {
|
||||
const factory _UserPreferences(
|
||||
{final SourceQualities audioQuality,
|
||||
final bool albumColorSync,
|
||||
final bool amoledDarkTheme,
|
||||
final bool checkUpdate,
|
||||
final bool normalizeAudio,
|
||||
final bool showSystemTrayIcon,
|
||||
final bool skipNonMusic,
|
||||
final bool systemTitleBar,
|
||||
final CloseBehavior closeBehavior,
|
||||
@JsonKey(
|
||||
fromJson: UserPreferences._accentColorSchemeFromJson,
|
||||
toJson: UserPreferences._accentColorSchemeToJson,
|
||||
readValue: UserPreferences._accentColorSchemeReadValue)
|
||||
final SpotubeColor accentColorScheme,
|
||||
final LayoutMode layoutMode,
|
||||
@JsonKey(
|
||||
fromJson: UserPreferences._localeFromJson,
|
||||
toJson: UserPreferences._localeToJson,
|
||||
readValue: UserPreferences._localeReadValue)
|
||||
final Locale locale,
|
||||
final Market recommendationMarket,
|
||||
final SearchMode searchMode,
|
||||
final String downloadLocation,
|
||||
final String pipedInstance,
|
||||
final ThemeMode themeMode,
|
||||
final AudioSource audioSource,
|
||||
final SourceCodecs streamMusicCodec,
|
||||
final SourceCodecs downloadMusicCodec,
|
||||
final bool discordPresence,
|
||||
final bool endlessPlayback}) = _$UserPreferencesImpl;
|
||||
|
||||
factory _UserPreferences.fromJson(Map<String, dynamic> json) =
|
||||
_$UserPreferencesImpl.fromJson;
|
||||
|
||||
@override
|
||||
SourceQualities get audioQuality;
|
||||
@override
|
||||
bool get albumColorSync;
|
||||
@override
|
||||
bool get amoledDarkTheme;
|
||||
@override
|
||||
bool get checkUpdate;
|
||||
@override
|
||||
bool get normalizeAudio;
|
||||
@override
|
||||
bool get showSystemTrayIcon;
|
||||
@override
|
||||
bool get skipNonMusic;
|
||||
@override
|
||||
bool get systemTitleBar;
|
||||
@override
|
||||
CloseBehavior get closeBehavior;
|
||||
@override
|
||||
@JsonKey(
|
||||
fromJson: UserPreferences._accentColorSchemeFromJson,
|
||||
toJson: UserPreferences._accentColorSchemeToJson,
|
||||
readValue: UserPreferences._accentColorSchemeReadValue)
|
||||
SpotubeColor get accentColorScheme;
|
||||
@override
|
||||
LayoutMode get layoutMode;
|
||||
@override
|
||||
@JsonKey(
|
||||
fromJson: UserPreferences._localeFromJson,
|
||||
toJson: UserPreferences._localeToJson,
|
||||
readValue: UserPreferences._localeReadValue)
|
||||
Locale get locale;
|
||||
@override
|
||||
Market get recommendationMarket;
|
||||
@override
|
||||
SearchMode get searchMode;
|
||||
@override
|
||||
String get downloadLocation;
|
||||
@override
|
||||
String get pipedInstance;
|
||||
@override
|
||||
ThemeMode get themeMode;
|
||||
@override
|
||||
AudioSource get audioSource;
|
||||
@override
|
||||
SourceCodecs get streamMusicCodec;
|
||||
@override
|
||||
SourceCodecs get downloadMusicCodec;
|
||||
@override
|
||||
bool get discordPresence;
|
||||
@override
|
||||
bool get endlessPlayback;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$UserPreferencesImplCopyWith<_$UserPreferencesImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||