├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── feature_request.yml └── workflows │ ├── main.yml │ ├── pc.yml │ └── pre.yml ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── gokadzev │ │ │ │ └── musify │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-hdpi │ │ │ ├── android12splash.png │ │ │ ├── audio_service_pause.png │ │ │ ├── audio_service_play_arrow.png │ │ │ ├── audio_service_skip_next.png │ │ │ ├── audio_service_skip_previous.png │ │ │ ├── audio_service_stop.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── splash.png │ │ │ ├── drawable-mdpi │ │ │ ├── android12splash.png │ │ │ ├── audio_service_pause.png │ │ │ ├── audio_service_play_arrow.png │ │ │ ├── audio_service_skip_next.png │ │ │ ├── audio_service_skip_previous.png │ │ │ ├── audio_service_stop.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── splash.png │ │ │ ├── drawable-night-hdpi │ │ │ ├── android12splash.png │ │ │ └── splash.png │ │ │ ├── drawable-night-mdpi │ │ │ ├── android12splash.png │ │ │ └── splash.png │ │ │ ├── drawable-night-v21 │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── drawable-night-xhdpi │ │ │ ├── android12splash.png │ │ │ └── splash.png │ │ │ ├── drawable-night-xxhdpi │ │ │ ├── android12splash.png │ │ │ └── splash.png │ │ │ ├── drawable-night-xxxhdpi │ │ │ ├── android12splash.png │ │ │ └── splash.png │ │ │ ├── drawable-night │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── drawable-v21 │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── drawable-xhdpi │ │ │ ├── android12splash.png │ │ │ ├── audio_service_pause.png │ │ │ ├── audio_service_play_arrow.png │ │ │ ├── audio_service_skip_next.png │ │ │ ├── audio_service_skip_previous.png │ │ │ ├── audio_service_stop.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── splash.png │ │ │ ├── drawable-xxhdpi │ │ │ ├── android12splash.png │ │ │ ├── audio_service_pause.png │ │ │ ├── audio_service_play_arrow.png │ │ │ ├── audio_service_skip_next.png │ │ │ ├── audio_service_skip_previous.png │ │ │ ├── audio_service_stop.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── splash.png │ │ │ ├── drawable-xxxhdpi │ │ │ ├── android12splash.png │ │ │ ├── audio_service_pause.png │ │ │ ├── audio_service_play_arrow.png │ │ │ ├── audio_service_skip_next.png │ │ │ ├── audio_service_skip_previous.png │ │ │ ├── audio_service_stop.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── splash.png │ │ │ ├── drawable │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ └── launcher_icon.xml │ │ │ ├── mipmap-hdpi │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-mdpi │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-xhdpi │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── launcher_icon.png │ │ │ ├── raw │ │ │ └── keep.xml │ │ │ ├── values-night-v31 │ │ │ └── styles.xml │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ ├── values-v31 │ │ │ └── styles.xml │ │ │ └── values │ │ │ ├── colors.xml │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── db │ └── playlists.db.json └── images │ ├── ic_launcher.png │ ├── ic_launcher_background.jpg │ ├── ic_launcher_foreground.png │ ├── ic_launcher_round.png │ └── splash.png ├── fastlane └── metadata │ └── android │ └── en-US │ ├── full_description.txt │ ├── images │ ├── icon.png │ └── phoneScreenshots │ │ ├── 01.jpg │ │ ├── 02.jpg │ │ └── 03.jpg │ └── short_description.txt ├── fonts └── ubuntu.ttf ├── l10n.yaml ├── lib ├── API │ └── musify.dart ├── customWidgets │ ├── custom_animated_bottom_bar.dart │ ├── delayed_display.dart │ ├── marque.dart │ ├── setting_bar.dart │ ├── song_bar.dart │ └── spinner.dart ├── helper │ ├── flutter_toast.dart │ ├── formatter.dart │ ├── mediaitem.dart │ ├── url_launcher.dart │ └── version.dart ├── localization │ ├── app_de.arb │ ├── app_en.arb │ ├── app_es.arb │ ├── app_fr.arb │ ├── app_he.arb │ ├── app_hi.arb │ ├── app_hu.arb │ ├── app_id.arb │ ├── app_it.arb │ ├── app_ka.arb │ ├── app_nl.arb │ ├── app_pl.arb │ ├── app_pt.arb │ ├── app_tr.arb │ ├── app_uk.arb │ └── app_zh.arb ├── main.dart ├── services │ ├── audio_handler.dart │ ├── audio_manager.dart │ ├── data_manager.dart │ ├── download_manager.dart │ ├── ext_storage.dart │ └── lyrics_service.dart ├── style │ ├── appColors.dart │ └── appTheme.dart └── ui │ ├── aboutPage.dart │ ├── homePage.dart │ ├── localSongsPage.dart │ ├── morePage.dart │ ├── player.dart │ ├── playlistPage.dart │ ├── playlistsPage.dart │ ├── rootPage.dart │ ├── searchPage.dart │ ├── userLikedSongsPage.dart │ └── userPlaylistsPage.dart ├── pubspec.lock ├── pubspec.yaml └── test └── widget_test.dart /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: ['https://www.buymeacoffee.com/gokadzev18'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: 🐞 Issue Report 2 | description: Report a issue in Musify 3 | labels: [bug] 4 | body: 5 | 6 | - type: textarea 7 | id: reproduce-steps 8 | attributes: 9 | label: Steps to reproduce 10 | description: Provide an example of the issue. 11 | placeholder: | 12 | Example: 13 | 1. First step 14 | 2. Second step 15 | 3. Issue here 16 | validations: 17 | required: true 18 | 19 | - type: textarea 20 | id: expected-behavior 21 | attributes: 22 | label: Expected behavior 23 | placeholder: | 24 | Example: 25 | "This should happen..." 26 | validations: 27 | required: true 28 | 29 | - type: textarea 30 | id: actual-behavior 31 | attributes: 32 | label: Actual behavior 33 | placeholder: | 34 | Example: 35 | "This happened instead..." 36 | validations: 37 | required: true 38 | 39 | - type: input 40 | id: musify-version 41 | attributes: 42 | label: Musify version 43 | description: | 44 | You can find your Musify version in **Settings**. 45 | placeholder: | 46 | Example: "1.0.0" 47 | validations: 48 | required: true 49 | 50 | - type: input 51 | id: android-version 52 | attributes: 53 | label: Android version 54 | description: | 55 | You can find this somewhere in your Android settings. 56 | placeholder: | 57 | Example: "Android 12" 58 | validations: 59 | required: true 60 | 61 | - type: textarea 62 | id: other-details 63 | attributes: 64 | label: Other details 65 | placeholder: | 66 | Additional details and attachments. 67 | - type: checkboxes 68 | id: acknowledgements 69 | attributes: 70 | label: Acknowledgements 71 | description: Your issue will be closed if you haven't done these steps. 72 | options: 73 | - label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open issue. 74 | required: true 75 | - label: I have written a short but informative title. 76 | required: true 77 | - label: I have updated the app to latest version **[Latest](https://github.com/gokadzev/Musify/releases)**. 78 | required: true 79 | - label: I will fill out all -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: ⭐ Feature request 2 | description: Suggest a feature to improve the app 3 | labels: [feature request] 4 | body: 5 | 6 | - type: textarea 7 | id: feature-description 8 | attributes: 9 | label: Describe your suggested feature 10 | description: How can an existing source be improved? 11 | placeholder: | 12 | Example: 13 | "It should work like this..." 14 | validations: 15 | required: true 16 | 17 | - type: textarea 18 | id: other-details 19 | attributes: 20 | label: Other details 21 | placeholder: | 22 | Additional details and attachments. 23 | - type: checkboxes 24 | id: acknowledgements 25 | attributes: 26 | label: Acknowledgements 27 | description: Your issue will be closed if you haven't done these steps. 28 | options: 29 | - label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open issue. 30 | required: true 31 | - label: I have written a short but informative title. 32 | required: true 33 | - label: I will fill out all of the requested information in this form. 34 | required: true 35 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | on: workflow_dispatch 2 | name: Test, Build and Release apk 3 | env: 4 | PROPERTIES_PATH: "./android/key.properties" 5 | jobs: 6 | build: 7 | name: Build APK 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v3 12 | 13 | # Setup Java environment in order to build the Android app. 14 | - uses: actions/setup-java@v3 15 | with: 16 | distribution: "zulu" 17 | java-version: "12.x" 18 | 19 | 20 | # Gradle cache for faster builds 21 | - uses: actions/cache@v3 22 | with: 23 | path: | 24 | ~/.gradle/caches 25 | ~/.gradle/wrapper 26 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} 27 | restore-keys: | 28 | ${{ runner.os }}-gradle- 29 | # Setup the flutter environment. 30 | - uses: subosito/flutter-action@v2 31 | with: 32 | channel: "stable" 33 | 34 | # 35 | - run: | 36 | echo keyPassword=\${{ secrets.KEY_STORE }} > ${{env.PROPERTIES_PATH}} 37 | echo storePassword=\${{ secrets.KEY_PASSWORD }} >> ${{env.PROPERTIES_PATH}} 38 | echo keyAlias=\${{ secrets.KEY_ALIAS }} >> ${{env.PROPERTIES_PATH}} 39 | # 40 | - run: echo "${{ secrets.KEYSTORE2 }}" | base64 --decode > android/app/key.jks 41 | 42 | # Get flutter dependencies. 43 | - run: flutter pub get 44 | # Check for any formatting issues in the code. 45 | - run: flutter format --set-exit-if-changed . 46 | # Statically analyze the Dart code for any errors. 47 | - run: flutter analyze . 48 | # Build arch apks. 49 | - run: flutter build apk --release --split-per-abi 50 | # Upload arm64 generated apk to the artifacts. 51 | - uses: actions/upload-artifact@v3 52 | with: 53 | name: Musify_arm64-v8a.apk 54 | path: build/app/outputs/apk/release/app-arm64-v8a-release.apk 55 | # Build universal apk. 56 | - run: flutter build apk --release 57 | # Upload universal generated apk to the artifacts. 58 | - uses: actions/upload-artifact@v3 59 | with: 60 | name: Musify.apk 61 | path: build/app/outputs/apk/release/app-release.apk -------------------------------------------------------------------------------- /.github/workflows/pc.yml: -------------------------------------------------------------------------------- 1 | on: workflow_dispatch 2 | name: Test, Build and Release PC 3 | jobs: 4 | build-and-release-linux: 5 | runs-on: ubuntu-latest 6 | 7 | steps: 8 | - uses: actions/checkout@v2 9 | - uses: subosito/flutter-action@v2 10 | with: 11 | channel: 'stable' 12 | - name: Install dependencies 13 | run: sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev 14 | - name: Install project dependencies 15 | run: flutter pub get 16 | - name: Generate intermediates 17 | run: flutter pub run build_runner build --delete-conflicting-outputs 18 | - name: Enable linux build 19 | run: flutter config --enable-linux-desktop 20 | - name: Build artifacts 21 | run: flutter build linux --release 22 | - name: Archive Release 23 | uses: thedoctor0/zip-release@master 24 | with: 25 | type: 'zip' 26 | filename: Musify-linux.zip 27 | directory: build/linux/x64/release/bundle 28 | - name: Linux Release 29 | uses: actions/upload-artifact@v3 30 | with: 31 | name: Musify-linux.zip 32 | path: build/linux/x64/release/bundle/Musify-linux.zip 33 | 34 | 35 | build-and-release-windows: 36 | runs-on: windows-latest 37 | 38 | steps: 39 | - uses: actions/checkout@v2 40 | - uses: subosito/flutter-action@v2 41 | with: 42 | channel: 'stable' 43 | - name: Install project dependencies 44 | run: flutter pub get 45 | - name: Generate intermediates 46 | run: flutter pub run build_runner build --delete-conflicting-outputs 47 | - name: Enable windows build 48 | run: flutter config --enable-windows-desktop 49 | - name: Build artifacts 50 | run: flutter build windows --release 51 | - name: Archive Release 52 | uses: thedoctor0/zip-release@master 53 | with: 54 | type: 'zip' 55 | filename: Musify-windows.zip 56 | directory: build/windows/runner/Release 57 | - name: Windows Release 58 | uses: actions/upload-artifact@v3 59 | with: 60 | name: Musify-windows.zip 61 | path: build/windows/runner/Release/Musify-windows.zip 62 | -------------------------------------------------------------------------------- /.github/workflows/pre.yml: -------------------------------------------------------------------------------- 1 | on: workflow_dispatch 2 | name: Test, Build and Pre Release apk 3 | env: 4 | PROPERTIES_PATH: "./android/key.properties" 5 | jobs: 6 | build: 7 | name: Build APK 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v3 12 | 13 | # Setup Java environment in order to build the Android app. 14 | - uses: actions/setup-java@v3 15 | with: 16 | distribution: "zulu" 17 | java-version: "12.x" 18 | 19 | 20 | # Gradle cache for faster builds 21 | - uses: actions/cache@v3 22 | with: 23 | path: | 24 | ~/.gradle/caches 25 | ~/.gradle/wrapper 26 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} 27 | restore-keys: | 28 | ${{ runner.os }}-gradle- 29 | # Setup the flutter environment. 30 | - uses: subosito/flutter-action@v2 31 | with: 32 | channel: "stable" 33 | 34 | # 35 | - run: | 36 | echo keyPassword=\${{ secrets.KEY_STORE }} > ${{env.PROPERTIES_PATH}} 37 | echo storePassword=\${{ secrets.KEY_PASSWORD }} >> ${{env.PROPERTIES_PATH}} 38 | echo keyAlias=\${{ secrets.KEY_ALIAS }} >> ${{env.PROPERTIES_PATH}} 39 | # 40 | - run: echo "${{ secrets.KEYSTORE2 }}" | base64 --decode > android/app/key.jks 41 | 42 | # Get flutter dependencies. 43 | - run: flutter pub get 44 | # Check for any formatting issues in the code. 45 | - run: flutter format --set-exit-if-changed . 46 | # Statically analyze the Dart code for any errors. 47 | - run: flutter analyze . 48 | # Build universal apk. 49 | - run: flutter build apk --release 50 | - uses: svenstaro/upload-release-action@v2 51 | with: 52 | repo_name: gokadzev/Musify 53 | repo_token: ${{ secrets.GITHUB_TOKEN }} 54 | file: build/app/outputs/apk/release/app-release.apk 55 | asset_name: Musify.apk 56 | tag: ${{ github.ref }} 57 | prerelease: true 58 | overwrite: true 59 | body: "New Musify Pre-Release! [only for testing purposes]" 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | /build/ 32 | 33 | # Web related 34 | lib/generated_plugin_registrant.dart 35 | 36 | # Symbolication related 37 | app.*.symbols 38 | 39 | # Obfuscation related 40 | app.*.map.json 41 | 42 | # Exceptions to above rules. 43 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 44 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 1ad9baa8b99a2897c20f9e6e54d3b9b359ade314 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | GitHub Readme Stats 3 |

Musify

4 |

Music Streaming and Downloading app made in Flutter!

5 |

6 | GitHub release License 7 |

8 |

9 | 10 | 11 | 12 |

Features

13 |

14 | Online Song Search :mag:
15 | Streaming Support :musical_note:
16 | Download Support :arrow_down:
17 | Play Local / Downloaded Songs Support :open_file_folder:
18 | High Quality mp3 / m4a / flac Format :fire:
19 | Lyrics Support :pencil:
20 | SponsorBlock Support :scissors:
21 | No Ads :no_entry_sign:
22 | No Subscriptions :dollar:
23 | 12 Supported Languages :us:
24 | Material UI & Accent Colors :art:
25 | 26 | 27 | --- 28 | 29 |

Screenshots

30 | 31 |
32 | screenshot 33 | screenshot 34 | screenshot 35 |
36 | 37 | --- 38 | 39 | 40 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | linter: 2 | rules: 3 | - always_declare_return_types 4 | - annotate_overrides 5 | - avoid_bool_literals_in_conditional_expressions 6 | - avoid_classes_with_only_static_members 7 | - avoid_empty_else 8 | - avoid_function_literals_in_foreach_calls 9 | - avoid_init_to_null 10 | - avoid_null_checks_in_equality_operators 11 | - avoid_print 12 | - avoid_relative_lib_imports 13 | - avoid_renaming_method_parameters 14 | - avoid_return_types_on_setters 15 | - avoid_returning_null 16 | - avoid_returning_null_for_future 17 | - avoid_returning_null_for_void 18 | - avoid_returning_this 19 | - avoid_shadowing_type_parameters 20 | - avoid_single_cascade_in_expression_statements 21 | - avoid_types_as_parameter_names 22 | - avoid_unnecessary_containers 23 | - avoid_unused_constructor_parameters 24 | - await_only_futures 25 | - camel_case_types 26 | - cancel_subscriptions 27 | - comment_references 28 | - constant_identifier_names 29 | - control_flow_in_finally 30 | - directives_ordering 31 | - empty_catches 32 | - empty_constructor_bodies 33 | - empty_statements 34 | - hash_and_equals 35 | - implementation_imports 36 | - iterable_contains_unrelated_type 37 | - library_names 38 | - library_prefixes 39 | - list_remove_unrelated_type 40 | - no_adjacent_strings_in_list 41 | - no_duplicate_case_values 42 | - no_logic_in_create_state 43 | - non_constant_identifier_names 44 | - noop_primitive_operations 45 | - null_closures 46 | - omit_local_variable_types 47 | - overridden_fields 48 | - package_api_docs 49 | - package_names 50 | - package_prefixed_library_names 51 | - prefer_adjacent_string_concatenation 52 | - prefer_collection_literals 53 | - prefer_conditional_assignment 54 | - prefer_const_constructors 55 | - prefer_contains 56 | - prefer_equal_for_default_values 57 | - prefer_final_fields 58 | - prefer_final_locals 59 | - prefer_generic_function_type_aliases 60 | - prefer_initializing_formals 61 | - prefer_interpolation_to_compose_strings 62 | - prefer_is_empty 63 | - prefer_is_not_empty 64 | - prefer_null_aware_operators 65 | - prefer_single_quotes 66 | - prefer_typing_uninitialized_variables 67 | - recursive_getters 68 | - require_trailing_commas 69 | - sized_box_for_whitespace 70 | - slash_for_doc_comments 71 | - sort_child_properties_last 72 | - sort_constructors_first 73 | - sort_pub_dependencies 74 | - sort_unnamed_constructors_first 75 | - test_types_in_equals 76 | - throw_in_finally 77 | - type_init_formals 78 | - unawaited_futures 79 | - unnecessary_await_in_return 80 | - unnecessary_brace_in_string_interps 81 | - unnecessary_const 82 | - unnecessary_getters_setters 83 | - unnecessary_lambdas 84 | - unnecessary_new 85 | - unnecessary_null_aware_assignments 86 | - unnecessary_parenthesis 87 | - unnecessary_statements 88 | - unnecessary_this 89 | - unrelated_type_equality_checks 90 | - use_colored_box 91 | - use_decorated_box 92 | - use_function_type_syntax_for_parameters 93 | - use_is_even_rather_than_modulo 94 | - use_named_constants 95 | - use_rethrow_when_possible 96 | - use_super_parameters 97 | - valid_regexps 98 | - void_checks -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | def keystoreProperties = new Properties() 29 | def keystorePropertiesFile = rootProject.file('key.properties') 30 | if (keystorePropertiesFile.exists()) { 31 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 32 | } 33 | 34 | def lifecycle_version = "2.4.0" 35 | 36 | android { 37 | compileSdkVersion 33 38 | ndkVersion flutter.ndkVersion 39 | 40 | compileOptions { 41 | sourceCompatibility JavaVersion.VERSION_1_8 42 | targetCompatibility JavaVersion.VERSION_1_8 43 | } 44 | 45 | kotlinOptions { 46 | jvmTarget = '1.8' 47 | } 48 | 49 | sourceSets { 50 | main.java.srcDirs += 'src/main/kotlin' 51 | } 52 | 53 | lintOptions { 54 | checkReleaseBuilds false 55 | abortOnError false 56 | } 57 | 58 | defaultConfig { 59 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 60 | applicationId "com.gokadzev.musify" 61 | minSdkVersion 21 62 | targetSdkVersion 33 63 | versionCode flutterVersionCode.toInteger() 64 | versionName flutterVersionName 65 | multiDexEnabled true 66 | } 67 | 68 | signingConfigs { 69 | release { 70 | //From decoded key 71 | storeFile = file('key.jks') 72 | 73 | //From key.properties 74 | keyAlias keystoreProperties['keyAlias'] 75 | keyPassword keystoreProperties['keyPassword'] 76 | storePassword keystoreProperties['storePassword'] 77 | } 78 | } 79 | 80 | buildTypes { 81 | release { 82 | // TODO: Add your own signing config for the release build. 83 | // Signing with the debug keys for now, so `flutter run --release` works. 84 | signingConfig signingConfigs.release 85 | shrinkResources false 86 | } 87 | } 88 | 89 | } 90 | 91 | flutter { 92 | source '../..' 93 | } 94 | 95 | dependencies { 96 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 97 | implementation 'com.android.support:multidex:1.0.3' 98 | implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version" 99 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" 100 | } 101 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 16 | 17 | 22 | 23 | 26 | 27 | 28 | 36 | 37 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/gokadzev/musify/MainActivity.kt: -------------------------------------------------------------------------------- 1 | import android.os.Build 2 | import android.os.Bundle 3 | import androidx.core.view.WindowCompat 4 | import io.flutter.embedding.android.FlutterActivity 5 | 6 | class MainActivity : FlutterActivity() { 7 | override fun onCreate(savedInstanceState: Bundle?) { 8 | // Aligns the Flutter view vertically with the window. 9 | WindowCompat.setDecorFitsSystemWindows(getWindow(), false) 10 | 11 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 12 | // Disable the Android splash screen fade out animation to avoid 13 | // a flicker before the similar frame is drawn in Flutter. 14 | splashScreen.setOnExitAnimationListener { splashScreenView -> splashScreenView.remove() } 15 | } 16 | 17 | super.onCreate(savedInstanceState) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-hdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/audio_service_pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-hdpi/audio_service_pause.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/audio_service_play_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-hdpi/audio_service_play_arrow.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/audio_service_skip_next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-hdpi/audio_service_skip_next.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/audio_service_skip_previous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-hdpi/audio_service_skip_previous.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/audio_service_stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-hdpi/audio_service_stop.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-hdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-mdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/audio_service_pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-mdpi/audio_service_pause.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/audio_service_play_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-mdpi/audio_service_play_arrow.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/audio_service_skip_next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-mdpi/audio_service_skip_next.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/audio_service_skip_previous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-mdpi/audio_service_skip_previous.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/audio_service_stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-mdpi/audio_service_stop.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-mdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-hdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-night-hdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-hdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-night-hdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-mdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-night-mdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-mdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-night-mdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-v21/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-night-v21/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xhdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-night-xhdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-night-xhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xxhdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-night-xxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xxxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-night-xxxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-night/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-v21/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xhdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/audio_service_pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xhdpi/audio_service_pause.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/audio_service_play_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xhdpi/audio_service_play_arrow.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/audio_service_skip_next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xhdpi/audio_service_skip_next.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/audio_service_skip_previous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xhdpi/audio_service_skip_previous.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/audio_service_stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xhdpi/audio_service_stop.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xxhdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/audio_service_pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xxhdpi/audio_service_pause.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/audio_service_play_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xxhdpi/audio_service_play_arrow.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/audio_service_skip_next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xxhdpi/audio_service_skip_next.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/audio_service_skip_previous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xxhdpi/audio_service_skip_previous.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/audio_service_stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xxhdpi/audio_service_stop.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xxxhdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/audio_service_pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xxxhdpi/audio_service_pause.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/audio_service_play_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xxxhdpi/audio_service_play_arrow.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/audio_service_skip_next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xxxhdpi/audio_service_skip_next.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/audio_service_skip_previous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xxxhdpi/audio_service_skip_previous.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/audio_service_stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xxxhdpi/audio_service_stop.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable-xxxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/drawable/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/mipmap-hdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/mipmap-mdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/raw/keep.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night-v31/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-v31/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #191919 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.7.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.2.2' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /assets/db/playlists.db.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "ytid": "PLgzTt0k8mXzEk586ze4BjvDXR7c-TUSnx", 3 | "title": "Top 50 Global", 4 | "subtitle": "Just Updated", 5 | "header_desc": "Top 50 Global Song.", 6 | "type": "playlist", 7 | "image": "https://charts-images.scdn.co/assets/locale_en/regional/daily/region_global_large.jpg", 8 | "list": [] 9 | }, 10 | { 11 | "ytid": "PLoumn5BIsUDd3IH7MVw2XiW9nQKzCFy4O", 12 | "title": "Best Pop Music Playlist 2022 - Most Listened Pop Songs 2022 (Today's Top Pop Hits 2022)", 13 | "subtitle": "Just Updated", 14 | "header_desc": "We recommend you to check other playlists or our favorite music charts. If you enjoyed listening to this one, maybe you", 15 | "type": "playlist", 16 | "image": "https://i.scdn.co/image/ab67706c0000bebbc14667db36e06ca21c2746fb", 17 | "list": [] 18 | }, 19 | { 20 | "ytid": "PLmQPPVKNGMHipaJbw0lHPuGPuKQDJkcdn", 21 | "title": "Lofi Remixes", 22 | "subtitle": "Just Updated", 23 | "header_desc": "", 24 | "type": "playlist", 25 | "image": "https://i.scdn.co/image/ab67616d0000b273ceeda3baf08689981f6a17a5", 26 | "list": [] 27 | }, 28 | { 29 | "ytid": "PLPZdY4vhqvRAKdgI75eWn5XM0gPqs3QMY", 30 | "title": "M+ike Remixes", 31 | "subtitle": "Just Updated", 32 | "header_desc": "", 33 | "type": "playlist", 34 | "image": "https://images.genius.com/593f78ac98312b1e6149cb9671a6bc47.500x500x1.jpg", 35 | "list": [] 36 | }, 37 | { 38 | "ytid": "PLHg022HMFzFDMNp9xBGy3sARnqxaPl3PG", 39 | "title": "Car Music 2022", 40 | "subtitle": "Just Updated", 41 | "header_desc": "Car Music 2022 - Best Car Music Playlist 2022 - Car Music Mix (Songs for Car Driving)", 42 | "type": "playlist", 43 | "image": "https://i.scdn.co/image/ab67706c0000bebbc82624b873d6a3392b0ab9cc", 44 | "list": [] 45 | }, 46 | { 47 | "ytid": "PLSR9lWowvoE3A9i4JVVHtQFjlJt0_LItG", 48 | "title": "TikTok Songs 2022 - Tik Tok Music - Best TikTok Songs 2022", 49 | "subtitle": "Just Updated", 50 | "header_desc": "TikTok Songs 2022 - Tik Tok Music - Best TikTok Songs 2022 Every week, we update our playlist with the latest hits. The", 51 | "type": "playlist", 52 | "image": "https://i.scdn.co/image/ab67616d0000b2739da92b8aa7cbcd57a20fe10e", 53 | "list": [] 54 | }, 55 | { 56 | "ytid": "PLm5t4IueiREEegTqfYaVB3aD5-ClBd5Dg", 57 | "title": "sped up songs", 58 | "subtitle": "Just Updated", 59 | "header_desc": "150%", 60 | "type": "playlist", 61 | "image": "https://i.scdn.co/image/ab67706f0000000328f3caa600067067bb348a0a", 62 | "list": [] 63 | }, 64 | { 65 | "ytid": "PLw9U13gRyHys_YyMKCWuG2gYQ1eG-vQv4", 66 | "title": "Big on the internet", 67 | "subtitle": "Just Updated", 68 | "header_desc": "", 69 | "type": "playlist", 70 | "image": "https://i.scdn.co/image/ab67706f000000030cff82b06291045fe23facaf", 71 | "list": [] 72 | }, 73 | { 74 | "ytid": "PLgzTt0k8mXzHcKebL8d0uYHfawiARhQja", 75 | "title": "Best Sad and Emotional Songs - Spotify Playlist 2022", 76 | "subtitle": "Just Updated", 77 | "header_desc": "If you are in a sad mood or you just want to listen to some sad songs and emotional songs, this is the perfect playlist ", 78 | "type": "playlist", 79 | "image": "https://i.scdn.co/image/ab67706c0000bebba7222f1f28cf4322f99585f1", 80 | "list": [] 81 | }, 82 | { 83 | "ytid": "RDCLAK5uy_lBNUteBRencHzKelu5iDHwLF6mYqjL-JU", 84 | "title": "Pop Certified", 85 | "subtitle": "Just Updated", 86 | "header_desc": "", 87 | "type": "playlist", 88 | "image": "https://i.scdn.co/image/ab67616d0000b2736ad4fa118d28a41fc649c8e8", 89 | "list": [] 90 | }, 91 | { 92 | "ytid": "RDCLAK5uy_kA_dvd-bpRQ98y6LwOjAnhQL5lyjNnZYA", 93 | "title": "Best New Indie", 94 | "subtitle": "Just Updated", 95 | "header_desc": "", 96 | "type": "playlist", 97 | "image": "https://i.scdn.co/image/ab67706c0000bebb252c20e18078d26d0450605b", 98 | "list": [] 99 | }, 100 | { 101 | "ytid": "RDCLAK5uy_no33oh6TOe0vPTFGabR24wAu3NeiVvc-Q", 102 | "title": "Electromix", 103 | "subtitle": "Just Updated", 104 | "header_desc": "", 105 | "type": "playlist", 106 | "image": "https://i.scdn.co/image/ab67616d0000b273a66c1376051e1c5228df9733", 107 | "list": [] 108 | }, 109 | { 110 | "ytid": "RDCLAK5uy_n0oLcyKJhNW8BmrnMySAoVuLjRZfgozG0", 111 | "title": "Energizing EDM", 112 | "subtitle": "Just Updated", 113 | "header_desc": "", 114 | "type": "playlist", 115 | "image": "https://i.scdn.co/image/ab67706c0000bebb10d786ec8874e176f92c49d8", 116 | "list": [] 117 | }, 118 | { 119 | "ytid": "RDCLAK5uy_lHUYsU7VTxndTCtf-ofbHDsvQWspcFBJ8", 120 | "title": "Unstoppable Pop", 121 | "subtitle": "Just Updated", 122 | "header_desc": "", 123 | "type": "playlist", 124 | "image": "https://i.scdn.co/image/ab67706c0000bebb0885e7a2c6186017bbb392a0", 125 | "list": [] 126 | }, 127 | { 128 | "ytid": "RDCLAK5uy_n0TxkLvMf0yENdVCRD31Oes1XEBoJgpIU", 129 | "title": "Electronic Motivation", 130 | "subtitle": "Just Updated", 131 | "header_desc": "", 132 | "type": "playlist", 133 | "image": "https://i.scdn.co/image/ab67706c0000bebb3ce718571dd7ac56a0c15f25", 134 | "list": [] 135 | }, 136 | { 137 | "ytid": "RDCLAK5uy_lrRVyinf4bGiN8dQ1jRWkVOMroYKAvnqE", 138 | "title": "Confidence Boost", 139 | "subtitle": "Just Updated", 140 | "header_desc": "", 141 | "type": "playlist", 142 | "image": "https://i.scdn.co/image/ab67616d0000b273411e532bde4068f5f301b22f", 143 | "list": [] 144 | }, 145 | { 146 | "ytid": "RDCLAK5uy_mpcC2CwnVbb6kBi_d99_FZvgG2QSi5ylo", 147 | "title": "Rock Adrenaline", 148 | "subtitle": "Just Updated", 149 | "header_desc": "", 150 | "type": "playlist", 151 | "image": "https://i.scdn.co/image/ab67616d0000b273bd0a1a068fe3b8e953682375", 152 | "list": [] 153 | }, 154 | { 155 | "ytid": "RDCLAK5uy_mnNGm2TBGoE7ciVFLrzepoNMWyreMuNlw", 156 | "title": "#ILoveAltPop", 157 | "subtitle": "Just Updated", 158 | "header_desc": "", 159 | "type": "playlist", 160 | "image": "https://i.scdn.co/image/ab67706f000000030ba1097327ab27f8ed29761f", 161 | "list": [] 162 | }, 163 | { 164 | "ytid": "RDCLAK5uy_mnBFITP45AFCdVtu8b7JfLFLbUZR46ObU", 165 | "title": "Essential EDM", 166 | "subtitle": "Just Updated", 167 | "header_desc": "", 168 | "type": "playlist", 169 | "image": "https://i.scdn.co/image/ab67616d0000b273727362a482b776115354a507", 170 | "list": [] 171 | }, 172 | { 173 | "ytid": "RDCLAK5uy_k-fiP0mCE_HlLqk-h15LlxGmjTCTn4_aA", 174 | "title": "House Rap Hits", 175 | "subtitle": "Just Updated", 176 | "header_desc": "", 177 | "type": "playlist", 178 | "image": "https://i.scdn.co/image/ab67616d0000b2734448c77918ba9ab6d63aadfb", 179 | "list": [] 180 | }, 181 | { 182 | "ytid": "RDCLAK5uy_nnZGCEPxzc5FASdbQVMufD25OfYBJlHqY", 183 | "title": "Modern Classical", 184 | "subtitle": "Just Updated", 185 | "header_desc": "", 186 | "type": "playlist", 187 | "image": "https://i.scdn.co/image/ab67616d0000b273c5c1aac2736bfb53fbbf943f", 188 | "list": [] 189 | }, 190 | { 191 | "ytid": "PL7zsB-C3aNu03RwSy2Bn3Ov3oaEReOlT5", 192 | "title": "Best Remixes Of Popular SONGS 2022 and 2023", 193 | "subtitle": "Just Updated", 194 | "header_desc": "Best Remixes Of Popular SONGS 2022 and 2023 Those are some of best remixes of popular songs for 2022, we hope u like th", 195 | "type": "playlist", 196 | "image": "https://i.scdn.co/image/ab67706c0000bebb445f93525180a82f1575087d", 197 | "list": [] 198 | }, 199 | { 200 | "ytid": "PLWEEt0QgQFInR8b2_sKk86VAGhLs_Iczf", 201 | "title": "OPM Top Hits: Original Pilipino Music 2022 (Pinoy Hits & Love Songs)", 202 | "subtitle": "Just Updated", 203 | "header_desc": "opm songs", 204 | "type": "playlist", 205 | "image": "https://i.scdn.co/image/ab67706c0000bebb5c6e74a654ff510d06bd1734", 206 | "list": [] 207 | }, 208 | { 209 | "ytid": "PLiy0XOfUv4hGbDDI0gx6sFqsYdcQ6zMWx", 210 | "title": "OPM 2000-2009 / Best Pinoy Songs Playlist 2000s Hits", 211 | "subtitle": "Just Updated", 212 | "header_desc": "Pinoy Rock Songs,Tagalog Hit Songs 2000, 2001, 2002,Filipino Songs Playlist 2003, 2004, 2005,OPM Songs 2006", 213 | "type": "playlist", 214 | "image": "https://i.scdn.co/image/ab67706c0000bebbc1aa43ecf170c86146b88e9a", 215 | "list": [] 216 | }, 217 | { 218 | "ytid": "PLfQAe5M2BkwCKimscRq-F9wkO5tUPY9TS", 219 | "title": "Mood Booster", 220 | "subtitle": "Just Updated", 221 | "header_desc": "", 222 | "type": "playlist", 223 | "image": "https://i.scdn.co/image/ab67706f00000003bd0e19e810bb4b55ab164a95", 224 | "list": [] 225 | }, 226 | { 227 | "ytid": "PLmYULo-LwF9Lsx-JRO0UN-dJWk_0pbd-L", 228 | "title": "Best of MrSuicideSheep", 229 | "subtitle": "Just Updated", 230 | "header_desc": "", 231 | "type": "playlist", 232 | "image": "https://i.scdn.co/image/ab67706c0000bebb5844370cd89c9055a53a9d1a", 233 | "list": [] 234 | }, 235 | { 236 | "ytid": "PLC1og_v3eb4hrv4wsqG1G5dsNZh9bIscJ", 237 | "title": "Trap Nation", 238 | "subtitle": "Just Updated", 239 | "header_desc": "", 240 | "type": "playlist", 241 | "image": "https://i.scdn.co/image/ab67706c0000bebbf7911965253c20f3210e3068", 242 | "list": [] 243 | } 244 | 245 | ] -------------------------------------------------------------------------------- /assets/images/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/assets/images/ic_launcher.png -------------------------------------------------------------------------------- /assets/images/ic_launcher_background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/assets/images/ic_launcher_background.jpg -------------------------------------------------------------------------------- /assets/images/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/assets/images/ic_launcher_foreground.png -------------------------------------------------------------------------------- /assets/images/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/assets/images/ic_launcher_round.png -------------------------------------------------------------------------------- /assets/images/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/assets/images/splash.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/full_description.txt: -------------------------------------------------------------------------------- 1 | Musify is an app for streaming and downloading music. Its features include: 2 | 3 | * Online Song Search 4 | * Streaming Support 5 | * Download Support 6 | * Play Local / Downloaded Songs Supported 7 | * High Quality m4a / mp3 / flac Format 8 | * Lyrics Support 9 | * No Ads 10 | * No Subscriptions 11 | * SponsorBlock Support 12 | * 12 Supported Languages 13 | * Material UI & Accent Colors 14 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/fastlane/metadata/android/en-US/images/icon.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/fastlane/metadata/android/en-US/images/phoneScreenshots/01.jpg -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/fastlane/metadata/android/en-US/images/phoneScreenshots/02.jpg -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/fastlane/metadata/android/en-US/images/phoneScreenshots/03.jpg -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/short_description.txt: -------------------------------------------------------------------------------- 1 | Music Streaming and Downloading app -------------------------------------------------------------------------------- /fonts/ubuntu.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaJasrai/Project-Flutter---Musify---MusicApplication/830c5cd91550aba8d4a7e9485eab4dcbbd3b5cbe/fonts/ubuntu.ttf -------------------------------------------------------------------------------- /l10n.yaml: -------------------------------------------------------------------------------- 1 | arb-dir: lib/localization 2 | template-arb-file: app_en.arb 3 | output-localization-file: app_localizations.dart -------------------------------------------------------------------------------- /lib/API/musify.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:math'; 3 | 4 | import 'package:audio_service/audio_service.dart'; 5 | import 'package:flutter/services.dart'; 6 | import 'package:flutter/widgets.dart'; 7 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 8 | import 'package:hive/hive.dart'; 9 | import 'package:http/http.dart' as http; 10 | import 'package:musify/helper/formatter.dart'; 11 | import 'package:musify/helper/mediaitem.dart'; 12 | import 'package:musify/services/audio_handler.dart'; 13 | import 'package:musify/services/audio_manager.dart'; 14 | import 'package:musify/services/data_manager.dart'; 15 | import 'package:musify/services/ext_storage.dart'; 16 | import 'package:musify/services/lyrics_service.dart'; 17 | import 'package:on_audio_query/on_audio_query.dart'; 18 | import 'package:permission_handler/permission_handler.dart'; 19 | import 'package:youtube_explode_dart/youtube_explode_dart.dart'; 20 | 21 | final yt = YoutubeExplode(); 22 | final OnAudioQuery _audioQuery = OnAudioQuery(); 23 | 24 | final random = Random(); 25 | 26 | List playlists = []; 27 | List userPlaylists = Hive.box('user').get('playlists', defaultValue: []); 28 | List userLikedSongsList = Hive.box('user').get('likedSongs', defaultValue: []); 29 | List suggestedPlaylists = []; 30 | List activePlaylist = []; 31 | 32 | final lyrics = ValueNotifier('null'); 33 | String lastFetchedLyrics = 'null'; 34 | 35 | int id = 0; 36 | 37 | Future fetchSongsList(String searchQuery) async { 38 | final List list = await yt.search.search(searchQuery); 39 | final searchedList = [ 40 | for (final s in list) 41 | returnSongLayout( 42 | 0, 43 | s, 44 | ) 45 | ]; 46 | 47 | return searchedList; 48 | } 49 | 50 | Future get10Music(dynamic playlistid) async { 51 | final List playlistSongs = 52 | await getData('cache', 'playlist10Songs$playlistid') ?? []; 53 | if (playlistSongs.isEmpty) { 54 | var index = 0; 55 | await for (final song in yt.playlists.getVideos(playlistid).take(10)) { 56 | playlistSongs.add( 57 | returnSongLayout( 58 | index, 59 | song, 60 | ), 61 | ); 62 | index += 1; 63 | } 64 | 65 | addOrUpdateData('cache', 'playlist10Songs$playlistid', playlistSongs); 66 | } 67 | 68 | return playlistSongs; 69 | } 70 | 71 | Future> getUserPlaylists() async { 72 | final playlistsByUser = []; 73 | for (final playlistID in userPlaylists) { 74 | final plist = await yt.playlists.get(playlistID); 75 | playlistsByUser.add({ 76 | 'ytid': plist.id, 77 | 'title': plist.title, 78 | 'subtitle': 'Just Updated', 79 | 'header_desc': plist.description.length < 120 80 | ? plist.description 81 | : plist.description.substring(0, 120), 82 | 'type': 'playlist', 83 | 'image': '', 84 | 'list': [] 85 | }); 86 | } 87 | return playlistsByUser; 88 | } 89 | 90 | String addUserPlaylist(String playlistId, BuildContext context) { 91 | if (playlistId.length != 34) { 92 | return '${AppLocalizations.of(context)!.notYTlist}!'; 93 | } else { 94 | userPlaylists.add(playlistId); 95 | addOrUpdateData('user', 'playlists', userPlaylists); 96 | return '${AppLocalizations.of(context)!.addedSuccess}!'; 97 | } 98 | } 99 | 100 | void removeUserPlaylist(String playlistId) { 101 | userPlaylists.remove(playlistId); 102 | addOrUpdateData('user', 'playlists', userPlaylists); 103 | } 104 | 105 | Future addUserLikedSong(dynamic songId) async { 106 | userLikedSongsList 107 | .add(await getSongDetails(userLikedSongsList.length, songId)); 108 | addOrUpdateData('user', 'likedSongs', userLikedSongsList); 109 | } 110 | 111 | void removeUserLikedSong(dynamic songId) { 112 | userLikedSongsList.removeWhere((song) => song['ytid'] == songId); 113 | addOrUpdateData('user', 'likedSongs', userLikedSongsList); 114 | } 115 | 116 | bool isSongAlreadyLiked(dynamic songId) { 117 | return userLikedSongsList.where((song) => song['ytid'] == songId).isNotEmpty; 118 | } 119 | 120 | Future getPlaylists([int? playlistsNum]) async { 121 | if (playlists.isEmpty) { 122 | playlists = 123 | json.decode(await rootBundle.loadString('assets/db/playlists.db.json')) 124 | as List; 125 | } 126 | 127 | if (playlistsNum != null) { 128 | if (suggestedPlaylists.isEmpty) { 129 | suggestedPlaylists = 130 | (playlists.toList()..shuffle()).take(playlistsNum).toList(); 131 | } 132 | return suggestedPlaylists; 133 | } else { 134 | return playlists; 135 | } 136 | } 137 | 138 | Future searchPlaylist(String query) async { 139 | if (playlists.isEmpty) { 140 | playlists = 141 | json.decode(await rootBundle.loadString('assets/db/playlists.db.json')) 142 | as List; 143 | } 144 | 145 | return playlists 146 | .where( 147 | (playlist) => 148 | playlist['title'].toLowerCase().contains(query.toLowerCase()), 149 | ) 150 | .toList(); 151 | } 152 | 153 | Future getRandomSong() async { 154 | const playlistId = 'PLgzTt0k8mXzEk586ze4BjvDXR7c-TUSnx'; 155 | final List playlistSongs = await getSongsFromPlaylist(playlistId); 156 | 157 | return playlistSongs[random.nextInt(playlistSongs.length)]; 158 | } 159 | 160 | Future getSongsFromPlaylist(dynamic playlistid) async { 161 | final List playlistSongs = 162 | await getData('cache', 'playlistSongs$playlistid') ?? []; 163 | if (playlistSongs.isEmpty) { 164 | var index = 0; 165 | await for (final song in yt.playlists.getVideos(playlistid)) { 166 | playlistSongs.add( 167 | returnSongLayout( 168 | index, 169 | song, 170 | ), 171 | ); 172 | index += 1; 173 | } 174 | addOrUpdateData('cache', 'playlistSongs$playlistid', playlistSongs); 175 | } 176 | 177 | return playlistSongs; 178 | } 179 | 180 | Future setActivePlaylist(List plist) async { 181 | if (plist is List) { 182 | activePlaylist = []; 183 | id = 0; 184 | final activeTempPlaylist = [ 185 | for (final song in plist) songModelToMediaItem(song, song.data) 186 | ]; 187 | 188 | await MyAudioHandler().addQueueItems(activeTempPlaylist); 189 | 190 | play(); 191 | } else { 192 | activePlaylist = plist; 193 | id = 0; 194 | await playSong(activePlaylist[id]); 195 | } 196 | } 197 | 198 | Future getPlaylistInfoForWidget(dynamic id) async { 199 | var searchPlaylist = playlists.where((list) => list['ytid'] == id).toList(); 200 | var isUserPlaylist = false; 201 | 202 | if (searchPlaylist.isEmpty) { 203 | final usPlaylists = await getUserPlaylists(); 204 | searchPlaylist = usPlaylists.where((list) => list['ytid'] == id).toList(); 205 | isUserPlaylist = true; 206 | } 207 | 208 | final playlist = searchPlaylist[0]; 209 | 210 | if (playlist['list'].length == 0) { 211 | searchPlaylist[searchPlaylist.indexOf(playlist)]['list'] = 212 | await getSongsFromPlaylist(playlist['ytid']); 213 | if (!isUserPlaylist) { 214 | playlists[playlists.indexOf(playlist)]['list'] = 215 | searchPlaylist[searchPlaylist.indexOf(playlist)]['list']; 216 | } 217 | } 218 | 219 | return playlist; 220 | } 221 | 222 | Future getSong(dynamic songId, bool geturl) async { 223 | final manifest = await yt.videos.streamsClient.getManifest(songId); 224 | if (geturl) { 225 | return manifest.audioOnly.withHighestBitrate().url.toString(); 226 | } else { 227 | return manifest.audioOnly.withHighestBitrate(); 228 | } 229 | } 230 | 231 | Future getSongDetails(dynamic songIndex, dynamic songId) async { 232 | final song = await yt.videos.get(songId); 233 | return returnSongLayout( 234 | songIndex, 235 | song, 236 | ); 237 | } 238 | 239 | Future> getLocalSongs() async { 240 | var localSongs = []; 241 | if (await ExtStorageProvider.requestPermission(Permission.storage)) { 242 | localSongs = await _audioQuery.querySongs( 243 | path: await ExtStorageProvider.getExtStorage(dirName: 'Music'), 244 | ); 245 | } 246 | 247 | return localSongs; 248 | } 249 | 250 | Future>> getSkipSegments(String id) async { 251 | try { 252 | final res = await http.get( 253 | Uri( 254 | scheme: 'https', 255 | host: 'sponsor.ajay.app', 256 | path: '/api/skipSegments', 257 | queryParameters: { 258 | 'videoID': id, 259 | 'category': [ 260 | 'sponsor', 261 | 'selfpromo', 262 | 'interaction', 263 | 'intro', 264 | 'outro', 265 | 'music_offtopic' 266 | ], 267 | 'actionType': 'skip' 268 | }, 269 | ), 270 | ); 271 | if (res.body != 'Not Found') { 272 | final data = jsonDecode(res.body); 273 | final segments = data.map((obj) { 274 | return Map.castFrom({ 275 | 'start': obj['segment'].first.toInt(), 276 | 'end': obj['segment'].last.toInt(), 277 | }); 278 | }).toList(); 279 | return List.castFrom>(segments); 280 | } else { 281 | return []; 282 | } 283 | } catch (e, stack) { 284 | debugPrint('$e $stack'); 285 | return []; 286 | } 287 | } 288 | 289 | Future getSongLyrics(String artist, String title) async { 290 | if (lastFetchedLyrics != '$artist - $title') { 291 | lyrics.value = 'null'; 292 | final _lyrics = await Lyrics().getLyrics(artist: artist, track: title); 293 | lyrics.value = _lyrics; 294 | lastFetchedLyrics = '$artist - $title'; 295 | return _lyrics; 296 | } 297 | 298 | return lyrics.value; 299 | } 300 | -------------------------------------------------------------------------------- /lib/customWidgets/custom_animated_bottom_bar.dart: -------------------------------------------------------------------------------- 1 | // pub: https://pub.dev/packages/flashy_tab_bar2 2 | // license: https://raw.githubusercontent.com/Cuberto/flashy-tabbar-android/master/LICENSE 3 | // remade (not original) 4 | 5 | import 'package:flutter/material.dart'; 6 | import 'package:musify/customWidgets/marque.dart'; 7 | 8 | class CustomAnimatedBottomBar extends StatelessWidget { 9 | CustomAnimatedBottomBar({ 10 | super.key, 11 | this.selectedIndex = 0, 12 | this.height = 60, 13 | this.showElevation = true, 14 | this.iconSize = 20, 15 | this.backgroundColor, 16 | this.animationDuration = const Duration(milliseconds: 170), 17 | this.animationCurve = Curves.linear, 18 | this.shadows = const [ 19 | BoxShadow( 20 | color: Colors.black12, 21 | blurRadius: 3, 22 | ), 23 | ], 24 | required this.items, 25 | required this.onItemSelected, 26 | }) { 27 | assert(height >= 55 && height <= 100); 28 | assert(items.length >= 2 && items.length <= 5); 29 | } 30 | 31 | final Curve animationCurve; 32 | final Duration animationDuration; 33 | final Color? backgroundColor; 34 | final double height; 35 | final double iconSize; 36 | final List items; 37 | final ValueChanged onItemSelected; 38 | final int selectedIndex; 39 | final List shadows; 40 | final bool showElevation; 41 | 42 | @override 43 | Widget build(BuildContext context) { 44 | final bg = (backgroundColor == null) 45 | ? Theme.of(context).bottomAppBarColor 46 | : backgroundColor; 47 | 48 | return DecoratedBox( 49 | decoration: BoxDecoration( 50 | color: bg, 51 | boxShadow: showElevation ? shadows : [], 52 | ), 53 | child: SafeArea( 54 | child: Container( 55 | width: double.infinity, 56 | height: height, 57 | padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 20), 58 | child: Row( 59 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 60 | children: items.map((item) { 61 | final index = items.indexOf(item); 62 | return Expanded( 63 | child: GestureDetector( 64 | onTap: () => onItemSelected(index), 65 | child: _FlashTabBarItem( 66 | item: item, 67 | tabBarHeight: height, 68 | iconSize: iconSize, 69 | isSelected: index == selectedIndex, 70 | backgroundColor: bg!, 71 | animationDuration: animationDuration, 72 | animationCurve: animationCurve, 73 | ), 74 | ), 75 | ); 76 | }).toList(), 77 | ), 78 | ), 79 | ), 80 | ); 81 | } 82 | } 83 | 84 | class BottomNavBarItem { 85 | BottomNavBarItem({ 86 | required this.icon, 87 | required this.title, 88 | this.activeColor = const Color(0xff272e81), 89 | this.inactiveColor = const Color(0xff9496c1), 90 | }); 91 | 92 | Color activeColor; 93 | final Icon icon; 94 | Color inactiveColor; 95 | final Text title; 96 | } 97 | 98 | class _FlashTabBarItem extends StatelessWidget { 99 | const _FlashTabBarItem({ 100 | required this.item, 101 | required this.isSelected, 102 | required this.tabBarHeight, 103 | required this.backgroundColor, 104 | required this.animationDuration, 105 | required this.animationCurve, 106 | required this.iconSize, 107 | }); 108 | 109 | final Curve animationCurve; 110 | final Duration animationDuration; 111 | final Color backgroundColor; 112 | final double iconSize; 113 | final bool isSelected; 114 | final BottomNavBarItem item; 115 | final double tabBarHeight; 116 | 117 | @override 118 | Widget build(BuildContext context) { 119 | return Container( 120 | color: backgroundColor, 121 | height: double.maxFinite, 122 | child: Stack( 123 | clipBehavior: Clip.hardEdge, 124 | alignment: Alignment.center, 125 | children: [ 126 | AnimatedAlign( 127 | duration: animationDuration, 128 | alignment: isSelected ? Alignment.topCenter : Alignment.center, 129 | child: AnimatedOpacity( 130 | opacity: isSelected ? 1.0 : 1.0, 131 | duration: animationDuration, 132 | child: IconTheme( 133 | data: IconThemeData( 134 | size: iconSize, 135 | color: isSelected 136 | ? item.activeColor.withOpacity(1) 137 | : item.inactiveColor, 138 | ), 139 | child: item.icon, 140 | ), 141 | ), 142 | ), 143 | AnimatedPositioned( 144 | curve: animationCurve, 145 | duration: animationDuration, 146 | top: isSelected ? -2.0 * iconSize : tabBarHeight / 4, 147 | child: Column( 148 | mainAxisAlignment: MainAxisAlignment.center, 149 | children: [ 150 | SizedBox( 151 | width: iconSize, 152 | height: iconSize, 153 | ), 154 | CustomPaint( 155 | painter: _CustomPath(backgroundColor), 156 | child: SizedBox( 157 | width: 80, 158 | height: iconSize, 159 | ), 160 | ) 161 | ], 162 | ), 163 | ), 164 | AnimatedAlign( 165 | alignment: isSelected ? Alignment.center : Alignment.bottomCenter, 166 | duration: animationDuration, 167 | curve: animationCurve, 168 | child: AnimatedOpacity( 169 | opacity: isSelected ? 1.0 : 0.0, 170 | duration: animationDuration, 171 | child: MarqueeWidget( 172 | direction: Axis.horizontal, 173 | child: DefaultTextStyle.merge( 174 | style: TextStyle( 175 | color: item.activeColor, 176 | fontWeight: FontWeight.bold, 177 | ), 178 | child: item.title, 179 | ), 180 | ), 181 | ), 182 | ), 183 | Positioned( 184 | bottom: 0, 185 | child: CustomPaint( 186 | painter: _CustomPath(backgroundColor), 187 | child: SizedBox( 188 | width: 80, 189 | height: iconSize - 8, 190 | ), 191 | ), 192 | ), 193 | Align( 194 | alignment: Alignment.bottomCenter, 195 | child: AnimatedOpacity( 196 | duration: animationDuration, 197 | opacity: isSelected ? 1.0 : 0.0, 198 | child: Container( 199 | width: 5, 200 | height: 5, 201 | alignment: Alignment.bottomCenter, 202 | margin: const EdgeInsets.all(5), 203 | decoration: BoxDecoration( 204 | color: item.activeColor, 205 | borderRadius: BorderRadius.circular(2.5), 206 | ), 207 | ), 208 | ), 209 | ) 210 | ], 211 | ), 212 | ); 213 | } 214 | } 215 | 216 | class _CustomPath extends CustomPainter { 217 | _CustomPath(this.backgroundColor); 218 | 219 | final Color backgroundColor; 220 | 221 | @override 222 | void paint(Canvas canvas, Size size) { 223 | final path = Path(); 224 | final paint = Paint(); 225 | 226 | path.lineTo(0, 0); 227 | path.lineTo(0, 2.0 * size.height); 228 | path.lineTo(1.0 * size.width, 2.0 * size.height); 229 | path.lineTo(1.0 * size.width, 1.0 * size.height); 230 | path.lineTo(0, 0); 231 | path.close(); 232 | 233 | paint.color = backgroundColor; 234 | canvas.drawPath(path, paint); 235 | } 236 | 237 | @override 238 | bool shouldRepaint(CustomPainter oldDelegate) { 239 | return oldDelegate != this; 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /lib/customWidgets/delayed_display.dart: -------------------------------------------------------------------------------- 1 | // pub: https://pub.dev/packages/delayed_display 2 | // license: https://raw.githubusercontent.com/ThomasEcalle/delayed_display/master/LICENSE 3 | // remade (not original) 4 | 5 | import 'dart:async'; 6 | 7 | import 'package:flutter/material.dart'; 8 | 9 | class DelayedDisplay extends StatefulWidget { 10 | /// DelayedDisplay constructor 11 | const DelayedDisplay({ 12 | required this.child, 13 | this.delay = Duration.zero, 14 | this.fadingDuration = const Duration(milliseconds: 800), 15 | this.slidingCurve = Curves.decelerate, 16 | this.slidingBeginOffset = const Offset(0, 0.35), 17 | this.fadeIn = true, 18 | }); 19 | 20 | /// Child that will be displayed with the animation and delay 21 | final Widget child; 22 | 23 | /// Delay before displaying the widget and the animations 24 | final Duration delay; 25 | 26 | /// Duration of the fading animation 27 | final Duration fadingDuration; 28 | 29 | /// Curve of the sliding animation 30 | final Curve slidingCurve; 31 | 32 | /// Offset of the widget at the beginning of the sliding animation 33 | final Offset slidingBeginOffset; 34 | 35 | /// If true, make the child appear, disappear otherwise. Default to true. 36 | final bool fadeIn; 37 | 38 | @override 39 | _DelayedDisplayState createState() => _DelayedDisplayState(); 40 | } 41 | 42 | class _DelayedDisplayState extends State 43 | with TickerProviderStateMixin { 44 | /// Controller of the opacity animation 45 | late AnimationController _opacityController; 46 | 47 | /// Sliding Animation offset 48 | late Animation _slideAnimationOffset; 49 | 50 | /// Timer used to delayed animation 51 | Timer? _timer; 52 | 53 | /// Simple getter for widget's delay 54 | Duration get delay => widget.delay; 55 | 56 | /// Simple getter for widget's opacityTransitionDuration 57 | Duration get opacityTransitionDuration => widget.fadingDuration; 58 | 59 | /// Simple getter for widget's slidingCurve 60 | Curve get slidingCurve => widget.slidingCurve; 61 | 62 | /// Simple getter for widget's beginOffset 63 | Offset get beginOffset => widget.slidingBeginOffset; 64 | 65 | /// Simple getter for widget's fadeIn 66 | bool get fadeIn => widget.fadeIn; 67 | 68 | /// Initialize controllers, curve and offset with given parameters or default values 69 | /// Use a Timer in order to delay the animations if needed 70 | @override 71 | void initState() { 72 | super.initState(); 73 | 74 | _opacityController = AnimationController( 75 | vsync: this, 76 | duration: opacityTransitionDuration, 77 | ); 78 | 79 | final curvedAnimation = CurvedAnimation( 80 | curve: slidingCurve, 81 | parent: _opacityController, 82 | ); 83 | 84 | _slideAnimationOffset = Tween( 85 | begin: beginOffset, 86 | end: Offset.zero, 87 | ).animate(curvedAnimation); 88 | 89 | _runFadeAnimation(); 90 | } 91 | 92 | /// Dispose the opacity controller 93 | @override 94 | void dispose() { 95 | _opacityController.dispose(); 96 | _timer?.cancel(); 97 | super.dispose(); 98 | } 99 | 100 | /// Whenever the widget is updated and that fadeIn is different from the oldWidget, triggers the fade in 101 | /// or out animation. 102 | @override 103 | void didUpdateWidget(DelayedDisplay oldWidget) { 104 | super.didUpdateWidget(oldWidget); 105 | if (oldWidget.fadeIn == fadeIn) { 106 | return; 107 | } 108 | _runFadeAnimation(); 109 | } 110 | 111 | void _runFadeAnimation() { 112 | _timer = Timer(delay, () { 113 | fadeIn ? _opacityController.forward() : _opacityController.reverse(); 114 | }); 115 | } 116 | 117 | @override 118 | Widget build(BuildContext context) { 119 | return FadeTransition( 120 | opacity: _opacityController, 121 | child: SlideTransition( 122 | position: _slideAnimationOffset, 123 | child: widget.child, 124 | ), 125 | ); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /lib/customWidgets/marque.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class MarqueeWidget extends StatefulWidget { 4 | const MarqueeWidget({ 5 | super.key, 6 | required this.child, 7 | this.direction = Axis.horizontal, 8 | this.animationDuration = const Duration(milliseconds: 6000), 9 | this.backDuration = const Duration(milliseconds: 800), 10 | this.pauseDuration = const Duration(milliseconds: 800), 11 | }); 12 | final Widget child; 13 | final Axis direction; 14 | final Duration animationDuration, backDuration, pauseDuration; 15 | 16 | @override 17 | _MarqueeWidgetState createState() => _MarqueeWidgetState(); 18 | } 19 | 20 | class _MarqueeWidgetState extends State { 21 | late ScrollController scrollController; 22 | 23 | @override 24 | void initState() { 25 | scrollController = ScrollController(initialScrollOffset: 50.0); 26 | WidgetsBinding.instance.addPostFrameCallback(scroll); 27 | super.initState(); 28 | } 29 | 30 | @override 31 | void dispose() { 32 | scrollController.dispose(); 33 | super.dispose(); 34 | } 35 | 36 | @override 37 | Widget build(BuildContext context) { 38 | return SingleChildScrollView( 39 | scrollDirection: widget.direction, 40 | controller: scrollController, 41 | child: widget.child, 42 | ); 43 | } 44 | 45 | void scroll(_) async { 46 | while (scrollController.hasClients) { 47 | await Future.delayed(widget.pauseDuration); 48 | if (scrollController.hasClients) { 49 | await scrollController.animateTo( 50 | scrollController.position.maxScrollExtent, 51 | duration: widget.animationDuration, 52 | curve: Curves.ease, 53 | ); 54 | } 55 | await Future.delayed(widget.pauseDuration); 56 | if (scrollController.hasClients) { 57 | await scrollController.animateTo( 58 | 0.0, 59 | duration: widget.backDuration, 60 | curve: Curves.easeOut, 61 | ); 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/customWidgets/setting_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:musify/style/appTheme.dart'; 3 | 4 | class SettingBar extends StatelessWidget { 5 | SettingBar(this.tileName, this.tileIcon, this.onTap); 6 | 7 | final Function() onTap; 8 | final String tileName; 9 | final IconData tileIcon; 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Padding( 14 | padding: const EdgeInsets.only(top: 8, left: 8, right: 8, bottom: 6), 15 | child: Card( 16 | child: ListTile( 17 | leading: Icon(tileIcon, color: accent.primary), 18 | title: Text( 19 | tileName, 20 | style: TextStyle(color: accent.primary), 21 | ), 22 | onTap: onTap, 23 | ), 24 | ), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/customWidgets/song_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; 4 | import 'package:musify/API/musify.dart'; 5 | import 'package:musify/services/audio_manager.dart'; 6 | import 'package:musify/services/download_manager.dart'; 7 | import 'package:musify/style/appTheme.dart'; 8 | 9 | class SongBar extends StatelessWidget { 10 | SongBar(this.song, this.moveBackAfterPlay, {super.key}); 11 | 12 | late final dynamic song; 13 | late final bool moveBackAfterPlay; 14 | late final songLikeStatus = 15 | ValueNotifier(isSongAlreadyLiked(song['ytid'])); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Container( 20 | padding: const EdgeInsets.only(left: 12, right: 12, bottom: 15), 21 | child: InkWell( 22 | borderRadius: BorderRadius.circular(20), 23 | onTap: () { 24 | playSong(song); 25 | if (activePlaylist.isNotEmpty) { 26 | activePlaylist = []; 27 | id = 0; 28 | } 29 | if (moveBackAfterPlay) { 30 | Navigator.pushReplacementNamed(context, '/'); 31 | } 32 | }, 33 | splashColor: accent.primary.withOpacity(0.4), 34 | hoverColor: accent.primary.withOpacity(0.4), 35 | focusColor: accent.primary.withOpacity(0.4), 36 | highlightColor: accent.primary.withOpacity(0.4), 37 | child: Row( 38 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 39 | children: [ 40 | CachedNetworkImage( 41 | width: 60, 42 | height: 60, 43 | imageUrl: song['lowResImage'].toString(), 44 | imageBuilder: (context, imageProvider) => DecoratedBox( 45 | decoration: BoxDecoration( 46 | borderRadius: BorderRadius.circular(12), 47 | image: DecorationImage( 48 | image: imageProvider, 49 | centerSlice: const Rect.fromLTRB(1, 1, 1, 1), 50 | ), 51 | ), 52 | ), 53 | ), 54 | Flexible( 55 | child: Column( 56 | crossAxisAlignment: CrossAxisAlignment.start, 57 | children: [ 58 | Container( 59 | alignment: Alignment.centerLeft, 60 | padding: const EdgeInsets.only(left: 15), 61 | child: Text( 62 | overflow: TextOverflow.ellipsis, 63 | (song['title']) 64 | .toString() 65 | .split('(')[0] 66 | .replaceAll('"', '"') 67 | .replaceAll('&', '&'), 68 | style: TextStyle( 69 | color: accent.primary, 70 | fontSize: 16, 71 | fontWeight: FontWeight.w700, 72 | ), 73 | ), 74 | ), 75 | const SizedBox( 76 | height: 5, 77 | ), 78 | Container( 79 | padding: const EdgeInsets.only(left: 15), 80 | child: Text( 81 | overflow: TextOverflow.ellipsis, 82 | song['more_info']['singers'].toString(), 83 | style: TextStyle( 84 | color: Theme.of(context).hintColor, 85 | fontWeight: FontWeight.w400, 86 | fontSize: 14, 87 | ), 88 | ), 89 | ), 90 | ], 91 | ), 92 | ), 93 | Row( 94 | mainAxisSize: MainAxisSize.min, 95 | children: [ 96 | ValueListenableBuilder( 97 | valueListenable: songLikeStatus, 98 | builder: (_, value, __) { 99 | if (value == true) { 100 | return IconButton( 101 | color: accent.primary, 102 | icon: const Icon(MdiIcons.star), 103 | onPressed: () => { 104 | removeUserLikedSong(song['ytid']), 105 | songLikeStatus.value = false 106 | }, 107 | ); 108 | } else { 109 | return IconButton( 110 | color: accent.primary, 111 | icon: const Icon(MdiIcons.starOutline), 112 | onPressed: () => { 113 | addUserLikedSong(song['ytid']), 114 | songLikeStatus.value = true 115 | }, 116 | ); 117 | } 118 | }, 119 | ), 120 | IconButton( 121 | color: accent.primary, 122 | icon: const Icon(MdiIcons.downloadOutline), 123 | onPressed: () => downloadSong(context, song), 124 | ), 125 | ], 126 | ), 127 | ], 128 | ), 129 | ), 130 | ); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /lib/customWidgets/spinner.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:musify/style/appTheme.dart'; 3 | 4 | class Spinner extends StatelessWidget { 5 | const Spinner({super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return Center( 10 | child: CircularProgressIndicator( 11 | valueColor: AlwaysStoppedAnimation(accent.primary), 12 | ), 13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/helper/flutter_toast.dart: -------------------------------------------------------------------------------- 1 | import 'package:fluttertoast/fluttertoast.dart'; 2 | import 'package:musify/style/appColors.dart'; 3 | import 'package:musify/style/appTheme.dart'; 4 | 5 | void showToast(String text) { 6 | Fluttertoast.showToast( 7 | backgroundColor: getMaterialColorFromColor(accent.primary), 8 | textColor: isAccentWhite(), 9 | msg: text, 10 | toastLength: Toast.LENGTH_SHORT, 11 | gravity: ToastGravity.BOTTOM, 12 | fontSize: 14, 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /lib/helper/formatter.dart: -------------------------------------------------------------------------------- 1 | import 'package:youtube_explode_dart/youtube_explode_dart.dart'; 2 | 3 | String formatSongTitle(String title) { 4 | return title 5 | .replaceAll('&', '&') 6 | .replaceAll(''', "'") 7 | .replaceAll('"', '"') 8 | .replaceAll('[Official Music Video]', '') 9 | .replaceAll('OFFICIAL MUSIC VIDEO', '') 10 | .replaceAll('Video', '') 11 | .replaceAll('[Official Video]', '') 12 | .replaceAll('[OFFICIAL VIDEO]', '') 13 | .replaceAll('[official music video]', '') 14 | .replaceAll('[Official Perfomance Video]', '') 15 | .replaceAll('[Lyrics]', '') 16 | .replaceAll('[Lyric Video]', '') 17 | .replaceAll('Lyric Video', '') 18 | .replaceAll('[Official Lyric Video]', '') 19 | .split(' (')[0] 20 | .split('|')[0] 21 | .trim(); 22 | } 23 | 24 | Map returnSongLayout(dynamic index, Video song) { 25 | return { 26 | 'id': index, 27 | 'ytid': song.id.toString(), 28 | 'title': formatSongTitle( 29 | song.title.split('-')[song.title.split('-').length - 1], 30 | ), 31 | 'image': song.thumbnails.standardResUrl, 32 | 'lowResImage': song.thumbnails.lowResUrl, 33 | 'highResImage': song.thumbnails.maxResUrl, 34 | 'album': '', 35 | 'type': 'song', 36 | 'more_info': { 37 | 'primary_artists': song.title.split('-')[0], 38 | 'singers': song.title.split('-')[0], 39 | } 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /lib/helper/mediaitem.dart: -------------------------------------------------------------------------------- 1 | import 'package:audio_service/audio_service.dart'; 2 | import 'package:on_audio_query/on_audio_query.dart'; 3 | 4 | Map mediaItemToMap(MediaItem mediaItem) { 5 | return { 6 | 'id': mediaItem.id, 7 | 'ytid': mediaItem.extras!['ytid'], 8 | 'album': mediaItem.album.toString(), 9 | 'artist': mediaItem.artist.toString(), 10 | 'title': mediaItem.title, 11 | 'highResImage': mediaItem.artUri.toString(), 12 | 'lowResImage': mediaItem.extras!['lowResImage'], 13 | 'url': mediaItem.extras!['url'].toString(), 14 | }; 15 | } 16 | 17 | MediaItem songModelToMediaItem(SongModel song, String songUrl) { 18 | return MediaItem( 19 | id: song.id.toString(), 20 | album: '', 21 | artist: '', 22 | title: song.displayName, 23 | artUri: Uri.parse(''), 24 | extras: { 25 | 'url': songUrl, 26 | 'lowResImage': '', 27 | 'ytid': '', 28 | 'localSongId': song.id, 29 | 'ogid': song.id 30 | }, 31 | ); 32 | } 33 | 34 | MediaItem mapToMediaItem(Map song, String songUrl) { 35 | return MediaItem( 36 | id: song['id'].toString(), 37 | album: '', 38 | artist: song['more_info']['singers'].toString(), 39 | title: song['title'].toString(), 40 | artUri: Uri.parse( 41 | song['highResImage'].toString(), 42 | ), 43 | extras: { 44 | 'url': songUrl, 45 | 'lowResImage': song['lowResImage'], 46 | 'ytid': song['ytid'], 47 | 'localSongId': song['localSongId'] 48 | }, 49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /lib/helper/url_launcher.dart: -------------------------------------------------------------------------------- 1 | import 'package:url_launcher/url_launcher.dart'; 2 | 3 | void launchURL(url) async { 4 | if (await canLaunchUrl(url)) { 5 | await launchUrl(url, mode: LaunchMode.externalApplication); 6 | } else { 7 | throw 'Could not launch $url'; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/helper/version.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:flutter_downloader/flutter_downloader.dart'; 5 | import 'package:musify/main.dart'; 6 | import 'package:musify/services/ext_storage.dart'; 7 | 8 | String? version; 9 | late String dlUrl; 10 | const apiUrl = 11 | 'https://raw.githubusercontent.com/gokadzev/Musify/update/check.json'; 12 | 13 | Future checkAppUpdates() async { 14 | version ??= packageInfo.version; 15 | final client = HttpClient(); 16 | final request = await client.getUrl(Uri.parse(apiUrl)); 17 | final response = await request.close(); 18 | final contentAsString = await utf8.decodeStream(response); 19 | final map = json.decode(contentAsString); 20 | if (map['version'].toString() != version) { 21 | return true; 22 | } else { 23 | return false; 24 | } 25 | } 26 | 27 | Future downloadAppUpdates() async { 28 | final client = HttpClient(); 29 | final request = await client.getUrl(Uri.parse(apiUrl)); 30 | final response = await request.close(); 31 | final contentAsString = await utf8.decodeStream(response); 32 | final map = json.decode(contentAsString); 33 | if (await getCPUArchitecture() == 'aarch64') { 34 | dlUrl = map['arm64url'].toString(); 35 | } else { 36 | dlUrl = map['url'].toString(); 37 | } 38 | final dlPath = await ExtStorageProvider.getExtStorage(dirName: 'Download'); 39 | final file = File('${dlPath!}/Musify.apk'); 40 | if (await file.exists()) { 41 | await file.delete(); 42 | } 43 | await FlutterDownloader.enqueue( 44 | url: dlUrl, 45 | savedDir: dlPath, 46 | saveInPublicStorage: true, 47 | ); 48 | } 49 | 50 | Future getCPUArchitecture() async { 51 | final info = await Process.run('uname', ['-m']); 52 | final cpu = info.stdout.toString().replaceAll('\n', ''); 53 | return cpu; 54 | } 55 | -------------------------------------------------------------------------------- /lib/localization/app_de.arb: -------------------------------------------------------------------------------- 1 | { 2 | "about": "Über", 3 | "accentChangeMsg": "Akzentfarbe wurde geändert", 4 | "accentColor": "Akzentfarbe", 5 | "add": "Hinzufügen", 6 | "addedSuccess": "Erfolgreich hinzugefügt", 7 | "appUpdateAvailableAndDownloading": "Update ist verfügbar und wird heruntergeladen", 8 | "appUpdateIsAvailable": "Update ist verfügbar", 9 | "appUpdateIsNotAvailable": "Kein Update verfügbar", 10 | "audioFileType": "Audiodateierweiterung", 11 | "audioFileTypeMsg": "Der Audiodateityp wurde geändert", 12 | "backupUserData": "Sicherung von Benutzerdaten", 13 | "backupedSuccessfully": "Erfolgreich gesichert", 14 | "cacheMsg": "Cache gelöscht", 15 | "clearCache": "Cache leeren", 16 | "clearSearchHistory": "Suchverlauf löschen", 17 | "downloadAppUpdate": "Update herunterladen", 18 | "downloadCompleted": "Download abgeschlossen", 19 | "downloadStarted": "Download gestartet", 20 | "falseMSG": "False", 21 | "home": "Home", 22 | "language": "Sprache", 23 | "languageMsg": "Sprache wurde geändert", 24 | "localSongs": "Lokale Lieder", 25 | "lyrics": "Liedtext", 26 | "lyricsNotAvailable": "Keine Liedtexte verfügbar ;(", 27 | "more": "Mehr", 28 | "notYTlist": "Das ist keine Youtube-Playlist ID", 29 | "nowPlaying": "Läuft gerade", 30 | "others": "Anderes", 31 | "pages": "Seiten", 32 | "playAll": "Alle wiedergeben", 33 | "playlist": "Wiedergabeliste", 34 | "playlists": "Wiedergabelisten", 35 | "queueInitText": "Warteschlange wird initialisiert... ", 36 | "recommendedForYou": "für Dich empfohlen", 37 | "restoreUserData": "Wiederherstellen von Benutzerdaten", 38 | "restoredSuccessfully": "Erfolgreich wiederhergestellt", 39 | "search": "Suche", 40 | "searchHistoryMsg": "Suchverlauf gelöscht", 41 | "settingChangedMsg": "Setting changed", 42 | "settings": "Einstellungen", 43 | "suggestedPlaylists": "Vorgeschlagene Wiedergabelisten", 44 | "supportDonate": "Support/Spenden", 45 | "themeMode": "Theme-Modus", 46 | "tools": "Tools", 47 | "trueMSG": "True", 48 | "useSystemColor": "Use System Color (Android 12 New Feature)", 49 | "userLikedSongs": "beliebte Lieder", 50 | "userPlaylists": "Wiedergabelisten", 51 | "yourDownloadedSongsHere": "heruntergeladene Lieder", 52 | "yourFavoriteSongsHere": "Lieblingslieder", 53 | "youtubePlaylistID": "Youtube-Playlist ID" 54 | } -------------------------------------------------------------------------------- /lib/localization/app_en.arb: -------------------------------------------------------------------------------- 1 | { 2 | "about": "About", 3 | "accentChangeMsg": "Accent color has been changed", 4 | "accentColor": "Accent color", 5 | "add": "Add", 6 | "addedSuccess": "Added successfully", 7 | "appUpdateAvailableAndDownloading": "App update is available and downloading", 8 | "appUpdateIsAvailable": "App update is available", 9 | "appUpdateIsNotAvailable": "App update is not available", 10 | "audioFileType": "Audio File Extension", 11 | "audioFileTypeMsg": "Audio File Type has been changed", 12 | "backupUserData": "Backup user data", 13 | "backupedSuccessfully": "Backuped Successfully", 14 | "cacheMsg": "Cache cleared", 15 | "clearCache": "Clear cache", 16 | "clearSearchHistory": "Clear Search History", 17 | "downloadAppUpdate": "Download app update", 18 | "downloadCompleted": "Download Completed", 19 | "downloadStarted": "Download Started", 20 | "falseMSG": "False", 21 | "home": "Home", 22 | "language": "Language", 23 | "languageMsg": "Language has been changed", 24 | "localSongs": "Local songs", 25 | "lyrics": "Lyrics", 26 | "lyricsNotAvailable": "No lyrics available ;(", 27 | "more": "More", 28 | "notYTlist": "This is not a Youtube playlist ID", 29 | "nowPlaying": "Now playing", 30 | "others": "Others", 31 | "pages": "Pages", 32 | "playAll": "Play all", 33 | "playlist": "Playlist", 34 | "playlists": "Playlists", 35 | "queueInitText": "Initialising queue...", 36 | "recommendedForYou": "Recommended for you", 37 | "restoreUserData": "Restore user data", 38 | "restoredSuccessfully": "Restored Successfully", 39 | "search": "Search", 40 | "searchHistoryMsg": "Search history cleared", 41 | "settingChangedMsg": "Setting changed", 42 | "settings": "Settings", 43 | "suggestedPlaylists": "Suggested playlists", 44 | "supportDonate": "Support/Donate", 45 | "themeMode": "Theme mode", 46 | "tools": "Tools", 47 | "trueMSG": "True", 48 | "useSystemColor": "Use System Color (Android 12 New Feature)", 49 | "userLikedSongs": "User liked songs", 50 | "userPlaylists": "User playlists", 51 | "yourDownloadedSongsHere": "Your downloaded songs here", 52 | "yourFavoriteSongsHere": "Your favorite songs here", 53 | "youtubePlaylistID": "Youtube playlist ID" 54 | } -------------------------------------------------------------------------------- /lib/localization/app_es.arb: -------------------------------------------------------------------------------- 1 | { 2 | "about": "Sobre", 3 | "accentChangeMsg": "Se ha cambiado el color del acento", 4 | "accentColor": "Acentuar el color", 5 | "add": "Agregar", 6 | "addedSuccess": "Added successfully", 7 | "appUpdateAvailableAndDownloading": "La actualización de la aplicación está disponible y se está descargando", 8 | "appUpdateIsAvailable": "La actualización de la aplicación está disponible", 9 | "appUpdateIsNotAvailable": "La actualización de la aplicación no está disponible", 10 | "audioFileType": "Extensión de archivo de audio", 11 | "audioFileTypeMsg": "Se ha cambiado el tipo de archivo de audio", 12 | "backupUserData": "Copia de seguridad de la información del usuario", 13 | "backupedSuccessfully": "Copia de seguridad exitosa", 14 | "cacheMsg": "Caché despejado", 15 | "clearCache": "Limpiar cache", 16 | "clearSearchHistory": "Limpiar historial de búsqueda", 17 | "downloadAppUpdate": "Descargar actualización de la aplicación", 18 | "downloadCompleted": "Download Completed", 19 | "downloadStarted": "Download Started", 20 | "falseMSG": "False", 21 | "home": "Home", 22 | "language": "Idioma", 23 | "languageMsg": "Se ha cambiado el idioma", 24 | "localSongs": "Canciones locales", 25 | "lyrics": "Letra", 26 | "lyricsNotAvailable": "No hay letras disponibles ;(", 27 | "more": "More", 28 | "notYTlist": "This is not a Youtube playlist ID", 29 | "nowPlaying": "Jugando ahora", 30 | "others": "Others", 31 | "pages": "Pages", 32 | "playAll": "Jugar todo", 33 | "playlist": "lista de reproducción", 34 | "playlists": "listas de reproducción", 35 | "queueInitText": "Inicializando cola...", 36 | "recommendedForYou": "Recomendado para ti", 37 | "restoreUserData": "Restaurar datos de usuario", 38 | "restoredSuccessfully": "Restaurado con éxito", 39 | "search": "Búsqueda", 40 | "searchHistoryMsg": "Historial de búsqueda borrado", 41 | "settingChangedMsg": "Setting changed", 42 | "settings": "Ajustes", 43 | "suggestedPlaylists": "Listas de reproducción sugeridas", 44 | "supportDonate": "Support/Donate", 45 | "themeMode": "Theme mode", 46 | "tools": "Tools", 47 | "trueMSG": "True", 48 | "useSystemColor": "Use System Color (Android 12 New Feature)", 49 | "userLikedSongs": "Al usuario le gustaron las canciones", 50 | "userPlaylists": "Listas de reproducción de usuario", 51 | "yourDownloadedSongsHere": "Tus canciones descargadas aquí", 52 | "yourFavoriteSongsHere": "Tus canciones favoritas aquí", 53 | "youtubePlaylistID": "lista de reproducción de youtube ID" 54 | } -------------------------------------------------------------------------------- /lib/localization/app_fr.arb: -------------------------------------------------------------------------------- 1 | { 2 | "about": "À propos de", 3 | "accentChangeMsg": "La couleur d'accentuation a été modifiée", 4 | "accentColor": "Couleur d'accentuation", 5 | "add": "Ajouter", 6 | "addedSuccess": "Added successfully", 7 | "appUpdateAvailableAndDownloading": "La mise à jour de l'application est disponible et en cours de téléchargement", 8 | "appUpdateIsAvailable": "La mise à jour de l'application est disponible", 9 | "appUpdateIsNotAvailable": "La mise à jour de l'application n'est pas disponible", 10 | "audioFileType": "Extension de fichier audio", 11 | "audioFileTypeMsg": "Le type de fichier audio a été modifié", 12 | "backupUserData": "Sauvegardez les données utilisateur", 13 | "backupedSuccessfully": "Sauvegarde réussie", 14 | "cacheMsg": "Cache vidé", 15 | "clearCache": "Vider le cache", 16 | "clearSearchHistory": "Effacer l'historique", 17 | "downloadAppUpdate": "Télécharger la mise à jour de l'application", 18 | "downloadCompleted": "Download Completed", 19 | "downloadStarted": "Download Started", 20 | "falseMSG": "False", 21 | "home": "Home", 22 | "language": "Langue", 23 | "languageMsg": "La langue a été modifiée", 24 | "localSongs": "Chansons locales", 25 | "lyrics": "Paroles", 26 | "lyricsNotAvailable": "Pas de paroles disponibles ;(", 27 | "more": "More", 28 | "notYTlist": "This is not a Youtube playlist ID", 29 | "nowPlaying": "Lecture en cours", 30 | "others": "Others", 31 | "pages": "Pages", 32 | "playAll": "Jouer à tous", 33 | "playlist": "Playlist", 34 | "playlists": "Playlists", 35 | "queueInitText": "Initialisation de la file d'attente... ", 36 | "recommendedForYou": "Recommandé pour vous", 37 | "restoreUserData": "Restaurer les données de l'utilisateur", 38 | "restoredSuccessfully": "Restauré avec succès", 39 | "search": "Chercher", 40 | "searchHistoryMsg": "Historique des recherches effacé", 41 | "settingChangedMsg": "Setting changed", 42 | "settings": "Réglages", 43 | "suggestedPlaylists": "Listes de lecture suggérées", 44 | "supportDonate": "Soutenir/Faire un don", 45 | "themeMode": "Theme mode", 46 | "tools": "Tools", 47 | "trueMSG": "True", 48 | "useSystemColor": "Use System Color (Android 12 New Feature)", 49 | "userLikedSongs": "Chansons aimées par l'utilisateur", 50 | "userPlaylists": "Listes de lecture utilisateur", 51 | "yourDownloadedSongsHere": "Vos chansons téléchargées ici", 52 | "yourFavoriteSongsHere": "Vos chansons préférées ici", 53 | "youtubePlaylistID": "ID de la liste de playlist Youtube" 54 | } -------------------------------------------------------------------------------- /lib/localization/app_he.arb: -------------------------------------------------------------------------------- 1 | { 2 | "about": "About", 3 | "accentChangeMsg": "Accent color has been changed", 4 | "accentColor": "Accent color", 5 | "add": "Add", 6 | "addedSuccess": "Added successfully", 7 | "appUpdateAvailableAndDownloading": "App update is available and downloading", 8 | "appUpdateIsAvailable": "App update is available", 9 | "appUpdateIsNotAvailable": "App update is not available", 10 | "audioFileType": "Audio File Extension", 11 | "audioFileTypeMsg": "Audio File Type has been changed", 12 | "backupUserData": "Backup user data", 13 | "backupedSuccessfully": "Backuped Successfully", 14 | "cacheMsg": "Cache cleared", 15 | "clearCache": "Clear cache", 16 | "clearSearchHistory": "Clear Search History", 17 | "downloadAppUpdate": "Download app update", 18 | "downloadCompleted": "Download Completed", 19 | "downloadStarted": "Download Started", 20 | "falseMSG": "False", 21 | "home": "Home", 22 | "language": "Language", 23 | "languageMsg": "Language has been changed", 24 | "localSongs": "Local songs", 25 | "lyrics": "Lyrics", 26 | "lyricsNotAvailable": "No lyrics available ;(", 27 | "more": "More", 28 | "notYTlist": "This is not a Youtube playlist ID", 29 | "nowPlaying": "Now playing", 30 | "others": "Others", 31 | "pages": "Pages", 32 | "playAll": "Play all", 33 | "playlist": "Playlist", 34 | "playlists": "Playlists", 35 | "queueInitText": "Initialising queue... ", 36 | "recommendedForYou": "Recommended for you", 37 | "restoreUserData": "Restore user data", 38 | "restoredSuccessfully": "Restored Successfully", 39 | "search": "Search", 40 | "searchHistoryMsg": "Search history cleared", 41 | "settingChangedMsg": "Setting changed", 42 | "settings": "Settings", 43 | "suggestedPlaylists": "Suggested playlists", 44 | "supportDonate": "Support/Donate", 45 | "themeMode": "Theme mode", 46 | "tools": "Tools", 47 | "trueMSG": "True", 48 | "useSystemColor": "Use System Color (Android 12 New Feature)", 49 | "userLikedSongs": "User liked songs", 50 | "userPlaylists": "User playlists", 51 | "yourDownloadedSongsHere": "Your downloaded songs here", 52 | "yourFavoriteSongsHere": "Your favorite songs here", 53 | "youtubePlaylistID": "Youtube playlist ID" 54 | } -------------------------------------------------------------------------------- /lib/localization/app_hi.arb: -------------------------------------------------------------------------------- 1 | { 2 | "about": "About", 3 | "accentChangeMsg": "Accent color has been changed", 4 | "accentColor": "Accent color", 5 | "add": "Add", 6 | "addedSuccess": "Added successfully", 7 | "appUpdateAvailableAndDownloading": "App update is available and downloading", 8 | "appUpdateIsAvailable": "App update is available", 9 | "appUpdateIsNotAvailable": "App update is not available", 10 | "audioFileType": "Audio File Extension", 11 | "audioFileTypeMsg": "Audio File Type has been changed", 12 | "backupUserData": "Backup user data", 13 | "backupedSuccessfully": "Backuped Successfully", 14 | "cacheMsg": "Cache cleared", 15 | "clearCache": "Clear cache", 16 | "clearSearchHistory": "Clear Search History", 17 | "downloadAppUpdate": "Download app update", 18 | "downloadCompleted": "Download Completed", 19 | "downloadStarted": "Download Started", 20 | "falseMSG": "False", 21 | "home": "Home", 22 | "language": "Language", 23 | "languageMsg": "Language has been changed", 24 | "localSongs": "Local songs", 25 | "lyrics": "Lyrics", 26 | "lyricsNotAvailable": "No lyrics available ;(", 27 | "more": "More", 28 | "notYTlist": "This is not a Youtube playlist ID", 29 | "nowPlaying": "Now playing", 30 | "others": "Others", 31 | "pages": "Pages", 32 | "playAll": "Play all", 33 | "playlist": "Playlist", 34 | "playlists": "Playlists", 35 | "queueInitText": "Initialising queue... ", 36 | "recommendedForYou": "Recommended for you", 37 | "restoreUserData": "Restore user data", 38 | "restoredSuccessfully": "Restored Successfully", 39 | "search": "Search", 40 | "searchHistoryMsg": "Search history cleared", 41 | "settingChangedMsg": "Setting changed", 42 | "settings": "Settings", 43 | "suggestedPlaylists": "Suggested playlists", 44 | "supportDonate": "Support/Donate", 45 | "themeMode": "Theme mode", 46 | "tools": "Tools", 47 | "trueMSG": "True", 48 | "useSystemColor": "Use System Color (Android 12 New Feature)", 49 | "userLikedSongs": "User liked songs", 50 | "userPlaylists": "User playlists", 51 | "yourDownloadedSongsHere": "Your downloaded songs here", 52 | "yourFavoriteSongsHere": "Your favorite songs here", 53 | "youtubePlaylistID": "Youtube playlist ID" 54 | } -------------------------------------------------------------------------------- /lib/localization/app_hu.arb: -------------------------------------------------------------------------------- 1 | { 2 | "about": "About", 3 | "accentChangeMsg": "Accent color has been changed", 4 | "accentColor": "Accent color", 5 | "add": "Add", 6 | "addedSuccess": "Added successfully", 7 | "appUpdateAvailableAndDownloading": "App update is available and downloading", 8 | "appUpdateIsAvailable": "App update is available", 9 | "appUpdateIsNotAvailable": "App update is not available", 10 | "audioFileType": "Audio File Extension", 11 | "audioFileTypeMsg": "Audio File Type has been changed", 12 | "backupUserData": "Backup user data", 13 | "backupedSuccessfully": "Backuped Successfully", 14 | "cacheMsg": "Cache cleared", 15 | "clearCache": "Clear cache", 16 | "clearSearchHistory": "Clear Search History", 17 | "downloadAppUpdate": "Download app update", 18 | "downloadCompleted": "Download Completed", 19 | "downloadStarted": "Download Started", 20 | "falseMSG": "False", 21 | "home": "Home", 22 | "language": "Language", 23 | "languageMsg": "Language has been changed", 24 | "localSongs": "Local songs", 25 | "lyrics": "Lyrics", 26 | "lyricsNotAvailable": "No lyrics available ;(", 27 | "more": "More", 28 | "notYTlist": "This is not a Youtube playlist ID", 29 | "nowPlaying": "Now playing", 30 | "others": "Others", 31 | "pages": "Pages", 32 | "playAll": "Play all", 33 | "playlist": "Playlist", 34 | "playlists": "Playlists", 35 | "queueInitText": "Initialising queue...", 36 | "recommendedForYou": "Recommended for you", 37 | "restoreUserData": "Restore user data", 38 | "restoredSuccessfully": "Restored Successfully", 39 | "search": "Search", 40 | "searchHistoryMsg": "Search history cleared", 41 | "settingChangedMsg": "Setting changed", 42 | "settings": "Settings", 43 | "suggestedPlaylists": "Suggested playlists", 44 | "supportDonate": "Support/Donate", 45 | "themeMode": "Theme mode", 46 | "tools": "Tools", 47 | "trueMSG": "True", 48 | "useSystemColor": "Use System Color (Android 12 New Feature)", 49 | "userLikedSongs": "User liked songs", 50 | "userPlaylists": "User playlists", 51 | "yourDownloadedSongsHere": "Your downloaded songs here", 52 | "yourFavoriteSongsHere": "Your favorite songs here", 53 | "youtubePlaylistID": "Youtube playlist ID" 54 | } -------------------------------------------------------------------------------- /lib/localization/app_id.arb: -------------------------------------------------------------------------------- 1 | { 2 | "about": "tentang", 3 | "accentChangeMsg": "Warna aksen telah diubah", 4 | "accentColor": "Warna aksen", 5 | "add": "tambah", 6 | "addedSuccess": "Added successfully", 7 | "appUpdateAvailableAndDownloading": "Pembaruan aplikasi tersedia dan diunduh", 8 | "appUpdateIsAvailable": "Pembaruan aplikasi tersedia", 9 | "appUpdateIsNotAvailable": "Pembaruan aplikasi tidak tersedia", 10 | "audioFileType": "Ekstensi Berkas Audio", 11 | "audioFileTypeMsg": "Jenis File Audio telah diubah", 12 | "backupUserData": "Cadangkan data pengguna", 13 | "backupedSuccessfully": "Backup Berhasil", 14 | "cacheMsg": "Cache dibersihkan", 15 | "clearCache": "Hapus cache", 16 | "clearSearchHistory": "Hapus Riwayat Pencarian", 17 | "downloadAppUpdate": "Unduh pembaruan aplikasi", 18 | "downloadCompleted": "Download Completed", 19 | "downloadStarted": "Download Started", 20 | "falseMSG": "False", 21 | "home": "Home", 22 | "language": "Bahasa", 23 | "languageMsg": "Bahasa telah diubah", 24 | "localSongs": "Lagu lokal", 25 | "lyrics": "Lirik", 26 | "lyricsNotAvailable": "Tidak ada lirik yang tersedia ;(", 27 | "more": "More", 28 | "notYTlist": "This is not a Youtube playlist ID", 29 | "nowPlaying": "Sedang dimainkan", 30 | "others": "Others", 31 | "pages": "Pages", 32 | "playAll": "Mainkan semua", 33 | "playlist": "Daftar putar", 34 | "playlists": "Daftar putar", 35 | "queueInitText": "Menginisialisasi antrean... ", 36 | "recommendedForYou": "Direkomendasikan untuk Anda", 37 | "restoreUserData": "Pulihkan data pengguna", 38 | "restoredSuccessfully": "Berhasil Dipulihkan", 39 | "search": "Pencarian", 40 | "searchHistoryMsg": "Riwayat pencarian dihapus", 41 | "settingChangedMsg": "Setting changed", 42 | "settings": "Pengaturan", 43 | "suggestedPlaylists": "Daftar putar yang disarankan", 44 | "supportDonate": "Support/Donate", 45 | "themeMode": "Theme mode", 46 | "tools": "Tools", 47 | "trueMSG": "True", 48 | "useSystemColor": "Use System Color (Android 12 New Feature)", 49 | "userLikedSongs": "Lagu yang disukai pengguna", 50 | "userPlaylists": "Daftar putar pengguna", 51 | "yourDownloadedSongsHere": "Lagu yang Anda download di sini", 52 | "yourFavoriteSongsHere": "Lagu favorit Anda di sini", 53 | "youtubePlaylistID": "ID daftar putar YouTube" 54 | } -------------------------------------------------------------------------------- /lib/localization/app_it.arb: -------------------------------------------------------------------------------- 1 | { 2 | "about": "Informazioni", 3 | "accentChangeMsg": "Il colore dell'accento è stato modificato", 4 | "accentColor": "Colore in risalto", 5 | "add": "Aggiungi", 6 | "addedSuccess": "Added successfully", 7 | "appUpdateAvailableAndDownloading": "Un aggiornamento dell'app è disponibile ed è in scaricamento", 8 | "appUpdateIsAvailable": "Aggiornamento dell'app disponibile", 9 | "appUpdateIsNotAvailable": "Aggiornamento dell'app non disponibile", 10 | "audioFileType": "Estensione File Audio", 11 | "audioFileTypeMsg": "Il tipo del file audio è stato cambiato", 12 | "backupUserData": "Salva dati utente", 13 | "backupedSuccessfully": "Salvati con successo", 14 | "cacheMsg": "Cache eliminata", 15 | "clearCache": "Pulisci cache", 16 | "clearSearchHistory": "Cancella cronologia delle ricerche", 17 | "downloadAppUpdate": "Scaricata l'aggiornamento dell'app", 18 | "downloadCompleted": "Download Completed", 19 | "downloadStarted": "Download Started", 20 | "falseMSG": "False", 21 | "home": "Home", 22 | "language": "Lingua", 23 | "languageMsg": "La lingua è stata cambiata", 24 | "localSongs": "Canzoni locali", 25 | "lyrics": "Testo", 26 | "lyricsNotAvailable": "Nessun testo disponibile ;(", 27 | "more": "More", 28 | "notYTlist": "This is not a Youtube playlist ID", 29 | "nowPlaying": "Riproducendo ora", 30 | "others": "Others", 31 | "pages": "Pages", 32 | "playAll": "Riproduci tutto", 33 | "playlist": "Playlist", 34 | "playlists": "Playlist", 35 | "queueInitText": "Inizializzando la coda... ", 36 | "recommendedForYou": "Consigliati per te", 37 | "restoreUserData": "Ripristina dati utente", 38 | "restoredSuccessfully": "Ripristinati con successo", 39 | "search": "Cerca", 40 | "searchHistoryMsg": "Cronologia delle ricerche cancellata", 41 | "settingChangedMsg": "Setting changed", 42 | "settings": "Impostazioni", 43 | "suggestedPlaylists": "Playlist suggerite", 44 | "supportDonate": "Support/Donate", 45 | "themeMode": "Theme mode", 46 | "tools": "Tools", 47 | "trueMSG": "True", 48 | "useSystemColor": "Use System Color (Android 12 New Feature)", 49 | "userLikedSongs": "Le tue canzoni preferite", 50 | "userPlaylists": "Le tue playlist", 51 | "yourDownloadedSongsHere": "Qui le tue canzoni scaricate", 52 | "yourFavoriteSongsHere": "Qui le tue canzoni preferite", 53 | "youtubePlaylistID": "ID playlist di Youtube" 54 | } -------------------------------------------------------------------------------- /lib/localization/app_ka.arb: -------------------------------------------------------------------------------- 1 | { 2 | "about": "შესახებ", 3 | "accentChangeMsg": "აქცენტის ფერი შეიცვალა", 4 | "accentColor": "აქცენტის ფერი", 5 | "add": "დამატება", 6 | "addedSuccess": "წარმატებით დაემატა", 7 | "appUpdateAvailableAndDownloading": "აპლიკაციის განახლება ხელმისაწვდომია და იტვირთება", 8 | "appUpdateIsAvailable": "აპლიკაციის განახლება ხელმისაწვდომია", 9 | "appUpdateIsNotAvailable": "აპლიკაციის განახლება არაა ხელმისაწვდომი", 10 | "audioFileType": "სიმღერის ფაილის დაბოლოვება", 11 | "audioFileTypeMsg": "სიმღერის ფაილის დაბოლოვება შეიცვალა", 12 | "backupUserData": "მომხმარებლის ინფორმაციის დარეზერვება", 13 | "backupedSuccessfully": "დარეზერვდა წარმატებით", 14 | "cacheMsg": "ქეში გასუფთავდა", 15 | "clearCache": "ქეშის გასუფთავება", 16 | "clearSearchHistory": "საძიებო ისტორიის გასუფთავება", 17 | "downloadAppUpdate": "აპლიკაციის განახლების ჩამოტვირთვა", 18 | "downloadCompleted": "ჩამოტვირთვა დასრულდა", 19 | "downloadStarted": "ჩამოტვირთვა დაიწყო", 20 | "falseMSG": "მცდარი", 21 | "home": "მთავარი", 22 | "language": "ენა", 23 | "languageMsg": "ენა შეიცვალა", 24 | "localSongs": "ადგილობრივი მუსიკა", 25 | "lyrics": "ტექსტი", 26 | "lyricsNotAvailable": "ტექსტი არაა ხელმისაწვდომი ;(", 27 | "more": "მეტი", 28 | "notYTlist": "ეს არ არის Youtube ფლეილისთ ID", 29 | "nowPlaying": "ახლა ჩართულია", 30 | "others": "სხვა", 31 | "pages": "გვერდები", 32 | "playAll": "ყველას ჩართვა", 33 | "playlist": "ფლეილისთი", 34 | "playlists": "ფლეილისთები", 35 | "queueInitText": "ხდება ინიციალიზება... ", 36 | "recommendedForYou": "შემოთავაზებები შენთვის", 37 | "restoreUserData": "მომხმარებლის ინფორმაციის დაბრუნება", 38 | "restoredSuccessfully": "წარმატებით დაბრუნდა", 39 | "search": "ძიება", 40 | "searchHistoryMsg": "საძიებო ისტორია გასუფთავდა", 41 | "settingChangedMsg": "პარამეტრი შეიცვალა", 42 | "settings": "პარამეტრები", 43 | "suggestedPlaylists": "შემოთავაზებული ფლეილისთები", 44 | "supportDonate": "დონაცია", 45 | "themeMode": "თემის რეჟიმი", 46 | "tools": "ხელსაწყოები", 47 | "trueMSG": "ჭეშმარიტი", 48 | "useSystemColor": "გამოიყენე სისტემის ფერი (Android 12 ახალი ფუნქცია)", 49 | "userLikedSongs": "მომხმარებლის მოწონებული სიმღერები", 50 | "userPlaylists": "მომხმარებლის ფლეილისთები", 51 | "yourDownloadedSongsHere": "შენი გადმოწერილი სიმღერები აქ", 52 | "yourFavoriteSongsHere": "შენი ფავორიტი სიმღერები აქ", 53 | "youtubePlaylistID": "Youtube ფლეილისთის ID" 54 | } -------------------------------------------------------------------------------- /lib/localization/app_nl.arb: -------------------------------------------------------------------------------- 1 | { 2 | "about": "over", 3 | "accentChangeMsg": "Accentkleur is gewijzigd", 4 | "accentColor": "Accentkleur", 5 | "add": "Toevoegen", 6 | "addedSuccess": "Added successfully", 7 | "appUpdateAvailableAndDownloading": "App-update is beschikbaar en wordt gedownload", 8 | "appUpdateIsAvailable": "App-update is beschikbaar", 9 | "appUpdateIsNotAvailable": "App-update is niet beschikbaar", 10 | "audioFileType": "Audiobestandsextensie", 11 | "audioFileTypeMsg": "Audiobestandstype is gewijzigd", 12 | "backupUserData": "Backup gebruikersgegevens", 13 | "backupedSuccessfully": "Succesvol back-up gemaakt", 14 | "cacheMsg": "Cache gewist", 15 | "clearCache": "Cache wissen", 16 | "clearSearchHistory": "Verwijder zoekgeschiedenis", 17 | "downloadAppUpdate": "App-update downloaden", 18 | "downloadCompleted": "Download Completed", 19 | "downloadStarted": "Download Started", 20 | "falseMSG": "False", 21 | "home": "Home", 22 | "language": "Taal", 23 | "languageMsg": "Taal is gewijzigd", 24 | "localSongs": "Lokale nummers", 25 | "lyrics": "tekst", 26 | "lyricsNotAvailable": "Geen songtekst beschikbaar ;(", 27 | "more": "More", 28 | "notYTlist": "This is not a Youtube playlist ID", 29 | "nowPlaying": "Nu aan het spelen", 30 | "others": "Others", 31 | "pages": "Pages", 32 | "playAll": "Alles afspelen", 33 | "playlist": "Afspeellijst", 34 | "playlists": "Afspeellijsten", 35 | "queueInitText": "Bezig met initialiseren van wachtrij... ", 36 | "recommendedForYou": "Aanbevolen voor jou", 37 | "restoreUserData": "Gebruikersgegevens herstellen", 38 | "restoredSuccessfully": "Succesvol hersteld", 39 | "search": "zoek", 40 | "searchHistoryMsg": "Zoekgeschiedenis gewist", 41 | "settingChangedMsg": "Setting changed", 42 | "settings": "Instellingen", 43 | "suggestedPlaylists": "Voorgestelde afspeellijsten", 44 | "supportDonate": "Support/Donate", 45 | "themeMode": "Theme mode", 46 | "tools": "Tools", 47 | "trueMSG": "True", 48 | "useSystemColor": "Use System Color (Android 12 New Feature)", 49 | "userLikedSongs": "Liedjes die gebruikers leuk vonden", 50 | "userPlaylists": "Gebruikersafspeellijsten", 51 | "yourDownloadedSongsHere": "Uw gedownloade nummers hier", 52 | "yourFavoriteSongsHere": "Uw favoriete nummers hier", 53 | "youtubePlaylistID": "Youtube-afspeellijst-ID" 54 | } -------------------------------------------------------------------------------- /lib/localization/app_pl.arb: -------------------------------------------------------------------------------- 1 | { 2 | "about": "O", 3 | "accentChangeMsg": "Zmieniono kolor akcentu", 4 | "accentColor": "Kolor akcentu", 5 | "add": "Dodaj", 6 | "addedSuccess": "Added successfully", 7 | "appUpdateAvailableAndDownloading": "Aktualizacja aplikacji jest dostępna i pobierana", 8 | "appUpdateIsAvailable": "Aktualizacja aplikacji jest dostępna", 9 | "appUpdateIsNotAvailable": "Aktualizacja aplikacji nie jest dostępna", 10 | "audioFileType": "Rozszerzenie pliku audio", 11 | "audioFileTypeMsg": "Typ pliku audio został zmieniony", 12 | "backupUserData": "Kopia zapasowa danych użytkownika", 13 | "backupedSuccessfully": "Utworzono kopię zapasową pomyślnie", 14 | "cacheMsg": "Pamięć wyczyszczona", 15 | "clearCache": "Wyczyść pamięć podręczną", 16 | "clearSearchHistory": "Wyczyść historię wyszukiwania", 17 | "downloadAppUpdate": "Pobierz aktualizację aplikacji", 18 | "downloadCompleted": "Download Completed", 19 | "downloadStarted": "Download Started", 20 | "falseMSG": "False", 21 | "home": "Home", 22 | "language": "Język", 23 | "languageMsg": "Język został zmieniony", 24 | "localSongs": "Piosenki lokalne", 25 | "lyrics": "teksty", 26 | "lyricsNotAvailable": "Brak dostępnych tekstów ;(", 27 | "more": "More", 28 | "notYTlist": "This is not a Youtube playlist ID", 29 | "nowPlaying": "Teraz gra", 30 | "others": "Others", 31 | "pages": "Pages", 32 | "playAll": "Odtwórz wszystko", 33 | "playlist": "Playlista", 34 | "playlists": "Playlisty", 35 | "queueInitText": "Inicjowanie kolejki...", 36 | "recommendedForYou": "Polecane dla Ciebie", 37 | "restoreUserData": "Przywróć dane użytkownika", 38 | "restoredSuccessfully": "Przywrócono pomyślnie", 39 | "search": "Szukaj", 40 | "searchHistoryMsg": "Historia wyszukiwania wyczyszczona", 41 | "settingChangedMsg": "Setting changed", 42 | "settings": "Ustawienia", 43 | "suggestedPlaylists": "Sugerowane listy odtwarzania", 44 | "supportDonate": "Support/Donate", 45 | "themeMode": "Theme mode", 46 | "tools": "Tools", 47 | "trueMSG": "True", 48 | "useSystemColor": "Use System Color (Android 12 New Feature)", 49 | "userLikedSongs": "Utwory lubiane przez użytkowników", 50 | "userPlaylists": "Playlisty użytkownika", 51 | "yourDownloadedSongsHere": "Twoje pobrane utwory tutaj", 52 | "yourFavoriteSongsHere": "Twoje ulubione piosenki tutaj", 53 | "youtubePlaylistID": "Identyfikator playlisty YouTube" 54 | } -------------------------------------------------------------------------------- /lib/localization/app_pt.arb: -------------------------------------------------------------------------------- 1 | { 2 | "about": "Sobre", 3 | "accentChangeMsg": "A cor de destaque foi alterada", 4 | "accentColor": "Cor de destaque", 5 | "add": "Adicionar", 6 | "addedSuccess": "Added successfully", 7 | "appUpdateAvailableAndDownloading": "A atualização do aplicativo está disponível e baixando", 8 | "appUpdateIsAvailable": "A atualização do aplicativo está disponível", 9 | "appUpdateIsNotAvailable": "A atualização do aplicativo não está disponível", 10 | "audioFileType": "Extensão de arquivo de áudio", 11 | "audioFileTypeMsg": "O tipo de arquivo de áudio foi alterado", 12 | "backupUserData": "Fazer backup dos dados do usuário", 13 | "backupedSuccessfully": "Backup feito com sucesso", 14 | "cacheMsg": "Cache limpo", 15 | "clearCache": "Limpar cache", 16 | "clearSearchHistory": "Limpar histórico de pesquisa", 17 | "downloadAppUpdate": "Baixar atualização do aplicativo", 18 | "downloadCompleted": "Download Completed", 19 | "downloadStarted": "Download Started", 20 | "falseMSG": "False", 21 | "home": "Home", 22 | "language": "Idioma", 23 | "languageMsg": "O idioma foi alterado", 24 | "localSongs": "Músicas locais", 25 | "lyrics": "Letra da música", 26 | "lyricsNotAvailable": "Nenhuma letra disponível ;(", 27 | "more": "More", 28 | "notYTlist": "This is not a Youtube playlist ID", 29 | "nowPlaying": "Agora tocando", 30 | "others": "Others", 31 | "pages": "Pages", 32 | "playAll": "Reproduzir todos", 33 | "playlist": "Lista de reprodução", 34 | "playlists": "Listas de reprodução", 35 | "queueInitText": "Inicializando a fila... ", 36 | "recommendedForYou": "Recomendado para você", 37 | "restoreUserData": "Restaurar dados do usuário", 38 | "restoredSuccessfully": "Restaurado com sucesso", 39 | "search": "Procurar", 40 | "searchHistoryMsg": "Histórico de pesquisa limpo", 41 | "settingChangedMsg": "Setting changed", 42 | "settings": "Configurações", 43 | "suggestedPlaylists": "Listas de reprodução sugeridas", 44 | "supportDonate": "Support/Donate", 45 | "themeMode": "Theme mode", 46 | "tools": "Tools", 47 | "trueMSG": "True", 48 | "useSystemColor": "Use System Color (Android 12 New Feature)", 49 | "userLikedSongs": "Músicas que o usuário gostou", 50 | "userPlaylists": "Listas de reprodução do usuário", 51 | "yourDownloadedSongsHere": "Suas músicas baixadas aqui", 52 | "yourFavoriteSongsHere": "Suas músicas favoritas aqui", 53 | "youtubePlaylistID": "ID da lista de reprodução do Youtube" 54 | } -------------------------------------------------------------------------------- /lib/localization/app_tr.arb: -------------------------------------------------------------------------------- 1 | { 2 | "about": "Hakkında", 3 | "accentChangeMsg": "Vurgu rengi değiştirildi", 4 | "accentColor": "vurgu rengi", 5 | "add": "Ekle", 6 | "addedSuccess": "Added successfully", 7 | "appUpdateAvailableAndDownloading": "Uygulama güncellemesi mevcut ve indiriliyor", 8 | "appUpdateIsAvailable": "Uygulama güncellemesi mevcut", 9 | "appUpdateIsNotAvailable": "Uygulama güncellemesi mevcut değil", 10 | "audioFileType": "Ses Dosyası Uzantısı", 11 | "audioFileTypeMsg": "Ses Dosyası Türü değiştirildi", 12 | "backupUserData": "Yedekleme kullanıcı verileri", 13 | "backupedSuccessfully": "Başarıyla Yedeklendi", 14 | "cacheMsg": "önbellek temizlendi", 15 | "clearCache": "Önbelleği temizle", 16 | "clearSearchHistory": "Arama geçmişini temizle", 17 | "downloadAppUpdate": "Uygulama güncellemesini indirin", 18 | "downloadCompleted": "Download Completed", 19 | "downloadStarted": "Download Started", 20 | "falseMSG": "False", 21 | "home": "Home", 22 | "language": "Dil", 23 | "languageMsg": "Dil değiştirildi", 24 | "localSongs": "Yerel şarkılar", 25 | "lyrics": "şarkı sözleri", 26 | "lyricsNotAvailable": "Şarkı sözü yok ;(", 27 | "more": "More", 28 | "notYTlist": "This is not a Youtube playlist ID", 29 | "nowPlaying": "Şimdi oynuyor", 30 | "others": "Others", 31 | "pages": "Pages", 32 | "playAll": "Hepsini Oynat", 33 | "playlist": "çalma listesi", 34 | "playlists": "çalma listeleri", 35 | "queueInitText": "Sıra başlatılıyor... ", 36 | "recommendedForYou": "sizin için tavsiye edilen", 37 | "restoreUserData": "Kullanıcı verilerini kurtar", 38 | "restoredSuccessfully": "Başarıyla Geri Yüklendi", 39 | "search": "arama", 40 | "searchHistoryMsg": "Arama geçmişi temizlendi", 41 | "settingChangedMsg": "Setting changed", 42 | "settings": "ayarlar", 43 | "suggestedPlaylists": "Önerilen oynatma listeleri", 44 | "supportDonate": "Support/Donate", 45 | "themeMode": "Theme mode", 46 | "tools": "Tools", 47 | "trueMSG": "True", 48 | "useSystemColor": "Use System Color (Android 12 New Feature)", 49 | "userLikedSongs": "Kullanıcının beğendiği şarkılar", 50 | "userPlaylists": "Kullanıcı oynatma listeleri", 51 | "yourDownloadedSongsHere": "İndirdiğiniz şarkılar burada", 52 | "yourFavoriteSongsHere": "En sevdiğin şarkılar burada", 53 | "youtubePlaylistID": "Youtube oynatma listesi ID" 54 | } -------------------------------------------------------------------------------- /lib/localization/app_uk.arb: -------------------------------------------------------------------------------- 1 | { 2 | "about": "Про", 3 | "accentChangeMsg": "Колір акценту змінено", 4 | "accentColor": "Акцентний колір", 5 | "add": "додати", 6 | "addedSuccess": "Added successfully", 7 | "appUpdateAvailableAndDownloading": "Оновлення програми доступне і завантажується", 8 | "appUpdateIsAvailable": "Доступне оновлення програми", 9 | "appUpdateIsNotAvailable": "Оновлення програми недоступне", 10 | "audioFileType": "Розширення аудіофайлу", 11 | "audioFileTypeMsg": "Тип аудіофайлу змінено", 12 | "backupUserData": "Резервне копіювання даних користувача", 13 | "backupedSuccessfully": "Резервне копіювання успішно", 14 | "cacheMsg": "Кеш очищений", 15 | "clearCache": "Очистити кеш", 16 | "clearSearchHistory": "Очистити історію пошуку", 17 | "downloadAppUpdate": "Завантажити оновлення програми", 18 | "downloadCompleted": "Download Completed", 19 | "downloadStarted": "Download Started", 20 | "falseMSG": "False", 21 | "home": "Home", 22 | "language": "Мову", 23 | "languageMsg": "Мова була змінена", 24 | "localSongs": "Місцеві пісні", 25 | "lyrics": "лірика", 26 | "lyricsNotAvailable": "Немає текстів ;(", 27 | "more": "More", 28 | "notYTlist": "This is not a Youtube playlist ID", 29 | "nowPlaying": "Зараз грає", 30 | "others": "Others", 31 | "pages": "Pages", 32 | "playAll": "Грати все", 33 | "playlist": "Список відтворення", 34 | "playlists": "Списки відтворення", 35 | "queueInitText": "Ініціалізація черги... ", 36 | "recommendedForYou": "рекомендовано для вас", 37 | "restoreUserData": "Відновити дані користувача", 38 | "restoredSuccessfully": "Відновлено успішно", 39 | "search": "Пошук", 40 | "searchHistoryMsg": "Історію пошуку очищено", 41 | "settingChangedMsg": "Setting changed", 42 | "settings": "Налаштування", 43 | "suggestedPlaylists": "Пропоновані списки відтворення", 44 | "supportDonate": "Support/Donate", 45 | "themeMode": "Theme mode", 46 | "tools": "Tools", 47 | "trueMSG": "True", 48 | "useSystemColor": "Use System Color (Android 12 New Feature)", 49 | "userLikedSongs": "Пісні сподобалися користувачеві", 50 | "userPlaylists": "Списки відтворення користувачів", 51 | "yourDownloadedSongsHere": "Ваші завантажені пісні тут", 52 | "yourFavoriteSongsHere": "Ваші улюблені пісні тут", 53 | "youtubePlaylistID": "Ідентифікатор списку відтворення YouTube" 54 | } -------------------------------------------------------------------------------- /lib/localization/app_zh.arb: -------------------------------------------------------------------------------- 1 | { 2 | "about": "关于", 3 | "accentChangeMsg": "重音颜色已更改", 4 | "accentColor": "强调色", 5 | "add": "添加", 6 | "addedSuccess": "Added successfully", 7 | "appUpdateAvailableAndDownloading": "应用程序更新可用并正在下载", 8 | "appUpdateIsAvailable": "应用程序更新可用", 9 | "appUpdateIsNotAvailable": "应用程序更新不可用", 10 | "audioFileType": "音频文件扩展名", 11 | "audioFileTypeMsg": "音频文件类型已更改", 12 | "backupUserData": "备份用户数据", 13 | "backupedSuccessfully": "备份成功", 14 | "cacheMsg": "缓存已清除", 15 | "clearCache": "清除缓存", 16 | "clearSearchHistory": "清除搜索历史", 17 | "downloadAppUpdate": "下载应用更新", 18 | "downloadCompleted": "Download Completed", 19 | "downloadStarted": "Download Started", 20 | "falseMSG": "False", 21 | "home": "Home", 22 | "language": "语", 23 | "languageMsg": "语言已更改", 24 | "localSongs": "本地歌曲", 25 | "lyrics": "歌词", 26 | "lyricsNotAvailable": "没有可用的歌词 ;(", 27 | "more": "More", 28 | "notYTlist": "This is not a Youtube playlist ID", 29 | "nowPlaying": "现在播放", 30 | "others": "Others", 31 | "pages": "Pages", 32 | "playAll": "全部播放", 33 | "playlist": "播放列表", 34 | "playlists": "播放列表", 35 | "queueInitText": "正在初始化队列...由于性能原因", 36 | "recommendedForYou": "为你推荐", 37 | "restoreUserData": "恢复用户数据", 38 | "restoredSuccessfully": "恢复成功", 39 | "search": "搜索", 40 | "searchHistoryMsg": "搜索记录已清除", 41 | "settingChangedMsg": "Setting changed", 42 | "settings": "设置", 43 | "suggestedPlaylists": "推荐的播放列表", 44 | "supportDonate": "Support/Donate", 45 | "themeMode": "Theme mode", 46 | "tools": "Tools", 47 | "trueMSG": "True", 48 | "useSystemColor": "Use System Color (Android 12 New Feature)", 49 | "userLikedSongs": "用户喜欢的歌曲", 50 | "userPlaylists": "用户播放列表", 51 | "yourDownloadedSongsHere": "您下载的歌曲在这里", 52 | "yourFavoriteSongsHere": "你最喜欢的歌曲在这里", 53 | "youtubePlaylistID": "Youtube 播放列表 ID" 54 | } -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:audio_service/audio_service.dart'; 4 | import 'package:audio_session/audio_session.dart'; 5 | import 'package:dynamic_color/dynamic_color.dart'; 6 | import 'package:flutter/foundation.dart'; 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter_downloader/flutter_downloader.dart'; 9 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 10 | import 'package:flutter_localizations/flutter_localizations.dart'; 11 | import 'package:get_it/get_it.dart'; 12 | import 'package:hive_flutter/hive_flutter.dart'; 13 | import 'package:musify/services/audio_handler.dart'; 14 | import 'package:musify/services/audio_manager.dart'; 15 | import 'package:musify/style/appColors.dart'; 16 | import 'package:musify/style/appTheme.dart'; 17 | import 'package:musify/ui/morePage.dart'; 18 | import 'package:musify/ui/rootPage.dart'; 19 | import 'package:package_info_plus/package_info_plus.dart'; 20 | 21 | GetIt getIt = GetIt.instance; 22 | late PackageInfo packageInfo; 23 | bool _interrupted = false; 24 | ThemeMode themeMode = ThemeMode.system; 25 | 26 | final codes = { 27 | 'English': 'en', 28 | 'Georgian': 'ka', 29 | 'Chinese': 'zh', 30 | 'Dutch': 'nl', 31 | 'German': 'de', 32 | 'Indonesian': 'id', 33 | 'Italian': 'it', 34 | 'Polish': 'pl', 35 | 'Portuguese': 'pt', 36 | 'Spanish': 'es', 37 | 'Turkish': 'tr', 38 | 'Ukrainian': 'uk', 39 | }; 40 | 41 | class MyApp extends StatefulWidget { 42 | const MyApp({super.key}); 43 | 44 | static Future setThemeMode( 45 | BuildContext context, 46 | ThemeMode newThemeMode, 47 | ) async { 48 | final state = context.findAncestorStateOfType<_MyAppState>()!; 49 | state.changeTheme(newThemeMode); 50 | } 51 | 52 | static Future setLocale( 53 | BuildContext context, 54 | Locale newLocale, 55 | ) async { 56 | final state = context.findAncestorStateOfType<_MyAppState>()!; 57 | state.changeLanguage(newLocale); 58 | } 59 | 60 | static Future setAccentColor( 61 | BuildContext context, 62 | Color newAccentColor, 63 | bool systemColorStatus, 64 | ) async { 65 | final state = context.findAncestorStateOfType<_MyAppState>()!; 66 | state.changeAccentColor(newAccentColor, systemColorStatus); 67 | } 68 | 69 | @override 70 | _MyAppState createState() => _MyAppState(); 71 | } 72 | 73 | class _MyAppState extends State { 74 | Locale _locale = const Locale('en', ''); 75 | 76 | void changeTheme(ThemeMode newThemeMode) { 77 | setState(() { 78 | themeMode = newThemeMode; 79 | }); 80 | } 81 | 82 | void changeLanguage(Locale locale) { 83 | setState(() { 84 | _locale = locale; 85 | }); 86 | } 87 | 88 | void changeAccentColor(Color newAccentColor, bool systemColorStatus) { 89 | setState(() { 90 | useSystemColor.value = systemColorStatus; 91 | accent = ColorScheme.fromSwatch( 92 | primarySwatch: getMaterialColorFromColor( 93 | newAccentColor, 94 | ), 95 | accentColor: newAccentColor, 96 | ); 97 | }); 98 | } 99 | 100 | @override 101 | void initState() { 102 | super.initState(); 103 | _locale = Locale( 104 | codes[Hive.box('settings').get('language', defaultValue: 'English') 105 | as String]!, 106 | ); 107 | themeMode = Hive.box('settings').get('themeMode', defaultValue: 'system') == 108 | 'system' 109 | ? ThemeMode.system 110 | : Hive.box('settings').get('themeMode') == 'light' 111 | ? ThemeMode.light 112 | : ThemeMode.dark; 113 | } 114 | 115 | @override 116 | void dispose() { 117 | Hive.close(); 118 | super.dispose(); 119 | } 120 | 121 | @override 122 | Widget build(BuildContext context) { 123 | return DynamicColorBuilder( 124 | builder: (lightColorScheme, darkColorScheme) { 125 | if (lightColorScheme != null && useSystemColor.value == true) 126 | accent = (themeMode == ThemeMode.light 127 | ? lightColorScheme 128 | : darkColorScheme)!; 129 | return MaterialApp( 130 | themeMode: themeMode, 131 | debugShowCheckedModeBanner: false, 132 | darkTheme: darkColorScheme != null && useSystemColor.value == true 133 | ? getAppDarkTheme().copyWith( 134 | colorScheme: darkColorScheme, 135 | ) 136 | : getAppDarkTheme(), 137 | theme: lightColorScheme != null && useSystemColor.value == true 138 | ? getAppLightTheme().copyWith( 139 | colorScheme: lightColorScheme, 140 | ) 141 | : getAppLightTheme(), 142 | localizationsDelegates: const [ 143 | AppLocalizations.delegate, 144 | GlobalMaterialLocalizations.delegate, 145 | GlobalWidgetsLocalizations.delegate, 146 | GlobalCupertinoLocalizations.delegate, 147 | ], 148 | supportedLocales: const [ 149 | Locale('en', ''), 150 | Locale('ka', ''), 151 | Locale('zh', ''), 152 | Locale('nl', ''), 153 | Locale('fr', ''), 154 | Locale('de', ''), 155 | Locale('he', ''), 156 | Locale('hi', ''), 157 | Locale('hu', ''), 158 | Locale('id', ''), 159 | Locale('it', ''), 160 | Locale('pl', ''), 161 | Locale('pt', ''), 162 | Locale('es', ''), 163 | Locale('ta', ''), 164 | Locale('tr', ''), 165 | Locale('uk', ''), 166 | Locale('ur', '') 167 | ], 168 | locale: _locale, 169 | initialRoute: '/', 170 | routes: { 171 | '/': (context) => Musify(), 172 | }, 173 | ); 174 | }, 175 | ); 176 | } 177 | } 178 | 179 | void main() async { 180 | WidgetsFlutterBinding.ensureInitialized(); 181 | await Hive.initFlutter(); 182 | await Hive.openBox('settings'); 183 | await Hive.openBox('user'); 184 | await Hive.openBox('cache'); 185 | await initialisation(); 186 | runApp(const MyApp()); 187 | } 188 | 189 | Future initialisation() async { 190 | final session = await AudioSession.instance; 191 | await session.configure(const AudioSessionConfiguration.music()); 192 | session.interruptionEventStream.listen((event) { 193 | if (event.begin) { 194 | if (audioPlayer.playing) { 195 | pause(); 196 | _interrupted = true; 197 | } 198 | } else { 199 | switch (event.type) { 200 | case AudioInterruptionType.pause: 201 | case AudioInterruptionType.duck: 202 | if (!audioPlayer.playing && _interrupted) { 203 | play(); 204 | } 205 | break; 206 | case AudioInterruptionType.unknown: 207 | break; 208 | } 209 | _interrupted = false; 210 | } 211 | }); 212 | final audioHandler = await AudioService.init( 213 | builder: MyAudioHandler.new, 214 | config: const AudioServiceConfig( 215 | androidNotificationChannelId: 'com.gokadzev.musify', 216 | androidNotificationChannelName: 'Musify', 217 | androidNotificationOngoing: true, 218 | androidNotificationIcon: 'mipmap/launcher_icon', 219 | androidShowNotificationBadge: true, 220 | ), 221 | ); 222 | getIt.registerSingleton(audioHandler); 223 | await enableBooster(); 224 | 225 | packageInfo = await PackageInfo.fromPlatform(); 226 | 227 | try { 228 | await FlutterDownloader.initialize( 229 | debug: kDebugMode, 230 | ignoreSsl: true, 231 | ); 232 | 233 | await FlutterDownloader.registerCallback(downloadCallback); 234 | } catch (e) { 235 | if (kDebugMode) { 236 | print(e); 237 | } 238 | } 239 | } 240 | 241 | @pragma('vm:entry-point') 242 | void downloadCallback(String id, DownloadTaskStatus status, int progress) {} 243 | -------------------------------------------------------------------------------- /lib/services/audio_manager.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:audio_service/audio_service.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:just_audio/just_audio.dart'; 6 | import 'package:musify/API/musify.dart'; 7 | import 'package:musify/helper/mediaitem.dart'; 8 | import 'package:musify/main.dart'; 9 | import 'package:musify/services/audio_handler.dart'; 10 | import 'package:musify/services/data_manager.dart'; 11 | import 'package:musify/ui/morePage.dart'; 12 | 13 | final _equalizer = AndroidEqualizer(); 14 | final _loudnessEnhancer = AndroidLoudnessEnhancer(); 15 | final _audioHandler = getIt(); 16 | 17 | AudioPlayer audioPlayer = AudioPlayer( 18 | audioPipeline: AudioPipeline( 19 | androidAudioEffects: [ 20 | _loudnessEnhancer, 21 | _equalizer, 22 | ], 23 | ), 24 | ); 25 | 26 | ValueNotifier duration = ValueNotifier(Duration.zero); 27 | ValueNotifier position = ValueNotifier(Duration.zero); 28 | 29 | final shuffleNotifier = ValueNotifier(false); 30 | final repeatNotifier = ValueNotifier(false); 31 | final playerState = ValueNotifier(audioPlayer.playerState); 32 | 33 | bool get hasNext => activePlaylist.isEmpty 34 | ? audioPlayer.hasNext 35 | : id + 1 <= activePlaylist.length; 36 | 37 | bool get hasPrevious => 38 | activePlaylist.isEmpty ? audioPlayer.hasPrevious : id - 1 >= 0; 39 | 40 | String get durationText => 41 | duration.value != null ? duration.value.toString().split('.').first : ''; 42 | 43 | String get positionText => 44 | position.value != null ? position.value.toString().split('.').first : ''; 45 | 46 | bool isMuted = false; 47 | 48 | Future playSong(Map song) async { 49 | if (song['ytid'].length == 0) { 50 | await MyAudioHandler() 51 | .addQueueItem(mapToMediaItem(song, song['songUrl'].toString())); 52 | } else { 53 | final songUrl = await getSong(song['ytid'], true); 54 | 55 | if (sponsorBlockSupport.value) { 56 | final segments = await getSkipSegments(song['ytid']); 57 | if (segments.isNotEmpty) { 58 | if (segments.length == 1) { 59 | await MyAudioHandler().addQueueItem( 60 | mapToMediaItem(song, songUrl), 61 | Duration(seconds: segments[0]['end']!), 62 | ); 63 | } else { 64 | await MyAudioHandler().addQueueItem( 65 | mapToMediaItem(song, songUrl), 66 | Duration(seconds: segments[0]['end']!), 67 | Duration(seconds: segments[1]['start']!), 68 | ); 69 | } 70 | } else { 71 | await MyAudioHandler().addQueueItem(mapToMediaItem(song, songUrl)); 72 | } 73 | } else { 74 | await MyAudioHandler().addQueueItem(mapToMediaItem(song, songUrl)); 75 | } 76 | } 77 | play(); 78 | } 79 | 80 | Future changeShuffleStatus() async { 81 | if (shuffleNotifier.value == true) { 82 | await audioPlayer.setShuffleModeEnabled(false); 83 | } else { 84 | await audioPlayer.setShuffleModeEnabled(true); 85 | } 86 | } 87 | 88 | void changeAutoPlayNextStatus() { 89 | if (playNextSongAutomatically.value == false) { 90 | playNextSongAutomatically.value = true; 91 | addOrUpdateData('settings', 'playNextSongAutomatically', true); 92 | } else { 93 | playNextSongAutomatically.value = false; 94 | addOrUpdateData('settings', 'playNextSongAutomatically', false); 95 | } 96 | } 97 | 98 | Future changeLoopStatus() async { 99 | if (repeatNotifier.value == false) { 100 | repeatNotifier.value = true; 101 | await audioPlayer.setLoopMode(LoopMode.one); 102 | } else { 103 | repeatNotifier.value = false; 104 | await audioPlayer.setLoopMode(LoopMode.off); 105 | } 106 | } 107 | 108 | void changeSponsorBlockStatus() { 109 | if (sponsorBlockSupport.value == false) { 110 | sponsorBlockSupport.value = true; 111 | addOrUpdateData('settings', 'sponsorBlockSupport', true); 112 | } else { 113 | sponsorBlockSupport.value = false; 114 | addOrUpdateData('settings', 'sponsorBlockSupport', false); 115 | } 116 | } 117 | 118 | Future enableBooster() async { 119 | await _loudnessEnhancer.setEnabled(true); 120 | await _loudnessEnhancer.setTargetGain(1); 121 | } 122 | 123 | void play() => _audioHandler.play(); 124 | 125 | void pause() => _audioHandler.pause(); 126 | 127 | void stop() => _audioHandler.stop(); 128 | 129 | void playNext() => _audioHandler.skipToNext(); 130 | 131 | void playPrevious() => _audioHandler.skipToPrevious(); 132 | 133 | Future mute(bool muted) async { 134 | if (muted) { 135 | await audioPlayer.setVolume(0); 136 | } else { 137 | await audioPlayer.setVolume(1); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /lib/services/data_manager.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:hive/hive.dart'; 4 | import 'package:musify/services/ext_storage.dart'; 5 | import 'package:permission_handler/permission_handler.dart'; 6 | 7 | void addOrUpdateData( 8 | String category, 9 | dynamic key, 10 | dynamic value, 11 | ) { 12 | if (!Hive.isBoxOpen(category)) { 13 | Hive.openBox(category); 14 | } 15 | Hive.box(category).put(key, value); 16 | } 17 | 18 | Future getData(String category, dynamic key) async { 19 | if (!Hive.isBoxOpen(category)) { 20 | await Hive.openBox(category); 21 | } 22 | return Hive.box(category).get(key); 23 | } 24 | 25 | void deleteData(String category, dynamic key) { 26 | if (!Hive.isBoxOpen(category)) { 27 | Hive.openBox(category); 28 | } 29 | Hive.box(category).delete(key); 30 | } 31 | 32 | void clearCache() async { 33 | if (!Hive.isBoxOpen('cache')) { 34 | await Hive.openBox('cache'); 35 | } 36 | await Hive.box('cache').clear(); 37 | } 38 | 39 | Future backupData() async { 40 | final boxNames = ['user', 'settings']; 41 | final dlPath = await ExtStorageProvider.getExtStorage(dirName: 'Musify/Data'); 42 | 43 | for (var i = 0; i < boxNames.length; i++) { 44 | await Hive.openBox(boxNames[i]); 45 | try { 46 | await File(Hive.box(boxNames[i]).path!) 47 | .copy('$dlPath/${boxNames[i]}Data.hive'); 48 | } catch (e) { 49 | await [ 50 | Permission.manageExternalStorage, 51 | ].request(); 52 | await File(Hive.box(boxNames[i]).path!) 53 | .copy('$dlPath/${boxNames[i]}Data.hive'); 54 | return 'Permissions problem, if you already gave requested permission, Backup data again!'; 55 | } 56 | } 57 | return 'Backuped Successfully!'; 58 | } 59 | 60 | Future restoreData() async { 61 | final boxNames = ['user', 'settings']; 62 | final uplPath = 63 | await ExtStorageProvider.getExtStorage(dirName: 'Musify/Data'); 64 | 65 | for (var i = 0; i < boxNames.length; i++) { 66 | await Hive.openBox(boxNames[i]); 67 | try { 68 | final box = await Hive.openBox(boxNames[i]); 69 | final boxPath = box.path; 70 | await File('${uplPath!}/${boxNames[i]}Data.hive').copy(boxPath!); 71 | } catch (e) { 72 | await [ 73 | Permission.manageExternalStorage, 74 | ].request(); 75 | return 'Permissions problem, if you already gave requested permission, Restore data again!'; 76 | } 77 | } 78 | 79 | return 'Restored Successfully!'; 80 | } 81 | -------------------------------------------------------------------------------- /lib/services/download_manager.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 5 | import 'package:musify/API/musify.dart'; 6 | import 'package:musify/helper/flutter_toast.dart'; 7 | import 'package:musify/services/ext_storage.dart'; 8 | import 'package:musify/ui/morePage.dart'; 9 | import 'package:permission_handler/permission_handler.dart'; 10 | import 'package:youtube_explode_dart/youtube_explode_dart.dart'; 11 | 12 | Future downloadSong(BuildContext context, dynamic song) async { 13 | if (await Permission.audio.status.isDenied) { 14 | await Permission.audio.request(); 15 | if (await Permission.audio.status.isPermanentlyDenied) { 16 | await openAppSettings(); 17 | } 18 | } 19 | 20 | if (await Permission.storage.status.isDenied) { 21 | await [ 22 | Permission.storage, 23 | Permission.accessMediaLocation, 24 | Permission.mediaLibrary, 25 | ].request(); 26 | 27 | if (await Permission.storage.status.isPermanentlyDenied) { 28 | await openAppSettings(); 29 | } 30 | } 31 | 32 | final filename = song['title'] 33 | .replaceAll(r'\', '') 34 | .replaceAll('/', '') 35 | .replaceAll('*', '') 36 | .replaceAll('?', '') 37 | .replaceAll('"', '') 38 | .replaceAll('<', '') 39 | .replaceAll('>', '') 40 | .replaceAll('|', '') + 41 | '.' + 42 | prefferedFileExtension.value; 43 | 44 | var filepath = ''; 45 | final dlPath = await ExtStorageProvider.getExtStorage(dirName: 'Music'); 46 | try { 47 | showToast( 48 | AppLocalizations.of(context)!.downloadStarted, 49 | ); 50 | await File('${dlPath!}/$filename') 51 | .create(recursive: true) 52 | .then((value) => filepath = value.path); 53 | await downloadFileFromYT(filename, filepath, dlPath, song).whenComplete( 54 | () => showToast( 55 | AppLocalizations.of(context)!.downloadCompleted, 56 | ), 57 | ); 58 | } catch (e) { 59 | await [Permission.manageExternalStorage].request(); 60 | await File('${dlPath!}/$filename') 61 | .create(recursive: true) 62 | .then((value) => filepath = value.path); 63 | await downloadFileFromYT(filename, filepath, dlPath, song).whenComplete( 64 | () => showToast( 65 | AppLocalizations.of(context)!.downloadCompleted, 66 | ), 67 | ); 68 | } 69 | } 70 | 71 | Future downloadFileFromYT( 72 | String filename, 73 | String filepath, 74 | String dlPath, 75 | dynamic song, 76 | ) async { 77 | final audioStream = await getSong(song['ytid'].toString(), false); 78 | final file = File(filepath); 79 | final fileStream = file.openWrite(); 80 | await yt.videos.streamsClient.get(audioStream as StreamInfo).pipe(fileStream); 81 | await fileStream.flush(); 82 | await fileStream.close(); 83 | } 84 | -------------------------------------------------------------------------------- /lib/services/ext_storage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:path_provider/path_provider.dart'; 3 | import 'package:permission_handler/permission_handler.dart'; 4 | 5 | // ignore: avoid_classes_with_only_static_members 6 | class ExtStorageProvider { 7 | // asking for permission 8 | static Future requestPermission(Permission permission) async { 9 | if (await permission.isGranted) { 10 | return true; 11 | } else { 12 | final result = await permission.request(); 13 | if (result == PermissionStatus.granted) { 14 | return true; 15 | } else { 16 | return false; 17 | } 18 | } 19 | } 20 | 21 | // getting external storage path 22 | static Future getExtStorage({required String dirName}) async { 23 | Directory? directory; 24 | 25 | try { 26 | // checking platform 27 | if (Platform.isAndroid) { 28 | if (await requestPermission(Permission.storage)) { 29 | directory = await getExternalStorageDirectory(); 30 | 31 | // getting main path 32 | final newPath = directory!.path 33 | .replaceFirst('Android/data/com.gokadzev.musify/files', dirName); 34 | 35 | directory = Directory(newPath); 36 | 37 | // checking if directory exist or not 38 | if (!await directory.exists()) { 39 | // if directory not exists then asking for permission to create folder 40 | await requestPermission(Permission.manageExternalStorage); 41 | //creating folder 42 | 43 | await directory.create(recursive: true); 44 | } 45 | if (await directory.exists()) { 46 | try { 47 | // if directory exists then returning the complete path 48 | return newPath; 49 | } catch (e) { 50 | rethrow; 51 | } 52 | } 53 | } else { 54 | return throw 'something went wrong'; 55 | } 56 | } else if (Platform.isIOS) { 57 | directory = await getApplicationDocumentsDirectory(); 58 | return directory.path; 59 | } else { 60 | directory = await getDownloadsDirectory(); 61 | return directory!.path; 62 | } 63 | } catch (e) { 64 | rethrow; 65 | } 66 | return directory.path; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/services/lyrics_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:http/http.dart' as http; 2 | 3 | class Lyrics { 4 | Lyrics({delimiter1, delimiter2}) { 5 | setDelimiters(delimiter1: delimiter1, delimiter2: delimiter2); 6 | } 7 | 8 | final String _url = 9 | 'https://www.google.com/search?client=safari&rls=en&ie=UTF-8&oe=UTF-8&q='; 10 | String _delimiter1 = 11 | '
'; 12 | String _delimiter2 = 13 | '
'; 14 | 15 | void setDelimiters({String? delimiter1, String? delimiter2}) { 16 | _delimiter1 = delimiter1 ?? _delimiter1; 17 | _delimiter2 = delimiter2 ?? _delimiter2; 18 | } 19 | 20 | Future getLyrics({String? track, String? artist}) async { 21 | if (track == null || artist == null) 22 | throw Exception('track and artist must not be null'); 23 | 24 | String lyrics; 25 | 26 | // try multiple queries 27 | try { 28 | lyrics = (await http 29 | .get(Uri.parse(Uri.encodeFull('$_url$track by $artist lyrics')))) 30 | .body; 31 | lyrics = lyrics.split(_delimiter1).last; 32 | lyrics = lyrics.split(_delimiter2).first; 33 | if (lyrics.contains('')) throw Error(); 34 | } catch (_) { 35 | try { 36 | lyrics = (await http.get( 37 | Uri.parse( 38 | Uri.encodeFull('$_url$track by $artist song lyrics'), 39 | ), 40 | )) 41 | .body; 42 | lyrics = lyrics.split(_delimiter1).last; 43 | lyrics = lyrics.split(_delimiter2).first; 44 | if (lyrics.contains('')) throw Error(); 45 | } catch (_) { 46 | try { 47 | lyrics = (await http.get( 48 | Uri.parse( 49 | Uri.encodeFull( 50 | '$_url${track.split("-").first} by $artist lyrics', 51 | ), 52 | ), 53 | )) 54 | .body; 55 | lyrics = lyrics.split(_delimiter1).last; 56 | lyrics = lyrics.split(_delimiter2).first; 57 | if (lyrics.contains('')) throw Error(); 58 | } catch (_) { 59 | // give up 60 | return 'not found'; 61 | } 62 | } 63 | } 64 | 65 | final split = lyrics.split('\n'); 66 | var result = ''; 67 | for (var i = 0; i < split.length; i++) { 68 | result = '$result${split[i]}\n'; 69 | } 70 | return result.trim(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/style/appColors.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:musify/style/appTheme.dart'; 3 | 4 | Color getShade(Color color, {bool darker = false, double value = .1}) { 5 | assert(value >= 0 && value <= 1); 6 | 7 | final hsl = HSLColor.fromColor(color); 8 | final hslDark = hsl.withLightness( 9 | (darker ? (hsl.lightness - value) : (hsl.lightness + value)) 10 | .clamp(0.0, 1.0), 11 | ); 12 | 13 | return hslDark.toColor(); 14 | } 15 | 16 | MaterialColor getMaterialColorFromColor(Color color) { 17 | final _colorShades = { 18 | 50: getShade(color, value: 0.5), 19 | 100: getShade(color, value: 0.4), 20 | 200: getShade(color, value: 0.3), 21 | 300: getShade(color, value: 0.2), 22 | 400: getShade(color, value: 0.1), 23 | 500: color, 24 | 600: getShade(color, value: 0.1, darker: true), 25 | 700: getShade(color, value: 0.15, darker: true), 26 | 800: getShade(color, value: 0.2, darker: true), 27 | 900: getShade(color, value: 0.25, darker: true), 28 | }; 29 | return MaterialColor(color.value, _colorShades); 30 | } 31 | 32 | Color isAccentWhite() { 33 | return accent.primary != const Color(0xFFFFFFFF) 34 | ? Colors.white 35 | : Colors.black; 36 | } 37 | -------------------------------------------------------------------------------- /lib/style/appTheme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:hive/hive.dart'; 3 | import 'package:musify/style/appColors.dart'; 4 | 5 | ColorScheme accent = ColorScheme.fromSwatch( 6 | primarySwatch: getMaterialColorFromColor( 7 | Color(Hive.box('settings').get('accentColor', defaultValue: 0xFFF08080)), 8 | ), 9 | accentColor: 10 | Color(Hive.box('settings').get('accentColor', defaultValue: 0xFFF08080)), 11 | ); 12 | 13 | ThemeData getAppDarkTheme() { 14 | return ThemeData( 15 | scaffoldBackgroundColor: const Color(0xFF121212), 16 | canvasColor: const Color(0xFF121212), 17 | appBarTheme: const AppBarTheme(backgroundColor: Color(0xFF121212)), 18 | bottomAppBarColor: const Color(0xFF151515), 19 | colorScheme: accent, 20 | visualDensity: VisualDensity.adaptivePlatformDensity, 21 | fontFamily: 'Ubuntu', 22 | useMaterial3: true, 23 | pageTransitionsTheme: const PageTransitionsTheme( 24 | builders: { 25 | TargetPlatform.android: ZoomPageTransitionsBuilder(), 26 | }, 27 | ), 28 | elevatedButtonTheme: ElevatedButtonThemeData( 29 | style: ButtonStyle( 30 | shape: MaterialStateProperty.all( 31 | RoundedRectangleBorder( 32 | borderRadius: BorderRadius.circular(8), 33 | ), 34 | ), 35 | ), 36 | ), 37 | cardTheme: CardTheme( 38 | color: const Color(0xFF151515), 39 | shape: RoundedRectangleBorder( 40 | borderRadius: BorderRadius.circular(10), 41 | ), 42 | elevation: 2.3, 43 | ), 44 | listTileTheme: const ListTileThemeData(textColor: Colors.white), 45 | iconTheme: const IconThemeData(color: Colors.white), 46 | hintColor: Colors.white, 47 | textTheme: const TextTheme( 48 | bodyText2: TextStyle(color: Colors.white), 49 | ), 50 | ); 51 | } 52 | 53 | ThemeData getAppLightTheme() { 54 | return ThemeData( 55 | scaffoldBackgroundColor: Colors.white, 56 | canvasColor: Colors.white, 57 | colorScheme: accent, 58 | visualDensity: VisualDensity.adaptivePlatformDensity, 59 | fontFamily: 'Ubuntu', 60 | useMaterial3: true, 61 | pageTransitionsTheme: const PageTransitionsTheme( 62 | builders: { 63 | TargetPlatform.android: ZoomPageTransitionsBuilder(), 64 | }, 65 | ), 66 | elevatedButtonTheme: ElevatedButtonThemeData( 67 | style: ButtonStyle( 68 | shape: MaterialStateProperty.all( 69 | RoundedRectangleBorder( 70 | borderRadius: BorderRadius.circular(8), 71 | ), 72 | ), 73 | ), 74 | ), 75 | cardTheme: CardTheme( 76 | shape: RoundedRectangleBorder( 77 | borderRadius: BorderRadius.circular(10), 78 | ), 79 | elevation: 2.3, 80 | ), 81 | listTileTheme: ListTileThemeData( 82 | selectedColor: accent.primary.withOpacity(0.4), 83 | ), 84 | iconTheme: const IconThemeData(color: Color(0xFF151515)), 85 | hintColor: const Color(0xFF151515), 86 | ); 87 | } 88 | -------------------------------------------------------------------------------- /lib/ui/aboutPage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 3 | import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; 4 | import 'package:musify/helper/url_launcher.dart'; 5 | import 'package:musify/helper/version.dart'; 6 | import 'package:musify/style/appTheme.dart'; 7 | 8 | class AboutPage extends StatelessWidget { 9 | const AboutPage({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Scaffold( 14 | appBar: AppBar( 15 | centerTitle: true, 16 | title: Text( 17 | AppLocalizations.of(context)!.about, 18 | style: TextStyle( 19 | color: accent.primary, 20 | fontSize: 25, 21 | fontWeight: FontWeight.w700, 22 | ), 23 | ), 24 | leading: IconButton( 25 | icon: Icon( 26 | Icons.arrow_back, 27 | color: accent.primary, 28 | ), 29 | onPressed: () => Navigator.pop(context, false), 30 | ), 31 | backgroundColor: Colors.transparent, 32 | elevation: 0, 33 | ), 34 | body: const SingleChildScrollView(child: AboutCards()), 35 | ); 36 | } 37 | } 38 | 39 | class AboutCards extends StatelessWidget { 40 | const AboutCards({super.key}); 41 | 42 | @override 43 | Widget build(BuildContext context) { 44 | return Column( 45 | children: [ 46 | Padding( 47 | padding: const EdgeInsets.only(top: 17, left: 8, right: 8, bottom: 6), 48 | child: Column( 49 | children: [ 50 | ListTile( 51 | title: Padding( 52 | padding: const EdgeInsets.all(13), 53 | child: Center( 54 | child: Text( 55 | 'Musify | $version', 56 | style: TextStyle( 57 | color: accent.primary, 58 | fontSize: 24, 59 | fontWeight: FontWeight.w600, 60 | ), 61 | ), 62 | ), 63 | ), 64 | ), 65 | ], 66 | ), 67 | ), 68 | const Padding( 69 | padding: EdgeInsets.only(bottom: 8, left: 10, right: 10), 70 | child: Divider( 71 | color: Colors.white24, 72 | thickness: 0.8, 73 | ), 74 | ), 75 | Padding( 76 | padding: const EdgeInsets.only(top: 8, left: 8, right: 8, bottom: 6), 77 | child: Card( 78 | child: ListTile( 79 | leading: Container( 80 | height: 50, 81 | width: 50, 82 | decoration: const BoxDecoration( 83 | shape: BoxShape.circle, 84 | image: DecorationImage( 85 | fit: BoxFit.fill, 86 | image: NetworkImage( 87 | 'https://avatars.githubusercontent.com/u/79704324?v=4', 88 | ), 89 | ), 90 | ), 91 | ), 92 | title: const Text( 93 | 'Valeri Gokadze', 94 | ), 95 | subtitle: const Text( 96 | 'Web/APP Developer', 97 | ), 98 | trailing: Wrap( 99 | children: [ 100 | IconButton( 101 | icon: Icon( 102 | MdiIcons.github, 103 | color: accent.primary, 104 | ), 105 | tooltip: 'Github', 106 | onPressed: () { 107 | launchURL( 108 | Uri.parse('https://github.com/gokadzev'), 109 | ); 110 | }, 111 | ), 112 | ], 113 | ), 114 | ), 115 | ), 116 | ) 117 | ], 118 | ); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /lib/ui/homePage.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 4 | import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; 5 | import 'package:musify/API/musify.dart'; 6 | import 'package:musify/customWidgets/delayed_display.dart'; 7 | import 'package:musify/customWidgets/song_bar.dart'; 8 | import 'package:musify/customWidgets/spinner.dart'; 9 | import 'package:musify/style/appTheme.dart'; 10 | import 'package:musify/ui/playlistPage.dart'; 11 | 12 | class HomePage extends StatefulWidget { 13 | @override 14 | _HomePageState createState() => _HomePageState(); 15 | } 16 | 17 | class _HomePageState extends State { 18 | @override 19 | Widget build(BuildContext context) { 20 | return Scaffold( 21 | appBar: AppBar( 22 | centerTitle: true, 23 | title: Text( 24 | 'Musify.', 25 | style: TextStyle( 26 | color: accent.primary, 27 | fontSize: 35, 28 | fontWeight: FontWeight.w800, 29 | ), 30 | ), 31 | elevation: 0, 32 | ), 33 | body: SingleChildScrollView( 34 | child: Column( 35 | crossAxisAlignment: CrossAxisAlignment.start, 36 | children: [ 37 | FutureBuilder( 38 | future: getPlaylists(5), 39 | builder: (context, data) { 40 | return data.hasData 41 | ? Wrap( 42 | children: [ 43 | Padding( 44 | padding: EdgeInsets.only( 45 | top: MediaQuery.of(context).size.height / 55, 46 | bottom: 10, 47 | left: 20, 48 | right: 20, 49 | ), 50 | child: Text( 51 | AppLocalizations.of(context)!.suggestedPlaylists, 52 | style: TextStyle( 53 | color: accent.primary, 54 | fontSize: 20, 55 | fontWeight: FontWeight.w700, 56 | ), 57 | ), 58 | ), 59 | SizedBox( 60 | height: 230, 61 | child: ListView.builder( 62 | scrollDirection: Axis.horizontal, 63 | itemCount: (data as dynamic).data.length as int, 64 | itemBuilder: (context, index) { 65 | return Padding( 66 | padding: const EdgeInsets.symmetric( 67 | horizontal: 15, 68 | ), 69 | child: CubeContainer( 70 | id: (data as dynamic) 71 | .data[index]['ytid'] 72 | .toString(), 73 | image: (data as dynamic) 74 | .data[index]['image'] 75 | .toString(), 76 | ), 77 | ); 78 | }, 79 | ), 80 | ) 81 | ], 82 | ) 83 | : const Center( 84 | child: Padding( 85 | padding: EdgeInsets.all(35), 86 | child: Spinner(), 87 | ), 88 | ); 89 | }, 90 | ), 91 | FutureBuilder( 92 | future: get10Music('PLgzTt0k8mXzEk586ze4BjvDXR7c-TUSnx'), 93 | builder: (context, data) { 94 | if (data.connectionState != ConnectionState.done) { 95 | return const Center( 96 | child: Padding( 97 | padding: EdgeInsets.all(35), 98 | child: Spinner(), 99 | ), 100 | ); 101 | } 102 | if (data.hasError) { 103 | return Center( 104 | child: Text( 105 | 'Error!', 106 | style: TextStyle(color: accent.primary, fontSize: 18), 107 | ), 108 | ); 109 | } 110 | if (!data.hasData) { 111 | return Center( 112 | child: Text( 113 | 'Nothing Found!', 114 | style: TextStyle(color: accent.primary, fontSize: 18), 115 | ), 116 | ); 117 | } 118 | return Wrap( 119 | children: [ 120 | Padding( 121 | padding: EdgeInsets.only( 122 | top: MediaQuery.of(context).size.height / 55, 123 | bottom: 10, 124 | left: 20, 125 | right: 20, 126 | ), 127 | child: Text( 128 | AppLocalizations.of(context)!.recommendedForYou, 129 | style: TextStyle( 130 | color: accent.primary, 131 | fontSize: 20, 132 | fontWeight: FontWeight.w700, 133 | ), 134 | ), 135 | ), 136 | Padding( 137 | padding: const EdgeInsets.symmetric( 138 | horizontal: 7, 139 | ), 140 | child: ListView.builder( 141 | shrinkWrap: true, 142 | addAutomaticKeepAlives: false, 143 | addRepaintBoundaries: false, 144 | physics: const BouncingScrollPhysics(), 145 | itemCount: (data as dynamic).data.length as int, 146 | itemBuilder: (context, index) { 147 | return SongBar( 148 | (data as dynamic).data[index], 149 | false, 150 | ); 151 | }, 152 | ), 153 | ) 154 | ], 155 | ); 156 | }, 157 | ), 158 | ], 159 | ), 160 | ), 161 | ); 162 | } 163 | } 164 | 165 | class CubeContainer extends StatelessWidget { 166 | const CubeContainer({ 167 | required this.id, 168 | required this.image, 169 | }); 170 | final String id; 171 | final String image; 172 | 173 | @override 174 | Widget build(BuildContext context) { 175 | final size = MediaQuery.of(context).size; 176 | return DelayedDisplay( 177 | delay: const Duration(milliseconds: 200), 178 | fadingDuration: const Duration(milliseconds: 400), 179 | child: GestureDetector( 180 | onTap: () { 181 | getPlaylistInfoForWidget(id).then( 182 | (value) => { 183 | Navigator.push( 184 | context, 185 | MaterialPageRoute( 186 | builder: (context) => PlaylistPage(playlist: value), 187 | ), 188 | ) 189 | }, 190 | ); 191 | }, 192 | child: Column( 193 | children: [ 194 | SizedBox( 195 | height: size.height / 4.15, 196 | width: size.width / 1.9, 197 | child: Card( 198 | color: Colors.transparent, 199 | child: CachedNetworkImage( 200 | imageUrl: image, 201 | imageBuilder: (context, imageProvider) => DecoratedBox( 202 | decoration: BoxDecoration( 203 | borderRadius: BorderRadius.circular(10), 204 | image: DecorationImage( 205 | image: imageProvider, 206 | fit: BoxFit.cover, 207 | ), 208 | ), 209 | ), 210 | errorWidget: (context, url, error) => DecoratedBox( 211 | decoration: BoxDecoration( 212 | borderRadius: BorderRadius.circular(10), 213 | color: const Color.fromARGB(30, 255, 255, 255), 214 | ), 215 | child: Column( 216 | mainAxisAlignment: MainAxisAlignment.center, 217 | children: [ 218 | Icon( 219 | MdiIcons.musicNoteOutline, 220 | size: 30, 221 | color: accent.primary, 222 | ), 223 | ], 224 | ), 225 | ), 226 | ), 227 | ), 228 | ), 229 | ], 230 | ), 231 | ), 232 | ); 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /lib/ui/playlistPage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:cached_network_image/cached_network_image.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 6 | import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; 7 | import 'package:musify/API/musify.dart'; 8 | import 'package:musify/customWidgets/song_bar.dart'; 9 | import 'package:musify/customWidgets/spinner.dart'; 10 | import 'package:musify/helper/flutter_toast.dart'; 11 | import 'package:musify/style/appColors.dart'; 12 | import 'package:musify/style/appTheme.dart'; 13 | 14 | class PlaylistPage extends StatefulWidget { 15 | const PlaylistPage({super.key, required this.playlist}); 16 | final dynamic playlist; 17 | 18 | @override 19 | _PlaylistPageState createState() => _PlaylistPageState(); 20 | } 21 | 22 | class _PlaylistPageState extends State { 23 | final _songsList = []; 24 | 25 | bool _isLoading = true; 26 | bool _hasMore = true; 27 | final _itemsPerPage = 35; 28 | var _currentPage = 0; 29 | var _currentLastLoadedId = 0; 30 | 31 | @override 32 | void initState() { 33 | super.initState(); 34 | _isLoading = true; 35 | _hasMore = true; 36 | _loadMore(); 37 | } 38 | 39 | @override 40 | void dispose() { 41 | super.dispose(); 42 | } 43 | 44 | void _loadMore() { 45 | _isLoading = true; 46 | fetch().then((List fetchedList) { 47 | if (!mounted) return; 48 | if (fetchedList.isEmpty) { 49 | setState(() { 50 | _isLoading = false; 51 | _hasMore = false; 52 | }); 53 | } else { 54 | setState(() { 55 | _isLoading = false; 56 | _songsList.addAll(fetchedList); 57 | }); 58 | } 59 | }); 60 | } 61 | 62 | Future fetch() async { 63 | final list = []; 64 | final _count = widget.playlist['list'].length as int; 65 | final n = min(_itemsPerPage, _count - _currentPage * _itemsPerPage); 66 | await Future.delayed(const Duration(seconds: 1), () { 67 | for (var i = 0; i < n; i++) { 68 | list.add(widget.playlist['list'][_currentLastLoadedId]); 69 | _currentLastLoadedId++; 70 | } 71 | }); 72 | _currentPage++; 73 | return list; 74 | } 75 | 76 | @override 77 | Widget build(BuildContext context) { 78 | return Scaffold( 79 | appBar: AppBar( 80 | centerTitle: true, 81 | title: Text( 82 | AppLocalizations.of(context)!.playlist, 83 | style: TextStyle( 84 | color: accent.primary, 85 | fontSize: 25, 86 | fontWeight: FontWeight.w700, 87 | ), 88 | ), 89 | leading: IconButton( 90 | icon: Icon( 91 | Icons.arrow_back, 92 | color: accent.primary, 93 | ), 94 | onPressed: () => Navigator.pop(context, false), 95 | ), 96 | elevation: 0, 97 | ), 98 | body: SingleChildScrollView( 99 | child: widget.playlist != null 100 | ? Column( 101 | children: [ 102 | Container( 103 | margin: const EdgeInsets.only(left: 10, right: 26), 104 | height: 250, 105 | width: 250, 106 | child: Card( 107 | color: Colors.transparent, 108 | child: widget.playlist['image'] != '' 109 | ? DecoratedBox( 110 | decoration: BoxDecoration( 111 | borderRadius: BorderRadius.circular(10), 112 | image: DecorationImage( 113 | fit: BoxFit.cover, 114 | image: CachedNetworkImageProvider( 115 | widget.playlist['image'].toString(), 116 | ), 117 | ), 118 | ), 119 | ) 120 | : Container( 121 | width: 200, 122 | height: 200, 123 | decoration: BoxDecoration( 124 | borderRadius: BorderRadius.circular(10), 125 | color: const Color.fromARGB(30, 255, 255, 255), 126 | ), 127 | child: Column( 128 | mainAxisAlignment: MainAxisAlignment.center, 129 | children: [ 130 | Icon( 131 | MdiIcons.musicNoteOutline, 132 | size: 30, 133 | color: accent.primary, 134 | ), 135 | Text( 136 | widget.playlist['title'].toString(), 137 | style: TextStyle(color: accent.primary), 138 | textAlign: TextAlign.center, 139 | ), 140 | ], 141 | ), 142 | ), 143 | ), 144 | ), 145 | Column( 146 | crossAxisAlignment: CrossAxisAlignment.center, 147 | children: [ 148 | const SizedBox(height: 12), 149 | Text( 150 | widget.playlist['title'].toString(), 151 | textAlign: TextAlign.center, 152 | style: TextStyle( 153 | color: accent.primary, 154 | fontSize: 18, 155 | fontWeight: FontWeight.w600, 156 | ), 157 | ), 158 | const SizedBox(height: 16), 159 | Text( 160 | widget.playlist['header_desc'].toString(), 161 | textAlign: TextAlign.center, 162 | style: TextStyle( 163 | color: accent.primary, 164 | fontSize: 10, 165 | fontWeight: FontWeight.w600, 166 | ), 167 | ), 168 | const SizedBox(height: 10), 169 | ElevatedButton( 170 | onPressed: () => { 171 | setActivePlaylist( 172 | widget.playlist['list'] as List, 173 | ), 174 | showToast( 175 | AppLocalizations.of(context)!.queueInitText, 176 | ), 177 | Navigator.pushReplacementNamed(context, '/'), 178 | }, 179 | style: ButtonStyle( 180 | backgroundColor: 181 | MaterialStateProperty.all(accent.primary), 182 | ), 183 | child: Text( 184 | AppLocalizations.of(context)!.playAll.toUpperCase(), 185 | style: TextStyle( 186 | color: isAccentWhite(), 187 | ), 188 | ), 189 | ), 190 | ], 191 | ), 192 | const SizedBox(height: 30), 193 | if (_songsList.isNotEmpty) 194 | ListView.builder( 195 | shrinkWrap: true, 196 | physics: const BouncingScrollPhysics(), 197 | addAutomaticKeepAlives: 198 | false, // may be problem with lazyload if it implemented 199 | addRepaintBoundaries: false, 200 | // Need to display a loading tile if more items are coming 201 | itemCount: 202 | _hasMore ? _songsList.length + 1 : _songsList.length, 203 | itemBuilder: (BuildContext context, int index) { 204 | if (index >= _songsList.length) { 205 | if (!_isLoading) { 206 | _loadMore(); 207 | } 208 | return const Spinner(); 209 | } 210 | return Padding( 211 | padding: const EdgeInsets.only(top: 5, bottom: 5), 212 | child: SongBar( 213 | _songsList[index], 214 | true, 215 | ), 216 | ); 217 | }, 218 | ) 219 | else 220 | const Spinner() 221 | ], 222 | ) 223 | : SizedBox( 224 | height: MediaQuery.of(context).size.height - 100, 225 | child: const Spinner(), 226 | ), 227 | ), 228 | ); 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /lib/ui/searchPage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 3 | import 'package:hive_flutter/hive_flutter.dart'; 4 | import 'package:musify/API/musify.dart'; 5 | import 'package:musify/customWidgets/song_bar.dart'; 6 | import 'package:musify/customWidgets/spinner.dart'; 7 | import 'package:musify/services/data_manager.dart'; 8 | import 'package:musify/style/appTheme.dart'; 9 | 10 | class SearchPage extends StatefulWidget { 11 | @override 12 | _SearchPageState createState() => _SearchPageState(); 13 | } 14 | 15 | List searchHistory = Hive.box('user').get('searchHistory', defaultValue: []); 16 | 17 | class _SearchPageState extends State { 18 | final TextEditingController _searchBar = TextEditingController(); 19 | final ValueNotifier _fetchingSongs = ValueNotifier(false); 20 | final FocusNode _inputNode = FocusNode(); 21 | String _searchQuery = ''; 22 | 23 | Future search() async { 24 | _searchQuery = _searchBar.text; 25 | if (_searchQuery.isNotEmpty) { 26 | _fetchingSongs.value = true; 27 | if (!searchHistory.contains(_searchQuery)) { 28 | searchHistory.insert(0, _searchQuery); 29 | addOrUpdateData('user', 'searchHistory', searchHistory); 30 | } 31 | _fetchingSongs.value = false; 32 | } 33 | setState(() {}); 34 | } 35 | 36 | @override 37 | Widget build(BuildContext context) { 38 | return Scaffold( 39 | appBar: AppBar( 40 | centerTitle: true, 41 | title: Text( 42 | AppLocalizations.of(context)!.search, 43 | style: TextStyle( 44 | color: accent.primary, 45 | fontSize: 30, 46 | fontWeight: FontWeight.w700, 47 | ), 48 | ), 49 | elevation: 0, 50 | ), 51 | body: SingleChildScrollView( 52 | child: Column( 53 | children: [ 54 | Padding( 55 | padding: const EdgeInsets.only( 56 | top: 12, 57 | bottom: 20, 58 | left: 12, 59 | right: 12, 60 | ), 61 | child: TextField( 62 | onSubmitted: (String value) { 63 | search(); 64 | FocusManager.instance.primaryFocus?.unfocus(); 65 | }, 66 | controller: _searchBar, 67 | focusNode: _inputNode, 68 | style: TextStyle( 69 | fontSize: 16, 70 | color: accent.primary, 71 | ), 72 | cursorColor: Colors.green[50], 73 | decoration: InputDecoration( 74 | filled: true, 75 | enabledBorder: OutlineInputBorder( 76 | borderRadius: const BorderRadius.all( 77 | Radius.circular(100), 78 | ), 79 | borderSide: BorderSide( 80 | color: Theme.of(context).backgroundColor, 81 | ), 82 | ), 83 | focusedBorder: OutlineInputBorder( 84 | borderRadius: const BorderRadius.all( 85 | Radius.circular(100), 86 | ), 87 | borderSide: BorderSide(color: accent.primary), 88 | ), 89 | suffixIcon: ValueListenableBuilder( 90 | valueListenable: _fetchingSongs, 91 | builder: (_, value, __) { 92 | if (value == true) { 93 | return IconButton( 94 | icon: const SizedBox( 95 | height: 18, 96 | width: 18, 97 | child: Spinner(), 98 | ), 99 | color: accent.primary, 100 | onPressed: () { 101 | search(); 102 | FocusManager.instance.primaryFocus?.unfocus(); 103 | }, 104 | ); 105 | } else { 106 | return IconButton( 107 | icon: Icon( 108 | Icons.search, 109 | color: accent.primary, 110 | ), 111 | color: accent.primary, 112 | onPressed: () { 113 | search(); 114 | FocusManager.instance.primaryFocus?.unfocus(); 115 | }, 116 | ); 117 | } 118 | }, 119 | ), 120 | border: InputBorder.none, 121 | hintText: '${AppLocalizations.of(context)!.search}...', 122 | hintStyle: TextStyle( 123 | color: accent.primary, 124 | ), 125 | contentPadding: const EdgeInsets.only( 126 | left: 18, 127 | right: 20, 128 | top: 14, 129 | bottom: 14, 130 | ), 131 | ), 132 | ), 133 | ), 134 | if (_searchQuery.isEmpty) 135 | ListView.builder( 136 | shrinkWrap: true, 137 | addAutomaticKeepAlives: false, 138 | addRepaintBoundaries: false, 139 | physics: const NeverScrollableScrollPhysics(), 140 | itemCount: searchHistory.length, 141 | itemBuilder: (BuildContext ctxt, int index) { 142 | return Padding( 143 | padding: const EdgeInsets.only(top: 8, bottom: 6), 144 | child: Card( 145 | child: ListTile( 146 | leading: Icon(Icons.search, color: accent.primary), 147 | title: Text( 148 | searchHistory[index], 149 | style: TextStyle(color: accent.primary), 150 | ), 151 | onTap: () async { 152 | _fetchingSongs.value = true; 153 | _searchQuery = searchHistory[index]; 154 | await fetchSongsList(searchHistory[index]); 155 | _fetchingSongs.value = false; 156 | setState(() {}); 157 | }, 158 | ), 159 | ), 160 | ); 161 | }, 162 | ) 163 | else 164 | FutureBuilder( 165 | future: fetchSongsList(_searchQuery), 166 | builder: (context, data) { 167 | return (data as dynamic).data != null 168 | ? ListView.builder( 169 | shrinkWrap: true, 170 | addAutomaticKeepAlives: false, 171 | addRepaintBoundaries: false, 172 | physics: const NeverScrollableScrollPhysics(), 173 | itemCount: (data as dynamic).data.length, 174 | itemBuilder: (BuildContext ctxt, int index) { 175 | return Padding( 176 | padding: const EdgeInsets.only(top: 5, bottom: 5), 177 | child: SongBar( 178 | (data as dynamic).data[index], 179 | false, 180 | ), 181 | ); 182 | }, 183 | ) 184 | : const Spinner(); 185 | }, 186 | ) 187 | ], 188 | ), 189 | ), 190 | ); 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /lib/ui/userLikedSongsPage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 3 | import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; 4 | import 'package:musify/API/musify.dart'; 5 | import 'package:musify/customWidgets/song_bar.dart'; 6 | import 'package:musify/style/appColors.dart'; 7 | import 'package:musify/style/appTheme.dart'; 8 | 9 | class UserLikedSongs extends StatefulWidget { 10 | const UserLikedSongs({super.key}); 11 | 12 | @override 13 | State createState() => _UserLikedSongsState(); 14 | } 15 | 16 | class _UserLikedSongsState extends State { 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | appBar: AppBar( 21 | centerTitle: true, 22 | title: Text( 23 | AppLocalizations.of(context)!.userLikedSongs, 24 | style: TextStyle( 25 | color: accent.primary, 26 | fontSize: 25, 27 | fontWeight: FontWeight.w700, 28 | ), 29 | ), 30 | leading: IconButton( 31 | icon: Icon( 32 | Icons.arrow_back, 33 | color: accent.primary, 34 | ), 35 | onPressed: () => Navigator.pop(context, false), 36 | ), 37 | elevation: 0, 38 | ), 39 | body: SingleChildScrollView( 40 | child: Column( 41 | children: [ 42 | Row( 43 | children: [ 44 | Container( 45 | margin: const EdgeInsets.only(left: 10, right: 10), 46 | height: 200, 47 | width: 200, 48 | child: Card( 49 | child: Container( 50 | width: 200, 51 | height: 200, 52 | decoration: BoxDecoration( 53 | borderRadius: BorderRadius.circular(10), 54 | color: Theme.of(context).backgroundColor.withAlpha(30), 55 | ), 56 | child: Column( 57 | mainAxisAlignment: MainAxisAlignment.center, 58 | children: [ 59 | Icon( 60 | MdiIcons.musicNoteOutline, 61 | size: 30, 62 | color: accent.primary, 63 | ), 64 | Text( 65 | AppLocalizations.of(context)!.userLikedSongs, 66 | style: TextStyle(color: accent.primary), 67 | textAlign: TextAlign.center, 68 | ), 69 | ], 70 | ), 71 | ), 72 | ), 73 | ), 74 | const SizedBox(width: 16), 75 | Expanded( 76 | child: Column( 77 | crossAxisAlignment: CrossAxisAlignment.start, 78 | children: [ 79 | const SizedBox(height: 12), 80 | Text( 81 | AppLocalizations.of(context)!.userLikedSongs, 82 | style: TextStyle( 83 | color: accent.primary, 84 | fontSize: 18, 85 | fontWeight: FontWeight.w600, 86 | ), 87 | ), 88 | const SizedBox(height: 16), 89 | Text( 90 | '${AppLocalizations.of(context)!.yourFavoriteSongsHere}!', 91 | style: TextStyle( 92 | color: accent.primary, 93 | fontSize: 10, 94 | fontWeight: FontWeight.w600, 95 | ), 96 | ), 97 | const Padding( 98 | padding: EdgeInsets.only(top: 5, bottom: 5), 99 | ), 100 | ElevatedButton( 101 | onPressed: () => { 102 | setActivePlaylist(userLikedSongsList), 103 | Navigator.pop(context, false) 104 | }, 105 | style: ButtonStyle( 106 | backgroundColor: 107 | MaterialStateProperty.all(accent.primary), 108 | ), 109 | child: Text( 110 | AppLocalizations.of(context)!.playAll.toUpperCase(), 111 | style: TextStyle( 112 | color: isAccentWhite(), 113 | ), 114 | ), 115 | ), 116 | ], 117 | ), 118 | ) 119 | ], 120 | ), 121 | const Padding(padding: EdgeInsets.only(top: 20)), 122 | ListView.builder( 123 | shrinkWrap: true, 124 | physics: const BouncingScrollPhysics(), 125 | addAutomaticKeepAlives: 126 | false, // may be problem with lazyload if it implemented 127 | addRepaintBoundaries: false, 128 | // Need to display a loading tile if more items are coming 129 | itemCount: userLikedSongsList.length, 130 | itemBuilder: (BuildContext context, int index) { 131 | return Padding( 132 | padding: const EdgeInsets.only(top: 5, bottom: 5), 133 | child: SongBar( 134 | userLikedSongsList[index], 135 | true, 136 | ), 137 | ); 138 | }, 139 | ) 140 | ], 141 | ), 142 | ), 143 | ); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /lib/ui/userPlaylistsPage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 3 | import 'package:musify/API/musify.dart'; 4 | import 'package:musify/customWidgets/spinner.dart'; 5 | import 'package:musify/helper/flutter_toast.dart'; 6 | import 'package:musify/style/appColors.dart'; 7 | import 'package:musify/style/appTheme.dart'; 8 | import 'package:musify/ui/playlistsPage.dart'; 9 | 10 | class UserPlaylistsPage extends StatefulWidget { 11 | const UserPlaylistsPage({super.key}); 12 | 13 | @override 14 | State createState() => _UserPlaylistsPageState(); 15 | } 16 | 17 | class _UserPlaylistsPageState extends State { 18 | @override 19 | Widget build(BuildContext context) { 20 | return Scaffold( 21 | appBar: AppBar( 22 | centerTitle: true, 23 | title: Text( 24 | AppLocalizations.of(context)!.userPlaylists, 25 | style: TextStyle( 26 | color: accent.primary, 27 | fontSize: 25, 28 | fontWeight: FontWeight.w700, 29 | ), 30 | ), 31 | leading: IconButton( 32 | icon: Icon( 33 | Icons.arrow_back, 34 | color: accent.primary, 35 | ), 36 | onPressed: () => Navigator.pop(context, false), 37 | ), 38 | elevation: 0, 39 | ), 40 | floatingActionButton: FloatingActionButton( 41 | onPressed: () { 42 | showDialog( 43 | context: context, 44 | builder: (BuildContext context) { 45 | var id = ''; 46 | return AlertDialog( 47 | backgroundColor: Theme.of(context).splashColor, 48 | content: Stack( 49 | children: [ 50 | TextField( 51 | decoration: InputDecoration( 52 | hintText: 53 | AppLocalizations.of(context)!.youtubePlaylistID, 54 | hintStyle: 55 | TextStyle(color: Theme.of(context).hintColor), 56 | ), 57 | onChanged: (value) { 58 | setState(() { 59 | id = value; 60 | }); 61 | }, 62 | ) 63 | ], 64 | ), 65 | actions: [ 66 | TextButton( 67 | child: Text( 68 | AppLocalizations.of(context)!.add.toUpperCase(), 69 | style: const TextStyle(color: Colors.black), 70 | ), 71 | onPressed: () { 72 | showToast(addUserPlaylist(id, context)); 73 | setState(() { 74 | Navigator.pop(context); 75 | }); 76 | }, 77 | ), 78 | ], 79 | ); 80 | }, 81 | ); 82 | }, 83 | backgroundColor: accent.primary, 84 | child: Icon( 85 | Icons.add, 86 | color: isAccentWhite(), 87 | ), 88 | ), 89 | body: SingleChildScrollView( 90 | child: Column( 91 | children: [ 92 | const Padding(padding: EdgeInsets.only(top: 20)), 93 | FutureBuilder( 94 | future: getUserPlaylists(), 95 | builder: (context, data) { 96 | return (data as dynamic).data != null 97 | ? GridView.builder( 98 | gridDelegate: 99 | const SliverGridDelegateWithMaxCrossAxisExtent( 100 | maxCrossAxisExtent: 200, 101 | crossAxisSpacing: 20, 102 | mainAxisSpacing: 20, 103 | ), 104 | shrinkWrap: true, 105 | physics: const ScrollPhysics(), 106 | itemCount: (data as dynamic).data.length as int, 107 | padding: const EdgeInsets.only( 108 | left: 16, 109 | right: 16, 110 | top: 16, 111 | bottom: 20, 112 | ), 113 | itemBuilder: (BuildContext context, index) { 114 | return Center( 115 | child: GestureDetector( 116 | onLongPress: () { 117 | removeUserPlaylist( 118 | (data as dynamic) 119 | .data[index]['ytid'] 120 | .toString(), 121 | ); 122 | setState(() {}); 123 | }, 124 | child: GetPlaylist( 125 | index: index, 126 | image: (data as dynamic).data[index]['image'], 127 | title: (data as dynamic) 128 | .data[index]['title'] 129 | .toString(), 130 | id: (data as dynamic).data[index]['ytid'], 131 | ), 132 | ), 133 | ); 134 | }, 135 | ) 136 | : const Spinner(); 137 | }, 138 | ), 139 | ], 140 | ), 141 | ), 142 | ); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: musify 2 | description: Music Streaming and Downloading app made in Flutter! 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 3.6.0+35 19 | 20 | environment: 21 | sdk: '>=2.18.2 <3.0.0' 22 | flutter: ^3.3.7 23 | 24 | # Dependencies specify other packages that your package needs in order to work. 25 | # To automatically upgrade your package dependencies to the latest versions 26 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 27 | # dependencies can be manually updated by changing the version numbers below to 28 | # the latest version available on pub.dev. To see which dependencies have newer 29 | # versions available, run `flutter pub outdated`. 30 | dependencies: 31 | audio_service: ^0.18.7 32 | audio_session: ^0.1.10 33 | cached_network_image: ^3.2.2 34 | dynamic_color: ^1.5.4 35 | flutter: 36 | sdk: flutter 37 | flutter_downloader: ^1.9.0 38 | flutter_localizations: 39 | sdk: flutter 40 | fluttertoast: ^8.0.9 41 | get_it: ^7.2.0 42 | hive: ^2.2.3 43 | hive_flutter: ^1.1.0 44 | http: ^0.13.5 45 | intl: ^0.17.0 46 | just_audio: ^0.9.30 47 | material_design_icons_flutter: ^5.0.6996 48 | on_audio_query: ^2.6.1 49 | package_info_plus: ^3.0.1 50 | path_provider: ^2.0.11 51 | permission_handler: ^10.2.0 52 | url_launcher: ^6.1.6 53 | youtube_explode_dart: ^1.12.1 54 | 55 | dev_dependencies: 56 | flutter_launcher_icons: ^0.10.0 57 | flutter_native_splash: ^2.2.13 58 | flutter_test: 59 | sdk: flutter 60 | 61 | # The "flutter_lints" package below contains a set of recommended lints to 62 | # encourage good coding practices. The lint set provided by the package is 63 | # activated in the `analysis_options.yaml` file located at the root of your 64 | # package. See that file for information about deactivating specific lint 65 | # rules and activating additional ones. 66 | # flutter_lints: ^2.0.0 67 | 68 | # For information on the generic Dart part of this file, see the 69 | # following page: https://dart.dev/tools/pub/pubspec 70 | 71 | # The following section is specific to Flutter packages. 72 | flutter: 73 | 74 | # The following line ensures that the Material Icons font is 75 | # included with your application, so that you can use the icons in 76 | # the material Icons class. 77 | generate: true 78 | uses-material-design: true 79 | fonts: 80 | - family: Ubuntu 81 | fonts: 82 | - asset: fonts/ubuntu.ttf 83 | 84 | # To add assets to your application, add an assets section, like this: 85 | assets: 86 | - assets/db/playlists.db.json 87 | 88 | 89 | flutter_native_splash: 90 | color: "#ffffff" 91 | image: assets/images/splash.png 92 | color_dark: "#151515" 93 | image_dark: assets/images/splash.png 94 | 95 | android_12: 96 | image: assets/images/splash.png 97 | icon_background_color: "#ffffff" 98 | image_dark: assets/images/splash.png 99 | icon_background_color_dark: "#151515" 100 | 101 | 102 | flutter_icons: 103 | android: "launcher_icon" 104 | adaptive_icon_background: "#191919" 105 | adaptive_icon_foreground: "assets/images/ic_launcher_foreground.png" 106 | adaptive_icon_round: "assets/images/ic_launcher_round.png" 107 | image_path: "assets/images/ic_launcher.png" 108 | ios: true 109 | 110 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility in the flutter_test package. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:musify/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(const MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | --------------------------------------------------------------------------------