Merge branch 'KRTirtho:master' into master

This commit is contained in:
Henrik Sozzi 2023-10-16 20:07:01 +02:00 committed by GitHub
commit f0903a8118
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
188 changed files with 5846 additions and 2488 deletions

View File

@ -8,4 +8,7 @@ SPOTIFY_SECRETS=
# 0 or 1
# 0 = disable
# 1 = enable
ENABLE_UPDATE_CHECK=
ENABLE_UPDATE_CHECK=
LASTFM_API_KEY=
LASTFM_API_SECRET=

View File

@ -4,7 +4,7 @@ on:
inputs:
version:
description: Version to release (x.x.x)
default: 3.1.2
default: 3.2.0
required: true
channel:
type: choice
@ -87,18 +87,22 @@ jobs:
make choco
mv dist/spotube.*.nupkg dist/Spotube-windows-x86_64.nupkg
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
if-no-files-found: error
name: Spotube-Release-Binaries
path: |
dist/Spotube-windows-x86_64.nupkg
dist/Spotube-windows-x86_64-setup.exe
- name: Debug With SSH When fails
if: ${{ failure() && inputs.debug && inputs.channel == 'nightly' }}
uses: mxschmitt/action-tmate@v3
with:
limit-access-to-actor: true
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: Spotube-Release-Binaries
path: dist/
linux:
runs-on: ubuntu-latest
steps:
@ -177,16 +181,23 @@ jobs:
mv dist/**/spotube-*-linux.rpm dist/Spotube-linux-x86_64.rpm
mv dist/**/spotube-*-linux.AppImage dist/Spotube-linux-x86_64.AppImage
- uses: actions/upload-artifact@v3
with:
if-no-files-found: error
name: Spotube-Release-Binaries
path: |
dist/Spotube-linux-x86_64.AppImage
dist/Spotube-linux-x86_64.deb
dist/Spotube-linux-x86_64.rpm
dist/spotube-linux-${{ env.BUILD_VERSION }}-x86_64.tar.xz
- name: Debug With SSH When fails
if: ${{ failure() && inputs.debug && inputs.channel == 'nightly' }}
uses: mxschmitt/action-tmate@v3
with:
limit-access-to-actor: true
- uses: actions/upload-artifact@v3
with:
name: Spotube-Release-Binaries
path: dist/
android:
runs-on: ubuntu-latest
@ -235,10 +246,8 @@ jobs:
- name: Build Apk
run: |
flutter build apk
flutter build appbundle
mv build/app/outputs/apk/release/app-release.apk build/Spotube-android-all-arch.apk
mv build/app/outputs/bundle/release/app-release.aab build/Spotube-playstore-all-arch.aab
flutter build apk --flavor ${{ inputs.channel }}
mv build/app/outputs/flutter-apk/app-${{ inputs.channel }}-release.apk build/Spotube-android-all-arch.apk
- name: Build Playstore AppBundle
run: |
@ -247,8 +256,17 @@ jobs:
export MANIFEST=android/app/src/main/AndroidManifest.xml
xmlstarlet ed -d '//meta-data[@android:name="com.google.android.gms.car.application"]' $MANIFEST > $MANIFEST.tmp
mv $MANIFEST.tmp $MANIFEST
flutter build appbundle
mv build/app/outputs/bundle/release/app-release.aab build/Spotube-playstore-all-arch.aab
flutter build appbundle --flavor ${{ inputs.channel }}
mv build/app/outputs/bundle/${{ inputs.channel }}Release/app-${{ inputs.channel }}-release.aab build/Spotube-playstore-all-arch.aab
- uses: actions/upload-artifact@v3
with:
if-no-files-found: error
name: Spotube-Release-Binaries
path: |
build/Spotube-android-all-arch.apk
build/Spotube-playstore-all-arch.aab
- name: Debug With SSH When fails
if: ${{ failure() && inputs.debug && inputs.channel == 'nightly' }}
@ -256,14 +274,8 @@ jobs:
with:
limit-access-to-actor: true
- uses: actions/upload-artifact@v3
with:
name: Spotube-Release-Binaries
path: |
build/Spotube-android-all-arch.apk
build/Spotube-playstore-all-arch.aab
macos:
runs-on: macos-12
steps:
- uses: actions/checkout@v4
@ -311,38 +323,23 @@ jobs:
mkdir -p build/${{ env.BUILD_VERSION }}
appdmg appdmg.json build/Spotube-macos-universal.dmg
- uses: actions/upload-artifact@v3
with:
if-no-files-found: error
name: Spotube-Release-Binaries
path: |
build/Spotube-macos-universal.dmg
- name: Debug With SSH When fails
if: ${{ failure() && inputs.debug && inputs.channel == 'nightly' }}
uses: mxschmitt/action-tmate@v3
with:
limit-access-to-actor: true
- uses: actions/upload-artifact@v3
with:
name: Spotube-Release-Binaries
path: |
build/Spotube-macos-universal.dmg
# linux_arm:
# runs-on: ubuntu-latest
# steps:
# - run: |
# sudo apt-get update -y
# sudo apt-get install -y curl
# - name: Extract branch name
# shell: bash
# run: echo "BRANCH=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_ENV
# - name: Trigger CircleCI Pipeline
# run: |
# curl -X POST https://circleci.com/api/v2/project/cci-f9azl/spotube/pipeline \
# --header "Circle-Token: ${{secrets.CCI_TOKEN}}" \
# --header "content-type: application/json" \
# --data '{"branch": "${{env.BRANCH}}", "parameters":{"GHA_Action":"true","version":"${{inputs.version}}","channel":"${{inputs.channel}}","dry_run":${{inputs.dry_run}}}}'
upload:
runs-on: ubuntu-latest
needs:
- windows
- linux
@ -366,6 +363,7 @@ jobs:
- uses: actions/upload-artifact@v3
with:
if-no-files-found: error
name: Spotube-Release-Binaries
path: |
RELEASE.md5sum

6
.vscode/launch.json vendored
View File

@ -5,7 +5,11 @@
"name": "spotube",
"type": "dart",
"request": "launch",
"program": "lib/main.dart"
"program": "lib/main.dart",
"args": [
"--flavor",
"dev"
]
},
{
"name": "spotube (profile)",

View File

@ -6,6 +6,7 @@
"instrumentalness",
"Mpris",
"riverpod",
"Scrobblenaut",
"speechiness",
"Spotube",
"winget"
@ -13,7 +14,7 @@
"editor.formatOnSave": true,
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.patterns": {
"pubspec.yaml": "pubspec.lock,analysis_options.yaml,.packages,.flutter-plugins,.flutter-plugins-dependencies",
"pubspec.yaml": "pubspec.lock,analysis_options.yaml,.packages,.flutter-plugins,.flutter-plugins-dependencies,flutter_launcher_icons*.yaml,flutter_native_splash*.yaml",
"README.md": "LICENSE,CODE_OF_CONDUCT.md,CONTRIBUTING.md,SECURITY.md,CONTRIBUTION.md,CHANGELOG.md,PRIVACY_POLICY.md",
}
}

View File

@ -2,6 +2,44 @@
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.2.0](https://github.com/KRTirtho/spotube/compare/v3.1.2...v3.2.0) (2023-10-16)
### Features
* ability to select/copy lyrics [#802](https://github.com/KRTirtho/spotube/issues/802) ([0eb9ee8](https://github.com/KRTirtho/spotube/commit/0eb9ee8648bee43a8009e6752674b1be646c0916))
* add Amoled theme [#724](https://github.com/KRTirtho/spotube/issues/724) ([5c5dbf6](https://github.com/KRTirtho/spotube/commit/5c5dbf69ecea95c92d3c3900ad690a500d75b4e2))
* add audio normalization [#164](https://github.com/KRTirtho/spotube/issues/164) ([da10ab2](https://github.com/KRTirtho/spotube/commit/da10ab2e291d4ba4d3082b9a6ae535639fb8f1b7))
* add restore default settings button ([94c3866](https://github.com/KRTirtho/spotube/commit/94c386638f2e5a42d21c8f157835443333ee6d5c))
* configurable audio normalization switch ([c325911](https://github.com/KRTirtho/spotube/commit/c325911c0d87758a203a52df02179c1513bad3fd))
* customizable stream/download file formats ([#757](https://github.com/KRTirtho/spotube/issues/757)) ([e54762b](https://github.com/KRTirtho/spotube/commit/e54762be6add6524ab614d103fc3557a101c75f4))
* improve and unify the logging framework ([#738](https://github.com/KRTirtho/spotube/issues/738)) ([c7432bb](https://github.com/KRTirtho/spotube/commit/c7432bbd986d576a93957f0a22bdbca5c1e87f20))
* LastFM scrobbling support ([#761](https://github.com/KRTirtho/spotube/issues/761)) ([f5bd907](https://github.com/KRTirtho/spotube/commit/f5bd90731d9abc19d684c8bcb231eff399e73023))
* loading indicator for genre and personalized pages ([ffe8d9c](https://github.com/KRTirtho/spotube/commit/ffe8d9ca6da25cb3e6fd2c781d5ed3a7b919510e))
* manual offline detection ([854ab89](https://github.com/KRTirtho/spotube/commit/854ab8910dffb2837c011d3439173a1f0ebe9c6c))
* show error dialog on failed to login ([101c325](https://github.com/KRTirtho/spotube/commit/101c32523d3be8c05527261f6f63f939d388ad79))
* sliding up player support ([083319f](https://github.com/KRTirtho/spotube/commit/083319fd2445ab179e3dcda0a6aeaca6f13dda29))
* swipe to open player view ([#765](https://github.com/KRTirtho/spotube/issues/765)) ([9aee056](https://github.com/KRTirtho/spotube/commit/9aee0568bf42eed9fea8d517e960a010abf0ebf2))
* thicken the scrollbars & make 'em interactive for mobile ([#764](https://github.com/KRTirtho/spotube/issues/764)) ([84a4bcd](https://github.com/KRTirtho/spotube/commit/84a4bcd948ab459489aaf6f39d6954776c3401d7))
* **translations:** add Arabic Translations ([#740](https://github.com/KRTirtho/spotube/issues/740)) ([38493f9](https://github.com/KRTirtho/spotube/commit/38493f9dd75303890857a626c0b276ee1ab75bb2))
* **translations:** add Farsi Translations ([#760](https://github.com/KRTirtho/spotube/issues/760)) ([fe42cfe](https://github.com/KRTirtho/spotube/commit/fe42cfe8430035d9b67dd158fb7b835ee4071497))
### Bug Fixes
* add libmpv1 for ubuntu-based systems ([#739](https://github.com/KRTirtho/spotube/issues/739)) ([5115e04](https://github.com/KRTirtho/spotube/commit/5115e041e78c20fce798a80f1d844bfc60746958))
* add xdg-user-dirs as deps ([f3e331e](https://github.com/KRTirtho/spotube/commit/f3e331ecf733995da24c9b907efc5ed4bd02ffdd))
* **android :** file_selector getDirectoryPath returns unusable content urls [#720](https://github.com/KRTirtho/spotube/issues/720) ([b3cf639](https://github.com/KRTirtho/spotube/commit/b3cf639ee2f970f4df9b394b260c3ad8a5732a9c))
* **android:** audio doesn't resume on interruption end ([15d466a](https://github.com/KRTirtho/spotube/commit/15d466a04538ec70c3a0c132f2baaaf8690f8d4e))
* **android:** system navigator back doesn't close player ([20d7092](https://github.com/KRTirtho/spotube/commit/20d70927c909347e84ffa8e456f8fab88d49d179))
* get rid of overflow errors & status bar dark color ([5bb8231](https://github.com/KRTirtho/spotube/commit/5bb8231782287faf75c778fadb3a03ac774d14f0))
* keyboard shortcuts changing route but not update sidebar ([2d93441](https://github.com/KRTirtho/spotube/commit/2d934411887bd104d8265236df5bf595c5ad2278))
* last track repeats ([ed6ca00](https://github.com/KRTirtho/spotube/commit/ed6ca006ce237ed8d509cde9ed47cd6ea3396b63))
* minor glitches ([e5d0aaf](https://github.com/KRTirtho/spotube/commit/e5d0aaf80d22b2291b6f7e7c5e18dd99ae1a7a82))
* not fetching all followed artists ([#759](https://github.com/KRTirtho/spotube/issues/759)) ([c09a572](https://github.com/KRTirtho/spotube/commit/c09a5729251d8df820442d55477455f78c19c52e))
* use audio_service_mpris plugin ([e29cc25](https://github.com/KRTirtho/spotube/commit/e29cc2578cab36729e235b117c1b5489c3452902))
* valid non-ASCII characters get removed from downloaded file name [#745](https://github.com/KRTirtho/spotube/issues/745) ([a7e102f](https://github.com/KRTirtho/spotube/commit/a7e102ffc726d00df369560ec9a7f742f9d387bb))
## [3.1.2](https://github.com/KRTirtho/spotube/compare/v3.1.1...v3.1.2) (2023-09-15)

View File

@ -204,6 +204,7 @@ If you are concerned, you can [read the reason of choosing this license](https:/
1. [auto_size_text](https://github.com/leisim/auto_size_text) - Flutter widget that automatically resizes text to fit perfectly within its bounds.
1. [buttons_tabbar](https://afonsoraposo.com) - A Flutter package that implements a TabBar where each label is a toggle button.
1. [cached_network_image](https://github.com/Baseflow/flutter_cached_network_image) - Flutter library to load and cache network images. Can also be used with placeholder and error widgets.
1. [catcher_2](https://github.com/ThexXTURBOXx/catcher_2) - Plugin for error catching which provides multiple handlers for dealing with errors when they are not caught by the developer.
1. [collection](https://pub.dev/packages/collection) - Collections and utilities functions and classes related to collections.
1. [cupertino_icons](https://pub.dev/packages/cupertino_icons) - Default icons asset for Cupertino widgets based on Apple styled icons
1. [curved_navigation_bar](https://github.com/rafalbednarczuk/curved_navigation_bar) - Stunning Animating Curved Shape Navigation Bar. Adjustable color, background color, animation curve, animation duration.
@ -215,9 +216,6 @@ If you are concerned, you can [read the reason of choosing this license](https:/
1. [duration](https://github.com/desktop-dart/duration) - Utilities to make working with 'Duration's easier. Formats duration in human readable form and also parses duration in human readable form to Dart's Duration.
1. [envied](https://github.com/petercinibulk/envied) - Explicitly reads environment variables into a dart file from a .env file for more security and faster start up times.
1. [file_selector](https://pub.dev/packages/file_selector) - Flutter plugin for opening and saving files, or selecting directories, using native file selection UI.
1. [fl_query](https://fl-query.vercel.app) - Asynchronous data caching, refetching & invalidation library for Flutter
1. [fl_query_hooks](https://fl-query.vercel.app) - Elite flutter_hooks compatible library for fl_query, the Asynchronous data caching, refetching & invalidation library for Flutter
1. [fl_query_devtools](https://fl-query.vercel.app) - Devtools support for Fl-Query
1. [fluentui_system_icons](https://github.com/microsoft/fluentui-system-icons/tree/main) - Fluent UI System Icons are a collection of familiar, friendly and modern icons from Microsoft.
1. [flutter_cache_manager](https://github.com/Baseflow/flutter_cache_manager/tree/develop/flutter_cache_manager) - Generic cache manager for flutter. Saves web files on the storages of the device and saves the cache info using sqflite.
1. [flutter_displaymode](https://github.com/ajinasokan/flutter_displaymode) - A Flutter plugin to set display mode (resolution, refresh rate) on Android platform. Allows to enable high refresh rate on supported devices.
@ -238,7 +236,6 @@ If you are concerned, you can [read the reason of choosing this license](https:/
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.
1. [internet_connection_checker](https://github.com/RounakTadvi/internet_connection_checker/tree/main) - A pure Dart library that checks for internet by opening a socket to a list of specified addresses, each with individual port and timeout. Defaults are provided for convenience.
1. [intl](https://pub.dev/packages/intl) - Contains code to deal with internationalized/localized messages, date and number formatting and parsing, bi-directional text, and other internationalization issues.
1. [introduction_screen](https://github.com/pyozer/introduction_screen) - Introduction/Onboarding package for flutter app with some customizations possibilities
1. [json_annotation](https://pub.dev/packages/json_annotation) - Classes and helper functions that support JSON code generation via the `json_serializable` package.
@ -265,11 +262,13 @@ If you are concerned, you can [read the reason of choosing this license](https:/
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.
1. [url_launcher](https://pub.dev/packages/url_launcher) - Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes.
1. [uuid](https://github.com/Daegalus/dart-uuid) - RFC4122 (v1, v4, v5, v6, v7, v8) UUID Generator and Parser for Dart
1. [uuid](https://pub.dev/packages/uuid) - RFC4122 (v1, v4, v5, v6, v7, v8) UUID Generator and Parser for Dart
1. [version](https://github.com/dartninja/version) - Provides a simple class for parsing and comparing semantic versions as defined by http://semver.org/
1. [visibility_detector](https://pub.dev/packages/visibility_detector) - A widget that detects the visibility of its child and notifies a callback.
1. [window_manager](https://github.com/leanflutter/window_manager) - This plugin allows Flutter desktop apps to resizing and repositioning the window.
1. [youtube_explode_dart](https://github.com/Hexer10/youtube_explode_dart) - A port in dart of the youtube explode library. Supports several API functions without the need of Youtube API Key.
1. [simple_icons](https://jlnrrg.github.io/) - The Simple Icon pack available as Flutter Icons. Provides over 1500 Free SVG icons for popular brands.
1. [audio_service_mpris](https://github.com/bdrazhzhov/audio-service-mpris) - audio_service platform interface supporting Media Player Remote Interfacing Specification.
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.
@ -280,8 +279,11 @@ 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. [catcher](https://github.com/jhomlala/catcher) - Plugin for error catching which provides multiple handlers for dealing with errors when they are not caught by the developer.
1. [fl_query](https://fl-query.vercel.app) - Asynchronous data caching, refetching & invalidation library for Flutter
1. [fl_query_hooks](https://fl-query.vercel.app) - Elite flutter_hooks compatible library for fl_query, the Asynchronous data caching, refetching & invalidation library for Flutter
1. [fl_query_devtools](https://fl-query.vercel.app) - Devtools support for Fl-Query
1. [flutter_desktop_tools](https://github.com/KRTirtho/flutter_desktop_tools) - Essential collection of tools for flutter desktop app development
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.
</details>

View File

@ -72,6 +72,28 @@ android {
signingConfig signingConfigs.release
}
}
flavorDimensions "default"
productFlavors {
nightly {
dimension "default"
resValue "string", "app_name", "Spotube Nightly"
applicationIdSuffix ".nightly"
versionNameSuffix "-nightly"
}
dev {
dimension "default"
resValue "string", "app_name", "Spotube Dev"
applicationIdSuffix ".dev"
versionNameSuffix "-dev"
}
stable {
dimension "default"
resValue "string", "app_name", "Spotube"
}
}
}
flutter {
@ -92,4 +114,4 @@ dependencies {
// other deps so just ignore
implementation 'com.android.support:multidex:2.0.1'
}
}

View File

@ -18,7 +18,7 @@
<application
android:allowBackup="false"
android:fullBackupContent="false"
android:label="Spotube"
android:label="@string/app_name"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:usesCleartextTraffic="true"

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<bitmap android:gravity="fill" android:src="@drawable/background"/>
</item>
<item>
<bitmap android:gravity="center" android:src="@drawable/splash"/>
</item>
<item>
<bitmap android:gravity="bottom" android:src="@drawable/branding"/>
</item>
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<bitmap android:gravity="fill" android:src="@drawable/background"/>
</item>
<item>
<bitmap android:gravity="center" android:src="@drawable/splash"/>
</item>
<item>
<bitmap android:gravity="bottom" android:src="@drawable/branding"/>
</item>
</layer-list>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenBackground">#000000</item>
<item name="android:windowSplashScreenBrandingImage">@drawable/android12branding</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
<item name="android:windowSplashScreenIconBackgroundColor">#000000</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
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine 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
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenBackground">#000000</item>
<item name="android:windowSplashScreenBrandingImage">@drawable/android12branding</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
<item name="android:windowSplashScreenIconBackgroundColor">#000000</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
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#242832</color>
</resources>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine 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
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

View File

@ -0,0 +1,359 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 762 762"
version="1.1"
id="svg270"
sodipodi:docname="spotube-nightly-logo.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xml:space="preserve"
inkscape:export-filename="spotube-logo.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
width="762"
height="762"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:bx="https://boxy-svg.com"><sodipodi:namedview
id="namedview272"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="0.26940768"
inkscape:cx="-413.87091"
inkscape:cy="562.34478"
inkscape:window-width="1518"
inkscape:window-height="1080"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="g236"
inkscape:lockguides="false"><inkscape:page
x="0"
y="0"
width="762"
height="762"
id="page3136" /><inkscape:page
x="640.44641"
y="132.29141"
width="89.999939"
height="89.999985"
id="page3138" /></sodipodi:namedview><defs
id="defs220"><linearGradient
id="linearGradient1211"><stop
offset="0.113"
style="stop-color:#ff4b4b;stop-opacity:1;"
id="stop1205" /><stop
offset="0.60799998"
style="stop-color:#d6a400;stop-opacity:1;"
id="stop1207" /><stop
offset="0.94400001"
style="stop-color:#ffffff;stop-opacity:1;"
id="stop1209" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient5535"><stop
style="stop-color:#3b2100;stop-opacity:1;"
offset="0.25885531"
id="stop5531" /><stop
style="stop-color:#004256;stop-opacity:1;"
offset="1"
id="stop5533" /></linearGradient><linearGradient
id="linearGradient2809"><stop
offset="0.113"
style="stop-color:#ff4b4b;stop-opacity:1;"
id="stop2803" /><stop
offset="0.60799998"
style="stop-color:#d6a400;stop-opacity:1;"
id="stop2805" /><stop
offset="0.94400001"
style="stop-color:#ffffff;stop-opacity:1;"
id="stop2807" /></linearGradient><linearGradient
id="linearGradient938"><stop
offset="0.113"
style="stop-color:#ff4b4b;stop-opacity:1;"
id="stop932" /><stop
offset="0.60799998"
style="stop-color:#d6a400;stop-opacity:1;"
id="stop934" /><stop
offset="0.94400001"
style="stop-color:#fffcf1;stop-opacity:1;"
id="stop936" /></linearGradient><radialGradient
id="gradient-2-0"
gradientUnits="userSpaceOnUse"
cx="251.179"
cy="248.821"
r="241.45"
gradientTransform="translate(-1.768285,0.589104)"
xlink:href="#gradient-2" /><linearGradient
id="gradient-2"><stop
offset="0.841"
style="stop-color: rgb(255, 255, 255);"
id="stop169" /><stop
offset="1"
style="stop-color: rgb(201, 201, 201);"
id="stop171" /></linearGradient><filter
id="drop-shadow-filter-0"
x="-0.050892502"
y="-0.050892502"
width="1.1017849"
height="1.1017849"
bx:preset="drop-shadow 1 0 0 10 0.42 rgba(201,201,201,1)"><feGaussianBlur
in="SourceAlpha"
stdDeviation="10"
id="feGaussianBlur174" /><feOffset
dx="0"
dy="0"
id="feOffset176" /><feComponentTransfer
result="offsetblur"
id="feComponentTransfer179"><feFuncA
id="spread-ctrl"
type="linear"
slope="0.84" /></feComponentTransfer><feFlood
flood-color="rgba(201,201,201,1)"
id="feFlood181" /><feComposite
in2="offsetblur"
operator="in"
id="feComposite183" /><feMerge
id="feMerge189"><feMergeNode
id="feMergeNode185" /><feMergeNode
in="SourceGraphic"
id="feMergeNode187" /></feMerge></filter><linearGradient
id="gradient-4-3"
gradientUnits="userSpaceOnUse"
x1="47.146"
y1="18.044001"
x2="47.146"
y2="75.353996"
xlink:href="#gradient-4" /><linearGradient
id="gradient-4"><stop
offset="0.113"
style="stop-color: rgb(83, 240, 111);"
id="stop193" /><stop
offset="0.608"
style="stop-color: rgb(0, 177, 86);"
id="stop195" /><stop
offset="0.944"
style="stop-color: rgb(2, 167, 156);"
id="stop197" /></linearGradient><filter
id="inner-shadow-filter-0"
x="-0.064836091"
y="-0.071329232"
width="1.1296722"
height="1.108079"
bx:preset="inner-shadow 1 0 0 4 0.5 rgba(0,0,0,0.7)"><feOffset
dx="0"
dy="0"
id="feOffset200" /><feGaussianBlur
stdDeviation="4"
id="feGaussianBlur202"
result="result1" /><feComposite
operator="out"
in="SourceGraphic"
in2="result1"
id="feComposite204" /><feComponentTransfer
result="choke"
id="feComponentTransfer208"><feFuncA
type="linear"
slope="1"
id="feFuncA206" /></feComponentTransfer><feFlood
flood-color="rgba(0,0,0,0.7)"
result="color"
id="feFlood210" /><feComposite
operator="in"
in="color"
in2="choke"
result="shadow"
id="feComposite212" /><feComposite
operator="over"
in="shadow"
in2="SourceGraphic"
id="feComposite214" /></filter><linearGradient
id="gradient-4-1"
gradientUnits="userSpaceOnUse"
x1="82.026001"
y1="144.832"
x2="82.026001"
y2="264.46201"
xlink:href="#linearGradient2809"
gradientTransform="translate(7.2213312)" /><linearGradient
id="gradient-4-2"
gradientUnits="userSpaceOnUse"
x1="143.69299"
y1="22.804001"
x2="143.69299"
y2="264.582"
xlink:href="#linearGradient938" /><linearGradient
id="gradient-4-0"
gradientUnits="userSpaceOnUse"
x1="205.862"
y1="146.28"
x2="205.862"
y2="265.91"
xlink:href="#gradient-4"
gradientTransform="translate(-7.2213312)" /><filter
style="color-interpolation-filters:sRGB"
inkscape:label="Drop Shadow"
id="filter2000"
x="-0.3425389"
y="-0.3425389"
width="1.6850778"
height="1.6850778"><feFlood
flood-opacity="1"
flood-color="rgb(0,0,0)"
result="flood"
id="feFlood1990" /><feComposite
in="flood"
in2="SourceGraphic"
operator="out"
result="composite1"
id="feComposite1992" /><feGaussianBlur
in="composite1"
stdDeviation="29.980818"
result="blur"
id="feGaussianBlur1994" /><feOffset
dx="0"
dy="0"
result="offset"
id="feOffset1996" /><feComposite
in="offset"
in2="SourceGraphic"
operator="atop"
result="fbSourceGraphic"
id="feComposite1998" /><feColorMatrix
result="fbSourceGraphicAlpha"
in="fbSourceGraphic"
values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0"
id="feColorMatrix2062" /><feFlood
id="feFlood2064"
flood-opacity="1"
flood-color="rgb(0,0,0)"
result="flood"
in="fbSourceGraphic" /><feComposite
in2="fbSourceGraphic"
id="feComposite2066"
in="flood"
operator="out"
result="composite1" /><feGaussianBlur
id="feGaussianBlur2068"
in="composite1"
stdDeviation="28.6433"
result="blur" /><feOffset
id="feOffset2070"
dx="0"
dy="0"
result="offset" /><feComposite
in2="fbSourceGraphic"
id="feComposite2072"
in="offset"
operator="atop"
result="fbSourceGraphic" /><feColorMatrix
result="fbSourceGraphicAlpha"
in="fbSourceGraphic"
values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0"
id="feColorMatrix3393" /><feFlood
id="feFlood3395"
flood-opacity="0.352941"
flood-color="rgb(0,0,0)"
result="flood"
in="fbSourceGraphic" /><feComposite
in2="fbSourceGraphic"
id="feComposite3397"
in="flood"
operator="in"
result="composite1" /><feGaussianBlur
id="feGaussianBlur3399"
in="composite1"
stdDeviation="6.59891"
result="blur" /><feOffset
id="feOffset3401"
dx="0"
dy="0"
result="offset" /><feComposite
in2="offset"
id="feComposite3403"
in="fbSourceGraphic"
operator="over"
result="composite2" /></filter><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1211"
id="linearGradient5506"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(117.34662)"
x1="82.026001"
y1="144.832"
x2="82.026001"
y2="264.46201" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient5535"
id="radialGradient5537"
cx="143.6935"
cy="143.69299"
fx="143.6935"
fy="143.69299"
r="152.72653"
gradientTransform="matrix(1,0,0,0.8506841,0,21.45565)"
gradientUnits="userSpaceOnUse" /></defs><circle
style="opacity:1;fill:#242832;fill-opacity:1;stroke:#000000;stroke-width:10;stroke-dasharray:none;stroke-opacity:0.961795;filter:url(#filter2000)"
id="path1157"
cx="381.48901"
cy="381.48901"
inkscape:label="path1157"
r="235.79112"
sodipodi:insensitive="true" /><g
transform="matrix(0.319972,0,0,0.323174,379.08153,437.03375)"
id="g228"><g
style="opacity:1;fill:none;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none"
transform="matrix(3.89,0,0,3.89,-175.05,-175.05)"
id="g226" /></g><g
id="g236"
style="fill:none;filter:url(#inner-shadow-filter-0)"
transform="matrix(1.107829,0,0,1.106267,221.95533,199.03714)"><path
d="m 78.642332,155.437 v 98.42 c 0,5.867 4.741,10.605 10.605,10.605 5.854,0 10.604995,-4.738 10.604995,-10.605 v -98.42 c 0,-5.856 -4.750995,-10.605 -10.604995,-10.605 -5.864,0 -10.605,4.744 -10.605,10.605 z"
style="fill:none;fill-opacity:1;stroke:url(#gradient-4-1);stroke-width:9.80924px;stroke-linecap:round;stroke-linejoin:round"
id="path230" /><path
d="m 29.456,264.582 h 23.351 v -116.85 c 0.064,-0.56 0.166,-1.119 0.166,-1.693 0,-50.412 40.69,-91.42 90.698,-91.42 50.002,0 90.692,41.008 90.692,91.42 0,0.771 0.113,1.518 0.228,2.263 v 116.28 h 23.354 c 16.254,0 29.442,-13.64 29.442,-30.469 v -60.936 c 0,-13.878 -8.989,-25.57 -21.261,-29.249 C 264.997,76.957 210.518,22.804 143.676,22.804 76.816,22.804 22.329,76.962 21.211,143.954 8.956,147.638 0,159.32 0,173.187 v 60.926 c 0,16.819 13.187,30.469 29.456,30.469 z"
style="fill:url(#radialGradient5537);fill-opacity:1;stroke:url(#gradient-4-2);stroke-width:18.0661;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
id="path232" /><path
d="M 49.735541,279.35822 C 23.7214,267.48486 38.122112,248.62719 80.85964,237.45225 c 14.400662,-3.49216 25.08508,-5.12184 43.66659,-4.88901 11.61348,0.23282 24.62053,3.49216 24.62053,3.49216 0,-42.13877 -0.46471,-121.7601 -0.46471,-160.872338 4.6454,0 7.89719,-0.232827 14.40071,-0.232827 0,2.328107 0,4.190613 0,6.053093 0,2.095305 0,3.259358 0.46471,4.656212 4.6454,14.66709 11.14893,20.48736 43.66659,38.41381 41.34392,23.04827 53.42195,36.78411 53.42195,55.17616 -0.46471,17.22802 -30.65954,54.01213 -37.16306,52.61528 9.29075,-13.03741 22.2978,-27.00606 25.54958,-38.64661 4.18085,-14.20147 -7.43263,-34.2232 -26.01414,-44.69971 -14.86522,-8.8468 -50.17016,-16.52957 -59.92547,-16.52957 0,0 -0.46472,84.74317 -0.46472,116.87109 0,5.35464 -9.7553,14.89989 -15.32977,18.15925 -25.54958,15.36551 -75.25519,22.34984 -97.553043,12.33896 z"
id="path3079"
style="stroke-width:3.28861" /><path
d="m 188.76763,155.437 v 98.42 c 0,5.867 4.741,10.605 10.60501,10.605 5.854,0 10.605,-4.738 10.605,-10.605 v -98.42 c 0,-5.856 -4.751,-10.605 -10.605,-10.605 -5.86401,0 -10.60501,4.744 -10.60501,10.605 z"
style="fill:none;stroke:url(#linearGradient5506);stroke-width:9.80924px;stroke-linecap:round;stroke-linejoin:round"
id="path5502" /></g><g
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
id="g240" /><g
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
id="g242" /><g
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
id="g244" /><g
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
id="g246" /><g
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
id="g248" /><g
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
id="g250" /><g
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
id="g252" /><g
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
id="g254" /><g
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
id="g256" /><g
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
id="g258" /><g
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
id="g260" /><g
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
id="g262" /><g
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
id="g264" /><g
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
id="g266" /><g
transform="matrix(0.972684,0,0,0.972684,193.06382,142.14148)"
id="g268" /></svg>

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -10,6 +10,7 @@ pkgbase = spotube-bin
depends = libsecret
depends = jsoncpp
depends = libnotify
depends = xdg-user-dirs
source = https://github.com/KRTirtho/spotube/releases/download/v2.3.0/Spotube-linux-x86_64.tar.xz
md5sums = 8cd6a7385c5c75d203dccd762f1d63ec

View File

@ -8,7 +8,7 @@ arch=(x86_64)
url="https://github.com/KRTirtho/spotube/"
license=('BSD-4-Clause')
groups=()
depends=('mpv' 'libappindicator-gtk3' 'libsecret' 'jsoncpp' 'libnotify')
depends=('mpv' 'libappindicator-gtk3' 'libsecret' 'jsoncpp' 'libnotify' 'xdg-user-dirs')
makedepends=()
checkdepends=()
optdepends=()

View File

@ -68,6 +68,7 @@ void main() async {
),
);
// ignore: avoid_print
print(
packageInfo
.map(
@ -76,6 +77,7 @@ void main() async {
)
.join('\n'),
);
// ignore: avoid_print
print(
gitPubspecs.map(
(package) {

View File

@ -35,6 +35,7 @@ void main(List<String> args) {
);
}
// ignore: avoid_print
print(
const JsonEncoder.withIndent(' ').convert(
args.isNotEmpty ? messagesWithValues[args.first] : messagesWithValues,

View File

@ -0,0 +1,5 @@
flutter_launcher_icons:
android: true
image_path: "assets/spotube-nightly-logo.png"
adaptive_icon_foreground: "assets/spotube-nightly-logo-foreground.jpg"
adaptive_icon_background: "#242832"

View File

@ -0,0 +1,9 @@
flutter_native_splash:
background_image: assets/bengali-patterns-bg.jpg
image: assets/spotube-nightly-logo.png
branding: assets/branding.png
android_12:
image: assets/spotube-nightly-logo_android12.png
branding: assets/branding.png
color: "#000000"
icon_background_color: "#000000"

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "BrandingImage.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "BrandingImage@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "BrandingImage@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "background.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

View File

@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "LaunchImage.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "LaunchImage@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "LaunchImage@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" image="LaunchBackgroundNightly" translatesAutoresizingMaskIntoConstraints="NO" id="tWc-Dq-wcI"/>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImageNightly" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"></imageView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" image="BrandingImageNightly" translatesAutoresizingMaskIntoConstraints="NO" id="Uyq-Kz-ftE"/>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="Uyq-Kz-ftE" secondAttribute="bottom" id="8Yb-q4-8bl"/>
<constraint firstItem="Uyq-Kz-ftE" firstAttribute="centerX" secondItem="YRO-k0-Ey4" secondAttribute="centerX" id="3kg-TC-cPP"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="3T2-ad-Qdv"/>
<constraint firstItem="tWc-Dq-wcI" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="RPx-PI-7Xg"/>
<constraint firstItem="tWc-Dq-wcI" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="SdS-ul-q2q"/>
<constraint firstAttribute="trailing" secondItem="tWc-Dq-wcI" secondAttribute="trailing" id="Swv-Gf-Rwn"/>
<constraint firstAttribute="trailing" secondItem="YRO-k0-Ey4" secondAttribute="trailing" id="TQA-XW-tRk"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="duK-uY-Gun"/>
<constraint firstItem="tWc-Dq-wcI" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="kV7-tw-vXt"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="xPn-NY-SIU"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImageNightly" width="509" height="509"/>
<image name="LaunchBackgroundNightly" width="1" height="1"/>
<image name="BrandingImageNightly" width="1" height="1"/>
</resources>
</document>

View File

@ -1,64 +1,64 @@
<?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>Sptube</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>
</dict>
</plist>
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Sptube</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>
</dict>
</plist>

View File

@ -36,6 +36,8 @@ class Assets {
static const AssetGenImage emptyBox = AssetGenImage('assets/empty_box.png');
static const AssetGenImage placeholder =
AssetGenImage('assets/placeholder.png');
static const AssetGenImage spotubeHeroBanner =
AssetGenImage('assets/spotube-hero-banner.png');
static const AssetGenImage spotubeLogoForeground =
AssetGenImage('assets/spotube-logo-foreground.jpg');
static const String spotubeLogoIco = 'assets/spotube-logo.ico';
@ -44,8 +46,21 @@ class Assets {
static const String spotubeLogoSvg = 'assets/spotube-logo.svg';
static const AssetGenImage spotubeLogoAndroid12 =
AssetGenImage('assets/spotube-logo_android12.png');
static const AssetGenImage spotubeNightlyLogoForeground =
AssetGenImage('assets/spotube-nightly-logo-foreground.jpg');
static const AssetGenImage spotubeNightlyLogoPng =
AssetGenImage('assets/spotube-nightly-logo.png');
static const String spotubeNightlyLogoSvg = 'assets/spotube-nightly-logo.svg';
static const AssetGenImage spotubeNightlyLogoAndroid12 =
AssetGenImage('assets/spotube-nightly-logo_android12.png');
static const AssetGenImage spotubeScreenshot =
AssetGenImage('assets/spotube-screenshot.png');
static const AssetGenImage spotubeTallCapsule =
AssetGenImage('assets/spotube-tall-capsule.png');
static const AssetGenImage spotubeWideCapsuleLarge =
AssetGenImage('assets/spotube-wide-capsule-large.png');
static const AssetGenImage spotubeWideCapsuleSmall =
AssetGenImage('assets/spotube-wide-capsule-small.png');
static const AssetGenImage spotubeBanner =
AssetGenImage('assets/spotube_banner.png');
static const AssetGenImage success = AssetGenImage('assets/success.png');
@ -60,12 +75,20 @@ class Assets {
branding,
emptyBox,
placeholder,
spotubeHeroBanner,
spotubeLogoForeground,
spotubeLogoIco,
spotubeLogoPng,
spotubeLogoSvg,
spotubeLogoAndroid12,
spotubeNightlyLogoForeground,
spotubeNightlyLogoPng,
spotubeNightlyLogoSvg,
spotubeNightlyLogoAndroid12,
spotubeScreenshot,
spotubeTallCapsule,
spotubeWideCapsuleLarge,
spotubeWideCapsuleSmall,
spotubeBanner,
success,
userPlaceholder

View File

@ -13,6 +13,12 @@ abstract class Env {
@EnviedField(varName: 'SPOTIFY_SECRETS')
static final String rawSpotifySecrets = _Env.rawSpotifySecrets;
@EnviedField(varName: 'LASTFM_API_KEY')
static final String lastFmApiKey = _Env.lastFmApiKey;
@EnviedField(varName: 'LASTFM_API_SECRET')
static final String lastFmApiSecret = _Env.lastFmApiSecret;
static final spotifySecrets = rawSpotifySecrets.split(',').map((e) {
final secrets = e.trim().split(":").map((e) => e.trim());
return {

View File

@ -1,5 +1,6 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:spotube/components/player/player_controls.dart';
@ -8,7 +9,6 @@ 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';
import 'package:spotube/utils/platform.dart';
import 'package:window_manager/window_manager.dart';
class PlayPauseIntent extends Intent {
final WidgetRef ref;
@ -115,7 +115,7 @@ class CloseAppAction extends Action<CloseAppIntent> {
@override
invoke(intent) {
if (kIsDesktop) {
windowManager.close();
DesktopTools.window.close();
} else {
SystemNavigator.pop();
}

View File

@ -36,10 +36,10 @@ abstract class LanguageLocals {
// name: "Amharic",
// nativeName: "አማርኛ",
// ),
// "ar": const ISOLanguageName(
// name: "Arabic",
// nativeName: "العربية",
// ),
"ar": const ISOLanguageName(
name: "Arabic",
nativeName: "العربية",
),
// "an": const ISOLanguageName(
// name: "Aragonese",
// nativeName: "Aragonés",
@ -508,10 +508,10 @@ abstract class LanguageLocals {
// name: "Pāli",
// nativeName: "पाऴि",
// ),
// "fa": const ISOLanguageName(
// name: "Persian",
// nativeName: "فارسی",
// ),
"fa": const ISOLanguageName(
name: "Persian",
nativeName: "فارسی",
),
"pl": const ISOLanguageName(
name: "Polish",
nativeName: "polski",
@ -684,10 +684,10 @@ abstract class LanguageLocals {
// name: "Uighur, Uyghur",
// nativeName: "Uyƣurqə, ئۇيغۇرچە‎",
// ),
// "uk": const ISOLanguageName(
// name: "Ukrainian",
// nativeName: "українська",
// ),
"uk": const ISOLanguageName(
name: "Ukrainian",
nativeName: "українська",
),
// "ur": const ISOLanguageName(
// name: "Urdu",
// nativeName: "اردو",

View File

@ -1,9 +1,10 @@
import 'package:catcher/catcher.dart';
import 'package:catcher_2/catcher_2.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:go_router/go_router.dart';
import 'package:spotify/spotify.dart' hide Search;
import 'package:spotube/pages/home/home.dart';
import 'package:spotube/pages/lastfm_login/lastfm_login.dart';
import 'package:spotube/pages/library/playlist_generate/playlist_generate.dart';
import 'package:spotube/pages/lyrics/mini_lyrics.dart';
import 'package:spotube/pages/search/search.dart';
@ -18,7 +19,6 @@ import 'package:spotube/pages/library/library.dart';
import 'package:spotube/pages/desktop_login/login_tutorial.dart';
import 'package:spotube/pages/desktop_login/desktop_login.dart';
import 'package:spotube/pages/lyrics/lyrics.dart';
import 'package:spotube/pages/player/player.dart';
import 'package:spotube/pages/playlist/playlist.dart';
import 'package:spotube/pages/root/root_app.dart';
import 'package:spotube/pages/settings/settings.dart';
@ -26,7 +26,7 @@ import 'package:spotube/pages/mobile_login/mobile_login.dart';
import '../pages/library/playlist_generate/playlist_generate_result.dart';
final rootNavigatorKey = Catcher.navigatorKey;
final rootNavigatorKey = Catcher2.navigatorKey;
final shellRouteNavigatorKey = GlobalKey<NavigatorState>();
final router = GoRouter(
navigatorKey: rootNavigatorKey,
@ -147,13 +147,10 @@ final router = GoRouter(
),
),
GoRoute(
path: "/player",
path: "/lastfm-login",
parentNavigatorKey: rootNavigatorKey,
pageBuilder: (context, state) {
return const SpotubePage(
child: PlayerView(),
);
},
pageBuilder: (context, state) =>
const SpotubePage(child: LastFMLoginPage()),
),
],
);

View File

@ -1,187 +1,189 @@
// Country Codes contributed by momobobe <https://github.com/momobobe>
import 'package:spotify/spotify.dart';
final spotifyMarkets = [
("AL", "Albania (AL)"),
("DZ", "Algeria (DZ)"),
("AD", "Andorra (AD)"),
("AO", "Angola (AO)"),
("AG", "Antigua and Barbuda (AG)"),
("AR", "Argentina (AR)"),
("AM", "Armenia (AM)"),
("AU", "Australia (AU)"),
("AT", "Austria (AT)"),
("AZ", "Azerbaijan (AZ)"),
("BH", "Bahrain (BH)"),
("BD", "Bangladesh (BD)"),
("BB", "Barbados (BB)"),
("BY", "Belarus (BY)"),
("BE", "Belgium (BE)"),
("BZ", "Belize (BZ)"),
("BJ", "Benin (BJ)"),
("BT", "Bhutan (BT)"),
("BO", "Bolivia (BO)"),
("BA", "Bosnia and Herzegovina (BA)"),
("BW", "Botswana (BW)"),
("BR", "Brazil (BR)"),
("BN", "Brunei Darussalam (BN)"),
("BG", "Bulgaria (BG)"),
("BF", "Burkina Faso (BF)"),
("BI", "Burundi (BI)"),
("CV", "Cabo Verde / Cape Verde (CV)"),
("KH", "Cambodia (KH)"),
("CM", "Cameroon (CM)"),
("CA", "Canada (CA)"),
("TD", "Chad (TD)"),
("CL", "Chile (CL)"),
("CO", "Colombia (CO)"),
("KM", "Comoros (KM)"),
("CR", "Costa Rica (CR)"),
("HR", "Croatia (HR)"),
("CW", "Curaçao (CW)"),
("CY", "Cyprus (CY)"),
("CZ", "Czech Republic (CZ)"),
("CI", "Ivory Coast (CI)"),
("CD", "Congo (CD)"),
("DK", "Denmark (DK)"),
("DJ", "Djibouti (DJ)"),
("DM", "Dominica (DM)"),
("DO", "Dominican Republic (DO)"),
("EC", "Ecuador (EC)"),
("EG", "Egypt (EG)"),
("SV", "El Salvador (SV)"),
("GQ", "Equatorial Guinea (GQ)"),
("EE", "Estonia (EE)"),
("SZ", "Eswatini (SZ)"),
("FJ", "Fiji (FJ)"),
("FI", "Finland (FI)"),
("FR", "France (FR)"),
("GA", "Gabon (GA)"),
("GE", "Georgia (GE)"),
("DE", "Germany (DE)"),
("GH", "Ghana (GH)"),
("GR", "Greece (GR)"),
("GD", "Grenada (GD)"),
("GT", "Guatemala (GT)"),
("GN", "Guinea (GN)"),
("GW", "Guinea-Bissau (GW)"),
("GY", "Guyana (GY)"),
("HT", "Haiti (HT)"),
("HN", "Honduras (HN)"),
("HK", "Hong Kong (HK)"),
("HU", "Hungary (HU)"),
("IS", "Iceland (IS)"),
("IN", "India (IN)"),
("ID", "Indonesia (ID)"),
("IQ", "Iraq (IQ)"),
("IE", "Ireland (IE)"),
("IL", "Israel (IL)"),
("IT", "Italy (IT)"),
("JM", "Jamaica (JM)"),
("JP", "Japan (JP)"),
("JO", "Jordan (JO)"),
("KZ", "Kazakhstan (KZ)"),
("KE", "Kenya (KE)"),
("KI", "Kiribati (KI)"),
("XK", "Kosovo (XK)"),
("KW", "Kuwait (KW)"),
("KG", "Kyrgyzstan (KG)"),
("LA", "Laos (LA)"),
("LV", "Latvia (LV)"),
("LB", "Lebanon (LB)"),
("LS", "Lesotho (LS)"),
("LR", "Liberia (LR)"),
("LY", "Libya (LY)"),
("LI", "Liechtenstein (LI)"),
("LT", "Lithuania (LT)"),
("LU", "Luxembourg (LU)"),
("MO", "Macao / Macau (MO)"),
("MG", "Madagascar (MG)"),
("MW", "Malawi (MW)"),
("MY", "Malaysia (MY)"),
("MV", "Maldives (MV)"),
("ML", "Mali (ML)"),
("MT", "Malta (MT)"),
("MH", "Marshall Islands (MH)"),
("MR", "Mauritania (MR)"),
("MU", "Mauritius (MU)"),
("MX", "Mexico (MX)"),
("FM", "Micronesia (FM)"),
("MD", "Moldova (MD)"),
("MC", "Monaco (MC)"),
("MN", "Mongolia (MN)"),
("ME", "Montenegro (ME)"),
("MA", "Morocco (MA)"),
("MZ", "Mozambique (MZ)"),
("NA", "Namibia (NA)"),
("NR", "Nauru (NR)"),
("NP", "Nepal (NP)"),
("NL", "Netherlands (NL)"),
("NZ", "New Zealand (NZ)"),
("NI", "Nicaragua (NI)"),
("NE", "Niger (NE)"),
("NG", "Nigeria (NG)"),
("MK", "North Macedonia (MK)"),
("NO", "Norway (NO)"),
("OM", "Oman (OM)"),
("PK", "Pakistan (PK)"),
("PW", "Palau (PW)"),
("PS", "Palestine (PS)"),
("PA", "Panama (PA)"),
("PG", "Papua New Guinea (PG)"),
("PY", "Paraguay (PY)"),
("PE", "Peru (PE)"),
("PH", "Philippines (PH)"),
("PL", "Poland (PL)"),
("PT", "Portugal (PT)"),
("QA", "Qatar (QA)"),
("CG", "Congo (CG)"),
("RO", "Romania (RO)"),
("RU", "Russia (RU)"),
("RW", "Rwanda (RW)"),
("WS", "Samoa (WS)"),
("SM", "San Marino (SM)"),
("SA", "Saudi Arabia (SA)"),
("SN", "Senegal (SN)"),
("RS", "Serbia (RS)"),
("SC", "Seychelles (SC)"),
("SL", "Sierra Leone (SL)"),
("SG", "Singapore (SG)"),
("SK", "Slovakia (SK)"),
("SI", "Slovenia (SI)"),
("SB", "Solomon Islands (SB)"),
("ZA", "South Africa (ZA)"),
("KR", "South Korea (KR)"),
("ES", "Spain (ES)"),
("LK", "Sri Lanka (LK)"),
("KN", "St. Kitts and Nevis (KN)"),
("LC", "St. Lucia (LC)"),
("SR", "Suriname (SR)"),
("SE", "Sweden (SE)"),
("CH", "Switzerland (CH)"),
("ST", "São Tomé and Príncipe (ST)"),
("TW", "Taiwan (TW)"),
("TJ", "Tajikistan (TJ)"),
("TZ", "Tanzania (TZ)"),
("TH", "Thailand (TH)"),
("BS", "The Bahamas (BS)"),
("GM", "The Gambia (GM)"),
("TL", "East Timor (TL)"),
("TG", "Togo (TG)"),
("TO", "Tonga (TO)"),
("TT", "Trinidad and Tobago (TT)"),
("TN", "Tunisia (TN)"),
("TR", "Turkey (TR)"),
("TV", "Tuvalu (TV)"),
("UG", "Uganda (UG)"),
("UA", "Ukraine (UA)"),
("AE", "United Arab Emirates (AE)"),
("GB", "United Kingdom (GB)"),
("US", "United States (US)"),
("UY", "Uruguay (UY)"),
("UZ", "Uzbekistan (UZ)"),
("VU", "Vanuatu (VU)"),
("VE", "Venezuela (VE)"),
("VN", "Vietnam (VN)"),
("ZM", "Zambia (ZM)"),
("ZW", "Zimbabwe (ZW)"),
(Market.AL, "Albania (AL)"),
(Market.DZ, "Algeria (DZ)"),
(Market.AD, "Andorra (AD)"),
(Market.AO, "Angola (AO)"),
(Market.AG, "Antigua and Barbuda (AG)"),
(Market.AR, "Argentina (AR)"),
(Market.AM, "Armenia (AM)"),
(Market.AU, "Australia (AU)"),
(Market.AT, "Austria (AT)"),
(Market.AZ, "Azerbaijan (AZ)"),
(Market.BH, "Bahrain (BH)"),
(Market.BD, "Bangladesh (BD)"),
(Market.BB, "Barbados (BB)"),
(Market.BY, "Belarus (BY)"),
(Market.BE, "Belgium (BE)"),
(Market.BZ, "Belize (BZ)"),
(Market.BJ, "Benin (BJ)"),
(Market.BT, "Bhutan (BT)"),
(Market.BO, "Bolivia (BO)"),
(Market.BA, "Bosnia and Herzegovina (BA)"),
(Market.BW, "Botswana (BW)"),
(Market.BR, "Brazil (BR)"),
(Market.BN, "Brunei Darussalam (BN)"),
(Market.BG, "Bulgaria (BG)"),
(Market.BF, "Burkina Faso (BF)"),
(Market.BI, "Burundi (BI)"),
(Market.CV, "Cabo Verde / Cape Verde (CV)"),
(Market.KH, "Cambodia (KH)"),
(Market.CM, "Cameroon (CM)"),
(Market.CA, "Canada (CA)"),
(Market.TD, "Chad (TD)"),
(Market.CL, "Chile (CL)"),
(Market.CO, "Colombia (CO)"),
(Market.KM, "Comoros (KM)"),
(Market.CR, "Costa Rica (CR)"),
(Market.HR, "Croatia (HR)"),
(Market.CW, "Curaçao (CW)"),
(Market.CY, "Cyprus (CY)"),
(Market.CZ, "Czech Republic (CZ)"),
(Market.CI, "Ivory Coast (CI)"),
(Market.CD, "Congo (CD)"),
(Market.DK, "Denmark (DK)"),
(Market.DJ, "Djibouti (DJ)"),
(Market.DM, "Dominica (DM)"),
(Market.DO, "Dominican Republic (DO)"),
(Market.EC, "Ecuador (EC)"),
(Market.EG, "Egypt (EG)"),
(Market.SV, "El Salvador (SV)"),
(Market.GQ, "Equatorial Guinea (GQ)"),
(Market.EE, "Estonia (EE)"),
(Market.SZ, "Eswatini (SZ)"),
(Market.FJ, "Fiji (FJ)"),
(Market.FI, "Finland (FI)"),
(Market.FR, "France (FR)"),
(Market.GA, "Gabon (GA)"),
(Market.GE, "Georgia (GE)"),
(Market.DE, "Germany (DE)"),
(Market.GH, "Ghana (GH)"),
(Market.GR, "Greece (GR)"),
(Market.GD, "Grenada (GD)"),
(Market.GT, "Guatemala (GT)"),
(Market.GN, "Guinea (GN)"),
(Market.GW, "Guinea-Bissau (GW)"),
(Market.GY, "Guyana (GY)"),
(Market.HT, "Haiti (HT)"),
(Market.HN, "Honduras (HN)"),
(Market.HK, "Hong Kong (HK)"),
(Market.HU, "Hungary (HU)"),
(Market.IS, "Iceland (IS)"),
(Market.IN, "India (IN)"),
(Market.ID, "Indonesia (ID)"),
(Market.IQ, "Iraq (IQ)"),
(Market.IE, "Ireland (IE)"),
(Market.IL, "Israel (IL)"),
(Market.IT, "Italy (IT)"),
(Market.JM, "Jamaica (JM)"),
(Market.JP, "Japan (JP)"),
(Market.JO, "Jordan (JO)"),
(Market.KZ, "Kazakhstan (KZ)"),
(Market.KE, "Kenya (KE)"),
(Market.KI, "Kiribati (KI)"),
(Market.XK, "Kosovo (XK)"),
(Market.KW, "Kuwait (KW)"),
(Market.KG, "Kyrgyzstan (KG)"),
(Market.LA, "Laos (LA)"),
(Market.LV, "Latvia (LV)"),
(Market.LB, "Lebanon (LB)"),
(Market.LS, "Lesotho (LS)"),
(Market.LR, "Liberia (LR)"),
(Market.LY, "Libya (LY)"),
(Market.LI, "Liechtenstein (LI)"),
(Market.LT, "Lithuania (LT)"),
(Market.LU, "Luxembourg (LU)"),
(Market.MO, "Macao / Macau (MO)"),
(Market.MG, "Madagascar (MG)"),
(Market.MW, "Malawi (MW)"),
(Market.MY, "Malaysia (MY)"),
(Market.MV, "Maldives (MV)"),
(Market.ML, "Mali (ML)"),
(Market.MT, "Malta (MT)"),
(Market.MH, "Marshall Islands (MH)"),
(Market.MR, "Mauritania (MR)"),
(Market.MU, "Mauritius (MU)"),
(Market.MX, "Mexico (MX)"),
(Market.FM, "Micronesia (FM)"),
(Market.MD, "Moldova (MD)"),
(Market.MC, "Monaco (MC)"),
(Market.MN, "Mongolia (MN)"),
(Market.ME, "Montenegro (ME)"),
(Market.MA, "Morocco (MA)"),
(Market.MZ, "Mozambique (MZ)"),
(Market.NA, "Namibia (NA)"),
(Market.NR, "Nauru (NR)"),
(Market.NP, "Nepal (NP)"),
(Market.NL, "Netherlands (NL)"),
(Market.NZ, "New Zealand (NZ)"),
(Market.NI, "Nicaragua (NI)"),
(Market.NE, "Niger (NE)"),
(Market.NG, "Nigeria (NG)"),
(Market.MK, "North Macedonia (MK)"),
(Market.NO, "Norway (NO)"),
(Market.OM, "Oman (OM)"),
(Market.PK, "Pakistan (PK)"),
(Market.PW, "Palau (PW)"),
(Market.PS, "Palestine (PS)"),
(Market.PA, "Panama (PA)"),
(Market.PG, "Papua New Guinea (PG)"),
(Market.PY, "Paraguay (PY)"),
(Market.PE, "Peru (PE)"),
(Market.PH, "Philippines (PH)"),
(Market.PL, "Poland (PL)"),
(Market.PT, "Portugal (PT)"),
(Market.QA, "Qatar (QA)"),
(Market.CG, "Congo (CG)"),
(Market.RO, "Romania (RO)"),
(Market.RU, "Russia (RU)"),
(Market.RW, "Rwanda (RW)"),
(Market.WS, "Samoa (WS)"),
(Market.SM, "San Marino (SM)"),
(Market.SA, "Saudi Arabia (SA)"),
(Market.SN, "Senegal (SN)"),
(Market.RS, "Serbia (RS)"),
(Market.SC, "Seychelles (SC)"),
(Market.SL, "Sierra Leone (SL)"),
(Market.SG, "Singapore (SG)"),
(Market.SK, "Slovakia (SK)"),
(Market.SI, "Slovenia (SI)"),
(Market.SB, "Solomon Islands (SB)"),
(Market.ZA, "South Africa (ZA)"),
(Market.KR, "South Korea (KR)"),
(Market.ES, "Spain (ES)"),
(Market.LK, "Sri Lanka (LK)"),
(Market.KN, "St. Kitts and Nevis (KN)"),
(Market.LC, "St. Lucia (LC)"),
(Market.SR, "Suriname (SR)"),
(Market.SE, "Sweden (SE)"),
(Market.CH, "Switzerland (CH)"),
(Market.ST, "São Tomé and Príncipe (ST)"),
(Market.TW, "Taiwan (TW)"),
(Market.TJ, "Tajikistan (TJ)"),
(Market.TZ, "Tanzania (TZ)"),
(Market.TH, "Thailand (TH)"),
(Market.BS, "The Bahamas (BS)"),
(Market.GM, "The Gambia (GM)"),
(Market.TL, "East Timor (TL)"),
(Market.TG, "Togo (TG)"),
(Market.TO, "Tonga (TO)"),
(Market.TT, "Trinidad and Tobago (TT)"),
(Market.TN, "Tunisia (TN)"),
(Market.TR, "Turkey (TR)"),
(Market.TV, "Tuvalu (TV)"),
(Market.UG, "Uganda (UG)"),
(Market.UA, "Ukraine (UA)"),
(Market.AE, "United Arab Emirates (AE)"),
(Market.GB, "United Kingdom (GB)"),
(Market.US, "United States (US)"),
(Market.UY, "Uruguay (UY)"),
(Market.UZ, "Uzbekistan (UZ)"),
(Market.VU, "Vanuatu (VU)"),
(Market.VE, "Venezuela (VE)"),
(Market.VN, "Vietnam (VN)"),
(Market.ZM, "Zambia (ZM)"),
(Market.ZW, "Zimbabwe (ZW)"),
];

View File

@ -1,6 +1,7 @@
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:simple_icons/simple_icons.dart';
abstract class SpotubeIcons {
static const home = FluentIcons.home_12_regular;
@ -97,4 +98,12 @@ abstract class SpotubeIcons {
static const user = FeatherIcons.user;
static const edit = FeatherIcons.edit;
static const web = FeatherIcons.globe;
static const amoled = FeatherIcons.sunset;
static const file = FeatherIcons.file;
static const stream = Icons.stream_rounded;
static const lastFm = SimpleIcons.lastdotfm;
static const spotify = SimpleIcons.spotify;
static const eye = FeatherIcons.eye;
static const noEye = FeatherIcons.eyeOff;
static const normalize = FeatherIcons.barChart2;
}

View File

@ -11,24 +11,7 @@ import 'package:spotube/services/audio_player/audio_player.dart';
import 'package:spotube/utils/service_utils.dart';
import 'package:spotube/utils/type_conversion_utils.dart';
enum AlbumType {
album,
single,
compilation;
factory AlbumType.from(String? type) {
switch (type) {
case "album":
return AlbumType.album;
case "single":
return AlbumType.single;
case "compilation":
return AlbumType.compilation;
default:
return AlbumType.album;
}
}
extension FormattedAlbumType on AlbumType {
String get formatted => name.replaceFirst(name[0], name[0].toUpperCase());
}
@ -71,7 +54,7 @@ class AlbumCard extends HookConsumerWidget {
isLoading: isPlaylistPlaying && playlist.isFetching == true,
title: album.name!,
description:
"${AlbumType.from(album.albumType!).formatted}${TypeConversionUtils.artists_X_String<ArtistSimple>(album.artists ?? [])}",
"${album.albumType?.formatted}${TypeConversionUtils.artists_X_String<ArtistSimple>(album.artists ?? [])}",
onTap: () {
ServiceUtils.push(context, "/album/${album.id}", extra: album);
},

View File

@ -20,6 +20,8 @@ class TokenLoginForm extends HookConsumerWidget {
final keyCodeController = useTextEditingController();
final mounted = useIsMounted();
final isLoading = useState(false);
return ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 400,
@ -45,27 +47,35 @@ class TokenLoginForm extends HookConsumerWidget {
),
const SizedBox(height: 20),
FilledButton(
onPressed: () async {
if (keyCodeController.text.isEmpty ||
directCodeController.text.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(context.l10n.fill_in_all_fields),
behavior: SnackBarBehavior.floating,
),
);
return;
}
final cookieHeader =
"sp_dc=${directCodeController.text}; sp_key=${keyCodeController.text}";
onPressed: isLoading.value
? null
: () async {
try {
isLoading.value = true;
if (keyCodeController.text.isEmpty ||
directCodeController.text.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(context.l10n.fill_in_all_fields),
behavior: SnackBarBehavior.floating,
),
);
return;
}
final cookieHeader =
"sp_dc=${directCodeController.text}; sp_key=${keyCodeController.text}";
authenticationNotifier.setCredentials(
await AuthenticationCredentials.fromCookie(cookieHeader),
);
if (mounted()) {
onDone?.call();
}
},
authenticationNotifier.setCredentials(
await AuthenticationCredentials.fromCookie(
cookieHeader),
);
if (mounted()) {
onDone?.call();
}
} finally {
isLoading.value = false;
}
},
child: Text(context.l10n.submit),
)
],

View File

@ -7,6 +7,7 @@ import 'package:spotify/spotify.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/album/album_card.dart';
import 'package:spotube/components/shared/inter_scrollbar/inter_scrollbar.dart';
import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart';
import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart';
import 'package:spotube/components/shared/waypoint.dart';
@ -70,39 +71,42 @@ class UserAlbums extends HookConsumerWidget {
child: SearchBar(
onChanged: (value) => searchText.value = value,
leading: const Icon(SpotubeIcons.filter),
hintText: context.l10n.filter_artist,
hintText: context.l10n.filter_albums,
),
),
),
),
body: SizedBox.expand(
child: SingleChildScrollView(
padding: const EdgeInsets.all(8.0),
child: InterScrollbar(
controller: controller,
child: Wrap(
runSpacing: 20,
alignment: WrapAlignment.center,
runAlignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
if (albums.isEmpty)
Container(
alignment: Alignment.topLeft,
padding: const EdgeInsets.all(16.0),
child: const ShimmerPlaybuttonCard(count: 4),
),
for (final album in albums)
AlbumCard(
TypeConversionUtils.simpleAlbum_X_Album(album),
),
if (albumsQuery.hasNextPage)
Waypoint(
controller: controller,
isGrid: true,
onTouchEdge: albumsQuery.fetchNext,
child: const ShimmerPlaybuttonCard(count: 1),
)
],
child: SingleChildScrollView(
padding: const EdgeInsets.all(8.0),
controller: controller,
child: Wrap(
runSpacing: 20,
alignment: WrapAlignment.center,
runAlignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
if (albums.isEmpty)
Container(
alignment: Alignment.topLeft,
padding: const EdgeInsets.all(16.0),
child: const ShimmerPlaybuttonCard(count: 4),
),
for (final album in albums)
AlbumCard(
TypeConversionUtils.simpleAlbum_X_Album(album),
),
if (albumsQuery.hasNextPage)
Waypoint(
controller: controller,
isGrid: true,
onTouchEdge: albumsQuery.fetchNext,
child: const ShimmerPlaybuttonCard(count: 1),
)
],
),
),
),
),

View File

@ -7,6 +7,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart';
import 'package:spotube/components/artist/artist_card.dart';
import 'package:spotube/components/shared/inter_scrollbar/inter_scrollbar.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/authentication_provider.dart';
import 'package:spotube/services/queries/queries.dart';
@ -78,18 +79,21 @@ class UserArtists extends HookConsumerWidget {
onRefresh: () async {
await artistQuery.refresh();
},
child: SingleChildScrollView(
child: InterScrollbar(
controller: controller,
child: SizedBox(
width: double.infinity,
child: SafeArea(
child: Center(
child: Wrap(
spacing: 15,
runSpacing: 5,
children: filteredArtists
.mapIndexed((index, artist) => ArtistCard(artist))
.toList(),
child: SingleChildScrollView(
controller: controller,
child: SizedBox(
width: double.infinity,
child: SafeArea(
child: Center(
child: Wrap(
spacing: 15,
runSpacing: 5,
children: filteredArtists
.mapIndexed((index, artist) => ArtistCard(artist))
.toList(),
),
),
),
),

View File

@ -1,6 +1,6 @@
import 'dart:io';
import 'package:catcher/catcher.dart';
import 'package:catcher_2/catcher_2.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@ -17,6 +17,7 @@ import 'package:permission_handler/permission_handler.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/shared/expandable_search/expandable_search.dart';
import 'package:spotube/components/shared/inter_scrollbar/inter_scrollbar.dart';
import 'package:spotube/components/shared/shimmers/shimmer_track_tile.dart';
import 'package:spotube/components/shared/sort_tracks_dropdown.dart';
import 'package:spotube/components/shared/track_table/track_tile.dart';
@ -28,7 +29,8 @@ import 'package:spotube/provider/user_preferences_provider.dart';
import 'package:spotube/utils/platform.dart';
import 'package:spotube/utils/service_utils.dart';
import 'package:spotube/utils/type_conversion_utils.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge.dart' show FfiException;
import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'
show FfiException;
const supportedAudioTypes = [
"audio/webm",
@ -76,14 +78,14 @@ final localTracksProvider = FutureProvider<List<LocalTrack>>((ref) async {
final mimetype = lookupMimeType(file.path);
return mimetype != null && supportedAudioTypes.contains(mimetype);
}).map(
(f) async {
(file) async {
try {
final metadata = await MetadataGod.readMetadata(file: f.path);
final metadata = await MetadataGod.readMetadata(file: file.path);
final imageFile = File(join(
(await getTemporaryDirectory()).path,
"spotube",
basenameWithoutExtension(f.path) +
basenameWithoutExtension(file.path) +
imgMimeToExt[metadata.picture?.mimeType ?? "image/jpeg"]!,
));
if (!await imageFile.exists() && metadata.picture != null) {
@ -94,12 +96,12 @@ final localTracksProvider = FutureProvider<List<LocalTrack>>((ref) async {
);
}
return {"metadata": metadata, "file": f, "art": imageFile.path};
return {"metadata": metadata, "file": file, "art": imageFile.path};
} catch (e, stack) {
if (e is FfiException) {
return {"file": f};
return {"file": file};
}
Catcher.reportCheckedError(e, stack);
Catcher2.reportCheckedError(e, stack);
return {};
}
},
@ -123,7 +125,7 @@ final localTracksProvider = FutureProvider<List<LocalTrack>>((ref) async {
return tracks;
} catch (e, stack) {
Catcher.reportCheckedError(e, stack);
Catcher2.reportCheckedError(e, stack);
return [];
}
});
@ -286,24 +288,26 @@ class UserLocalTracks extends HookConsumerWidget {
onRefresh: () async {
ref.refresh(localTracksProvider);
},
child: ListView.builder(
physics: const AlwaysScrollableScrollPhysics(),
itemCount: filteredTracks.length,
itemBuilder: (context, index) {
final track = filteredTracks[index];
return TrackTile(
index: index,
track: track,
userPlaylist: false,
onTap: () async {
await playLocalTracks(
ref,
sortedTracks,
currentTrack: track,
);
},
);
},
child: InterScrollbar(
child: ListView.builder(
physics: const AlwaysScrollableScrollPhysics(),
itemCount: filteredTracks.length,
itemBuilder: (context, index) {
final track = filteredTracks[index];
return TrackTile(
index: index,
track: track,
userPlaylist: false,
onTap: () async {
await playLocalTracks(
ref,
sortedTracks,
currentTrack: track,
);
},
);
},
),
),
),
);

View File

@ -8,6 +8,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/playlist/playlist_create_dialog.dart';
import 'package:spotube/components/shared/inter_scrollbar/inter_scrollbar.dart';
import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart';
import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart';
import 'package:spotube/components/playlist/playlist_card.dart';
@ -79,59 +80,63 @@ class UserPlaylists extends HookConsumerWidget {
return RefreshIndicator(
onRefresh: playlistsQuery.refresh,
child: SingleChildScrollView(
child: InterScrollbar(
controller: controller,
physics: const AlwaysScrollableScrollPhysics(),
child: Waypoint(
child: SingleChildScrollView(
controller: controller,
onTouchEdge: () {
if (playlistsQuery.hasNextPage) {
playlistsQuery.fetchNext();
}
},
child: SafeArea(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(10),
child: SearchBar(
onChanged: (value) => searchText.value = value,
hintText: context.l10n.filter_playlists,
leading: const Icon(SpotubeIcons.filter),
physics: const AlwaysScrollableScrollPhysics(),
child: Waypoint(
controller: controller,
onTouchEdge: () {
if (playlistsQuery.hasNextPage) {
playlistsQuery.fetchNext();
}
},
child: SafeArea(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(10),
child: SearchBar(
onChanged: (value) => searchText.value = value,
hintText: context.l10n.filter_playlists,
leading: const Icon(SpotubeIcons.filter),
),
),
),
AnimatedCrossFade(
duration: const Duration(milliseconds: 300),
crossFadeState: playlistsQuery.isLoadingPage ||
!playlistsQuery.hasPageData
? CrossFadeState.showFirst
: CrossFadeState.showSecond,
firstChild:
const Center(child: ShimmerPlaybuttonCard(count: 7)),
secondChild: Wrap(
runSpacing: 10,
alignment: WrapAlignment.center,
children: [
Row(
children: [
const SizedBox(width: 10),
const PlaylistCreateDialogButton(),
const SizedBox(width: 10),
ElevatedButton.icon(
icon: const Icon(SpotubeIcons.magic),
label: Text(context.l10n.generate_playlist),
onPressed: () {
GoRouter.of(context).push("/library/generate");
},
),
const SizedBox(width: 10),
],
),
...playlists.map((playlist) => PlaylistCard(playlist))
],
AnimatedCrossFade(
duration: const Duration(milliseconds: 300),
crossFadeState: !playlistsQuery.hasPageData &&
!playlistsQuery.hasPageError &&
!playlistsQuery.isLoadingNextPage
? CrossFadeState.showFirst
: CrossFadeState.showSecond,
firstChild:
const Center(child: ShimmerPlaybuttonCard(count: 7)),
secondChild: Wrap(
runSpacing: 10,
alignment: WrapAlignment.center,
children: [
Row(
children: [
const SizedBox(width: 10),
const PlaylistCreateDialogButton(),
const SizedBox(width: 10),
ElevatedButton.icon(
icon: const Icon(SpotubeIcons.magic),
label: Text(context.l10n.generate_playlist),
onPressed: () {
GoRouter.of(context).push("/library/generate");
},
),
const SizedBox(width: 10),
],
),
...playlists.map((playlist) => PlaylistCard(playlist))
],
),
),
),
],
],
),
),
),
),

View File

@ -15,6 +15,7 @@ import 'package:spotube/components/shared/animated_gradient.dart';
import 'package:spotube/components/shared/dialogs/track_details_dialog.dart';
import 'package:spotube/components/shared/page_window_title_bar.dart';
import 'package:spotube/components/shared/image/universal_image.dart';
import 'package:spotube/components/shared/panels/sliding_up_panel.dart';
import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/hooks/use_custom_status_bar_color.dart';
@ -26,8 +27,10 @@ import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
import 'package:spotube/utils/type_conversion_utils.dart';
class PlayerView extends HookConsumerWidget {
final PanelController panelController;
const PlayerView({
Key? key,
required this.panelController,
}) : super(key: key);
@override
@ -45,7 +48,7 @@ class PlayerView extends HookConsumerWidget {
useEffect(() {
if (mediaQuery.lgAndUp) {
WidgetsBinding.instance.addPostFrameCallback((_) {
GoRouter.of(context).pop();
panelController.close();
});
}
return null;
@ -60,64 +63,96 @@ class PlayerView extends HookConsumerWidget {
);
final palette = usePaletteGenerator(albumArt);
final bgColor = palette.dominantColor?.color ?? theme.colorScheme.primary;
final titleTextColor = palette.dominantColor?.titleTextColor;
final bodyTextColor = palette.dominantColor?.bodyTextColor;
final bgColor = palette.dominantColor?.color ?? theme.colorScheme.primary;
final GlobalKey<ScaffoldState> scaffoldKey =
useMemoized(() => GlobalKey(), []);
useEffect(() {
WidgetsBinding.instance.renderView.automaticSystemUiAdjustment = false;
return () {
WidgetsBinding.instance.renderView.automaticSystemUiAdjustment = true;
};
}, [panelController.isPanelOpen]);
useCustomStatusBarColor(
bgColor,
GoRouterState.of(context).matchedLocation == "/player",
panelController.isPanelOpen,
noSetBGColor: true,
automaticSystemUiAdjustment: false,
);
return IconTheme(
data: theme.iconTheme.copyWith(color: bodyTextColor),
child: Scaffold(
appBar: PageWindowTitleBar(
backgroundColor: Colors.transparent,
foregroundColor: titleTextColor,
toolbarOpacity: 1,
leading: const BackButton(),
actions: [
IconButton(
icon: const Icon(SpotubeIcons.info, size: 18),
tooltip: context.l10n.details,
style: IconButton.styleFrom(foregroundColor: bodyTextColor),
onPressed: currentTrack == null
? null
: () {
showDialog(
context: context,
builder: (context) {
return TrackDetailsDialog(
track: currentTrack,
);
});
},
)
final topPadding = MediaQueryData.fromView(View.of(context)).padding.top;
return WillPopScope(
onWillPop: () async {
panelController.close();
return false;
},
child: IconTheme(
data: theme.iconTheme.copyWith(color: bodyTextColor),
child: AnimateGradient(
animateAlignments: true,
primaryBegin: Alignment.topLeft,
primaryEnd: Alignment.bottomLeft,
secondaryBegin: Alignment.bottomRight,
secondaryEnd: Alignment.topRight,
duration: const Duration(seconds: 15),
primaryColors: [
palette.dominantColor?.color ?? theme.colorScheme.primary,
palette.mutedColor?.color ?? theme.colorScheme.secondary,
],
),
extendBodyBehindAppBar: true,
body: SizedBox(
height: double.infinity,
child: AnimateGradient(
animateAlignments: true,
primaryBegin: Alignment.topLeft,
primaryEnd: Alignment.bottomLeft,
secondaryBegin: Alignment.bottomRight,
secondaryEnd: Alignment.topRight,
duration: const Duration(seconds: 15),
primaryColors: [
palette.dominantColor?.color ?? theme.colorScheme.primary,
palette.mutedColor?.color ?? theme.colorScheme.secondary,
],
secondaryColors: [
(palette.darkVibrantColor ?? palette.lightVibrantColor)?.color ??
theme.colorScheme.primaryContainer,
(palette.darkMutedColor ?? palette.lightMutedColor)?.color ??
theme.colorScheme.secondaryContainer,
],
child: SingleChildScrollView(
secondaryColors: [
(palette.darkVibrantColor ?? palette.lightVibrantColor)?.color ??
theme.colorScheme.primaryContainer,
(palette.darkMutedColor ?? palette.lightMutedColor)?.color ??
theme.colorScheme.secondaryContainer,
],
child: Scaffold(
key: scaffoldKey,
backgroundColor: Colors.transparent,
appBar: PreferredSize(
preferredSize: Size.fromHeight(
kToolbarHeight + topPadding,
),
child: Padding(
padding: EdgeInsets.only(top: topPadding),
child: PageWindowTitleBar(
backgroundColor: Colors.transparent,
foregroundColor: titleTextColor,
toolbarOpacity: 1,
leading: IconButton(
icon: const Icon(SpotubeIcons.angleDown, size: 18),
onPressed: panelController.close,
),
actions: [
IconButton(
icon: const Icon(SpotubeIcons.info, size: 18),
tooltip: context.l10n.details,
style:
IconButton.styleFrom(foregroundColor: bodyTextColor),
onPressed: currentTrack == null
? null
: () {
showDialog(
context: context,
builder: (context) {
return TrackDetailsDialog(
track: currentTrack,
);
});
},
)
],
),
),
),
extendBodyBehindAppBar: true,
body: SingleChildScrollView(
child: Container(
alignment: Alignment.center,
width: double.infinity,
@ -190,7 +225,7 @@ class PlayerView extends HookConsumerWidget {
color: bodyTextColor,
),
onRouteChange: (route) {
GoRouter.of(context).pop();
panelController.close();
GoRouter.of(context).push(route);
},
),

View File

@ -13,7 +13,6 @@ import 'package:spotube/extensions/context.dart';
import 'package:spotube/extensions/duration.dart';
import 'package:spotube/models/local_track.dart';
import 'package:spotube/models/logger.dart';
import 'package:spotube/models/spotube_track.dart';
import 'package:spotube/provider/download_manager_provider.dart';
import 'package:spotube/provider/authentication_provider.dart';
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';

View File

@ -2,16 +2,17 @@ import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.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/player/player_track_details.dart';
import 'package:spotube/components/root/spotube_navigation_bar.dart';
import 'package:spotube/components/shared/panels/sliding_up_panel.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/collections/intents.dart';
import 'package:spotube/hooks/use_progress.dart';
import 'package:spotube/components/player/player.dart';
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
import 'package:spotube/services/audio_player/audio_player.dart';
import 'package:spotube/utils/service_utils.dart';
class PlayerOverlay extends HookConsumerWidget {
final String albumArt;
@ -39,22 +40,32 @@ class PlayerOverlay extends HookConsumerWidget {
topRight: Radius.circular(10),
);
return GestureDetector(
onVerticalDragEnd: (details) {
int sensitivity = 8;
if (details.primaryVelocity != null &&
details.primaryVelocity! < -sensitivity) {
ServiceUtils.push(context, "/player");
}
final mediaQuery = MediaQuery.of(context);
final panelController = useMemoized(() => PanelController(), []);
useEffect(() {
return () {
panelController.dispose();
};
}, []);
return SlidingUpPanel(
maxHeight: mediaQuery.size.height,
backdropEnabled: false,
minHeight: canShow ? 53 : 0,
onPanelSlide: (position) {
final invertedPosition = 1 - position;
ref.read(navigationPanelHeight.notifier).state = 50 * invertedPosition;
},
child: ClipRRect(
controller: panelController,
collapsed: ClipRRect(
borderRadius: radius,
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15),
child: AnimatedContainer(
duration: const Duration(milliseconds: 250),
width: MediaQuery.of(context).size.width,
height: canShow ? 53 : 0,
width: mediaQuery.size.width,
decoration: BoxDecoration(
color: theme.colorScheme.secondaryContainer.withOpacity(.8),
borderRadius: radius,
@ -95,18 +106,16 @@ class PlayerOverlay extends HookConsumerWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () =>
GoRouter.of(context).push("/player"),
child: Container(
width: double.infinity,
color: Colors.transparent,
child: PlayerTrackDetails(
albumArt: albumArt,
color: textColor,
),
child: GestureDetector(
onTap: () {
panelController.open();
},
child: Container(
width: double.infinity,
color: Colors.transparent,
child: PlayerTrackDetails(
albumArt: albumArt,
color: textColor,
),
),
),
@ -165,6 +174,26 @@ class PlayerOverlay extends HookConsumerWidget {
),
),
),
panelBuilder: (position) {
// this is the reason we're getting an update
final navigationHeight = ref.watch(navigationPanelHeight);
if (navigationHeight == 50) return const SizedBox();
return IgnorePointer(
ignoring: !panelController.isPanelOpen,
child: AnimatedContainer(
clipBehavior: Clip.antiAlias,
duration: const Duration(milliseconds: 250),
decoration: navigationHeight == 0
? const BoxDecoration(borderRadius: BorderRadius.zero)
: const BoxDecoration(borderRadius: radius),
child: HorizontalScrollableWidget(
child: PlayerView(panelController: panelController),
),
),
);
},
);
}
}

Some files were not shown because too many files have changed in this diff Show More