Compare commits
No commits in common. "master" and "v3.0.1" have entirely different histories.
@ -1,6 +0,0 @@
|
|||||||
build
|
|
||||||
dist
|
|
||||||
.dart_tool
|
|
||||||
.idea
|
|
||||||
.github
|
|
||||||
.git
|
|
17
.env.example
@ -1,12 +1,11 @@
|
|||||||
|
SUPABASE_URL=
|
||||||
|
SUPABASE_API_KEY=
|
||||||
|
|
||||||
|
# The format:
|
||||||
|
# SPOTIFY_SECRETS=clintId1:clientSecret1,clientId2:clientSecret2
|
||||||
|
SPOTIFY_SECRETS=
|
||||||
|
|
||||||
# 0 or 1
|
# 0 or 1
|
||||||
# 0 = disable
|
# 0 = disable
|
||||||
# 1 = enable
|
# 1 = enable
|
||||||
ENABLE_UPDATE_CHECK=$ENABLE_UPDATE_CHECK
|
ENABLE_UPDATE_CHECK=
|
||||||
|
|
||||||
LASTFM_API_KEY=$LASTFM_API_KEY
|
|
||||||
LASTFM_API_SECRET=$LASTFM_API_SECRET
|
|
||||||
|
|
||||||
# Release channel. Can be: nightly, stable
|
|
||||||
RELEASE_CHANNEL=$RELEASE_CHANNEL
|
|
||||||
|
|
||||||
HIDE_DONATIONS=$HIDE_DONATIONS
|
|
@ -1,3 +1,4 @@
|
|||||||
{
|
{
|
||||||
"flutterSdkVersion": "3.35.2"
|
"flutterSdkVersion": "3.10.0",
|
||||||
|
"flavors": {}
|
||||||
}
|
}
|
25
.github/Dockerfile
vendored
@ -1,25 +0,0 @@
|
|||||||
ARG FLUTTER_VERSION
|
|
||||||
|
|
||||||
FROM --platform=linux/arm64 krtirtho/flutter_distributor:${FLUTTER_VERSION}
|
|
||||||
|
|
||||||
ARG BUILD_VERSION
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
RUN chown -R $(whoami) /app
|
|
||||||
|
|
||||||
RUN rustup target add aarch64-unknown-linux-gnu
|
|
||||||
|
|
||||||
RUN flutter pub get
|
|
||||||
|
|
||||||
RUN alias dpkg-deb="dpkg-deb --Zxz" &&\
|
|
||||||
flutter_distributor package --platform=linux --targets=deb --skip-clean
|
|
||||||
|
|
||||||
RUN make tar VERSION=${BUILD_VERSION} ARCH=arm64 PKG_ARCH=aarch64
|
|
||||||
|
|
||||||
RUN mv build/spotube-linux-*-aarch64.tar.xz dist/ &&\
|
|
||||||
mv dist/**/spotube-*-linux.deb dist/Spotube-linux-aarch64.deb
|
|
||||||
|
|
||||||
CMD [ "sleep", "5000000" ]
|
|
43
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -7,13 +7,8 @@ labels:
|
|||||||
body:
|
body:
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: Is there an existing issue for this? (Please read the description)
|
label: Is there an existing issue for this?
|
||||||
description: |
|
description: Make sure to check if this issue is a duplicate.
|
||||||
🚨 PLEASE! Make sure to check if this issue is a duplicate. 🚨
|
|
||||||
|
|
||||||
Don't waste our time, we are working hard to make Spotube better for you.
|
|
||||||
|
|
||||||
Try with multiple similar keywords, and check the closed issues too.
|
|
||||||
options:
|
options:
|
||||||
- label: I have searched the existing issues
|
- label: I have searched the existing issues
|
||||||
required: true
|
required: true
|
||||||
@ -21,47 +16,29 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Current Behavior
|
label: Current Behavior
|
||||||
description: Write what you are experiencing currently.
|
description: Write what you are experiencing currently.
|
||||||
placeholder: |
|
|
||||||
The app isn't working as expected. It crashes when I do this...
|
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Expected Behavior
|
label: Expected Behavior
|
||||||
description: Write what you expected to happen.
|
description: Write what you expected to happen.
|
||||||
placeholder: |
|
|
||||||
The app should do this when I do that...
|
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Steps to reproduce
|
label: Steps to reproduce
|
||||||
description: Steps to reproduce the issue. A not well written description might lead to the delay in fixing the issue.
|
description: Steps to reproduce the issue. A not well written description might delay the resolve of it.
|
||||||
placeholder: |
|
placeholder: |
|
||||||
1. I opened the app
|
1. I opened the app
|
||||||
2. I did this
|
2. I did this
|
||||||
3. And that
|
3. And that
|
||||||
4. Then this happened
|
4. Then this happened
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Logs
|
|
||||||
description: |
|
|
||||||
If you have any logs, paste them here. Make sure to remove any sensitive information.
|
|
||||||
You can find the logs in the app's Settings > Developers > Logs page.
|
|
||||||
value: |
|
|
||||||
<details>
|
|
||||||
<summary>Logs</summary>
|
|
||||||
|
|
||||||
```
|
|
||||||
<Replace this line by pasting your logs here>
|
|
||||||
```
|
|
||||||
</details>
|
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: Operating System
|
label: Operating System
|
||||||
description: The OS in which you used Spotube to face the issue. Use comma to separate multiple OS.
|
description: The OS in which you used Spotube to face the issue.
|
||||||
placeholder: Android, Linux, macOS or Windows? Make sure to include the version too.
|
placeholder: Android, Linux, macOS or Windows? Make sure to include the version too.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
@ -76,7 +53,7 @@ body:
|
|||||||
description: Where did you install Spotube from?
|
description: Where did you install Spotube from?
|
||||||
multiple: true
|
multiple: true
|
||||||
options:
|
options:
|
||||||
- "Website (spotube.krtirtho.dev)"
|
- "Website (spotube.netlify.app) or (spotube.krtirtho.dev)"
|
||||||
- "GitHub Releases (Binary)"
|
- "GitHub Releases (Binary)"
|
||||||
- "GitHub Actions (Nightly Binary)"
|
- "GitHub Actions (Nightly Binary)"
|
||||||
- "Play Store (Android)"
|
- "Play Store (Android)"
|
||||||
@ -94,13 +71,3 @@ body:
|
|||||||
description: Anything else you'd like to include?
|
description: Anything else you'd like to include?
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Self grab
|
|
||||||
description: |
|
|
||||||
If you are a developer and want to work on this issue yourself, you can check this box and wait for maintainer response. Any contributions are welcome!
|
|
||||||
|
|
||||||
This project is maintained by one person. So PRs are always welcome. This is the best way to get your issue fixed faster.
|
|
||||||
options:
|
|
||||||
- label: I'm ready to work on this issue!
|
|
||||||
required: false
|
|
||||||
|
7
.github/ISSUE_TEMPLATE/new_feature.yml
vendored
@ -36,10 +36,3 @@ body:
|
|||||||
description: Anything else you'd like to include?
|
description: Anything else you'd like to include?
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Self grab
|
|
||||||
description: If you are a developer and want to work on this issue yourself, you can check this box and wait for maintainer response. We welcome contributions!
|
|
||||||
options:
|
|
||||||
- label: I'm ready to work on this issue!
|
|
||||||
required: false
|
|
33
.github/workflows/potential-duplicates.yml
vendored
@ -1,33 +0,0 @@
|
|||||||
name: Detect Potential Duplicates Issues
|
|
||||||
on:
|
|
||||||
issues:
|
|
||||||
types:
|
|
||||||
- opened
|
|
||||||
- edited
|
|
||||||
jobs:
|
|
||||||
run:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: wow-actions/potential-duplicates@v1
|
|
||||||
with:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
# Issue title filter work with anymatch https://www.npmjs.com/package/anymatch.
|
|
||||||
# Any matched issue will stop detection immediately.
|
|
||||||
# You can specify multi filters in each line.
|
|
||||||
filter: ''
|
|
||||||
# Exclude keywords in title before detecting.
|
|
||||||
exclude: ''
|
|
||||||
# Label to set, when potential duplicates are detected.
|
|
||||||
label: potential-duplicate
|
|
||||||
# Get issues with state to compare. Supported state: 'all', 'closed', 'open'.
|
|
||||||
state: all
|
|
||||||
# If similarity is higher than this threshold([0,1]), issue will be marked as duplicate.
|
|
||||||
threshold: 0.6
|
|
||||||
# Reactions to be add to comment when potential duplicates are detected.
|
|
||||||
# Available reactions: "-1", "+1", "confused", "laugh", "heart", "hooray", "rocket", "eyes"
|
|
||||||
reactions: eyes
|
|
||||||
# Comment to post when potential duplicates are detected.
|
|
||||||
comment: >
|
|
||||||
Potential duplicates: {{#issues}}
|
|
||||||
- [#{{ number }}] {{ title }} ({{ accuracy }}%)
|
|
||||||
{{/issues}}
|
|
37
.github/workflows/pr-lint.yml
vendored
@ -1,37 +0,0 @@
|
|||||||
name: Lint
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
env:
|
|
||||||
FLUTTER_VERSION: 3.35.2
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
lint:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
|
||||||
- uses: subosito/flutter-action@v2
|
|
||||||
with:
|
|
||||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
|
||||||
|
|
||||||
- name: Dummy Envs
|
|
||||||
run: |
|
|
||||||
envsubst < .env.example > .env
|
|
||||||
env:
|
|
||||||
ENABLE_UPDATE_CHECK: true
|
|
||||||
LASTFM_API_KEY: xxx
|
|
||||||
LASTFM_API_SECRET: xxx
|
|
||||||
RELEASE_CHANNEL: nightly
|
|
||||||
HIDE_DONATIONS: 0
|
|
||||||
|
|
||||||
- name: Configure repo
|
|
||||||
run: |
|
|
||||||
flutter pub get
|
|
||||||
dart run build_runner build --delete-conflicting-outputs
|
|
||||||
|
|
||||||
- name: Lint Dart files
|
|
||||||
run: |
|
|
||||||
dart analyze --no-fatal-warnings
|
|
49
.github/workflows/spotube-publish-binary.yml
vendored
@ -4,7 +4,7 @@ on:
|
|||||||
inputs:
|
inputs:
|
||||||
version:
|
version:
|
||||||
description: Version to publish (x.x.x)
|
description: Version to publish (x.x.x)
|
||||||
default: 4.0.0
|
default: 3.0.0
|
||||||
required: true
|
required: true
|
||||||
dry_run:
|
dry_run:
|
||||||
description: Dry run
|
description: Dry run
|
||||||
@ -12,22 +12,22 @@ on:
|
|||||||
type: boolean
|
type: boolean
|
||||||
default: true
|
default: true
|
||||||
jobs:
|
jobs:
|
||||||
description: Jobs to run (flathub,aur,winget,chocolatey,playstore)
|
description: Jobs to run (flathub,aur,winget,chocolatey)
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
default: "flathub,aur,winget,chocolatey,playstore"
|
default: "flathub,aur,winget,chocolatey"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
flathub:
|
flathub:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
if: contains(inputs.jobs, 'flathub')
|
if: contains(inputs.jobs, 'flathub')
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: KRTirtho/com.github.KRTirtho.Spotube
|
repository: KRTirtho/com.github.KRTirtho.Spotube
|
||||||
token: ${{ secrets.FLATHUB_TOKEN }}
|
token: ${{ secrets.FLATHUB_TOKEN }}
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
path: spotube
|
path: spotube
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ jobs:
|
|||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
if: contains(inputs.jobs, 'aur')
|
if: contains(inputs.jobs, 'aur')
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: dsaltares/fetch-gh-release-asset@master
|
- uses: dsaltares/fetch-gh-release-asset@master
|
||||||
with:
|
with:
|
||||||
@ -66,7 +66,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Release to AUR
|
- name: Release to AUR
|
||||||
if: ${{ !inputs.dry_run }}
|
if: ${{ !inputs.dry_run }}
|
||||||
uses: KSXGitHub/github-actions-deploy-aur@v2.7.2
|
uses: KSXGitHub/github-actions-deploy-aur@v2.7.0
|
||||||
with:
|
with:
|
||||||
pkgname: spotube-bin
|
pkgname: spotube-bin
|
||||||
pkgbuild: aur-struct/PKGBUILD
|
pkgbuild: aur-struct/PKGBUILD
|
||||||
@ -76,12 +76,12 @@ jobs:
|
|||||||
commit_message: Updated to v${{ inputs.version }}
|
commit_message: Updated to v${{ inputs.version }}
|
||||||
|
|
||||||
winget:
|
winget:
|
||||||
runs-on: ubuntu-latest
|
runs-on: windows-latest
|
||||||
if: contains(inputs.jobs, 'winget')
|
if: contains(inputs.jobs, 'winget')
|
||||||
steps:
|
steps:
|
||||||
- name: Release winget package
|
- name: Release winget package
|
||||||
if: ${{ !inputs.dry_run }}
|
if: ${{ !inputs.dry_run }}
|
||||||
uses: vedantmgoyal9/winget-releaser@main
|
uses: vedantmgoyal2009/winget-releaser@v2
|
||||||
with:
|
with:
|
||||||
version: ${{ inputs.version }}
|
version: ${{ inputs.version }}
|
||||||
release-tag: v${{ inputs.version }}
|
release-tag: v${{ inputs.version }}
|
||||||
@ -104,34 +104,3 @@ jobs:
|
|||||||
- name: Publish to Chocolatey Repository
|
- name: Publish to Chocolatey Repository
|
||||||
if: ${{ !inputs.dry_run }}
|
if: ${{ !inputs.dry_run }}
|
||||||
run: choco push Spotube-windows-x86_64.nupkg --source https://push.chocolatey.org/
|
run: choco push Spotube-windows-x86_64.nupkg --source https://push.chocolatey.org/
|
||||||
|
|
||||||
playstore:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: contains(inputs.jobs, 'playstore')
|
|
||||||
steps:
|
|
||||||
- name: Tagname (workflow dispatch)
|
|
||||||
run: echo 'TAG_NAME=${{inputs.version}}' >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- uses: robinraju/release-downloader@main
|
|
||||||
with:
|
|
||||||
repository: KRTirtho/spotube
|
|
||||||
tag: v${{ env.TAG_NAME }}
|
|
||||||
tarBall: false
|
|
||||||
zipBall: false
|
|
||||||
out-file-path: dist
|
|
||||||
fileName: "Spotube-playstore-all-arch.aab"
|
|
||||||
|
|
||||||
- name: Create service-account.json
|
|
||||||
run: |
|
|
||||||
echo "${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_BASE64 }}" | base64 -d > service-account.json
|
|
||||||
|
|
||||||
- name: Upload Android Release to Play Store
|
|
||||||
if: ${{!inputs.dry_run}}
|
|
||||||
uses: r0adkll/upload-google-play@v1
|
|
||||||
with:
|
|
||||||
serviceAccountJson: ./service-account.json
|
|
||||||
releaseFiles: ./dist/Spotube-playstore-all-arch.aab
|
|
||||||
packageName: oss.krtirtho.spotube
|
|
||||||
track: production
|
|
||||||
status: draft
|
|
||||||
releaseName: ${{ env.TAG_NAME }}
|
|
||||||
|
428
.github/workflows/spotube-release-binary.yml
vendored
@ -2,123 +2,90 @@ name: Spotube Release Binary
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
|
version:
|
||||||
|
description: Version to release (x.x.x)
|
||||||
|
default: 3.0.0
|
||||||
|
required: true
|
||||||
channel:
|
channel:
|
||||||
type: choice
|
type: choice
|
||||||
|
description: Release Channel
|
||||||
|
required: true
|
||||||
options:
|
options:
|
||||||
- stable
|
- stable
|
||||||
- nightly
|
- nightly
|
||||||
default: nightly
|
default: nightly
|
||||||
description: The release channel
|
|
||||||
debug:
|
debug:
|
||||||
|
description: Debug on failed when channel is nightly
|
||||||
|
required: true
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
description: Debug with SSH toggle
|
|
||||||
required: false
|
|
||||||
dry_run:
|
dry_run:
|
||||||
|
description: Dry run
|
||||||
|
required: true
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: true
|
||||||
description: Dry run without uploading to release
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
FLUTTER_VERSION: 3.35.2
|
FLUTTER_VERSION: '3.10.0'
|
||||||
FLUTTER_CHANNEL: master
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_platform:
|
windows:
|
||||||
strategy:
|
runs-on: windows-latest
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: ubuntu-22.04
|
|
||||||
platform: linux
|
|
||||||
arch: x86
|
|
||||||
files: |
|
|
||||||
dist/Spotube-linux-x86_64.deb
|
|
||||||
dist/Spotube-linux-x86_64.rpm
|
|
||||||
dist/spotube-linux-*-x86_64.tar.xz
|
|
||||||
- os: ubuntu-22.04-arm
|
|
||||||
platform: linux
|
|
||||||
arch: arm64
|
|
||||||
files: |
|
|
||||||
dist/Spotube-linux-aarch64.deb
|
|
||||||
dist/spotube-linux-*-aarch64.tar.xz
|
|
||||||
- os: ubuntu-22.04
|
|
||||||
platform: android
|
|
||||||
arch: all
|
|
||||||
files: |
|
|
||||||
build/Spotube-android-all-arch.apk
|
|
||||||
build/Spotube-playstore-all-arch.aab
|
|
||||||
- os: windows-latest
|
|
||||||
platform: windows
|
|
||||||
arch: x86
|
|
||||||
files: |
|
|
||||||
dist/Spotube-windows-x86_64.nupkg
|
|
||||||
dist/Spotube-windows-x86_64-setup.exe
|
|
||||||
- os: macos-14
|
|
||||||
platform: ios
|
|
||||||
arch: all
|
|
||||||
files: |
|
|
||||||
Spotube-iOS.ipa
|
|
||||||
- os: macos-14
|
|
||||||
platform: macos
|
|
||||||
arch: all
|
|
||||||
files: |
|
|
||||||
build/Spotube-macos-universal.dmg
|
|
||||||
build/Spotube-macos-universal.pkg
|
|
||||||
runs-on: ${{matrix.os}}
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: subosito/flutter-action@v2.18.0
|
- uses: subosito/flutter-action@v2.10.0
|
||||||
with:
|
with:
|
||||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
|
||||||
channel: ${{ env.FLUTTER_CHANNEL }}
|
|
||||||
cache: true
|
cache: true
|
||||||
git-source: https://github.com/flutter/flutter.git
|
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||||
|
|
||||||
- name: Setup Java
|
- name: Replace pubspec version and BUILD_VERSION Env (nightly)
|
||||||
if: ${{matrix.platform == 'android'}}
|
if: ${{ inputs.channel == 'nightly' }}
|
||||||
uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
distribution: "zulu"
|
|
||||||
java-version: "17"
|
|
||||||
cache: "gradle"
|
|
||||||
check-latest: true
|
|
||||||
|
|
||||||
- name: Setup Rust toolchain
|
|
||||||
uses: dtolnay/rust-toolchain@stable
|
|
||||||
with:
|
|
||||||
toolchain: stable
|
|
||||||
|
|
||||||
- name: Install Xcode
|
|
||||||
if: ${{matrix.platform == 'ios'}}
|
|
||||||
uses: maxim-lobanov/setup-xcode@v1
|
|
||||||
with:
|
|
||||||
xcode-version: "16.2"
|
|
||||||
|
|
||||||
- name: Install ${{matrix.platform}} dependencies
|
|
||||||
run: |
|
run: |
|
||||||
|
choco install sed make yq -y
|
||||||
|
yq -i '.version |= sub("\+\d+", "+${{ inputs.channel }}.")' pubspec.yaml
|
||||||
|
yq -i '.version += strenv(GITHUB_RUN_NUMBER)' pubspec.yaml
|
||||||
|
"BUILD_VERSION=${{ inputs.version }}+${{ inputs.channel }}.${{ github.run_number }}" >> $env:GITHUB_ENV
|
||||||
|
|
||||||
|
- name: BUILD_VERSION Env (stable)
|
||||||
|
if: ${{ inputs.channel == 'stable' }}
|
||||||
|
run: |
|
||||||
|
"BUILD_VERSION=${{ inputs.version }}" >> $env:GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Replace version in files
|
||||||
|
run: |
|
||||||
|
choco install sed make -y
|
||||||
|
sed -i "s/%{{SPOTUBE_VERSION}}%/${{ env.BUILD_VERSION }}/" windows/runner/Runner.rc
|
||||||
|
sed -i "s/%{{SPOTUBE_VERSION}}%/${{ env.BUILD_VERSION }}/" choco-struct/tools/VERIFICATION.txt
|
||||||
|
sed -i "s/%{{SPOTUBE_VERSION}}%/${{ env.BUILD_VERSION }}/" choco-struct/spotube.nuspec
|
||||||
|
|
||||||
|
- name: Create Stable .env
|
||||||
|
if: ${{ inputs.channel == 'stable' }}
|
||||||
|
run: echo '${{ secrets.DOTENV_RELEASE }}' > .env
|
||||||
|
|
||||||
|
- name: Create Nightly .env
|
||||||
|
if: ${{ inputs.channel == 'nightly' }}
|
||||||
|
run: echo '${{ secrets.DOTENV_NIGHTLY }}' > .env
|
||||||
|
|
||||||
|
- name: Generating Secrets
|
||||||
|
run: |
|
||||||
|
flutter config --enable-windows-desktop
|
||||||
flutter pub get
|
flutter pub get
|
||||||
dart cli/cli.dart install-dependencies --platform=${{matrix.platform}}
|
dart run build_runner build --delete-conflicting-outputs --enable-experiment=records,patterns
|
||||||
|
|
||||||
- name: Sign Apk
|
- name: Build Windows Executable
|
||||||
if: ${{matrix.platform == 'android'}}
|
|
||||||
run: |
|
run: |
|
||||||
echo '${{ secrets.KEYSTORE }}' | base64 --decode > android/app/upload-keystore.jks
|
dart pub global activate flutter_distributor
|
||||||
echo '${{ secrets.KEY_PROPERTIES }}' > android/key.properties
|
make innoinstall
|
||||||
|
flutter_distributor package --platform=windows --targets=exe --skip-clean
|
||||||
|
mv dist/**/spotube-*-windows-setup.exe dist/Spotube-windows-x86_64-setup.exe
|
||||||
|
|
||||||
- name: Build ${{matrix.platform}} binaries
|
- name: Create Chocolatey Package and set hash
|
||||||
run: dart cli/cli.dart build --arch=${{matrix.arch}} ${{matrix.platform}}
|
if: ${{ inputs.channel == 'stable' }}
|
||||||
env:
|
run: |
|
||||||
CHANNEL: ${{inputs.channel}}
|
Set-Variable -Name HASH -Value (Get-FileHash dist\Spotube-windows-x86_64-setup.exe).Hash
|
||||||
DOTENV: ${{secrets.DOTENV_RELEASE}}
|
sed -i "s/%{{WIN_SHA256}}%/$HASH/" choco-struct/tools/VERIFICATION.txt
|
||||||
|
make choco
|
||||||
- uses: actions/upload-artifact@v4
|
mv dist/spotube.*.nupkg dist/Spotube-windows-x86_64.nupkg
|
||||||
with:
|
|
||||||
if-no-files-found: error
|
|
||||||
name: ${{matrix.platform}}-${{matrix.arch}}
|
|
||||||
path: ${{matrix.files}}
|
|
||||||
|
|
||||||
- name: Debug With SSH When fails
|
- name: Debug With SSH When fails
|
||||||
if: ${{ failure() && inputs.debug && inputs.channel == 'nightly' }}
|
if: ${{ failure() && inputs.debug && inputs.channel == 'nightly' }}
|
||||||
@ -126,14 +93,244 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
limit-access-to-actor: true
|
limit-access-to-actor: true
|
||||||
|
|
||||||
upload:
|
- name: Upload Artifact
|
||||||
runs-on: ubuntu-22.04
|
uses: actions/upload-artifact@v3
|
||||||
needs:
|
|
||||||
- build_platform
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/download-artifact@v4
|
|
||||||
with:
|
with:
|
||||||
|
name: Spotube-Release-Binaries
|
||||||
|
path: dist/
|
||||||
|
|
||||||
|
linux:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: subosito/flutter-action@v2.10.0
|
||||||
|
with:
|
||||||
|
cache: true
|
||||||
|
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||||
|
|
||||||
|
- name: Get current date
|
||||||
|
id: date
|
||||||
|
run: echo "::set-output name=date::$(date +'%Y-%m-%d')"
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -y
|
||||||
|
sudo apt-get install -y tar clang cmake ninja-build pkg-config libgtk-3-dev make python3-pip python3-setuptools desktop-file-utils libgdk-pixbuf2.0-dev fakeroot strace fuse libunwind-dev locate patchelf gir1.2-appindicator3-0.1 libappindicator3-1 libappindicator3-dev libsecret-1-0 libjsoncpp25 libsecret-1-dev libjsoncpp-dev libnotify-bin libnotify-dev mpv libmpv-dev
|
||||||
|
|
||||||
|
- name: Install AppImage Tool
|
||||||
|
run: |
|
||||||
|
wget -O appimagetool "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage"
|
||||||
|
chmod +x appimagetool
|
||||||
|
mv appimagetool /usr/local/bin/
|
||||||
|
|
||||||
|
- name: Replace pubspec version and BUILD_VERSION Env (nightly)
|
||||||
|
if: ${{ inputs.channel == 'nightly' }}
|
||||||
|
run: |
|
||||||
|
curl -sS https://webi.sh/yq | sh
|
||||||
|
yq -i '.version |= sub("\+\d+", "+${{ inputs.channel }}.")' pubspec.yaml
|
||||||
|
yq -i '.version += strenv(GITHUB_RUN_NUMBER)' pubspec.yaml
|
||||||
|
echo "BUILD_VERSION=${{ inputs.version }}+${{ inputs.channel }}.${{ github.run_number }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: BUILD_VERSION Env (stable)
|
||||||
|
if: ${{ inputs.channel == 'stable' }}
|
||||||
|
run: |
|
||||||
|
echo "BUILD_VERSION=${{ inputs.version }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Create Stable .env
|
||||||
|
if: ${{ inputs.channel == 'stable' }}
|
||||||
|
run: echo '${{ secrets.DOTENV_RELEASE }}' > .env
|
||||||
|
|
||||||
|
- name: Create Nightly .env
|
||||||
|
if: ${{ inputs.channel == 'nightly' }}
|
||||||
|
run: echo '${{ secrets.DOTENV_NIGHTLY }}' > .env
|
||||||
|
|
||||||
|
- name: Replace Version in files
|
||||||
|
run: |
|
||||||
|
sed -i 's|%{{APPDATA_RELEASE}}%|<release version="${{ env.BUILD_VERSION }}" date="${{ steps.date.outputs.date }}" />|' linux/com.github.KRTirtho.Spotube.appdata.xml
|
||||||
|
|
||||||
|
- name: Generate Secrets
|
||||||
|
run: |
|
||||||
|
flutter config --enable-linux-desktop
|
||||||
|
flutter pub get
|
||||||
|
dart run build_runner build --delete-conflicting-outputs --enable-experiment=records,patterns
|
||||||
|
|
||||||
|
- name: Build Linux Packages
|
||||||
|
run: |
|
||||||
|
dart pub global activate flutter_distributor
|
||||||
|
alias dpkg-deb="dpkg-deb --Zxz"
|
||||||
|
flutter_distributor package --platform=linux --targets=deb
|
||||||
|
flutter_distributor package --platform=linux --targets=appimage
|
||||||
|
flutter_distributor package --platform=linux --targets=rpm
|
||||||
|
|
||||||
|
- name: Create tar.xz (stable)
|
||||||
|
if: ${{ inputs.channel == 'stable' }}
|
||||||
|
run: make tar VERSION=${{ env.BUILD_VERSION }}
|
||||||
|
|
||||||
|
- name: Create tar.xz (nightly)
|
||||||
|
if: ${{ inputs.channel == 'nightly' }}
|
||||||
|
run: make tar VERSION=nightly
|
||||||
|
|
||||||
|
- name: Move Files to dist
|
||||||
|
run: |
|
||||||
|
mv build/spotube-linux-*-x86_64.tar.xz dist/
|
||||||
|
mv dist/**/spotube-*-linux.deb dist/Spotube-linux-x86_64.deb
|
||||||
|
mv dist/**/spotube-*-linux.rpm dist/Spotube-linux-x86_64.rpm
|
||||||
|
mv dist/**/spotube-*-linux.AppImage dist/Spotube-linux-x86_64.AppImage
|
||||||
|
|
||||||
|
- 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
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: subosito/flutter-action@v2.10.0
|
||||||
|
with:
|
||||||
|
cache: true
|
||||||
|
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -y
|
||||||
|
sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev make python3-pip python3-setuptools patchelf desktop-file-utils libgdk-pixbuf2.0-dev fakeroot strace fuse
|
||||||
|
|
||||||
|
- name: Replace pubspec version and BUILD_VERSION Env (nightly)
|
||||||
|
if: ${{ inputs.channel == 'nightly' }}
|
||||||
|
run: |
|
||||||
|
curl -sS https://webi.sh/yq | sh
|
||||||
|
yq -i '.version |= sub("\+\d+", "+${{ inputs.channel }}.")' pubspec.yaml
|
||||||
|
yq -i '.version += strenv(GITHUB_RUN_NUMBER)' pubspec.yaml
|
||||||
|
echo "BUILD_VERSION=${{ inputs.version }}+${{ inputs.channel }}.${{ github.run_number }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: BUILD_VERSION Env (stable)
|
||||||
|
if: ${{ inputs.channel == 'stable' }}
|
||||||
|
run: |
|
||||||
|
echo "BUILD_VERSION=${{ inputs.version }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Create Stable .env
|
||||||
|
if: ${{ inputs.channel == 'stable' }}
|
||||||
|
run: echo '${{ secrets.DOTENV_RELEASE }}' > .env
|
||||||
|
|
||||||
|
- name: Create Nightly .env
|
||||||
|
if: ${{ inputs.channel == 'nightly' }}
|
||||||
|
run: echo '${{ secrets.DOTENV_NIGHTLY }}' > .env
|
||||||
|
|
||||||
|
- name: Generate Secrets
|
||||||
|
run: |
|
||||||
|
flutter pub get
|
||||||
|
dart run build_runner build --delete-conflicting-outputs --enable-experiment=records,patterns
|
||||||
|
|
||||||
|
- name: Sign Apk
|
||||||
|
run: |
|
||||||
|
echo '${{ secrets.KEYSTORE }}' | base64 --decode > android/app/upload-keystore.jks
|
||||||
|
echo '${{ secrets.KEY_PROPERTIES }}' > android/key.properties
|
||||||
|
|
||||||
|
- 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
|
||||||
|
|
||||||
|
- name: Build Playstore AppBundle
|
||||||
|
run: |
|
||||||
|
echo 'ENABLE_UPDATE_CHECK=0' >> .env
|
||||||
|
dart run build_runner build --delete-conflicting-outputs --enable-experiment=records,patterns
|
||||||
|
flutter build appbundle
|
||||||
|
mv build/app/outputs/bundle/release/app-release.aab build/Spotube-playstore-all-arch.aab
|
||||||
|
|
||||||
|
- 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-android-all-arch.apk
|
||||||
|
build/Spotube-playstore-all-arch.aab
|
||||||
|
|
||||||
|
macos:
|
||||||
|
runs-on: macos-12
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: subosito/flutter-action@v2.10.0
|
||||||
|
with:
|
||||||
|
cache: true
|
||||||
|
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||||
|
|
||||||
|
- name: Replace pubspec version and BUILD_VERSION Env (nightly)
|
||||||
|
if: ${{ inputs.channel == 'nightly' }}
|
||||||
|
run: |
|
||||||
|
brew install yq
|
||||||
|
yq -i '.version |= sub("\+\d+", "+${{ inputs.channel }}.")' pubspec.yaml
|
||||||
|
yq -i '.version += strenv(GITHUB_RUN_NUMBER)' pubspec.yaml
|
||||||
|
echo "BUILD_VERSION=${{ inputs.version }}+${{ inputs.channel }}.${{ github.run_number }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: BUILD_VERSION Env (stable)
|
||||||
|
if: ${{ inputs.channel == 'stable' }}
|
||||||
|
run: |
|
||||||
|
echo "BUILD_VERSION=${{ inputs.version }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Create Stable .env
|
||||||
|
if: ${{ inputs.channel == 'stable' }}
|
||||||
|
run: echo '${{ secrets.DOTENV_RELEASE }}' > .env
|
||||||
|
|
||||||
|
- name: Create Nightly .env
|
||||||
|
if: ${{ inputs.channel == 'nightly' }}
|
||||||
|
run: echo '${{ secrets.DOTENV_NIGHTLY }}' > .env
|
||||||
|
|
||||||
|
- name: Generate Secrets
|
||||||
|
run: |
|
||||||
|
flutter pub get
|
||||||
|
flutter pub remove media_kit_native_event_loop
|
||||||
|
dart run build_runner build --delete-conflicting-outputs --enable-experiment=records,patterns
|
||||||
|
|
||||||
|
- name: Build Macos App
|
||||||
|
run: |
|
||||||
|
flutter config --enable-macos-desktop
|
||||||
|
flutter build macos
|
||||||
|
du -sh build/macos/Build/Products/Release/spotube.app
|
||||||
|
|
||||||
|
- name: Package Macos App
|
||||||
|
run: |
|
||||||
|
npm install -g appdmg
|
||||||
|
mkdir -p build/${{ env.BUILD_VERSION }}
|
||||||
|
appdmg appdmg.json 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
|
||||||
|
|
||||||
|
upload:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- windows
|
||||||
|
- linux
|
||||||
|
- android
|
||||||
|
- macos
|
||||||
|
steps:
|
||||||
|
- uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: Spotube-Release-Binaries
|
||||||
path: ./Spotube-Release-Binaries
|
path: ./Spotube-Release-Binaries
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
@ -142,19 +339,13 @@ jobs:
|
|||||||
- name: Generate Checksums
|
- name: Generate Checksums
|
||||||
run: |
|
run: |
|
||||||
tree .
|
tree .
|
||||||
find Spotube-Release-Binaries -type f -exec md5sum {} \; >> RELEASE.md5sum
|
md5sum Spotube-Release-Binaries/* >> RELEASE.md5sum
|
||||||
find Spotube-Release-Binaries -type f -exec sha256sum {} \; >> RELEASE.sha256sum
|
sha256sum Spotube-Release-Binaries/* >> RELEASE.sha256sum
|
||||||
sed -i 's|Spotube-Release-Binaries/.*/\([^/]*\)$|\1|' RELEASE.sha256sum RELEASE.md5sum
|
|
||||||
sed -i 's|Spotube-Release-Binaries/||' RELEASE.sha256sum RELEASE.md5sum
|
sed -i 's|Spotube-Release-Binaries/||' RELEASE.sha256sum RELEASE.md5sum
|
||||||
|
|
||||||
- name: Extract pubspec version
|
- uses: actions/upload-artifact@v3
|
||||||
run: |
|
|
||||||
echo "PUBSPEC_VERSION=$(grep -oP 'version:\s*\K[^+]+(?=\+)' pubspec.yaml)" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
with:
|
||||||
if-no-files-found: error
|
name: Spotube-Release-Binaries
|
||||||
name: sums
|
|
||||||
path: |
|
path: |
|
||||||
RELEASE.md5sum
|
RELEASE.md5sum
|
||||||
RELEASE.sha256sum
|
RELEASE.sha256sum
|
||||||
@ -164,12 +355,12 @@ jobs:
|
|||||||
uses: ncipollo/release-action@v1
|
uses: ncipollo/release-action@v1
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
tag: v${{ env.PUBSPEC_VERSION }} # mind the "v" prefix
|
tag: v${{ inputs.version }} # mind the "v" prefix
|
||||||
omitBodyDuringUpdate: true
|
omitBodyDuringUpdate: true
|
||||||
omitNameDuringUpdate: true
|
omitNameDuringUpdate: true
|
||||||
omitPrereleaseDuringUpdate: true
|
omitPrereleaseDuringUpdate: true
|
||||||
allowUpdates: true
|
allowUpdates: true
|
||||||
artifacts: Spotube-Release-Binaries/**/*,RELEASE.sha256sum,RELEASE.md5sum
|
artifacts: Spotube-Release-Binaries/*,RELEASE.sha256sum,RELEASE.md5sum
|
||||||
|
|
||||||
- name: Upload Release Binaries (nightly)
|
- name: Upload Release Binaries (nightly)
|
||||||
if: ${{ !inputs.dry_run && inputs.channel == 'nightly' }}
|
if: ${{ !inputs.dry_run && inputs.channel == 'nightly' }}
|
||||||
@ -181,15 +372,4 @@ jobs:
|
|||||||
omitNameDuringUpdate: true
|
omitNameDuringUpdate: true
|
||||||
omitPrereleaseDuringUpdate: true
|
omitPrereleaseDuringUpdate: true
|
||||||
allowUpdates: true
|
allowUpdates: true
|
||||||
artifacts: Spotube-Release-Binaries/**/*,RELEASE.sha256sum,RELEASE.md5sum
|
artifacts: Spotube-Release-Binaries/*,RELEASE.sha256sum,RELEASE.md5sum
|
||||||
body: |
|
|
||||||
Build Number: ${{github.run_number}}
|
|
||||||
|
|
||||||
Nightly release includes newest features but may contain bugs
|
|
||||||
It is preferred to use the stable version unless you know what you're doing
|
|
||||||
|
|
||||||
- name: Debug With SSH When fails
|
|
||||||
if: ${{ failure() && inputs.debug && inputs.channel == 'nightly' }}
|
|
||||||
uses: mxschmitt/action-tmate@v3
|
|
||||||
with:
|
|
||||||
limit-access-to-actor: true
|
|
||||||
|
9
.gitignore
vendored
@ -73,13 +73,6 @@ dist
|
|||||||
appimage-build
|
appimage-build
|
||||||
|
|
||||||
android/key.properties
|
android/key.properties
|
||||||
|
.fvm/flutter_sdk
|
||||||
|
|
||||||
**/pb_data
|
**/pb_data
|
||||||
|
|
||||||
tm.json
|
|
||||||
|
|
||||||
# FVM Version Cache
|
|
||||||
.fvm/
|
|
||||||
|
|
||||||
android/build
|
|
||||||
android/app/.cxx
|
|
||||||
|
16
.metadata
@ -1,11 +1,11 @@
|
|||||||
# This file tracks properties of this Flutter project.
|
# This file tracks properties of this Flutter project.
|
||||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
#
|
#
|
||||||
# This file should be version controlled and should not be manually edited.
|
# This file should be version controlled.
|
||||||
|
|
||||||
version:
|
version:
|
||||||
revision: "d7b523b356d15fb81e7d340bbe52b47f93937323"
|
revision: eb6d86ee27deecba4a83536aa20f366a6044895c
|
||||||
channel: "stable"
|
channel: stable
|
||||||
|
|
||||||
project_type: app
|
project_type: app
|
||||||
|
|
||||||
@ -13,11 +13,11 @@ project_type: app
|
|||||||
migration:
|
migration:
|
||||||
platforms:
|
platforms:
|
||||||
- platform: root
|
- platform: root
|
||||||
create_revision: d7b523b356d15fb81e7d340bbe52b47f93937323
|
create_revision: eb6d86ee27deecba4a83536aa20f366a6044895c
|
||||||
base_revision: d7b523b356d15fb81e7d340bbe52b47f93937323
|
base_revision: eb6d86ee27deecba4a83536aa20f366a6044895c
|
||||||
- platform: windows
|
- platform: macos
|
||||||
create_revision: d7b523b356d15fb81e7d340bbe52b47f93937323
|
create_revision: eb6d86ee27deecba4a83536aa20f366a6044895c
|
||||||
base_revision: d7b523b356d15fb81e7d340bbe52b47f93937323
|
base_revision: eb6d86ee27deecba4a83536aa20f366a6044895c
|
||||||
|
|
||||||
# User provided section
|
# User provided section
|
||||||
|
|
||||||
|
34
.vscode/launch.json
vendored
@ -5,28 +5,7 @@
|
|||||||
"name": "spotube",
|
"name": "spotube",
|
||||||
"type": "dart",
|
"type": "dart",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "lib/main.dart",
|
"program": "lib/main.dart"
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "spotube (mobile)",
|
|
||||||
"type": "dart",
|
|
||||||
"request": "launch",
|
|
||||||
"program": "lib/main.dart",
|
|
||||||
"args": [
|
|
||||||
"--flavor",
|
|
||||||
"dev"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "spotube (mobile-skia)",
|
|
||||||
"type": "dart",
|
|
||||||
"request": "launch",
|
|
||||||
"program": "lib/main.dart",
|
|
||||||
"args": [
|
|
||||||
"--flavor",
|
|
||||||
"dev",
|
|
||||||
"--no-enable-impeller"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "spotube (profile)",
|
"name": "spotube (profile)",
|
||||||
@ -41,17 +20,6 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "lib/main.dart",
|
"program": "lib/main.dart",
|
||||||
"flutterMode": "release"
|
"flutterMode": "release"
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "spotube (mobile) (release)",
|
|
||||||
"type": "dart",
|
|
||||||
"request": "launch",
|
|
||||||
"program": "lib/main.dart",
|
|
||||||
"flutterMode": "release",
|
|
||||||
"args": [
|
|
||||||
"--flavor",
|
|
||||||
"dev"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"compounds": []
|
"compounds": []
|
||||||
|
43
.vscode/settings.json
vendored
@ -1,34 +1,13 @@
|
|||||||
{
|
{
|
||||||
"cmake.configureOnOpen": false,
|
"cmake.configureOnOpen": false,
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"acousticness",
|
"acousticness",
|
||||||
"ambiguate",
|
"danceability",
|
||||||
"Amoled",
|
"instrumentalness",
|
||||||
"Buildless",
|
"Mpris",
|
||||||
"configurators",
|
"riverpod",
|
||||||
"danceability",
|
"speechiness",
|
||||||
"fuzzywuzzy",
|
"Spotube",
|
||||||
"gapless",
|
"winget"
|
||||||
"instrumentalness",
|
]
|
||||||
"isrc",
|
|
||||||
"Mpris",
|
|
||||||
"RGBO",
|
|
||||||
"riverpod",
|
|
||||||
"Scrobblenaut",
|
|
||||||
"shadcn",
|
|
||||||
"skeletonizer",
|
|
||||||
"songlink",
|
|
||||||
"speechiness",
|
|
||||||
"Spotube",
|
|
||||||
"titlebar",
|
|
||||||
"winget"
|
|
||||||
],
|
|
||||||
"editor.formatOnSave": true,
|
|
||||||
"explorer.fileNesting.enabled": true,
|
|
||||||
"explorer.fileNesting.patterns": {
|
|
||||||
"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",
|
|
||||||
"*.dart": "${capture}.g.dart,${capture}.freezed.dart"
|
|
||||||
},
|
|
||||||
"dart.flutterSdkPath": ".fvm/versions/3.35.2"
|
|
||||||
}
|
}
|
170
.vscode/snippets.code-snippets
vendored
@ -1,170 +0,0 @@
|
|||||||
{
|
|
||||||
"PaginatedState": {
|
|
||||||
"scope": "dart",
|
|
||||||
"prefix": "paginatedState",
|
|
||||||
"description": "Generate a PaginatedState",
|
|
||||||
"body": [
|
|
||||||
"class ${1:Model}State extends PaginatedState<${2:Model}> {",
|
|
||||||
" ${1:Model}State({",
|
|
||||||
" required super.items,",
|
|
||||||
" required super.offset,",
|
|
||||||
" required super.limit,",
|
|
||||||
" required super.hasMore,",
|
|
||||||
" });",
|
|
||||||
" ",
|
|
||||||
" @override",
|
|
||||||
" ${1:Model}State copyWith({",
|
|
||||||
" List<${2:Model}>? items,",
|
|
||||||
" int? offset,",
|
|
||||||
" int? limit,",
|
|
||||||
" bool? hasMore,",
|
|
||||||
" }) {",
|
|
||||||
" return ${1:Model}State(",
|
|
||||||
" items: items ?? this.items,",
|
|
||||||
" offset: offset ?? this.offset,",
|
|
||||||
" limit: limit ?? this.limit,",
|
|
||||||
" hasMore: hasMore ?? this.hasMore,",
|
|
||||||
" );",
|
|
||||||
" }",
|
|
||||||
"}"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"PaginatedAsyncNotifier": {
|
|
||||||
"scope": "dart",
|
|
||||||
"prefix": "paginatedAsyncNotifier",
|
|
||||||
"description": "Generate a PaginatedAsyncNotifier",
|
|
||||||
"body": [
|
|
||||||
"class ${1:NotifierName}Notifier extends PaginatedAsyncNotifier<${3:Item}, ${2:Model}State> {",
|
|
||||||
" ${1:NotifierName}Notifier() : super();",
|
|
||||||
" ",
|
|
||||||
" @override",
|
|
||||||
" fetch(int offset, int limit) async {",
|
|
||||||
" throw UnimplementedError();",
|
|
||||||
" }",
|
|
||||||
" ",
|
|
||||||
" @override",
|
|
||||||
" build() async {",
|
|
||||||
" throw UnimplementedError();",
|
|
||||||
" }",
|
|
||||||
"}"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"PaginaitedNotifierWithState": {
|
|
||||||
"scope": "dart",
|
|
||||||
"prefix": "paginatedNotifierWithState",
|
|
||||||
"description": "Generate a PaginatedNotifier with PaginatedState",
|
|
||||||
"body": [
|
|
||||||
"class $1State extends PaginatedState<$2> {",
|
|
||||||
" $1State({",
|
|
||||||
" required super.items,",
|
|
||||||
" required super.offset,",
|
|
||||||
" required super.limit,",
|
|
||||||
" required super.hasMore,",
|
|
||||||
" });",
|
|
||||||
" ",
|
|
||||||
" @override",
|
|
||||||
" $1State copyWith({",
|
|
||||||
" List<$2>? items,",
|
|
||||||
" int? offset,",
|
|
||||||
" int? limit,",
|
|
||||||
" bool? hasMore,",
|
|
||||||
" }) {",
|
|
||||||
" return $1State(",
|
|
||||||
" items: items ?? this.items,",
|
|
||||||
" offset: offset ?? this.offset,",
|
|
||||||
" limit: limit ?? this.limit,",
|
|
||||||
" hasMore: hasMore ?? this.hasMore,",
|
|
||||||
" );",
|
|
||||||
" }",
|
|
||||||
"}",
|
|
||||||
" ",
|
|
||||||
"class $1Notifier",
|
|
||||||
" extends PaginatedAsyncNotifier<$2, $1State> {",
|
|
||||||
" $1Notifier() : super();",
|
|
||||||
" ",
|
|
||||||
" @override",
|
|
||||||
" fetch(int offset, int limit) async {",
|
|
||||||
" throw UnimplementedError();",
|
|
||||||
" }",
|
|
||||||
" ",
|
|
||||||
" @override",
|
|
||||||
" build() async {",
|
|
||||||
" throw UnimplementedError();",
|
|
||||||
" }",
|
|
||||||
"}",
|
|
||||||
" ",
|
|
||||||
"final ${1/(.*)/${1:/camelcase}/}Provider = AsyncNotifierProvider<$1Notifier, $1State>(",
|
|
||||||
" ()=> $1Notifier(),",
|
|
||||||
");"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"FamilyPaginatedAsyncNotifier": {
|
|
||||||
"scope": "dart",
|
|
||||||
"prefix": "familyPaginatedAsyncNotifier",
|
|
||||||
"description": "Generate a FamilyPaginatedAsyncNotifier",
|
|
||||||
"body": [
|
|
||||||
"class ${1:NotifierName}Notifier extends FamilyPaginatedAsyncNotifier<${3:Item}, ${2:Model}State, {$4:Arg}> {",
|
|
||||||
" ${1:NotifierName}Notifier() : super();",
|
|
||||||
" ",
|
|
||||||
" @override",
|
|
||||||
" fetch(arg, offset, limit) async {",
|
|
||||||
" throw UnimplementedError();",
|
|
||||||
" }",
|
|
||||||
" ",
|
|
||||||
" @override",
|
|
||||||
" build(arg) async {",
|
|
||||||
" throw UnimplementedError();",
|
|
||||||
" }",
|
|
||||||
"}"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"FamilyPaginaitedNotifierWithState": {
|
|
||||||
"scope": "dart",
|
|
||||||
"prefix": "familyPaginatedNotifierWithState",
|
|
||||||
"description": "Generate a FamilyPaginatedAsyncNotifier with PaginatedState",
|
|
||||||
"body": [
|
|
||||||
"class $1State extends PaginatedState<$2> {",
|
|
||||||
" $1State({",
|
|
||||||
" required super.items,",
|
|
||||||
" required super.offset,",
|
|
||||||
" required super.limit,",
|
|
||||||
" required super.hasMore,",
|
|
||||||
" });",
|
|
||||||
" ",
|
|
||||||
" @override",
|
|
||||||
" $1State copyWith({",
|
|
||||||
" List<$2>? items,",
|
|
||||||
" int? offset,",
|
|
||||||
" int? limit,",
|
|
||||||
" bool? hasMore,",
|
|
||||||
" }) {",
|
|
||||||
" return $1State(",
|
|
||||||
" items: items ?? this.items,",
|
|
||||||
" offset: offset ?? this.offset,",
|
|
||||||
" limit: limit ?? this.limit,",
|
|
||||||
" hasMore: hasMore ?? this.hasMore,",
|
|
||||||
" );",
|
|
||||||
" }",
|
|
||||||
"}",
|
|
||||||
" ",
|
|
||||||
"class $1Notifier",
|
|
||||||
" extends FamilyPaginatedAsyncNotifier<$2, $1State, $3> {",
|
|
||||||
" $1Notifier() : super();",
|
|
||||||
" ",
|
|
||||||
" @override",
|
|
||||||
" fetch(arg, offset, limit) async {",
|
|
||||||
" throw UnimplementedError();",
|
|
||||||
" }",
|
|
||||||
" ",
|
|
||||||
" @override",
|
|
||||||
" build(arg) async {",
|
|
||||||
" throw UnimplementedError();",
|
|
||||||
" }",
|
|
||||||
"}",
|
|
||||||
" ",
|
|
||||||
"final ${1/(.*)/${1:/camelcase}/}Provider = AsyncNotifierProviderFamily<$1Notifier, $1State, $3>(",
|
|
||||||
" ()=> $1Notifier(),",
|
|
||||||
");"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
}
|
|
1184
CHANGELOG.md
@ -25,14 +25,13 @@ All types of contributions are encouraged and valued. See the [Table of Contents
|
|||||||
- [Before Submitting an Enhancement](#before-submitting-an-enhancement)
|
- [Before Submitting an Enhancement](#before-submitting-an-enhancement)
|
||||||
- [How Do I Submit a Good Enhancement Suggestion?](#how-do-i-submit-a-good-enhancement-suggestion)
|
- [How Do I Submit a Good Enhancement Suggestion?](#how-do-i-submit-a-good-enhancement-suggestion)
|
||||||
- [Your First Code Contribution](#your-first-code-contribution)
|
- [Your First Code Contribution](#your-first-code-contribution)
|
||||||
- [Submit Translations](#submit-translations)
|
|
||||||
|
|
||||||
## Code of Conduct
|
## Code of Conduct
|
||||||
|
|
||||||
This project and everyone participating in it is governed by the
|
This project and everyone participating in it is governed by the
|
||||||
[Spotube Code of Conduct](https://github.com/KRTirtho/spotube/blob/master/CODE_OF_CONDUCT.md).
|
[Spotube Code of Conduct](https://github.com/KRTirtho/spotube/blob/master/CODE_OF_CONDUCT.md).
|
||||||
By participating, you are expected to uphold this code. Please report unacceptable behavior
|
By participating, you are expected to uphold this code. Please report unacceptable behavior
|
||||||
to krtirtho@gmail.com.
|
to <>.
|
||||||
|
|
||||||
## I Have a Question
|
## I Have a Question
|
||||||
|
|
||||||
@ -119,48 +118,30 @@ Enhancement suggestions are tracked as [GitHub issues](https://github.com/KRTirt
|
|||||||
|
|
||||||
Do the following:
|
Do the following:
|
||||||
|
|
||||||
- Install [Dart](https://dart.dev/get-dart) and [fvm](https://fvm.app/documentation/getting-started/installation)
|
- Download the latest Flutter SDK (>=3.10.0) & enable desktop support
|
||||||
- Install Development dependencies in linux
|
- Install Development dependencies in linux
|
||||||
- Debian (>=12/Bookworm)/Ubuntu
|
- Debian (>=12/Bookworm)/Ubuntu
|
||||||
```bash
|
```bash
|
||||||
$ apt-get install mpv libmpv-dev libappindicator3-1 gir1.2-appindicator3-0.1 libappindicator3-dev libsecret-1-0 libjsoncpp25 libsecret-1-dev libjsoncpp-dev libnotify-bin libnotify-dev avahi-daemon avahi-discover avahi-utils libnss-mdns mdns-scan libwebkit2gtk-4.1-0 libwebkit2gtk-4.1-dev libsoup-3.0-0 libsoup-3.0-dev
|
$ apt-get install mpv libmpv-dev libappindicator3-1 gir1.2-appindicator3-0.1 libappindicator3-dev libsecret-1-0 libjsoncpp25 libsecret-1-dev libjsoncpp-dev libnotify-bin libnotify-dev
|
||||||
```
|
```
|
||||||
- Use `libjsoncpp1` instead of `libjsoncpp25` (for Ubuntu < 22.04)
|
- Use `libjsoncpp1` instead of `libjsoncpp25` (for Ubuntu < 22.04)
|
||||||
- Arch/Manjaro
|
- Arch/Manjaro
|
||||||
```bash
|
```bash
|
||||||
yay -S mpv libappindicator-gtk3 libsecret jsoncpp libnotify avahi nss-mdns mdns-scan webkit2gtk-4.1 libsoup3
|
yay -S mpv libappindicator-gtk3 libsecret jsoncpp libnotify
|
||||||
```
|
```
|
||||||
- Fedora
|
- Fedora
|
||||||
```bash
|
```bash
|
||||||
dnf install mpv mpv-devel libappindicator-gtk3 libappindicator-gtk3-devel libsecret libsecret-devel jsoncpp jsoncpp-devel libnotify libnotify-devel avahi mdns-scan nss-mdns webkit2gtk4.1 webkit2gtk4.1-devel libsoup3 libsoup3-devel
|
dnf install mpv mpv-devel libappindicator-gtk3 libappindicator-gtk3-devel libsecret libsecret-devel jsoncpp jsoncpp-devel libnotify libnotify-devel NetworkManager
|
||||||
```
|
```
|
||||||
- Clone the Repo
|
- Clone the Repo
|
||||||
- Create a `.env` in root of the project following the `.env.example` template
|
- Create a `.env` in root of the project following the `.env.example` template
|
||||||
- Now run the following to bootstrap the project
|
- Now run the following to bootstrap the project
|
||||||
```bash
|
```bash
|
||||||
fvm flutter pub get && fvm dart run build_runner build --delete-conflicting-outputs
|
flutter pub get && dart run build_runner build --delete-conflicting-outputs --enable-experiment=records,patterns
|
||||||
```
|
```
|
||||||
- Finally run these following commands in the root of the project to start the Spotube Locally
|
- Finally run these following commands in the root of the project to start the Spotube Locally
|
||||||
```bash
|
```bash
|
||||||
fvm flutter run -d <window|macos|linux|(<android-device-id>)>
|
flutter run -d <window|macos|linux|(<android-device-id>)>
|
||||||
```
|
```
|
||||||
|
|
||||||
Do debugging/testing/build etc then submit to us with PR against the development branch (dev) & we'll review your code
|
Do debugging/testing/build etc then submit to us with PR against the development branch (master) & we'll review your code
|
||||||
|
|
||||||
|
|
||||||
### Submit Translations
|
|
||||||
|
|
||||||
Make sure you're familiar with [Flutter localization](https://docs.flutter.dev/ui/accessibility-and-localization/internationalization). Then, you can start translating the app by following these steps:
|
|
||||||
|
|
||||||
- Do all the steps in [Your First Code Contribution](#your-first-code-contribution)
|
|
||||||
- Make sure application starts in debug mode
|
|
||||||
- Now, in `lib/l10n/app_<2-letter code of your language>.arb` (create if not exists) add necessary translations
|
|
||||||
> (You can follow the `lib/l10n/app_en.arb` for reference)
|
|
||||||
- If you're adding missing translations, you can check the `/untranslated_messages.json` file to see which messages are missing in your native locale
|
|
||||||
- If you added entirely new translations:
|
|
||||||
- Add `const Locale('<2-letter language code>', '<2-letter ISO country code>')` in `lib/l10n/l10n.dart`'s `static final all = [...]` variable
|
|
||||||
- Uncomment the Map entry of your locale from `lib/collections/language_codes.dart`'s `static final Map isoLangs = {` variable
|
|
||||||
- Now restart (hot restart if running already) the app in debug mode & go to "Settings" > "Language" & see if your locale shows up
|
|
||||||
- If it does, select it & see if the app is translated properly
|
|
||||||
- Now git commit the changes & push
|
|
||||||
- Finally, submit a PR against the development branch (dev) & we'll review your code
|
|
||||||
|
6
LICENSE
@ -1,12 +1,12 @@
|
|||||||
BSD-4-Clause License
|
BSD-4-Clause License
|
||||||
|
|
||||||
Copyright (c) 2025 Kingkor Roy Tirtho. All rights reserved.
|
Copyright (c) 2023 Kingkor Roy Tirtho. All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
3. All advertising materials mentioning features or use of this software must display the following acknowledgement:
|
3. All advertising materials mentioning features or use of this software must display the following acknowledgement:
|
||||||
This product includes software developed by Kingkor Roy Tirtho.
|
This product includes software developed by Kingkor Roy Tirtho.
|
||||||
4. Neither the name of the Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
4. Neither the name of the Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
THIS SOFTWARE IS PROVIDED BY KINGKOR ROY TIRTHO AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KINGKOR ROY TIRTHO AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
THIS SOFTWARE IS PROVIDED BY KINGKOR ROY TIRTHO AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KINGKOR ROY TIRTHO AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
33
Makefile
@ -1,18 +1,31 @@
|
|||||||
INNO_VERSION=6.2.0
|
INNO_VERSION=6.2.0
|
||||||
TEMP_DIR=/tmp/spotube-tar
|
TEMP_DIR=/tmp/spotube-tar
|
||||||
USR_SHARE=deb-struct/usr/share
|
USR_SHARE=deb-struct/usr/share
|
||||||
BUNDLE_DIR=build/linux/${ARCH}/release/bundle
|
BUNDLE_DIR=build/linux/x64/release/bundle
|
||||||
MIRRORLIST=${PWD}/build/mirrorlist
|
MIRRORLIST=${PWD}/build/mirrorlist
|
||||||
|
deb:
|
||||||
|
mkdir -p ${USR_SHARE}/spotube\
|
||||||
|
&& mkdir -p $(USR_SHARE)/applications $(USR_SHARE)/icons/spotube $(USR_SHARE)/spotube $(USR_SHARE)/appdata\
|
||||||
|
&& cp -r $(BUNDLE_DIR)/* $(USR_SHARE)/spotube\
|
||||||
|
&& cp linux/spotube.desktop $(USR_SHARE)/applications/\
|
||||||
|
&& cp linux/com.github.KRTirtho.Spotube.appdata.xml $(USR_SHARE)/appdata/spotube.appdata.xml\
|
||||||
|
&& cp assets/spotube-logo.png $(USR_SHARE)/icons/spotube\
|
||||||
|
&& sed -i 's|com.github.KRTirtho.Spotube|spotube|' $(USR_SHARE)/appdata/spotube.appdata.xml\
|
||||||
|
&& dpkg-deb -b deb-struct/ build/Spotube-linux-x86_64.deb
|
||||||
|
|
||||||
tar:
|
tar:
|
||||||
mkdir -p $(TEMP_DIR)\
|
mkdir -p $(TEMP_DIR)\
|
||||||
&& cp -r $(BUNDLE_DIR)/* $(TEMP_DIR)\
|
&& cp -r $(BUNDLE_DIR)/* $(TEMP_DIR)\
|
||||||
&& cp linux/spotube.desktop $(TEMP_DIR)\
|
&& cp linux/spotube.desktop $(TEMP_DIR)\
|
||||||
&& cp assets/branding/spotube-logo.png $(TEMP_DIR)\
|
&& cp assets/spotube-logo.png $(TEMP_DIR)\
|
||||||
&& cp linux/com.github.KRTirtho.Spotube.appdata.xml $(TEMP_DIR)\
|
&& cp linux/com.github.KRTirtho.Spotube.appdata.xml $(TEMP_DIR)\
|
||||||
&& tar -cJf build/spotube-linux-${VERSION}-${PKG_ARCH}.tar.xz -C $(TEMP_DIR) .\
|
&& tar -cJf build/spotube-linux-${VERSION}-x86_64.tar.xz -C $(TEMP_DIR) .\
|
||||||
&& rm -rf $(TEMP_DIR)
|
&& rm -rf $(TEMP_DIR)
|
||||||
|
|
||||||
|
appimage:
|
||||||
|
appimage-builder --recipe AppImageBuilder.yml\
|
||||||
|
&& mv Spotube-*-x86_64.AppImage build
|
||||||
|
|
||||||
aursrcinfo:
|
aursrcinfo:
|
||||||
docker run -e EXPORT_SRC=1 -v ${PWD}/aur-struct:/pkg -v ${MIRRORLIST}:/etc/pacman.d/mirrorlist:ro whynothugo/makepkg
|
docker run -e EXPORT_SRC=1 -v ${PWD}/aur-struct:/pkg -v ${MIRRORLIST}:/etc/pacman.d/mirrorlist:ro whynothugo/makepkg
|
||||||
|
|
||||||
@ -28,7 +41,6 @@ publishaur:
|
|||||||
|
|
||||||
innoinstall:
|
innoinstall:
|
||||||
powershell curl -o build\installer.exe http://files.jrsoftware.org/is/6/innosetup-${INNO_VERSION}.exe
|
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
|
powershell build\installer.exe /verysilent /allusers /dir=build\iscc
|
||||||
|
|
||||||
inno:
|
inno:
|
||||||
@ -43,16 +55,3 @@ apk:
|
|||||||
|
|
||||||
gensums:
|
gensums:
|
||||||
sh -c scripts/gensums.sh
|
sh -c scripts/gensums.sh
|
||||||
|
|
||||||
migrate:
|
|
||||||
dart run drift_dev make-migrations
|
|
||||||
|
|
||||||
dmg:
|
|
||||||
flutter build macos &&\
|
|
||||||
if [ -f dist/Spotube-macos-universal.dmg ];\
|
|
||||||
then rm dist/Spotube-macos-universal.dmg;\
|
|
||||||
fi &&\
|
|
||||||
appdmg appdmg.json dist/Spotube-macos-universal.dmg
|
|
||||||
|
|
||||||
changelog:
|
|
||||||
git-cliff --unreleased
|
|
242
README.md
@ -1,46 +1,49 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<img width="600" src="assets/branding/spotube_banner.png" alt="Spotube Logo">
|
<img width="600" src="assets/spotube_banner.png" alt="Spotube Logo">
|
||||||
|
|
||||||
A cross-platform extensible open-source music streaming platform.<br>
|
An open source, cross-platform Spotify client that doesn't require Premium nor uses Electron!<br />
|
||||||
Bring your own music metadata/playlist with plugins created by community or by yourself. A small step towards the decentralized music streaming era!
|
That uses Spotify's data/discovery API and YouTube (or Piped.video) as audio source
|
||||||
|
|
||||||
Btw it's not just another Electron app 😉
|
<a href="https://spotube.netlify.app"><img alt="Visit the website" height="56" src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/documentation/website_vector.svg"></a>
|
||||||
|
<a href="https://discord.gg/uJ94vxB6vg"><img alt="Discord Server" height="56" src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/social/discord-plural_vector.svg"></a>
|
||||||
|
|
||||||
<a href="https://spotube.krtirtho.dev"><img alt="Visit the website" height="56" src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/documentation/website_vector.svg"></a>
|
<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://discord.gg/uJ94vxB6vg"><img alt="Discord Server" height="56" src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/social/discord-plural_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>
|
||||||
|
|
||||||
<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://opencollective.com/spotube"><img src="https://opencollective.com/spotube/donate/button.png?color=blue" alt="Donate to our Open Collective" height="45"></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>
|

|
||||||
|
|
||||||
---
|

|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## 🌃 Features
|
## 🌃 Features
|
||||||
|
|
||||||
- 🧩 Plugin powered, supports any platform or custom music service through plugins.
|
- 🚫 No ads, thanks to the use of public & free Spotify and YT Music APIs¹
|
||||||
- 🗺️ Community driven plugins for popular platforms or create your own.
|
- ⬇️ Downloadable tracks
|
||||||
- ⬇️ Freely downloadable tracks with tagged metadata.
|
- 🖥️ 📱 Cross-platform support
|
||||||
- 🖥️ 📱 Cross-platform support.
|
- 🪶 Small size & less data usage
|
||||||
- 🪶 Small size & less data usage.
|
- 🕵️ Anonymous/guest login
|
||||||
- 🕒 Time synced lyrics regardless of the plugin support.
|
- 🕒 Time synced lyrics
|
||||||
- ✋ No telemetry, diagnostics or user data collection.
|
- ✋ No telemetry, diagnostics or user data collection
|
||||||
- 🚀 Native performance.
|
- 🚀 Native performance
|
||||||
- 📖 Open source/libre software.
|
- 📖 Open source/libre software
|
||||||
- 🔉 Playback control is done locally, not on the server.
|
- 🔉 Playback control is done locally, not on the server
|
||||||
|
|
||||||
|
**¹** It is still **recommended** to support the creators by watching/liking/subscribing to the artists' YouTube channels or liking their tracks on Spotify (or purchasing a Spotify Premium subscription too).
|
||||||
|
|
||||||
|
### ❌ Unsupported features
|
||||||
|
|
||||||
|
- 🗣️ **Spotify Shows & Podcasts:** Shows and Podcasts can **never be supported** because the audio tracks are _only_ available on Spotify and accessing them would require Spotify Premium.
|
||||||
|
- 🎧 **Spotify Listen Along:** [Coming soon!](https://github.com/KRTirtho/spotube/issues/8)
|
||||||
|
|
||||||
## 📜 ⬇️ Installation guide
|
## 📜 ⬇️ Installation guide
|
||||||
|
|
||||||
New versions usually release every 3-4 months.<br />
|
New releases usually appear after 3-4 months.<br />
|
||||||
This handy table lists all the methods you can use to install Spotube:
|
This handy table lists all methods you can use to install Spotube:
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
@ -58,13 +61,17 @@ This handy table lists all the methods you can use to install Spotube:
|
|||||||
<td>MacOS</td>
|
<td>MacOS</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="https://github.com/KRTirtho/spotube/releases/latest/download/Spotube-macos-universal.dmg">
|
<a href="https://github.com/KRTirtho/spotube/releases/latest/download/Spotube-macos-universal.dmg">
|
||||||
<img width="220" alt="MacOS Download" src="https://memory-map.com/wp-content/uploads/download-mac-OS-01.svg">
|
<img width="220" alt="MacOS Download" src="https://reachify.io/wp-content/uploads/2018/09/mac-download-button-1.png">
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Android</td>
|
<td>Android</td>
|
||||||
<td>
|
<td>
|
||||||
|
<a href="https://play.google.com/store/apps/details?id=oss.krtirtho.spotube">
|
||||||
|
<img width="220" alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png">
|
||||||
|
</a>
|
||||||
|
<br>
|
||||||
<a href="https://github.com/KRTirtho/spotube/releases/latest/download/Spotube-android-all-arch.apk">
|
<a href="https://github.com/KRTirtho/spotube/releases/latest/download/Spotube-android-all-arch.apk">
|
||||||
<img width="220" alt="APK download" src="https://user-images.githubusercontent.com/114044633/223920025-83687de0-e463-4c5d-8122-e06e4bb7d40c.png">
|
<img width="220" alt="APK download" src="https://user-images.githubusercontent.com/114044633/223920025-83687de0-e463-4c5d-8122-e06e4bb7d40c.png">
|
||||||
</a>
|
</a>
|
||||||
@ -74,19 +81,6 @@ This handy table lists all the methods you can use to install Spotube:
|
|||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<tr>
|
|
||||||
<td>iOS</td>
|
|
||||||
<td>
|
|
||||||
<a href="https://github.com/KRTirtho/spotube/releases/latest/download/Spotube-iOS.ipa">
|
|
||||||
<img width="220" alt="Download iOS IPA" src="https://github.com/user-attachments/assets/3e50d93d-fb39-435c-be6b-337745f7c423">
|
|
||||||
</a>
|
|
||||||
<br/>
|
|
||||||
<blockquote style="color:red">
|
|
||||||
*iPA file only. Requires sideloading with <a href="https://altstore.io/">AltStore</a> or similar tools.
|
|
||||||
</blockquote>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Flatpak</td>
|
<td>Flatpak</td>
|
||||||
<td>
|
<td>
|
||||||
@ -98,7 +92,12 @@ This handy table lists all the methods you can use to install Spotube:
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>AppImage</td>
|
<td>AppImage</td>
|
||||||
<td>AppImage's lacking stability led to it's temporary removal. More information at https://github.com/KRTirtho/spotube/issues/1082</td>
|
<td>
|
||||||
|
<a href="https://github.com/KRTirtho/spotube/releases/latest/download/Spotube-linux-x86_64.AppImage">
|
||||||
|
<img width="220" alt="Download AppImage" src="https://user-images.githubusercontent.com/61944859/169455015-13385466-8901-48fe-ba90-b62d58b0be64.png">
|
||||||
|
</a>
|
||||||
|
<p><b>Note:</b> <a href="https://github.com/TheAssassin/AppImageLauncher">AppimageLauncher</a> is required!</p>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Debian/Ubuntu</td>
|
<td>Debian/Ubuntu</td>
|
||||||
@ -106,7 +105,7 @@ This handy table lists all the methods you can use to install Spotube:
|
|||||||
<a href="https://github.com/KRTirtho/spotube/releases/latest/download/Spotube-linux-x86_64.deb">
|
<a href="https://github.com/KRTirtho/spotube/releases/latest/download/Spotube-linux-x86_64.deb">
|
||||||
<img width="220" alt="Debian/Ubuntu Download" src="https://user-images.githubusercontent.com/61944859/169097994-e92aff78-fd75-4c93-b6e4-f072a4b5a7ed.png">
|
<img width="220" alt="Debian/Ubuntu Download" src="https://user-images.githubusercontent.com/61944859/169097994-e92aff78-fd75-4c93-b6e4-f072a4b5a7ed.png">
|
||||||
</a>
|
</a>
|
||||||
<p>Then run: <code>sudo apt install ./Spotube-linux-x86_64.deb</code></p>
|
<p>Then run: <code>sudo apt install Spotube-linux-x86_64.deb</code></p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -134,15 +133,6 @@ This handy table lists all the methods you can use to install Spotube:
|
|||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td>Macos - <a href="https://brew.sh">Homebrew</a></td>
|
|
||||||
<td>
|
|
||||||
<pre lang="bash">
|
|
||||||
brew tap krtirtho/apps
|
|
||||||
brew install --cask spotube
|
|
||||||
</pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Windows - <a href="https://chocolatey.org">Chocolatey</a></td>
|
<td>Windows - <a href="https://chocolatey.org">Chocolatey</a></td>
|
||||||
<td>
|
<td>
|
||||||
@ -178,6 +168,7 @@ You can compile Spotube's source code by [following these instructions](CONTRIBU
|
|||||||
|
|
||||||
- [Kingkor Roy Tirtho](https://github.com/KRTirtho) - The Founder, Maintainer and Lead Developer
|
- [Kingkor Roy Tirtho](https://github.com/KRTirtho) - The Founder, Maintainer and Lead Developer
|
||||||
- [Owen Connor](https://github.com/owencz1998) - The Cool Discord Moderator
|
- [Owen Connor](https://github.com/owencz1998) - The Cool Discord Moderator
|
||||||
|
- [RaptaG](https://github.com/RaptaG) - The GitHub Moderator and Community Manager
|
||||||
- [Piotr Rogowski](https://github.com/karniv00l) - The MacOS Developer
|
- [Piotr Rogowski](https://github.com/karniv00l) - The MacOS Developer
|
||||||
- [Rusty Apple](https://github.com/RustyApple) - The Mysterious Unknown Guy
|
- [Rusty Apple](https://github.com/RustyApple) - The Mysterious Unknown Guy
|
||||||
|
|
||||||
@ -185,153 +176,108 @@ You can compile Spotube's source code by [following these instructions](CONTRIBU
|
|||||||
|
|
||||||
Spotube is open source and licensed under the [BSD-4-Clause](/LICENSE) License.
|
Spotube is open source and licensed under the [BSD-4-Clause](/LICENSE) License.
|
||||||
|
|
||||||
If you are curious, you can [read the reason of choosing this license](https://dev.to/krtirtho/choosing-open-source-license-wisely-1m3p).
|
If you are concerned, you can [read the reason of choosing this license](https://dev.to/krtirtho/choosing-open-source-license-wisely-1m3p).
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>
|
<summary>
|
||||||
<h2><code>[Click to show]</code> 🙏 Services/Package/Plugin Credits</h2>
|
<h2><code>[Click to show]</code> 🙏 Library/Plugin/Framework Credits</h2>
|
||||||
</summary>
|
</summary>
|
||||||
|
|
||||||
### Services
|
|
||||||
|
|
||||||
1. [Flutter](https://flutter.dev) - Flutter transforms the app development process. Build, test, and deploy beautiful mobile, web, desktop, and embedded apps from a single codebase
|
1. [Flutter](https://flutter.dev) - Flutter transforms the app development process. Build, test, and deploy beautiful mobile, web, desktop, and embedded apps from a single codebase
|
||||||
1. [MPV](https://mpv.io) - mpv is a free (as in freedom) media player for the command line. It supports a wide variety of media file formats, audio and video codecs, and subtitle types.
|
1. [Spotify API](https://developer.spotify.com/documentation/web-api) - The Spotify Web API is a RESTful API that provides access to Spotify data
|
||||||
1. [Musicbrainz](https://musicbrainz.org) - MusicBrainz is a MetaBrainz project that aims to create a collaborative music database that is similar to the freedb project.
|
|
||||||
1. [Listenbrainz](https://listenbrainz.org) - ListenBrainz is a open-source project by the MetaBrainz Foundation that allows users to crowdsource and publicly store their digital music listening data.
|
|
||||||
1. [Piped](https://piped-docs.kavin.rocks/) - Piped is a privacy friendly alternative YouTube frontend, which is efficient and scalable by design.
|
1. [Piped](https://piped-docs.kavin.rocks/) - Piped is a privacy friendly alternative YouTube frontend, which is efficient and scalable by design.
|
||||||
1. [Invidious](https://invidious.io/) - Invidious is an open source alternative front-end to YouTube.
|
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. [yt-dlp](https://github.com/yt-dlp/yt-dlp) - A feature-rich command-line audio/video downloader.
|
|
||||||
1. [NewPipeExtractor](https://github.com/TeamNewPipe/NewPipeExtractor) - NewPipe's core library for extracting data from streaming sites.
|
|
||||||
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. [LRCLib](https://lrclib.net/) - A public synced lyric API.
|
|
||||||
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. [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. [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
|
1. [Flatpak](https://flatpak.org) - Flatpak is a utility for software deployment and package management for Linux
|
||||||
1. [SponsorBlock](https://sponsor.ajay.app) - SponsorBlock is an open-source crowdsourced browser extension and open API for skipping sponsor segments in YouTube videos.
|
1. [SponsorBlock](https://sponsor.ajay.app) - SponsorBlock is an open-source crowdsourced browser extension and open API for skipping sponsor segments in YouTube videos.
|
||||||
1. [Inno Setup](https://jrsoftware.org/isinfo.php) - Inno Setup is a free installer for Windows programs by Jordan Russell and Martijn Laan
|
1. [Inno Setup](https://jrsoftware.org/isinfo.php) - Inno Setup is a free installer for Windows programs by Jordan Russell and Martijn Laan
|
||||||
1. [F-Droid](https://f-droid.org) - F-Droid is an installable catalogue of FOSS (Free and Open Source Software) applications for the Android platform. The client makes it easy to browse, install, and keep track of updates on your device
|
1. [F-Droid](https://f-droid.org) - F-Droid is an installable catalogue of FOSS (Free and Open Source Software) applications for the Android platform. The client makes it easy to browse, install, and keep track of updates on your device
|
||||||
1. [LastFM](https://last.fm) - Last.fm is a music streaming and discovery platform that helps users discover and share new music. It tracks users' music listening habits across many devices and platforms.
|
|
||||||
|
|
||||||
### Dependencies
|
|
||||||
|
|
||||||
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. [args](https://pub.dev/packages/args) - Library for defining parsers for parsing raw command-line arguments into a set of options and values using GNU and POSIX style options.
|
1. [args](https://pub.dev/packages/args) - Library for defining parsers for parsing raw command-line arguments into a set of options and values using GNU and POSIX style options.
|
||||||
1. [async](https://pub.dev/packages/async) - Utility functions and classes related to the 'dart:async' library.
|
1. [async](https://pub.dev/packages/async) - Utility functions and classes related to the 'dart:async' library.
|
||||||
1. [audio_service](https://pub.dev/packages/audio_service) - Flutter plugin to play audio in the background while the screen is off.
|
1. [audio_service](https://github.com/ryanheise/audio_service/tree/master/audio_service) - Flutter plugin to play audio in the background while the screen is off.
|
||||||
1. [audio_service_mpris](https://github.com/bdrazhzhov/audio-service-mpris) - audio_service platform interface supporting Media Player Remote Interfacing Specification.
|
|
||||||
1. [audio_session](https://github.com/ryanheise/audio_session) - Sets the iOS audio session category and Android audio attributes for your app, and manages your app's audio focus, mixing and ducking behaviour.
|
1. [audio_session](https://github.com/ryanheise/audio_session) - Sets the iOS audio session category and Android audio attributes for your app, and manages your app's audio focus, mixing and ducking behaviour.
|
||||||
1. [auto_route](https://github.com/Milad-Akarie/auto_route_library) - AutoRoute is a declarative routing solution, where everything needed for navigation is automatically generated for you.
|
|
||||||
1. [auto_size_text](https://github.com/leisim/auto_size_text) - Flutter widget that automatically resizes text to fit perfectly within its bounds.
|
1. [auto_size_text](https://github.com/leisim/auto_size_text) - Flutter widget that automatically resizes text to fit perfectly within its bounds.
|
||||||
1. [bonsoir](https://bonsoir.skyost.eu) - A Zeroconf library that allows you to discover network services and to broadcast your own. Based on Apple Bonjour and Android NSD.
|
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. [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. [connectivity_plus](https://github.com/fluttercommunity/plus_plugins) - Flutter plugin for discovering the state of the network (WiFi & mobile/cellular) connectivity on Android and iOS.
|
1. [collection](https://pub.dev/packages/collection) - Collections and utilities functions and classes related to collections.
|
||||||
1. [device_info_plus](https://github.com/fluttercommunity/plus_plugins) - Flutter plugin providing detailed information about the device (make, model, etc.), and Android or iOS version the app is running on.
|
1. [cupertino_icons](https://pub.dev/packages/cupertino_icons) - Default icons asset for Cupertino widgets based on Apple styled icons
|
||||||
1. [dio](https://github.com/cfug/dio) - A powerful HTTP networking package,supports Interceptors,Aborting and canceling a request,Custom adapters, Transformers, etc.
|
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.
|
||||||
1. [drift](https://drift.simonbinder.eu/) - Drift is a reactive library to store relational data in Dart and Flutter applications.
|
|
||||||
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. [encrypt](https://pub.dev/packages/encrypt) - A set of high-level APIs over PointyCastle for two-way cryptography.
|
|
||||||
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. [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_picker](https://github.com/miguelpruivo/plugins_flutter_file_picker) - A package that allows you to use a native file explorer to pick single or multiple absolute file paths, with extension filtering support.
|
1. [file_picker](https://github.com/miguelpruivo/plugins_flutter_file_picker) - A package that allows you to use a native file explorer to pick single or multiple absolute file paths, with extension filtering support.
|
||||||
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_connectivity_plus_adapter](https://fl-query.vercel.app) - Connectivity Plus adapter for FlQuery Connectivity
|
||||||
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. [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_cache_manager](https://github.com/Baseflow/flutter_cache_manager/tree/master/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_discord_rpc](https://pub.dev/packages/flutter_discord_rpc) - Discord RPC support for Flutter desktop platforms
|
|
||||||
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.
|
|
||||||
1. [flutter_feather_icons](https://github.com/muj-programmer/flutter_feather_icons) - Feather is a collection of simply beautiful open source icons. Each icon is designed on a 24x24 grid with an emphasis on simplicity, consistency and usability.
|
1. [flutter_feather_icons](https://github.com/muj-programmer/flutter_feather_icons) - Feather is a collection of simply beautiful open source icons. Each icon is designed on a 24x24 grid with an emphasis on simplicity, consistency and usability.
|
||||||
1. [flutter_form_builder](https://github.com/flutter-form-builder-ecosystem) - This package helps in creation of forms in Flutter by removing the boilerplate code, reusing validation, react to changes, and collect final user input.
|
|
||||||
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_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_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_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 reactive caching and data-binding framework. Riverpod makes working with asynchronous code a breeze.
|
1. [flutter_riverpod](https://riverpod.dev) - A simple way to access state from anywhere in your application while robust and testable.
|
||||||
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_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_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_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. [flutter_undraw](https://github.com/KRTirtho/flutter_undraw) - Undraw.co Illustrations for Flutter with customization options
|
|
||||||
1. [form_builder_validators](https://github.com/flutter-form-builder-ecosystem) - Form Builder Validators set of validators for FlutterFormBuilder. Provides common validators and a way to make your own.
|
|
||||||
1. [form_validator](https://github.com/TheMisir/form-validator) - Simplest form validation library for flutter's form field widgets
|
|
||||||
1. [freezed_annotation](https://pub.dev/packages/freezed_annotation) - Annotations for the freezed code-generator. This package does nothing without freezed too.
|
|
||||||
1. [fuzzywuzzy](https://github.com/sphericalkat/dart-fuzzywuzzy) - An implementation of the popular fuzzywuzzy package in Dart, to suit all your fuzzy string matching/searching needs!
|
1. [fuzzywuzzy](https://github.com/sphericalkat/dart-fuzzywuzzy) - An implementation of the popular fuzzywuzzy package in Dart, to suit all your fuzzy string matching/searching needs!
|
||||||
1. [gap](https://github.com/letsar/gap) - Flutter widgets for easily adding gaps inside Flex widgets such as Columns and Rows or scrolling views.
|
1. [go_router](https://pub.dev/packages/go_router) - A declarative router for Flutter based on Navigation 2 supporting deep linking, data-driven routes and more
|
||||||
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. [home_widget](https://pub.dev/packages/home_widget) - A plugin to provide a common interface for creating HomeScreen Widgets for Android and iOS.
|
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 reactive caching and data-binding framework. Riverpod makes working with asynchronous code a breeze.
|
1. [hooks_riverpod](https://riverpod.dev) - A simple way to access state from anywhere in your application while robust and testable.
|
||||||
1. [html](https://pub.dev/packages/html) - APIs for parsing and manipulating HTML content outside the browser.
|
1. [html](https://pub.dev/packages/html) - APIs for parsing and manipulating HTML content outside the browser.
|
||||||
1. [html_unescape](https://github.com/filiph/html_unescape) - A small library for un-escaping HTML. Supports all Named Character References, Decimal Character References and Hexadecimal Character References.
|
|
||||||
1. [http](https://pub.dev/packages/http) - A composable, multi-platform, Future-based API for HTTP requests.
|
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. [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. [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. [invidious](https://pub.dev/packages/invidious) - Invidious API client for Dart and Flutter.
|
1. [introduction_screen](https://github.com/pyozer/introduction_screen) - Introduction/Onboarding package for flutter app with some customizations possibilities
|
||||||
1. [jiosaavn](https://github.com/KRTirtho/jiosaavn) - Unofficial API client for jiosaavn.com
|
|
||||||
1. [json_annotation](https://pub.dev/packages/json_annotation) - Classes and helper functions that support JSON code generation via the `json_serializable` package.
|
1. [json_annotation](https://pub.dev/packages/json_annotation) - Classes and helper functions that support JSON code generation via the `json_serializable` package.
|
||||||
1. [local_notifier](https://github.com/leanflutter/local_notifier) - This plugin allows Flutter desktop apps to displaying local notifications.
|
|
||||||
1. [logger](https://pub.dev/packages/logger) - Small, easy to use and extensible logger which prints beautiful logs.
|
1. [logger](https://pub.dev/packages/logger) - Small, easy to use and extensible logger which prints beautiful logs.
|
||||||
1. [logging](https://pub.dev/packages/logging) - Provides APIs for debugging and error logging, similar to loggers in other languages, such as the Closure JS Logger and java.util.logging.Logger.
|
1. [media_kit](https://github.com/alexmercerind/media_kit) - A complete video & audio playback library for Flutter & Dart. Performant, stable, feature-proof & modular.
|
||||||
1. [lrc](https://pub.dev/packages/lrc) - A Dart-only package that creates, parses, and handles LRC, which is a format that stores song lyrics.
|
1. [media_kit_libs_android_audio](https://github.com/alexmercerind/media_kit.git) - Android package providing audio (only) native libraries for package:media_kit.
|
||||||
1. [media_kit](https://github.com/media-kit/media-kit) - A cross-platform video player & audio player for Flutter & Dart. Performant, stable, feature-proof & modular.
|
1. [media_kit_libs_ios_audio](https://github.com/alexmercerind/media_kit.git) - iOS package providing audio native libraries for package:media_kit.
|
||||||
1. [media_kit_libs_audio](https://github.com/media-kit/media-kit.git) - package:media_kit audio (only) playback native libraries for all platforms.
|
1. [media_kit_libs_linux](https://github.com/alexmercerind/media_kit.git) - GNU/Linux dependency package for package:media_kit. Necessary for initialization.
|
||||||
1. [metadata_god](https://pub.dev/packages/metadata_god) - Plugin for retrieving and writing audio tags/metadata from audio files
|
1. [media_kit_libs_macos_audio](https://github.com/alexmercerind/media_kit.git) - macOS package providing audio native libraries for package:media_kit.
|
||||||
|
1. [media_kit_libs_windows_audio](https://github.com/alexmercerind/media_kit.git) - Windows package providing audio (only) native libraries for package:media_kit.
|
||||||
|
1. [metadata_god](https://github.com/KRTirtho/metadata_god) - Plugin for retrieving and writing audio tags/metadata from audio files
|
||||||
1. [mime](https://pub.dev/packages/mime) - Utilities for handling media (MIME) types, including determining a type from a file extension and file contents.
|
1. [mime](https://pub.dev/packages/mime) - Utilities for handling media (MIME) types, including determining a type from a file extension and file contents.
|
||||||
1. [open_file](https://pub.dev/packages/open_file) - A plug-in that can call native APP to open files with string result in flutter, support iOS(UTI) / android(intent) / PC(ffi) / web(dart:html)
|
1. [package_info_plus](https://plus.fluttercommunity.dev/) - Flutter plugin for querying information about the application package, such as CFBundleVersion on iOS or versionCode on Android.
|
||||||
1. [package_info_plus](https://github.com/fluttercommunity/plus_plugins) - Flutter plugin for querying information about the application package, such as CFBundleVersion on iOS or versionCode on Android.
|
|
||||||
1. [palette_generator](https://pub.dev/packages/palette_generator) - Flutter package for generating palette colors from a source image.
|
1. [palette_generator](https://pub.dev/packages/palette_generator) - Flutter package for generating palette colors from a source image.
|
||||||
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](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. [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. [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. [riverpod](https://riverpod.dev) - A reactive caching and data-binding framework. Riverpod makes working with asynchronous code a breeze.
|
|
||||||
1. [scroll_to_index](https://github.com/quire-io/scroll-to-index) - Scroll to a specific child of any scrollable widget in Flutter
|
1. [scroll_to_index](https://github.com/quire-io/scroll-to-index) - Scroll to a specific child of any scrollable widget in Flutter
|
||||||
1. [shadcn_flutter](https://github.com/sunarya-thito/shadcn_flutter) - Beautifully designed components from Shadcn/UI is now available for Flutter
|
|
||||||
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. [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. [shelf](https://pub.dev/packages/shelf) - A model for web server middleware that encourages composition and easy reuse.
|
1. [sidebarx](https://github.com/Frezyx/sidebarx) - flutter multiplatform navigation sidebar / side navigationbar / drawer widget
|
||||||
1. [shelf_router](https://pub.dev/packages/shelf_router) - A convenient request router for the shelf web-framework, with support for URL-parameters, nested routers and routers generated from source annotations.
|
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. [shelf_web_socket](https://pub.dev/packages/shelf_web_socket) - A shelf handler that wires up a listener for every connection.
|
1. [smtc_windows](https://github.com/KRTirtho/smtc_windows) - Windows `SystemMediaTransportControls` implementation for Flutter giving access to Windows OS Media Control applet.
|
||||||
1. [simple_icons](https://teavelopment.com/) - The Simple Icon pack available as Flutter Icons. Provides over 1500 Free SVG icons for popular brands.
|
1. [spotify](https://github.com/rinukkusu/spotify-dart) - An incomplete dart library for interfacing with the Spotify Web API.
|
||||||
1. [skeletonizer](https://github.com/Milad-Akarie/skeletonizer) - Converts already built widgets into skeleton loaders with no extra effort.
|
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. [sliding_up_panel](https://github.com/akshathjain/sliding_up_panel) - A draggable Flutter widget that makes implementing a SlidingUpPanel much easier!
|
|
||||||
1. [sliver_tools](https://github.com/Kavantix) - A set of useful sliver tools that are missing from the flutter framework
|
|
||||||
1. [smtc_windows](https://pub.dev/packages/smtc_windows) - Windows `SystemMediaTransportControls` implementation for Flutter giving access to Windows OS Media Control applet.
|
|
||||||
1. [sqlite3](https://github.com/simolus3/sqlite3.dart/tree/main/sqlite3) - Provides lightweight yet convenient bindings to SQLite by using dart:ffi
|
|
||||||
1. [sqlite3_flutter_libs](https://github.com/simolus3/sqlite3.dart/tree/main/sqlite3_flutter_libs) - Flutter plugin to include native sqlite3 libraries with your app
|
|
||||||
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://github.com/bdlukaa/system_theme/tree/master/system_theme) - A plugin to get the current system theme info. Supports Android, Web, Windows, Linux and macOS
|
|
||||||
1. [test](https://pub.dev/packages/test) - A full featured library for writing and running Dart tests across platforms.
|
|
||||||
1. [timezone](https://pub.dev/packages/timezone) - Time zone database and time zone aware DateTime.
|
|
||||||
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. [titlebar_buttons](https://github.com/gtk-flutter/titlebar_buttons) - A package which provides most of the titlebar buttons from windows, linux and macos.
|
||||||
1. [tray_manager](https://github.com/leanflutter/tray_manager) - This plugin allows Flutter desktop apps to defines system tray.
|
|
||||||
1. [url_launcher](https://pub.dev/packages/url_launcher) - Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes.
|
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://pub.dev/packages/uuid) - RFC4122 (v1, v4, v5, v6, v7, v8) UUID Generator and Parser for Dart
|
1. [uuid](https://github.com/Daegalus/dart-uuid) - RFC4122 (v1, v4, v5) UUID Generator and Parser for all Dart platforms (Web, VM, Flutter)
|
||||||
1. [version](https://github.com/dartninja/version) - Provides a simple class for parsing and comparing semantic versions as defined by http://semver.org/
|
1. [version](https://github.com/dartninja/version) - Provides a simple class for parsing and comparing semantic versions as defined by http://semver.org/
|
||||||
1. [very_good_infinite_list](https://github.com/VeryGoodOpenSource/very_good_infinite_list) - A library for easily displaying paginated data, created by Very Good Ventures. Great for activity feeds, news feeds, and more.
|
|
||||||
1. [visibility_detector](https://pub.dev/packages/visibility_detector) - A widget that detects the visibility of its child and notifies a callback.
|
1. [visibility_detector](https://pub.dev/packages/visibility_detector) - A widget that detects the visibility of its child and notifies a callback.
|
||||||
1. [web_socket_channel](https://pub.dev/packages/web_socket_channel) - StreamChannel wrappers for WebSockets. Provides a cross-platform WebSocketChannel API, a cross-platform implementation of that API that communicates over an underlying StreamChannel.
|
|
||||||
1. [wikipedia_api](https://github.com/KRTirtho/wikipedia_api) - Wikipedia API for dart and flutter
|
|
||||||
1. [win32_registry](https://pub.dev/packages/win32_registry) - A package that provides a friendly Dart API for accessing the Windows Registry.
|
|
||||||
1. [window_manager](https://github.com/leanflutter/window_manager) - This plugin allows Flutter desktop apps to resizing and repositioning the window.
|
1. [window_manager](https://github.com/leanflutter/window_manager) - This plugin allows Flutter desktop apps to resizing and repositioning the window.
|
||||||
|
1. [piped_client](https://github.com/KRTirtho/piped_client) - API Client for piped.video
|
||||||
|
1. [supabase_flutter](https://supabase.com) - Flutter integration for Supabase. This package makes it simple for developers to build secure and scalable products.
|
||||||
|
1. [device_preview](https://github.com/aloisdeniel/flutter_device_preview) - Approximate how your Flutter app looks and performs on another device.
|
||||||
|
1. [media_kit_native_event_loop](https://github.com/alexmercerind/media_kit) - Platform specific threaded event handling for media_kit. Enables support for higher number of concurrent instances.
|
||||||
|
1. [dbus](https://github.com/canonical/dbus.dart) - A native Dart implementation of the D-Bus message bus client. This package allows Dart applications to directly access services on the Linux desktop.
|
||||||
|
1. [background_downloader](https://pub.dev/packages/background_downloader) - A multi-platform background file downloader and uploader. Define the task, enqueue and monitor progress
|
||||||
|
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. [disable_battery_optimization](https://github.com/pvsvamsi/Disable-Battery-Optimizations) - Flutter plugin to check and disable battery optimizations. Also shows custom steps to disable the optimizations in devices like mi, xiaomi, samsung, oppo, huawei, oneplus etc
|
||||||
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. [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. [http_parser](https://pub.dev/packages/http_parser) - A platform-independent package for parsing and serializing HTTP formats.
|
|
||||||
1. [collection](https://pub.dev/packages/collection) - Collections and utilities functions and classes related to collections.
|
|
||||||
1. [otp_util](https://github.com/dushiling) - otp_util is a dart package to generate and verify one-time passwords,it It provides two methods TOPT and HOTP.They are Time-based OTPs and Counter-based OTPs.
|
|
||||||
1. [dio_http2_adapter](https://github.com/cfug/dio) - An adapter that combines HTTP/2 and dio. Supports reusing connections, header compression, etc.
|
|
||||||
1. [build_runner](https://pub.dev/packages/build_runner) - A build system for Dart code generation and modular compilation.
|
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. [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.
|
||||||
1. [flutter_gen_runner](https://github.com/FlutterGen/flutter_gen) - The Flutter code generator for your assets, fonts, colors, … — Get rid of all String-based APIs.
|
1. [flutter_gen_runner](https://github.com/FlutterGen/flutter_gen) - The Flutter code generator for your assets, fonts, colors, … — Get rid of all String-based APIs.
|
||||||
1. [flutter_launcher_icons](https://github.com/fluttercommunity/flutter_launcher_icons) - A package which simplifies the task of updating your Flutter app's launcher icon.
|
1. [flutter_launcher_icons](https://github.com/fluttercommunity/flutter_launcher_icons) - A package which simplifies the task of updating your Flutter app's launcher icon.
|
||||||
1. [flutter_lints](https://pub.dev/packages/flutter_lints) - Recommended lints for Flutter apps, packages, and plugins to encourage good coding practices.
|
1. [flutter_lints](https://pub.dev/packages/flutter_lints) - Recommended lints for Flutter apps, packages, and plugins to encourage good coding practices.
|
||||||
|
1. [hive_generator](https://github.com/hivedb/hive/tree/master/hive_generator) - Extension for Hive. Automatically generates TypeAdapters to store any class.
|
||||||
1. [json_serializable](https://pub.dev/packages/json_serializable) - Automatically generate code for converting to and from JSON by annotating Dart classes.
|
1. [json_serializable](https://pub.dev/packages/json_serializable) - Automatically generate code for converting to and from JSON by annotating Dart classes.
|
||||||
1. [freezed](https://pub.dev/packages/freezed) - Code generation for immutable classes that has a simple syntax/API without compromising on the features.
|
|
||||||
1. [process_run](https://github.com/tekartik/process_run.dart/blob/master/packages/process_run) - Process run helpers for Linux/Win/Mac and which like feature for finding executables.
|
|
||||||
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. [pub_api_client](https://github.com/leoafarias/pub_api_client) - An API Client for Pub to interact with public package information.
|
1. [pub_api_client](https://github.com/leoafarias/pub_api_client) - An API Client for Pub to interact with public package information.
|
||||||
1. [xml](https://github.com/renggli/dart-xml) - A lightweight library for parsing, traversing, querying, transforming and building XML documents.
|
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. [io](https://pub.dev/packages/io) - Utilities for the Dart VM Runtime including support for ANSI colors, file copying, and standard exit code values.
|
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. [drift_dev](https://drift.simonbinder.eu/) - Dev-dependency for users of drift. Contains the generator and development tools.
|
1. [flutter_desktop_tools](https://github.com/KRTirtho/flutter_desktop_tools) - Essential collection of tools for flutter desktop app development
|
||||||
1. [auto_route_generator](https://github.com/Milad-Akarie/auto_route_library) - AutoRoute is a declarative routing solution, where everything needed for navigation is automatically generated for you.
|
1. [window_size](https://github.com/google/flutter-desktop-embedding.git) - Allows resizing and repositioning the window containing Flutter.
|
||||||
1. [desktop_webview_window](https://github.com/MixinNetwork/flutter-plugins/tree/main/packages/desktop_webview_window) - Show a webview window on your flutter desktop application.
|
|
||||||
1. [disable_battery_optimization](https://github.com/pvsvamsi/Disable-Battery-Optimizations) - Flutter plugin to check and disable battery optimizations. Also shows custom steps to disable the optimizations in devices like mi, xiaomi, samsung, oppo, huawei, oneplus etc
|
|
||||||
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.
|
|
||||||
1. [flutter_broadcasts](https://github.com/KRTirtho/flutter_broadcasts.git) - A plugin for sending and receiving broadcasts with Android intents and iOS notifications.
|
|
||||||
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. [yt_dlp_dart](https://github.com/KRTirtho/yt_dlp_dart.git) - yt-dlp binding in Dart
|
|
||||||
1. [flutter_new_pipe_extractor](https://github.com/KRTirtho/flutter_new_pipe_extractor) - NewPipeExtractor binding for Flutter (Android only)
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<div align="center"><h4>© Copyright Spotube 2025</h4></div>
|
<div align="center"><h4>© Copyright Spotube 2023</h4></div>
|
||||||
|
@ -25,16 +25,10 @@ linter:
|
|||||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||||
file_names: false
|
file_names: false
|
||||||
avoid_renaming_method_parameters: false
|
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
# Additional information about this file can be found at
|
||||||
# https://dart.dev/guides/language/analysis-options
|
# https://dart.dev/guides/language/analysis-options
|
||||||
analyzer:
|
analyzer:
|
||||||
errors:
|
enable-experiment:
|
||||||
invalid_annotation_target: ignore
|
- records
|
||||||
exclude:
|
- patterns
|
||||||
- "**.freezed.dart"
|
|
||||||
- "**.g.dart"
|
|
||||||
- "**.gr.dart"
|
|
||||||
- "**/generated_plugin_registrant.dart"
|
|
||||||
- test/**/*.dart
|
|
@ -1,9 +1,3 @@
|
|||||||
plugins {
|
|
||||||
id "com.android.application"
|
|
||||||
id "kotlin-android"
|
|
||||||
id "dev.flutter.flutter-gradle-plugin"
|
|
||||||
}
|
|
||||||
|
|
||||||
def localProperties = new Properties()
|
def localProperties = new Properties()
|
||||||
def localPropertiesFile = rootProject.file('local.properties')
|
def localPropertiesFile = rootProject.file('local.properties')
|
||||||
if (localPropertiesFile.exists()) {
|
if (localPropertiesFile.exists()) {
|
||||||
@ -12,6 +6,11 @@ if (localPropertiesFile.exists()) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
||||||
|
if (flutterRoot == null) {
|
||||||
|
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
||||||
|
}
|
||||||
|
|
||||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||||
if (flutterVersionCode == null) {
|
if (flutterVersionCode == null) {
|
||||||
flutterVersionCode = '1'
|
flutterVersionCode = '1'
|
||||||
@ -22,23 +21,22 @@ if (flutterVersionName == null) {
|
|||||||
flutterVersionName = '1.0'
|
flutterVersionName = '1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apply plugin: 'com.android.application'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
def keystoreProperties = new Properties()
|
def keystoreProperties = new Properties()
|
||||||
def keystorePropertiesFile = rootProject.file('key.properties')
|
def keystorePropertiesFile = rootProject.file('key.properties')
|
||||||
if (keystorePropertiesFile.exists()) {
|
if (keystorePropertiesFile.exists()) {
|
||||||
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||||
}
|
}
|
||||||
|
|
||||||
def composeVersion = "1.4.8"
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace "oss.krtirtho.spotube"
|
compileSdkVersion 33
|
||||||
|
|
||||||
compileSdkVersion 36
|
ndkVersion "21.4.7075529"
|
||||||
|
|
||||||
ndkVersion = "27.0.12077973"
|
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
coreLibraryDesugaringEnabled true
|
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
@ -51,18 +49,11 @@ android {
|
|||||||
main.java.srcDirs += 'src/main/kotlin'
|
main.java.srcDirs += 'src/main/kotlin'
|
||||||
}
|
}
|
||||||
|
|
||||||
buildFeatures {
|
|
||||||
compose true
|
|
||||||
}
|
|
||||||
|
|
||||||
composeOptions {
|
|
||||||
kotlinCompilerExtensionVersion "$composeVersion" // Correlates with org.jetbrains.kotlin.android plugin in settings.gradle
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
applicationId "oss.krtirtho.spotube"
|
applicationId "oss.krtirtho.spotube"
|
||||||
minSdkVersion 24
|
minSdkVersion 24
|
||||||
targetSdkVersion 35
|
targetSdkVersion flutter.targetSdkVersion
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
@ -76,42 +67,10 @@ android {
|
|||||||
storePassword keystoreProperties['storePassword']
|
storePassword keystoreProperties['storePassword']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
signingConfig signingConfigs.release
|
signingConfig signingConfigs.release
|
||||||
}
|
}
|
||||||
debug {
|
|
||||||
signingConfig signingConfigs.release
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
flavorDimensions "default"
|
|
||||||
|
|
||||||
productFlavors {
|
|
||||||
nightly {
|
|
||||||
dimension "default"
|
|
||||||
resValue "string", "app_name_en", "Spotube Nightly"
|
|
||||||
applicationIdSuffix ".nightly"
|
|
||||||
versionNameSuffix "-nightly"
|
|
||||||
signingConfig signingConfigs.release
|
|
||||||
}
|
|
||||||
dev {
|
|
||||||
dimension "default"
|
|
||||||
resValue "string", "app_name_en", "Spotube Dev"
|
|
||||||
applicationIdSuffix ".dev"
|
|
||||||
versionNameSuffix "-dev"
|
|
||||||
signingConfig signingConfigs.release
|
|
||||||
}
|
|
||||||
stable {
|
|
||||||
dimension "default"
|
|
||||||
resValue "string", "app_name_en", "Spotube"
|
|
||||||
signingConfig signingConfigs.release
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
packagingOptions {
|
|
||||||
resources.excludes += "DebugProbesKt.bin"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,21 +78,18 @@ flutter {
|
|||||||
source '../..'
|
source '../..'
|
||||||
}
|
}
|
||||||
|
|
||||||
def glanceVersion = "1.1.1"
|
|
||||||
dependencies {
|
dependencies {
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.4'
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
constraints {
|
||||||
|
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version") {
|
||||||
|
because("kotlin-stdlib-jdk7 is now a part of kotlin-stdlib")
|
||||||
|
}
|
||||||
|
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version") {
|
||||||
|
because("kotlin-stdlib-jdk8 is now a part of kotlin-stdlib")
|
||||||
|
}
|
||||||
|
}
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
|
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
|
||||||
|
|
||||||
// other deps so just ignore
|
// other deps so just ignore
|
||||||
implementation 'com.android.support:multidex:2.0.1'
|
implementation 'com.android.support:multidex:2.0.1'
|
||||||
|
|
||||||
implementation "androidx.glance:glance-appwidget:$glanceVersion"
|
|
||||||
implementation "androidx.glance:glance-appwidget-preview:$glanceVersion"
|
|
||||||
implementation "androidx.glance:glance-preview:$glanceVersion"
|
|
||||||
implementation "androidx.glance:glance-material3:$glanceVersion"
|
|
||||||
implementation "androidx.glance:glance-material:$glanceVersion"
|
|
||||||
implementation "androidx.work:work-runtime-ktx:2.8.1"
|
|
||||||
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3"
|
|
||||||
implementation 'com.google.code.gson:gson:2.11.0'
|
|
||||||
}
|
}
|
58
android/app/proguard-rules.pro
vendored
@ -1,59 +1 @@
|
|||||||
#Flutter Wrapper
|
|
||||||
# -keep class io.flutter.app.** { *; }
|
|
||||||
-keep class io.flutter.plugin.** { *; }
|
|
||||||
-keep class io.flutter.util.** { *; }
|
|
||||||
-keep class io.flutter.view.** { *; }
|
|
||||||
# -keep class io.flutter.** { *; }
|
|
||||||
-keep class io.flutter.plugins.** { *; }
|
|
||||||
-keep class de.prosiebensat1digital.** { *; }
|
|
||||||
|
|
||||||
-keep class androidx.lifecycle.DefaultLifecycleObserver
|
-keep class androidx.lifecycle.DefaultLifecycleObserver
|
||||||
|
|
||||||
-keepnames class kotlinx.serialization.** { *; }
|
|
||||||
-keepnames class oss.krtirtho.spotube.glance.models.** { *; }
|
|
||||||
-keep @kotlinx.serialization.Serializable class *
|
|
||||||
-keepclassmembers class ** {
|
|
||||||
@kotlinx.serialization.* <fields>;
|
|
||||||
}
|
|
||||||
|
|
||||||
## We don't need beans
|
|
||||||
-dontwarn java.beans.BeanDescriptor
|
|
||||||
-dontwarn java.beans.BeanInfo
|
|
||||||
-dontwarn java.beans.IntrospectionException
|
|
||||||
-dontwarn java.beans.Introspector
|
|
||||||
-dontwarn java.beans.PropertyDescriptor
|
|
||||||
|
|
||||||
## Rules for NewPipeExtractor
|
|
||||||
-keep class org.schabi.newpipe.extractor.timeago.patterns.** { *; }
|
|
||||||
-keep class org.mozilla.javascript.** { *; }
|
|
||||||
-keep class org.mozilla.classfile.ClassFileWriter
|
|
||||||
-dontwarn org.mozilla.javascript.tools.**
|
|
||||||
|
|
||||||
-dontwarn javax.script.AbstractScriptEngine
|
|
||||||
-dontwarn javax.script.Bindings
|
|
||||||
-dontwarn javax.script.Compilable
|
|
||||||
-dontwarn javax.script.CompiledScript
|
|
||||||
-dontwarn javax.script.Invocable
|
|
||||||
-dontwarn javax.script.ScriptContext
|
|
||||||
-dontwarn javax.script.ScriptEngine
|
|
||||||
-dontwarn javax.script.ScriptEngineFactory
|
|
||||||
-dontwarn javax.script.ScriptException
|
|
||||||
-dontwarn javax.script.SimpleBindings
|
|
||||||
-dontwarn jdk.dynalink.CallSiteDescriptor
|
|
||||||
-dontwarn jdk.dynalink.DynamicLinker
|
|
||||||
-dontwarn jdk.dynalink.DynamicLinkerFactory
|
|
||||||
-dontwarn jdk.dynalink.NamedOperation
|
|
||||||
-dontwarn jdk.dynalink.Namespace
|
|
||||||
-dontwarn jdk.dynalink.NamespaceOperation
|
|
||||||
-dontwarn jdk.dynalink.Operation
|
|
||||||
-dontwarn jdk.dynalink.RelinkableCallSite
|
|
||||||
-dontwarn jdk.dynalink.StandardNamespace
|
|
||||||
-dontwarn jdk.dynalink.StandardOperation
|
|
||||||
-dontwarn jdk.dynalink.linker.GuardedInvocation
|
|
||||||
-dontwarn jdk.dynalink.linker.GuardingDynamicLinker
|
|
||||||
-dontwarn jdk.dynalink.linker.LinkRequest
|
|
||||||
-dontwarn jdk.dynalink.linker.LinkerServices
|
|
||||||
-dontwarn jdk.dynalink.linker.TypeBasedGuardingDynamicLinker
|
|
||||||
-dontwarn jdk.dynalink.linker.support.CompositeTypeBasedGuardingDynamicLinker
|
|
||||||
-dontwarn jdk.dynalink.linker.support.Guards
|
|
||||||
-dontwarn jdk.dynalink.support.ChainedCallSite
|
|
@ -1,19 +1,7 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
<!-- Flutter needs it to communicate with the running application
|
package="oss.krtirtho.spotube">
|
||||||
|
<!-- Flutter needs it to communicate with the running application
|
||||||
to allow setting breakpoints, to provide hot reload, etc.
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
-->
|
-->
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<application
|
|
||||||
android:name="${applicationName}"
|
|
||||||
android:allowBackup="false"
|
|
||||||
android:fullBackupContent="false"
|
|
||||||
android:icon="@mipmap/ic_launcher"
|
|
||||||
android:label="@string/app_name_en"
|
|
||||||
android:requestLegacyExternalStorage="true"
|
|
||||||
android:usesCleartextTraffic="true">
|
|
||||||
<!-- Disable Impeller -->
|
|
||||||
<meta-data
|
|
||||||
android:name="io.flutter.embedding.android.EnableImpeller"
|
|
||||||
android:value="false" />
|
|
||||||
</application>
|
|
||||||
</manifest>
|
</manifest>
|
@ -1,12 +1,10 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="oss.krtirtho.spotube">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
|
||||||
|
|
||||||
<queries>
|
<queries>
|
||||||
<!-- If your app opens https URLs -->
|
<!-- If your app opens https URLs -->
|
||||||
@ -17,78 +15,49 @@
|
|||||||
</queries>
|
</queries>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name="${applicationName}"
|
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:fullBackupContent="false"
|
android:fullBackupContent="false"
|
||||||
|
android:label="Spotube"
|
||||||
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name_en"
|
android:usesCleartextTraffic="true"
|
||||||
android:requestLegacyExternalStorage="true"
|
android:requestLegacyExternalStorage="true"
|
||||||
android:usesCleartextTraffic="true">
|
>
|
||||||
<!-- Enable Impeller -->
|
|
||||||
<!-- <meta-data
|
|
||||||
android:name="io.flutter.embedding.android.EnableImpeller"
|
|
||||||
android:value="false" /> -->
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="com.ryanheise.audioservice.AudioServiceActivity"
|
android:name="com.ryanheise.audioservice.AudioServiceActivity"
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:hardwareAccelerated="true"
|
android:launchMode="singleTop"
|
||||||
android:launchMode="singleInstance"
|
|
||||||
android:theme="@style/LaunchTheme"
|
android:theme="@style/LaunchTheme"
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
|
android:windowSoftInputMode="adjustResize"
|
||||||
|
>
|
||||||
<!--
|
<!--
|
||||||
Specifies an Android theme to apply to this Activity as soon as
|
Specifies an Android theme to apply to this Activity as soon as
|
||||||
the Android process has started. This theme is visible to the user
|
the Android process has started. This theme is visible to the user
|
||||||
while the Flutter UI initializes. After that, this theme continues
|
while the Flutter UI initializes. After that, this theme continues
|
||||||
to determine the Window background behind the Flutter UI.
|
to determine the Window background behind the Flutter UI.
|
||||||
-->
|
-->
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="io.flutter.embedding.android.NormalTheme"
|
android:name="io.flutter.embedding.android.NormalTheme"
|
||||||
android:resource="@style/NormalTheme" />
|
android:resource="@style/NormalTheme"
|
||||||
|
/>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.VIEW" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
|
||||||
</intent-filter>
|
|
||||||
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.SEND" />
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
<data android:mimeType="text/*" />
|
|
||||||
</intent-filter>
|
|
||||||
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.VIEW" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
|
||||||
<data android:scheme="spotube" />
|
|
||||||
</intent-filter>
|
|
||||||
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="es.antonborri.home_widget.action.LAUNCH" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<!-- AudioService Config -->
|
<!-- AudioService Config -->
|
||||||
<service
|
<service android:name="com.ryanheise.audioservice.AudioService"
|
||||||
android:name="com.ryanheise.audioservice.AudioService"
|
android:foregroundServiceType="mediaPlayback"
|
||||||
android:exported="true"
|
android:exported="true">
|
||||||
android:foregroundServiceType="mediaPlayback">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.media.browse.MediaBrowserService" />
|
<action android:name="android.media.browse.MediaBrowserService" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
<receiver
|
<receiver android:name="com.ryanheise.audioservice.MediaButtonReceiver"
|
||||||
android:name="com.ryanheise.audioservice.MediaButtonReceiver"
|
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||||
@ -96,40 +65,8 @@
|
|||||||
</receiver>
|
</receiver>
|
||||||
<!-- =================== -->
|
<!-- =================== -->
|
||||||
|
|
||||||
<meta-data
|
|
||||||
android:name="com.google.android.gms.car.application"
|
|
||||||
android:resource="@xml/automotive_app_desc" />
|
|
||||||
|
|
||||||
<!-- Home Widget config -->
|
|
||||||
<receiver
|
|
||||||
android:name=".glance.HomePlayerWidgetReceiver"
|
|
||||||
android:exported="true">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
|
||||||
</intent-filter>
|
|
||||||
<meta-data
|
|
||||||
android:name="android.appwidget.provider"
|
|
||||||
android:resource="@xml/home_player_widget_config" />
|
|
||||||
</receiver>
|
|
||||||
|
|
||||||
<receiver
|
|
||||||
android:name="es.antonborri.home_widget.HomeWidgetBackgroundReceiver"
|
|
||||||
android:exported="true">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="es.antonborri.home_widget.action.BACKGROUND" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
|
|
||||||
<service
|
|
||||||
android:name="es.antonborri.home_widget.HomeWidgetBackgroundService"
|
|
||||||
android:exported="true"
|
|
||||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
|
||||||
<!-- =================== -->
|
|
||||||
|
|
||||||
<!-- Don't delete the meta-data below.
|
<!-- Don't delete the meta-data below.
|
||||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
<meta-data
|
<meta-data android:name="flutterEmbedding" android:value="2" />
|
||||||
android:name="flutterEmbedding"
|
|
||||||
android:value="2" />
|
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
@ -1,207 +0,0 @@
|
|||||||
package oss.krtirtho.spotube.glance
|
|
||||||
|
|
||||||
import HomeWidgetGlanceState
|
|
||||||
import HomeWidgetGlanceStateDefinition
|
|
||||||
import android.R
|
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.drawable.Icon
|
|
||||||
import android.net.Uri
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.unit.DpSize
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.glance.GlanceId
|
|
||||||
import androidx.glance.GlanceModifier
|
|
||||||
import androidx.glance.GlanceTheme
|
|
||||||
import androidx.glance.Image
|
|
||||||
import androidx.glance.ImageProvider
|
|
||||||
import androidx.glance.LocalSize
|
|
||||||
import androidx.glance.action.ActionParameters
|
|
||||||
import androidx.glance.action.actionParametersOf
|
|
||||||
import androidx.glance.action.clickable
|
|
||||||
import androidx.glance.background
|
|
||||||
import androidx.glance.appwidget.GlanceAppWidget
|
|
||||||
import androidx.glance.appwidget.SizeMode
|
|
||||||
import androidx.glance.appwidget.action.ActionCallback
|
|
||||||
import androidx.glance.appwidget.action.actionRunCallback
|
|
||||||
import androidx.glance.appwidget.background
|
|
||||||
import androidx.glance.appwidget.components.CircleIconButton
|
|
||||||
import androidx.glance.appwidget.components.Scaffold
|
|
||||||
import androidx.glance.appwidget.cornerRadius
|
|
||||||
import androidx.glance.appwidget.provideContent
|
|
||||||
import androidx.glance.background
|
|
||||||
import androidx.glance.currentState
|
|
||||||
import androidx.glance.layout.Alignment
|
|
||||||
import androidx.glance.layout.Box
|
|
||||||
import androidx.glance.layout.Column
|
|
||||||
import androidx.glance.layout.ContentScale
|
|
||||||
import androidx.glance.layout.Row
|
|
||||||
import androidx.glance.layout.Spacer
|
|
||||||
import androidx.glance.layout.fillMaxSize
|
|
||||||
import androidx.glance.layout.fillMaxWidth
|
|
||||||
import androidx.glance.layout.padding
|
|
||||||
import androidx.glance.layout.size
|
|
||||||
import androidx.glance.preview.ExperimentalGlancePreviewApi
|
|
||||||
import androidx.glance.preview.Preview
|
|
||||||
import androidx.glance.state.GlanceStateDefinition
|
|
||||||
import com.google.gson.Gson
|
|
||||||
import es.antonborri.home_widget.HomeWidgetBackgroundIntent
|
|
||||||
import es.antonborri.home_widget.actionStartActivity
|
|
||||||
import oss.krtirtho.spotube.MainActivity
|
|
||||||
import oss.krtirtho.spotube.glance.models.Track
|
|
||||||
import oss.krtirtho.spotube.glance.widgets.FlutterAssetImageProvider
|
|
||||||
import oss.krtirtho.spotube.glance.widgets.TrackDetailsView
|
|
||||||
import oss.krtirtho.spotube.glance.widgets.TrackProgress
|
|
||||||
|
|
||||||
val gson = Gson()
|
|
||||||
val serverAddressKey = ActionParameters.Key<String>("serverAddress")
|
|
||||||
|
|
||||||
class Breakpoints {
|
|
||||||
companion object {
|
|
||||||
val SMALL_SQUARE = DpSize(100.dp, 100.dp)
|
|
||||||
val HORIZONTAL_RECTANGLE = DpSize(250.dp, 100.dp)
|
|
||||||
val BIG_SQUARE = DpSize(250.dp, 250.dp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class HomePlayerWidget : GlanceAppWidget() {
|
|
||||||
|
|
||||||
override val sizeMode = SizeMode.Responsive(
|
|
||||||
setOf(
|
|
||||||
Breakpoints.SMALL_SQUARE,
|
|
||||||
Breakpoints.HORIZONTAL_RECTANGLE,
|
|
||||||
Breakpoints.BIG_SQUARE
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
override val stateDefinition: GlanceStateDefinition<*>?
|
|
||||||
get() = HomeWidgetGlanceStateDefinition()
|
|
||||||
|
|
||||||
override suspend fun provideGlance(context: Context, id: GlanceId) {
|
|
||||||
provideContent {
|
|
||||||
GlanceContent(context, currentState())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@OptIn(ExperimentalGlancePreviewApi::class)
|
|
||||||
@Preview(widthDp = 100, heightDp = 100)
|
|
||||||
@Composable
|
|
||||||
private fun GlanceContent(context: Context, currentState: HomeWidgetGlanceState) {
|
|
||||||
val prefs = currentState.preferences
|
|
||||||
val size = LocalSize.current
|
|
||||||
|
|
||||||
val activeTrackStr = prefs.getString("activeTrack", null)
|
|
||||||
|
|
||||||
val isPlaying = prefs.getBoolean("isPlaying", false)
|
|
||||||
val playbackServerAddress = prefs.getString("playbackServerAddress", null) ?: ""
|
|
||||||
|
|
||||||
var activeTrack: Track? = null
|
|
||||||
if (activeTrackStr != null) {
|
|
||||||
activeTrack = gson.fromJson(activeTrackStr, Track::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val playIcon = Icon.createWithResource(context, R.drawable.ic_media_play);
|
|
||||||
val pauseIcon = Icon.createWithResource(context, R.drawable.ic_media_pause);
|
|
||||||
val previousIcon = Icon.createWithResource(context, R.drawable.ic_media_previous);
|
|
||||||
val nextIcon = Icon.createWithResource(context, R.drawable.ic_media_next);
|
|
||||||
|
|
||||||
GlanceTheme {
|
|
||||||
Box(
|
|
||||||
modifier = GlanceModifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.cornerRadius(8.dp)
|
|
||||||
.background(
|
|
||||||
color = GlanceTheme.colors.surface.getColor(context)
|
|
||||||
)
|
|
||||||
.clickable {
|
|
||||||
actionStartActivity<MainActivity>(context)
|
|
||||||
}
|
|
||||||
,
|
|
||||||
) {
|
|
||||||
Box(
|
|
||||||
modifier = GlanceModifier
|
|
||||||
.background(
|
|
||||||
color =
|
|
||||||
GlanceTheme.colors.surface.getColor(context)
|
|
||||||
.copy(alpha = 0.5f),
|
|
||||||
)
|
|
||||||
.fillMaxSize(),
|
|
||||||
) {}
|
|
||||||
Column(
|
|
||||||
modifier = GlanceModifier.padding(top = 10.dp, start = 10.dp, end = 10.dp)
|
|
||||||
) {
|
|
||||||
Row(verticalAlignment = Alignment.Vertical.CenterVertically) {
|
|
||||||
TrackDetailsView(activeTrack)
|
|
||||||
}
|
|
||||||
Spacer(modifier = GlanceModifier.size(6.dp))
|
|
||||||
if (size != Breakpoints.SMALL_SQUARE) {
|
|
||||||
TrackProgress(prefs)
|
|
||||||
}
|
|
||||||
Spacer(modifier = GlanceModifier.size(6.dp))
|
|
||||||
Row(
|
|
||||||
modifier = GlanceModifier.fillMaxWidth(),
|
|
||||||
horizontalAlignment = Alignment.Horizontal.CenterHorizontally
|
|
||||||
) {
|
|
||||||
CircleIconButton(
|
|
||||||
imageProvider = ImageProvider(previousIcon),
|
|
||||||
contentDescription = "Previous",
|
|
||||||
onClick = actionRunCallback<PreviousAction>(
|
|
||||||
parameters = actionParametersOf(serverAddressKey to playbackServerAddress)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
Spacer(modifier = GlanceModifier.size(6.dp))
|
|
||||||
CircleIconButton(
|
|
||||||
imageProvider =
|
|
||||||
if (isPlaying) ImageProvider(pauseIcon)
|
|
||||||
else ImageProvider(playIcon),
|
|
||||||
contentDescription = "Play/Pause",
|
|
||||||
onClick = actionRunCallback<PlayPauseAction>(
|
|
||||||
parameters = actionParametersOf(serverAddressKey to playbackServerAddress)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
Spacer(modifier = GlanceModifier.size(6.dp))
|
|
||||||
CircleIconButton(
|
|
||||||
imageProvider = ImageProvider(nextIcon),
|
|
||||||
contentDescription = "Previous",
|
|
||||||
onClick = actionRunCallback<NextAction>(
|
|
||||||
parameters = actionParametersOf(
|
|
||||||
serverAddressKey to playbackServerAddress
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PlayPauseAction : InteractiveAction("toggle-playback")
|
|
||||||
class NextAction : InteractiveAction("next")
|
|
||||||
class PreviousAction : InteractiveAction("previous")
|
|
||||||
|
|
||||||
|
|
||||||
abstract class InteractiveAction(val command: String) : ActionCallback {
|
|
||||||
override suspend fun onAction(
|
|
||||||
context: Context,
|
|
||||||
glanceId: GlanceId,
|
|
||||||
parameters: ActionParameters
|
|
||||||
) {
|
|
||||||
val serverAddress = parameters[serverAddressKey] ?: ""
|
|
||||||
|
|
||||||
Log.d("HomePlayerWidget", "Sending command $command to $serverAddress")
|
|
||||||
|
|
||||||
if (serverAddress == null || serverAddress.isEmpty()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val backgroundIntent = HomeWidgetBackgroundIntent.getBroadcast(
|
|
||||||
context,
|
|
||||||
Uri.parse("spotube://playback/$command?serverAddress=$serverAddress")
|
|
||||||
)
|
|
||||||
backgroundIntent.send()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
package oss.krtirtho.spotube.glance
|
|
||||||
|
|
||||||
import HomeWidgetGlanceWidgetReceiver
|
|
||||||
|
|
||||||
class HomePlayerWidgetReceiver : HomeWidgetGlanceWidgetReceiver<HomePlayerWidget>() {
|
|
||||||
override val glanceAppWidget = HomePlayerWidget()
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
package oss.krtirtho.spotube.glance.models
|
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class AlbumSimple(
|
|
||||||
@SerializedName("album_type")
|
|
||||||
val albumType: AlbumType?,
|
|
||||||
|
|
||||||
@SerializedName("available_markets")
|
|
||||||
val availableMarkets: List<Market>?,
|
|
||||||
|
|
||||||
val href: String?,
|
|
||||||
val id: String?,
|
|
||||||
val images: List<Image>?,
|
|
||||||
val name: String?,
|
|
||||||
|
|
||||||
@SerializedName("release_date")
|
|
||||||
val releaseDate: String?,
|
|
||||||
|
|
||||||
@SerializedName("release_date_precision")
|
|
||||||
val releaseDatePrecision: DatePrecision?,
|
|
||||||
|
|
||||||
val type: String?,
|
|
||||||
val uri: String?,
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
enum class AlbumType {
|
|
||||||
album,
|
|
||||||
single,
|
|
||||||
compilation
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class DatePrecision {
|
|
||||||
year,
|
|
||||||
month,
|
|
||||||
day
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
package oss.krtirtho.spotube.glance.models
|
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class Artist(
|
|
||||||
val href: String?,
|
|
||||||
val id: String?,
|
|
||||||
val name: String?,
|
|
||||||
val type: String?,
|
|
||||||
val uri: String?,
|
|
||||||
|
|
||||||
val followers: Followers?,
|
|
||||||
val genres: List<String>?,
|
|
||||||
val images: List<Image>?,
|
|
||||||
|
|
||||||
@SerializedName("popularity")
|
|
||||||
val popularity: Int?
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class Followers(
|
|
||||||
val total: Int?
|
|
||||||
)
|
|
@ -1,10 +0,0 @@
|
|||||||
package oss.krtirtho.spotube.glance.models
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class Image(
|
|
||||||
val height: Int?,
|
|
||||||
val width: Int?,
|
|
||||||
val path: String,
|
|
||||||
)
|
|
@ -1,37 +0,0 @@
|
|||||||
package oss.krtirtho.spotube.glance.models
|
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class Track(
|
|
||||||
val album: AlbumSimple?, val artists: List<Artist>?,
|
|
||||||
|
|
||||||
@SerializedName("available_markets") val availableMarkets: List<Market>?,
|
|
||||||
|
|
||||||
@SerializedName("disc_number") val discNumber: Int?,
|
|
||||||
|
|
||||||
@SerializedName("duration_ms") val durationMs: Int,
|
|
||||||
|
|
||||||
val explicit: Boolean?, val href: String?, val id: String?,
|
|
||||||
|
|
||||||
@SerializedName("is_playable") val isPlayable: Boolean?,
|
|
||||||
|
|
||||||
val name: String?,
|
|
||||||
|
|
||||||
@SerializedName("popularity") val popularity: Int?,
|
|
||||||
|
|
||||||
@SerializedName("preview_url") val previewUrl: String?,
|
|
||||||
|
|
||||||
@SerializedName("track_number") val trackNumber: Int?,
|
|
||||||
|
|
||||||
val type: String?, val uri: String?
|
|
||||||
) {
|
|
||||||
val duration: kotlin.time.Duration
|
|
||||||
get() = durationMs.toLong().milliseconds
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class Market {
|
|
||||||
AD, AE, AF, AG, AI, AL, AM, AO, AQ, AR, AS, AT, AU, AW, AX, AZ, BA, BB, BD, BE, BF, BG, BH, BI, BJ, BL, BM, BN, BO, BQ, BR, BS, BT, BV, BW, BY, BZ, CA, CC, CD, CF, CG, CH, CI, CK, CL, CM, CN, CO, CR, CU, CV, CW, CX, CY, CZ, DE, DJ, DK, DM, DO, DZ, EC, EE, EG, EH, ER, ES, ET, FI, FJ, FK, FM, FO, FR, GA, GB, GD, GE, GF, GG, GH, GI, GL, GM, GN, GP, GQ, GR, GS, GT, GU, GW, GY, HK, HM, HN, HR, HT, HU, ID, IE, IL, IM, IN, IO, IQ, IR, IS, IT, JE, JM, JO, JP, KE, KG, KH, KI, KM, KN, KP, KR, KW, KY, KZ, LA, LB, LC, LI, LK, LR, LS, LT, LU, LV, LY, MA, MC, MD, ME, MF, MG, MH, MK, ML, MM, MN, MO, MP, MQ, MR, MS, MT, MU, MV, MW, MX, MY, MZ, NA, NC, NE, NF, NG, NI, NL, NO, NP, NR, NU, NZ, OM, PA, PE, PF, PG, PH, PK, PL, PM, PN, PR, PS, PT, PW, PY, QA, RE, RO, RS, RU, RW, SA, SB, SC, SD, SE, SG, SH, SI, SJ, SK, SL, SM, SN, SO, SR, SS, ST, SV, SX, SY, SZ, TC, TD, TF, TG, TH, TJ, TK, TL, TM, TN, TO, TR, TT, TV, TW, TZ, UA, UG, UM, US, UY, UZ, VA, VC, VE, VG, VI, VN, VU, WF, WS, XK, YE, YT, ZA, ZM, ZW,
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
package oss.krtirtho.spotube.glance.widgets
|
|
||||||
|
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import android.util.Base64
|
|
||||||
import androidx.glance.ImageProvider
|
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
fun Base64ImageProvider(base64: String): ImageProvider {
|
|
||||||
var bytes = Base64.decode(base64, Base64.DEFAULT);
|
|
||||||
|
|
||||||
var bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size);
|
|
||||||
|
|
||||||
return ImageProvider(bitmap)
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
package oss.krtirtho.spotube.glance.widgets
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import androidx.glance.ImageProvider
|
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
fun FlutterAssetImageProvider(context: Context, path: String): ImageProvider {
|
|
||||||
var inputStream = context.assets.open("flutter_assets/$path")
|
|
||||||
|
|
||||||
return ImageProvider(
|
|
||||||
BitmapFactory.decodeStream(inputStream)
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
package oss.krtirtho.spotube.glance.widgets
|
|
||||||
|
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import android.net.Uri
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.compose.ui.unit.sp
|
|
||||||
import androidx.glance.GlanceModifier
|
|
||||||
import androidx.glance.GlanceTheme
|
|
||||||
import androidx.glance.Image
|
|
||||||
import androidx.glance.ImageProvider
|
|
||||||
import androidx.glance.LocalContext
|
|
||||||
import androidx.glance.LocalSize
|
|
||||||
import androidx.glance.appwidget.cornerRadius
|
|
||||||
import androidx.glance.layout.Alignment
|
|
||||||
import androidx.glance.layout.Row
|
|
||||||
import androidx.glance.layout.Column
|
|
||||||
import androidx.glance.layout.ContentScale
|
|
||||||
import androidx.glance.layout.Spacer
|
|
||||||
import androidx.glance.layout.size
|
|
||||||
import androidx.glance.text.FontWeight
|
|
||||||
import androidx.glance.text.Text
|
|
||||||
import androidx.glance.text.TextStyle
|
|
||||||
import oss.krtirtho.spotube.glance.Breakpoints
|
|
||||||
import oss.krtirtho.spotube.glance.models.Track
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun TrackDetailsView(activeTrack: Track?) {
|
|
||||||
val context = LocalContext.current
|
|
||||||
|
|
||||||
val size = LocalSize.current
|
|
||||||
|
|
||||||
val artistStr = activeTrack?.artists?.map { it.name }?.joinToString(", ") ?: "<No Artist>"
|
|
||||||
val imgLocalPath = activeTrack?.album?.images?.get(0)?.path;
|
|
||||||
val title = activeTrack?.name ?: "<No Track>"
|
|
||||||
|
|
||||||
|
|
||||||
Image(
|
|
||||||
provider =
|
|
||||||
if (imgLocalPath == null)
|
|
||||||
ImageProvider(
|
|
||||||
BitmapFactory.decodeResource(
|
|
||||||
context.resources,
|
|
||||||
android.R.drawable.ic_delete
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else ImageProvider(BitmapFactory.decodeFile(imgLocalPath)),
|
|
||||||
contentDescription = "Album Art",
|
|
||||||
modifier = GlanceModifier.cornerRadius(8.dp)
|
|
||||||
.size(
|
|
||||||
if (size.height < 200.dp) 50.dp
|
|
||||||
else 100.dp
|
|
||||||
),
|
|
||||||
contentScale = ContentScale.Fit
|
|
||||||
)
|
|
||||||
Spacer(modifier = GlanceModifier.size(6.dp))
|
|
||||||
Column {
|
|
||||||
Text(
|
|
||||||
text = title,
|
|
||||||
style = TextStyle(
|
|
||||||
fontSize = 16.sp,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
color = GlanceTheme.colors.onBackground
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if (size != Breakpoints.SMALL_SQUARE) {
|
|
||||||
Spacer(modifier = GlanceModifier.size(6.dp))
|
|
||||||
Text(
|
|
||||||
text = artistStr,
|
|
||||||
style = TextStyle(
|
|
||||||
fontSize = 14.sp,
|
|
||||||
color = GlanceTheme.colors.onBackground
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,77 +0,0 @@
|
|||||||
package oss.krtirtho.spotube.glance.widgets
|
|
||||||
|
|
||||||
import android.content.SharedPreferences
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.glance.GlanceModifier
|
|
||||||
import androidx.glance.GlanceTheme
|
|
||||||
import androidx.glance.LocalSize
|
|
||||||
import androidx.glance.appwidget.LinearProgressIndicator
|
|
||||||
import androidx.glance.layout.Column
|
|
||||||
import androidx.glance.layout.Row
|
|
||||||
import androidx.glance.layout.Spacer
|
|
||||||
import androidx.glance.layout.fillMaxWidth
|
|
||||||
import androidx.glance.layout.size
|
|
||||||
import androidx.glance.text.Text
|
|
||||||
import androidx.glance.text.TextStyle
|
|
||||||
import kotlin.math.max
|
|
||||||
import kotlin.time.Duration
|
|
||||||
import kotlin.time.Duration.Companion.seconds
|
|
||||||
import oss.krtirtho.spotube.glance.Breakpoints
|
|
||||||
|
|
||||||
fun Duration.format(): String {
|
|
||||||
return this.toComponents { hour, minutes, seconds, nanoseconds ->
|
|
||||||
var paddedSeconds = seconds.toString().padStart(2, '0')
|
|
||||||
var paddedMinutes = minutes.toString().padStart(2, '0')
|
|
||||||
var paddedHour = hour.toString().padStart(2, '0')
|
|
||||||
if (hour == 0L) {
|
|
||||||
"$paddedMinutes:$paddedSeconds"
|
|
||||||
} else {
|
|
||||||
"$paddedHour:$paddedMinutes:$paddedSeconds"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun TrackProgress(prefs: SharedPreferences) {
|
|
||||||
val size = LocalSize.current
|
|
||||||
val position = prefs.getInt("position", 0).seconds
|
|
||||||
var duration = prefs.getInt("duration", 0).seconds
|
|
||||||
|
|
||||||
var progress = position.inWholeSeconds.toFloat() / max(duration.inWholeSeconds.toFloat(), 1.0f)
|
|
||||||
|
|
||||||
var textStyle =
|
|
||||||
TextStyle(
|
|
||||||
color = GlanceTheme.colors.onBackground,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (size == Breakpoints.HORIZONTAL_RECTANGLE) {
|
|
||||||
Row(modifier = GlanceModifier.fillMaxWidth()) {
|
|
||||||
Text(text = position.format(), style = textStyle)
|
|
||||||
Spacer(modifier = GlanceModifier.size(6.dp))
|
|
||||||
LinearProgressIndicator(
|
|
||||||
progress = progress,
|
|
||||||
modifier = GlanceModifier.defaultWeight(),
|
|
||||||
color = GlanceTheme.colors.primary,
|
|
||||||
backgroundColor = GlanceTheme.colors.primaryContainer,
|
|
||||||
)
|
|
||||||
Spacer(modifier = GlanceModifier.size(6.dp))
|
|
||||||
Text(text = duration.format(), style = textStyle)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Column(modifier = GlanceModifier.fillMaxWidth()) {
|
|
||||||
LinearProgressIndicator(
|
|
||||||
progress = progress,
|
|
||||||
modifier = GlanceModifier.fillMaxWidth(),
|
|
||||||
color = GlanceTheme.colors.primary,
|
|
||||||
backgroundColor = GlanceTheme.colors.primaryContainer,
|
|
||||||
)
|
|
||||||
Spacer(modifier = GlanceModifier.size(6.dp))
|
|
||||||
Row(modifier = GlanceModifier.fillMaxWidth()) {
|
|
||||||
Text(text = position.format(), style = textStyle)
|
|
||||||
Spacer(modifier = GlanceModifier.defaultWeight())
|
|
||||||
Text(text = duration.format(), style = textStyle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 91 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 214 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 247 KiB After Width: | Height: | Size: 253 KiB |
Before Width: | Height: | Size: 3.0 MiB After Width: | Height: | Size: 3.1 MiB |
@ -6,7 +6,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<bitmap android:gravity="center" android:src="@drawable/splash"/>
|
<bitmap android:gravity="center" android:src="@drawable/splash"/>
|
||||||
</item>
|
</item>
|
||||||
<item android:bottom="0dp">
|
<item>
|
||||||
<bitmap android:gravity="bottom" android:src="@drawable/branding"/>
|
<bitmap android:gravity="bottom" android:src="@drawable/branding"/>
|
||||||
</item>
|
</item>
|
||||||
</layer-list>
|
</layer-list>
|
||||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 91 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 214 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 94 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 247 KiB After Width: | Height: | Size: 253 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 100 KiB |
Before Width: | Height: | Size: 3.0 MiB After Width: | Height: | Size: 3.1 MiB |
@ -6,7 +6,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<bitmap android:gravity="center" android:src="@drawable/splash"/>
|
<bitmap android:gravity="center" android:src="@drawable/splash"/>
|
||||||
</item>
|
</item>
|
||||||
<item android:bottom="0dp">
|
<item>
|
||||||
<bitmap android:gravity="bottom" android:src="@drawable/branding"/>
|
<bitmap android:gravity="bottom" android:src="@drawable/branding"/>
|
||||||
</item>
|
</item>
|
||||||
</layer-list>
|
</layer-list>
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/ic_launcher_background"/>
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground>
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
<inset
|
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
|
||||||
android:drawable="@drawable/ic_launcher_foreground"
|
|
||||||
android:inset="16%" />
|
|
||||||
</foreground>
|
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 27 KiB |
@ -5,10 +5,6 @@
|
|||||||
<!-- Show a splash screen on the activity. Automatically removed when
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
Flutter draws its first frame -->
|
Flutter draws its first frame -->
|
||||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
<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>
|
</style>
|
||||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
This theme determines the color of the Android Window while your
|
This theme determines the color of the Android Window while your
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
<automotiveApp>
|
|
||||||
<uses name="media" />
|
|
||||||
</automotiveApp>
|
|
@ -1,7 +0,0 @@
|
|||||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:initialLayout="@layout/glance_default_loading_layout"
|
|
||||||
android:minWidth="100dp"
|
|
||||||
android:minHeight="100dp"
|
|
||||||
android:resizeMode="horizontal|vertical"
|
|
||||||
android:updatePeriodMillis="10000">
|
|
||||||
</appwidget-provider>
|
|
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 7.6 KiB |
Before Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 7.6 KiB |
Before Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 7.6 KiB |
Before Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 94 KiB |