diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 00000000..e297939f
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,67 @@
+name: Analyze, build and distribute Android application
+
+on:
+ push:
+ branches:
+ - master
+
+env:
+ flutter_version: "1.22.5"
+
+jobs:
+ buildApk:
+ name: Analyze, build and distribute Android application
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out
+ uses: actions/checkout@v1
+ - name: Set up Java
+ uses: actions/setup-java@v1
+ with:
+ java-version: 1.8
+ - name: Cache Flutter dependencies
+ uses: actions/cache@v1
+ with:
+ path: /opt/hostedtoolcache/flutter
+ key: ${{ runner.OS }}-flutter-install-cache-${{ env.flutter_version }}
+ - name: Set up flutter
+ uses: subosito/flutter-action@v1
+ with:
+ flutter-version: ${{ env.flutter_version }}
+ - name: Get dependencies
+ run: flutter pub get
+ - name: Format source code
+ run: find -name "*.dart" -not -path "./lib/generated/*" -exec flutter format --set-exit-if-changed {} \;
+ - name: Analyze source code
+ run: flutter analyze .
+ - name: Add config files
+ env:
+ KEYSTORE_PROPERTIES: ${{ secrets.KEYSTORE_PROPERTIES }}
+ GOOGLE_SERVICES_STAGE_JSON: ${{ secrets.GOOGLE_SERVICES_STAGE_JSON }}
+ GOOGLE_SERVICES_PROD_JSON: ${{ secrets.GOOGLE_SERVICES_PROD_JSON }}
+ DEBUG_JKS: ${{ secrets.DEBUG_JKS }}
+ RELEASE_JKS: ${{ secrets.RELEASE_JKS }}
+ run: |
+ sudo echo "$KEYSTORE_PROPERTIES" > android/keystore.properties
+ sudo echo $GOOGLE_SERVICES_STAGE_JSON > android/app/src/stage/google-services.json
+ sudo echo $GOOGLE_SERVICES_PROD_JSON > android/app/src/prod/google-services.json
+ sudo echo "$DEBUG_JKS" | base64 -d > android/debug.jks
+ sudo echo "$RELEASE_JKS" | base64 -d > android/release.jks
+ - name: Build stage application
+ run: flutter build apk --flavor stage
+ - name: Build production application
+ run: flutter build apk --flavor prod
+ - name: Distribute stage via Firebase
+ uses: wzieba/Firebase-Distribution-Github-Action@v1
+ with:
+ appId: ${{ secrets.STAGE_APP_ID }}
+ token: ${{ secrets.FIREBASE_TOKEN }}
+ groups: testers
+ file: build/app/outputs/flutter-apk/app-stage-release.apk
+ - name: Distribute production via Firebase
+ uses: wzieba/Firebase-Distribution-Github-Action@v1
+ with:
+ appId: ${{ secrets.PROD_APP_ID }}
+ token: ${{ secrets.FIREBASE_TOKEN }}
+ groups: testers
+ file: build/app/outputs/flutter-apk/app-prod-release.apk
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..71ea3119
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,43 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Web related
+lib/generated_plugin_registrant.dart
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+/fastlane/service_account.json
diff --git a/.metadata b/.metadata
new file mode 100644
index 00000000..911b6b72
--- /dev/null
+++ b/.metadata
@@ -0,0 +1,10 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+ revision: 81a45ec2e5f80fa71d5135f1702ce540558b416d
+ channel: beta
+
+project_type: app
diff --git a/.run/Dev.run.xml b/.run/Dev.run.xml
new file mode 100644
index 00000000..8e84394c
--- /dev/null
+++ b/.run/Dev.run.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/song_checker.dart.run.xml b/.run/song_checker.dart.run.xml
new file mode 100644
index 00000000..0071c77f
--- /dev/null
+++ b/.run/song_checker.dart.run.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/users_mitigator.dart.run.xml b/.run/users_mitigator.dart.run.xml
new file mode 100644
index 00000000..c2e76b85
--- /dev/null
+++ b/.run/users_mitigator.dart.run.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..f288702d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..ca9608b8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,90 @@
+# Hit Notes - Play instruments
+
+![Analyze, build and distribute Android application](https://github.com/cuong0993/HitNotes/workflows/Analyze,%20build%20and%20distribute%20Android%20application/badge.svg)
+
+Rhythm-based mobile game. Even you don't have any basic knowledge of music instruments, you still can play with falling notes in the game! ✨
+
+Currently available on the Play store.
+
+
+
+# Main Features
+
+- Hundreds of songs, from classics of Beethoven, Chopin, Mozart, or Schubert, folk songs to POP/EDM. Will be updated continuously
+- Adjustable difficulty level
+- Adjustable tempo
+- Many game modes
+- Many musical instruments
+- Play offline without the internet
+- Upload your song and play your way
+
+![screenshots](./fastlane/metadata/en-US/images/phoneScreenshots/1_en-US.png)
+![screenshots](./fastlane/metadata/en-US/images/phoneScreenshots/2_en-US.png)
+![screenshots](./fastlane/metadata/en-US/images/phoneScreenshots/3_en-US.png)
+
+# Project Structure
+
+This is a Flutter mobile game targeting Android and iOS.
+
+The code for the Flutter app is contained in the folder `lib` and the
+different native apps are in `android` and `ios`.
+
+Extra project assets are in `assets`.
+
+Firebase config files and cloud functions are in the `firebase` folder.
+
+Google Play store listing files are in the `fastlane` folder.
+
+# Prerequisites & Getting Started
+
+## Client
+
+To build and run the mobile apps you’ll need to install [Flutter](https://flutter.dev) and its dependencies. To verify your installation run in the project’s root directory:****
+
+```
+$ flutter doctor
+```
+
+## Backend (Firebase)
+
+The backend is build using Firebase’s `node.js` SDK. All files are provided in the `firebase` folder. To deploy the code create an app and install the firebase CLI (See steps 1 & 2 in [Getting started](https://firebase.google.com/docs/functions/get-started)).
+
+### B1. Setup sign-in method
+An initial sign-in method needs to be configured.
+
+- Select your project in [console.firebase.google.com](https://console.firebase.google.com).
+- Navigate to `Authentication`
+- Select `Sign-in methods` and activate `Google`, `Facebook` and `Anonymous`.
+
+### B2. Configure firebase app
+
+Next, you’ll need to configure your firebase app for Flutter as described in [Add Firebase to an App / Flutter](https://firebase.google.com/docs/flutter/setup)
+
+**Android**
+
+Follow the instructions in `android/README.md`.
+
+### B3. Deploy firebase functions
+
+Navigate to the `firebase` directory and deploy all functions and configuration using:
+
+```
+$ firebase deploy --project={projectId}
+```
+
+### B4. Synchronize storage
+
+Install Google Cloud SDK, navigate to the `storage` directory and synchronize storage files using:
+
+```
+$ gsutil -m rsync -r -d ./ gs://{projectId}.appspot.com
+```
+
+### B4. Synchronize database
+
+Install https://github.com/jloosli/node-firestore-import-export, get service account json file https://firebase.google.com/docs/admin/setup#initialize-sdk, navigate to the `database` directory, backup/restore database using:
+
+```
+$ export GOOGLE_APPLICATION_CREDENTIALS="service-account.json"; ./backup-firestore.sh
+$ export GOOGLE_APPLICATION_CREDENTIALS="service-account.json"; ./restore-firestore.sh
+```
diff --git a/analysis_options.yaml b/analysis_options.yaml
new file mode 100644
index 00000000..435dbb1b
--- /dev/null
+++ b/analysis_options.yaml
@@ -0,0 +1,97 @@
+# Pedantic package: The Dart analyzer settings and best practices used internally at Google.
+include: package:pedantic/analysis_options.yaml
+
+# Analysis from Flutter stable channel
+# https://github.com/flutter/flutter/blob/stable/packages/flutter/lib/analysis_options_user.yaml
+
+# Specify analysis options.
+#
+# Until there are meta linter rules, each desired lint must be explicitly enabled.
+# See: https://github.com/dart-lang/linter/issues/288
+#
+# For a list of lints, see: http://dart-lang.github.io/linter/lints/
+# See the configuration guide for more
+# https://github.com/dart-lang/sdk/tree/master/pkg/analyzer#configuring-the-analyzer
+#
+# There are four similar analysis options files in the flutter repos:
+# - analysis_options.yaml
+# - packages/flutter/lib/analysis_options_user.yaml (this file)
+# - https://github.com/flutter/plugins/blob/master/analysis_options.yaml
+# - https://github.com/flutter/engine/blob/master/analysis_options.yaml
+#
+# This file contains the analysis options used by "flutter analyze" and the
+# dartanalyzer when analyzing code outside the flutter repository. It isn't named
+# 'analysis_options.yaml' because otherwise editors would use it when analyzing
+# the flutter tool itself.
+#
+# When editing, make sure you keep this and /analysis_options.yaml consistent.
+
+analyzer:
+ errors:
+ # treat missing required parameters as a warning (not a hint)
+ missing_required_param: error
+ omit_local_variable_types: error
+ include_file_not_found: error
+ unused_element: error
+ unused_field: error
+ unused_local_variable: error
+ exclude: # excluded generated files
+ - lib/songs/song.dart
+ - lib/game/tile/tile_chunk.dart
+ - lib/game/note/note.dart
+ - lib/generated/**
+
+linter:
+ rules:
+ # these rules are documented on and in the same order as
+ # the Dart Lint rules page to make maintenance easier
+ # https://github.com/dart-lang/linter/blob/master/example/all.yaml
+ - always_declare_return_types
+ # - always_specify_types
+ - annotate_overrides
+ # - avoid_as
+ - avoid_web_libraries_in_flutter
+ - await_only_futures
+ - camel_case_types
+ - cancel_subscriptions
+ - close_sinks
+ # - comment_references # we do not presume as to what people want to reference in their dartdocs
+ # - constant_identifier_names # https://github.com/dart-lang/linter/issues/204
+ - control_flow_in_finally
+ - empty_statements
+ - hash_and_equals
+ - implementation_imports
+ - invariant_booleans
+ - iterable_contains_unrelated_type
+ - library_prefixes
+ - list_remove_unrelated_type
+ - literal_only_boolean_expressions
+ - non_constant_identifier_names
+ # - one_member_abstracts
+ - only_throw_errors
+ - overridden_fields
+ - package_api_docs
+ - package_names
+ - package_prefixed_library_names
+ - prefer_mixin # https://github.com/dart-lang/language/issues/32
+ # - public_member_api_docs
+ # - sort_constructors_first
+ - sort_unnamed_constructors_first
+ - test_types_in_equals
+ - throw_in_finally
+ - type_annotate_public_apis # subset of always_specify_types
+ - unawaited_futures
+ - unnecessary_brace_in_string_interps
+ - unnecessary_getters_setters
+ - unnecessary_statements
+
+ # The below linters are added manually
+ - directives_ordering
+ - file_names
+ # - lines_longer_than_80_chars
+ #- public_member_api_docs
+ - prefer_final_in_for_each
+ # - sort_constructors_first
+ # - avoid_as
+ - prefer_final_locals
+ - prefer_relative_imports
diff --git a/android/.gitignore b/android/.gitignore
new file mode 100644
index 00000000..9cf0b64f
--- /dev/null
+++ b/android/.gitignore
@@ -0,0 +1,11 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+keystore.properties
+debug.jks
+release.jks
+google-services.json
\ No newline at end of file
diff --git a/android/README.md b/android/README.md
new file mode 100644
index 00000000..7a07d7d6
--- /dev/null
+++ b/android/README.md
@@ -0,0 +1,35 @@
+# Android client
+
+## Setting up Firebase
+
+Create a project on the Firebase console [here](https://console.firebase.google.com/)
+
+1. To add Firebase to your app, click on the android icon or click the gear icon to go to project
+settings to find the android icon.
+
+2. Register your application by filing up the form with the package name (applicationId)
+and the app nickname if you like.
+> Find Your package name which is generally the applicationId in your app-level build.gradle file
+
+3. Download the `google-service.json` file that is generated for you. Find it and move it inside
+the folder `android/app/` of the project. The firebase sdk is already added to the project.
+
+4. On the fourth step of registration, run the app to verify the configuration via the Firebase
+console.
+
+## Distribution
+
+To build this application for distribution,
+provide files `debug.jks` and `release.jks` containing the signing keys,
+and the `key.properties` with the following content:
+
+```
+debugStorePassword=.....
+debugKeyPassword=.....
+debugKeyAlias=.....
+debugStoreFile=../debug.jks
+releaseStorePassword=.....
+releaseKeyPassword=.....
+releaseKeyAlias=.....
+releaseStoreFile=../release.jks
+```
diff --git a/android/app/build.gradle b/android/app/build.gradle
new file mode 100644
index 00000000..189e3806
--- /dev/null
+++ b/android/app/build.gradle
@@ -0,0 +1,98 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withReader('UTF-8') { reader ->
+ localProperties.load(reader)
+ }
+}
+
+def keystoreProperties = new Properties()
+def keystorePropertiesFile = rootProject.file('keystore.properties')
+if (keystorePropertiesFile.exists()) {
+ keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+ throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+apply plugin: 'com.google.gms.google-services'
+apply plugin: 'com.google.firebase.crashlytics'
+android {
+ compileSdkVersion 30
+ ndkVersion "22.0.7026061"
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ lintOptions {
+ disable 'InvalidPackage'
+ }
+
+ defaultConfig {
+ applicationId "com.chaomao.hittick"
+ minSdkVersion 16
+ targetSdkVersion 30
+ multiDexEnabled true
+ versionCode 22
+ versionName "1.2.3"
+ }
+
+ signingConfigs {
+ debug {
+ keyAlias keystoreProperties['debugKeyAlias']
+ keyPassword keystoreProperties['debugKeyPassword']
+ storeFile keystoreProperties['debugStoreFile'] ? file(keystoreProperties['debugStoreFile']) : null
+ storePassword keystoreProperties['debugStorePassword']
+ }
+ release {
+ keyAlias keystoreProperties['releaseKeyAlias']
+ keyPassword keystoreProperties['releaseKeyPassword']
+ storeFile keystoreProperties['releaseStoreFile'] ? file(keystoreProperties['releaseStoreFile']) : null
+ storePassword keystoreProperties['releaseStorePassword']
+ }
+ }
+
+ buildTypes {
+ debug {
+ signingConfig signingConfigs.debug
+ }
+ release {
+ signingConfig signingConfigs.release
+ }
+ }
+
+ variantFilter { variant ->
+ def names = variant.flavors*.name
+ if (variant.buildType.name == 'release' && names.contains("dev")) {
+ setIgnore(true)
+ }
+ }
+
+ flavorDimensions "default"
+ productFlavors {
+ dev {
+ }
+
+ stage {
+ }
+
+ prod {
+ }
+ }
+}
+
+flutter {
+ source '../..'
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation 'com.android.support:multidex:1.0.3'
+ implementation platform('com.google.firebase:firebase-bom:26.1.0')
+}
diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 00000000..29f9e597
--- /dev/null
+++ b/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,5 @@
+
+
+
+
diff --git a/android/app/src/debug/res/values/strings_no_translate.xml b/android/app/src/debug/res/values/strings_no_translate.xml
new file mode 100644
index 00000000..f709eefc
--- /dev/null
+++ b/android/app/src/debug/res/values/strings_no_translate.xml
@@ -0,0 +1,4 @@
+
+
+ Hit Notes Dev
+
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..8ecea145
--- /dev/null
+++ b/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/kotlin/com/chaomao/hitnotes/MainActivity.kt b/android/app/src/main/kotlin/com/chaomao/hitnotes/MainActivity.kt
new file mode 100644
index 00000000..e722e1d1
--- /dev/null
+++ b/android/app/src/main/kotlin/com/chaomao/hitnotes/MainActivity.kt
@@ -0,0 +1,5 @@
+package com.chaomao.hitnotes
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity : FlutterActivity()
diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 00000000..929a0a5e
--- /dev/null
+++ b/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,9 @@
+
+
+
+ -
+
+
+
diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 00000000..8ae0e296
--- /dev/null
+++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 00000000..8ae0e296
--- /dev/null
+++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..00ed4098
Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png
new file mode 100644
index 00000000..0a7f3378
Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png differ
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 00000000..0f4d84d9
Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 00000000..e655c7df
Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/android/app/src/main/res/mipmap-hdpi/img_chaomao.png b/android/app/src/main/res/mipmap-hdpi/img_chaomao.png
new file mode 100644
index 00000000..f31b4ac2
Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/img_chaomao.png differ
diff --git a/android/app/src/main/res/mipmap-ldpi/ic_launcher.png b/android/app/src/main/res/mipmap-ldpi/ic_launcher.png
new file mode 100644
index 00000000..a065a5df
Binary files /dev/null and b/android/app/src/main/res/mipmap-ldpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-ldpi/ic_launcher_background.png b/android/app/src/main/res/mipmap-ldpi/ic_launcher_background.png
new file mode 100644
index 00000000..569be1b8
Binary files /dev/null and b/android/app/src/main/res/mipmap-ldpi/ic_launcher_background.png differ
diff --git a/android/app/src/main/res/mipmap-ldpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-ldpi/ic_launcher_foreground.png
new file mode 100644
index 00000000..f8d1e6b9
Binary files /dev/null and b/android/app/src/main/res/mipmap-ldpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/main/res/mipmap-ldpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-ldpi/ic_launcher_round.png
new file mode 100644
index 00000000..713872cb
Binary files /dev/null and b/android/app/src/main/res/mipmap-ldpi/ic_launcher_round.png differ
diff --git a/android/app/src/main/res/mipmap-ldpi/img_chaomao.png b/android/app/src/main/res/mipmap-ldpi/img_chaomao.png
new file mode 100644
index 00000000..a19d0cae
Binary files /dev/null and b/android/app/src/main/res/mipmap-ldpi/img_chaomao.png differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..27728d54
Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png
new file mode 100644
index 00000000..ca2c558f
Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 00000000..657d8fdc
Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 00000000..581a3bf8
Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/android/app/src/main/res/mipmap-mdpi/img_chaomao.png b/android/app/src/main/res/mipmap-mdpi/img_chaomao.png
new file mode 100644
index 00000000..2fe3a572
Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/img_chaomao.png differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..543c467d
Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png
new file mode 100644
index 00000000..44361d40
Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 00000000..a537559a
Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..e03eb158
Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/img_chaomao.png b/android/app/src/main/res/mipmap-xhdpi/img_chaomao.png
new file mode 100644
index 00000000..7c4b2161
Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/img_chaomao.png differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..b8864f32
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png
new file mode 100644
index 00000000..6719cf6b
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 00000000..1a83e6ae
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..fe4cec35
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/img_chaomao.png b/android/app/src/main/res/mipmap-xxhdpi/img_chaomao.png
new file mode 100644
index 00000000..ce4cbbec
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/img_chaomao.png differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000..068fca23
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png
new file mode 100644
index 00000000..c27695e3
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 00000000..d585086e
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..2cb7c961
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/img_chaomao.png b/android/app/src/main/res/mipmap-xxxhdpi/img_chaomao.png
new file mode 100644
index 00000000..50c426ef
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/img_chaomao.png differ
diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml
new file mode 100644
index 00000000..d8862059
--- /dev/null
+++ b/android/app/src/main/res/values/colors.xml
@@ -0,0 +1,4 @@
+
+
+ #000000
+
diff --git a/android/app/src/main/res/values/strings_no_translate.xml b/android/app/src/main/res/values/strings_no_translate.xml
new file mode 100644
index 00000000..80125b10
--- /dev/null
+++ b/android/app/src/main/res/values/strings_no_translate.xml
@@ -0,0 +1,4 @@
+
+
+ 134284937214878
+
diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml
new file mode 100644
index 00000000..44b07882
--- /dev/null
+++ b/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/android/app/src/prod/res/values/strings_no_translate.xml b/android/app/src/prod/res/values/strings_no_translate.xml
new file mode 100644
index 00000000..35679e33
--- /dev/null
+++ b/android/app/src/prod/res/values/strings_no_translate.xml
@@ -0,0 +1,4 @@
+
+
+ Hit Notes
+
diff --git a/android/app/src/stage/res/values/strings_no_translate.xml b/android/app/src/stage/res/values/strings_no_translate.xml
new file mode 100644
index 00000000..35d145b4
--- /dev/null
+++ b/android/app/src/stage/res/values/strings_no_translate.xml
@@ -0,0 +1,4 @@
+
+
+ Hit Notes Stage
+
diff --git a/android/build.gradle b/android/build.gradle
new file mode 100644
index 00000000..17211732
--- /dev/null
+++ b/android/build.gradle
@@ -0,0 +1,33 @@
+buildscript {
+ ext.kotlin_version = '1.4.21'
+ repositories {
+ google()
+ jcenter()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.6.4'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath 'com.google.gms:google-services:4.3.4'
+ classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1'
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+}
+subprojects {
+ project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/android/gradle.properties b/android/gradle.properties
new file mode 100644
index 00000000..a6738207
--- /dev/null
+++ b/android/gradle.properties
@@ -0,0 +1,4 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
+android.enableR8=true
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..bc24dcf0
--- /dev/null
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 23 08:50:38 CEST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
diff --git a/android/settings.gradle b/android/settings.gradle
new file mode 100644
index 00000000..44e62bcf
--- /dev/null
+++ b/android/settings.gradle
@@ -0,0 +1,11 @@
+include ':app'
+
+def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
+def properties = new Properties()
+
+assert localPropertiesFile.exists()
+localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+
+def flutterSdkPath = properties.getProperty("flutter.sdk")
+assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
diff --git a/assets/images/1.5x/img_app_icon.png b/assets/images/1.5x/img_app_icon.png
new file mode 100644
index 00000000..585acb6a
Binary files /dev/null and b/assets/images/1.5x/img_app_icon.png differ
diff --git a/assets/images/1.5x/img_clef.png b/assets/images/1.5x/img_clef.png
new file mode 100644
index 00000000..610b0ac3
Binary files /dev/null and b/assets/images/1.5x/img_clef.png differ
diff --git a/assets/images/1.5x/img_clock.png b/assets/images/1.5x/img_clock.png
new file mode 100644
index 00000000..dba38ef9
Binary files /dev/null and b/assets/images/1.5x/img_clock.png differ
diff --git a/assets/images/1.5x/img_facebook.png b/assets/images/1.5x/img_facebook.png
new file mode 100644
index 00000000..6674d57b
Binary files /dev/null and b/assets/images/1.5x/img_facebook.png differ
diff --git a/assets/images/1.5x/img_google.png b/assets/images/1.5x/img_google.png
new file mode 100644
index 00000000..e2de7bf2
Binary files /dev/null and b/assets/images/1.5x/img_google.png differ
diff --git a/assets/images/1.5x/img_guitar.png b/assets/images/1.5x/img_guitar.png
new file mode 100644
index 00000000..b8c7751c
Binary files /dev/null and b/assets/images/1.5x/img_guitar.png differ
diff --git a/assets/images/1.5x/img_note.png b/assets/images/1.5x/img_note.png
new file mode 100644
index 00000000..9c0abe0a
Binary files /dev/null and b/assets/images/1.5x/img_note.png differ
diff --git a/assets/images/1.5x/img_single_note.png b/assets/images/1.5x/img_single_note.png
new file mode 100644
index 00000000..7577c712
Binary files /dev/null and b/assets/images/1.5x/img_single_note.png differ
diff --git a/assets/images/1.5x/img_staff.png b/assets/images/1.5x/img_staff.png
new file mode 100644
index 00000000..fb80e297
Binary files /dev/null and b/assets/images/1.5x/img_staff.png differ
diff --git a/assets/images/1.5x/img_star.png b/assets/images/1.5x/img_star.png
new file mode 100644
index 00000000..154e90ce
Binary files /dev/null and b/assets/images/1.5x/img_star.png differ
diff --git a/assets/images/1.5x/img_star_rate.png b/assets/images/1.5x/img_star_rate.png
new file mode 100644
index 00000000..9aacb8d8
Binary files /dev/null and b/assets/images/1.5x/img_star_rate.png differ
diff --git a/assets/images/1.5x/img_star_rate_disable.png b/assets/images/1.5x/img_star_rate_disable.png
new file mode 100644
index 00000000..f396262f
Binary files /dev/null and b/assets/images/1.5x/img_star_rate_disable.png differ
diff --git a/assets/images/1.5x/img_touch.png b/assets/images/1.5x/img_touch.png
new file mode 100644
index 00000000..dc443685
Binary files /dev/null and b/assets/images/1.5x/img_touch.png differ
diff --git a/assets/images/2.0x/img_app_icon.png b/assets/images/2.0x/img_app_icon.png
new file mode 100644
index 00000000..9dbe05c8
Binary files /dev/null and b/assets/images/2.0x/img_app_icon.png differ
diff --git a/assets/images/2.0x/img_clef.png b/assets/images/2.0x/img_clef.png
new file mode 100644
index 00000000..b897b7c2
Binary files /dev/null and b/assets/images/2.0x/img_clef.png differ
diff --git a/assets/images/2.0x/img_clock.png b/assets/images/2.0x/img_clock.png
new file mode 100644
index 00000000..32df4cb8
Binary files /dev/null and b/assets/images/2.0x/img_clock.png differ
diff --git a/assets/images/2.0x/img_facebook.png b/assets/images/2.0x/img_facebook.png
new file mode 100644
index 00000000..1ac26299
Binary files /dev/null and b/assets/images/2.0x/img_facebook.png differ
diff --git a/assets/images/2.0x/img_google.png b/assets/images/2.0x/img_google.png
new file mode 100644
index 00000000..e29e570c
Binary files /dev/null and b/assets/images/2.0x/img_google.png differ
diff --git a/assets/images/2.0x/img_guitar.png b/assets/images/2.0x/img_guitar.png
new file mode 100644
index 00000000..54f43251
Binary files /dev/null and b/assets/images/2.0x/img_guitar.png differ
diff --git a/assets/images/2.0x/img_note.png b/assets/images/2.0x/img_note.png
new file mode 100644
index 00000000..6905a902
Binary files /dev/null and b/assets/images/2.0x/img_note.png differ
diff --git a/assets/images/2.0x/img_single_note.png b/assets/images/2.0x/img_single_note.png
new file mode 100644
index 00000000..c5ba1cd1
Binary files /dev/null and b/assets/images/2.0x/img_single_note.png differ
diff --git a/assets/images/2.0x/img_staff.png b/assets/images/2.0x/img_staff.png
new file mode 100644
index 00000000..2d142585
Binary files /dev/null and b/assets/images/2.0x/img_staff.png differ
diff --git a/assets/images/2.0x/img_star.png b/assets/images/2.0x/img_star.png
new file mode 100644
index 00000000..b6e816b9
Binary files /dev/null and b/assets/images/2.0x/img_star.png differ
diff --git a/assets/images/2.0x/img_star_rate.png b/assets/images/2.0x/img_star_rate.png
new file mode 100644
index 00000000..a4b8a478
Binary files /dev/null and b/assets/images/2.0x/img_star_rate.png differ
diff --git a/assets/images/2.0x/img_star_rate_disable.png b/assets/images/2.0x/img_star_rate_disable.png
new file mode 100644
index 00000000..27f3c62d
Binary files /dev/null and b/assets/images/2.0x/img_star_rate_disable.png differ
diff --git a/assets/images/2.0x/img_touch.png b/assets/images/2.0x/img_touch.png
new file mode 100644
index 00000000..6ede133d
Binary files /dev/null and b/assets/images/2.0x/img_touch.png differ
diff --git a/assets/images/3.0x/img_app_icon.png b/assets/images/3.0x/img_app_icon.png
new file mode 100644
index 00000000..5a5ebf85
Binary files /dev/null and b/assets/images/3.0x/img_app_icon.png differ
diff --git a/assets/images/3.0x/img_clef.png b/assets/images/3.0x/img_clef.png
new file mode 100644
index 00000000..a6474138
Binary files /dev/null and b/assets/images/3.0x/img_clef.png differ
diff --git a/assets/images/3.0x/img_clock.png b/assets/images/3.0x/img_clock.png
new file mode 100644
index 00000000..9b620947
Binary files /dev/null and b/assets/images/3.0x/img_clock.png differ
diff --git a/assets/images/3.0x/img_facebook.png b/assets/images/3.0x/img_facebook.png
new file mode 100644
index 00000000..b9a7b5b0
Binary files /dev/null and b/assets/images/3.0x/img_facebook.png differ
diff --git a/assets/images/3.0x/img_google.png b/assets/images/3.0x/img_google.png
new file mode 100644
index 00000000..161ef4c3
Binary files /dev/null and b/assets/images/3.0x/img_google.png differ
diff --git a/assets/images/3.0x/img_guitar.png b/assets/images/3.0x/img_guitar.png
new file mode 100644
index 00000000..62a4900e
Binary files /dev/null and b/assets/images/3.0x/img_guitar.png differ
diff --git a/assets/images/3.0x/img_note.png b/assets/images/3.0x/img_note.png
new file mode 100644
index 00000000..5ce13047
Binary files /dev/null and b/assets/images/3.0x/img_note.png differ
diff --git a/assets/images/3.0x/img_single_note.png b/assets/images/3.0x/img_single_note.png
new file mode 100644
index 00000000..45b5ecf3
Binary files /dev/null and b/assets/images/3.0x/img_single_note.png differ
diff --git a/assets/images/3.0x/img_staff.png b/assets/images/3.0x/img_staff.png
new file mode 100644
index 00000000..0e27a160
Binary files /dev/null and b/assets/images/3.0x/img_staff.png differ
diff --git a/assets/images/3.0x/img_star.png b/assets/images/3.0x/img_star.png
new file mode 100644
index 00000000..275189b1
Binary files /dev/null and b/assets/images/3.0x/img_star.png differ
diff --git a/assets/images/3.0x/img_star_rate.png b/assets/images/3.0x/img_star_rate.png
new file mode 100644
index 00000000..81d763a9
Binary files /dev/null and b/assets/images/3.0x/img_star_rate.png differ
diff --git a/assets/images/3.0x/img_star_rate_disable.png b/assets/images/3.0x/img_star_rate_disable.png
new file mode 100644
index 00000000..eda6250d
Binary files /dev/null and b/assets/images/3.0x/img_star_rate_disable.png differ
diff --git a/assets/images/3.0x/img_touch.png b/assets/images/3.0x/img_touch.png
new file mode 100644
index 00000000..38812507
Binary files /dev/null and b/assets/images/3.0x/img_touch.png differ
diff --git a/assets/images/4.0x/img_app_icon.png b/assets/images/4.0x/img_app_icon.png
new file mode 100644
index 00000000..2c3a3a87
Binary files /dev/null and b/assets/images/4.0x/img_app_icon.png differ
diff --git a/assets/images/4.0x/img_clef.png b/assets/images/4.0x/img_clef.png
new file mode 100644
index 00000000..31107b6b
Binary files /dev/null and b/assets/images/4.0x/img_clef.png differ
diff --git a/assets/images/4.0x/img_clock.png b/assets/images/4.0x/img_clock.png
new file mode 100644
index 00000000..505f963e
Binary files /dev/null and b/assets/images/4.0x/img_clock.png differ
diff --git a/assets/images/4.0x/img_facebook.png b/assets/images/4.0x/img_facebook.png
new file mode 100644
index 00000000..0692ea1a
Binary files /dev/null and b/assets/images/4.0x/img_facebook.png differ
diff --git a/assets/images/4.0x/img_google.png b/assets/images/4.0x/img_google.png
new file mode 100644
index 00000000..50c23f6a
Binary files /dev/null and b/assets/images/4.0x/img_google.png differ
diff --git a/assets/images/4.0x/img_guitar.png b/assets/images/4.0x/img_guitar.png
new file mode 100644
index 00000000..72720bf5
Binary files /dev/null and b/assets/images/4.0x/img_guitar.png differ
diff --git a/assets/images/4.0x/img_note.png b/assets/images/4.0x/img_note.png
new file mode 100644
index 00000000..2405b93f
Binary files /dev/null and b/assets/images/4.0x/img_note.png differ
diff --git a/assets/images/4.0x/img_single_note.png b/assets/images/4.0x/img_single_note.png
new file mode 100644
index 00000000..0379af97
Binary files /dev/null and b/assets/images/4.0x/img_single_note.png differ
diff --git a/assets/images/4.0x/img_staff.png b/assets/images/4.0x/img_staff.png
new file mode 100644
index 00000000..03c18512
Binary files /dev/null and b/assets/images/4.0x/img_staff.png differ
diff --git a/assets/images/4.0x/img_star.png b/assets/images/4.0x/img_star.png
new file mode 100644
index 00000000..389d6c6a
Binary files /dev/null and b/assets/images/4.0x/img_star.png differ
diff --git a/assets/images/4.0x/img_star_rate.png b/assets/images/4.0x/img_star_rate.png
new file mode 100644
index 00000000..a29d5985
Binary files /dev/null and b/assets/images/4.0x/img_star_rate.png differ
diff --git a/assets/images/4.0x/img_star_rate_disable.png b/assets/images/4.0x/img_star_rate_disable.png
new file mode 100644
index 00000000..4f9809fd
Binary files /dev/null and b/assets/images/4.0x/img_star_rate_disable.png differ
diff --git a/assets/images/4.0x/img_touch.png b/assets/images/4.0x/img_touch.png
new file mode 100644
index 00000000..438a0998
Binary files /dev/null and b/assets/images/4.0x/img_touch.png differ
diff --git a/assets/images/img_app_icon.png b/assets/images/img_app_icon.png
new file mode 100644
index 00000000..fdcb4e46
Binary files /dev/null and b/assets/images/img_app_icon.png differ
diff --git a/assets/images/img_clef.png b/assets/images/img_clef.png
new file mode 100644
index 00000000..be0db997
Binary files /dev/null and b/assets/images/img_clef.png differ
diff --git a/assets/images/img_clock.png b/assets/images/img_clock.png
new file mode 100644
index 00000000..d9e2ac4a
Binary files /dev/null and b/assets/images/img_clock.png differ
diff --git a/assets/images/img_facebook.png b/assets/images/img_facebook.png
new file mode 100644
index 00000000..d4e80b3f
Binary files /dev/null and b/assets/images/img_facebook.png differ
diff --git a/assets/images/img_google.png b/assets/images/img_google.png
new file mode 100644
index 00000000..7d6c5001
Binary files /dev/null and b/assets/images/img_google.png differ
diff --git a/assets/images/img_guitar.png b/assets/images/img_guitar.png
new file mode 100644
index 00000000..6bdb4dc9
Binary files /dev/null and b/assets/images/img_guitar.png differ
diff --git a/assets/images/img_note.png b/assets/images/img_note.png
new file mode 100644
index 00000000..3b307151
Binary files /dev/null and b/assets/images/img_note.png differ
diff --git a/assets/images/img_single_note.png b/assets/images/img_single_note.png
new file mode 100644
index 00000000..be5d19f8
Binary files /dev/null and b/assets/images/img_single_note.png differ
diff --git a/assets/images/img_staff.png b/assets/images/img_staff.png
new file mode 100644
index 00000000..1a96765d
Binary files /dev/null and b/assets/images/img_staff.png differ
diff --git a/assets/images/img_star.png b/assets/images/img_star.png
new file mode 100644
index 00000000..e89131c6
Binary files /dev/null and b/assets/images/img_star.png differ
diff --git a/assets/images/img_star_rate.png b/assets/images/img_star_rate.png
new file mode 100644
index 00000000..96772cd8
Binary files /dev/null and b/assets/images/img_star_rate.png differ
diff --git a/assets/images/img_star_rate_disable.png b/assets/images/img_star_rate_disable.png
new file mode 100644
index 00000000..0f6ef4c7
Binary files /dev/null and b/assets/images/img_star_rate_disable.png differ
diff --git a/assets/images/img_touch.png b/assets/images/img_touch.png
new file mode 100644
index 00000000..881ec78f
Binary files /dev/null and b/assets/images/img_touch.png differ
diff --git a/database/.gitignore b/database/.gitignore
new file mode 100644
index 00000000..305336f1
--- /dev/null
+++ b/database/.gitignore
@@ -0,0 +1,3 @@
+adminsdk-dev.json
+adminsdk-stage.json
+adminsdk-prod.json
\ No newline at end of file
diff --git a/database/backup-firestore.sh b/database/backup-firestore.sh
new file mode 100644
index 00000000..6c616355
--- /dev/null
+++ b/database/backup-firestore.sh
@@ -0,0 +1,5 @@
+# curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
+# sudo apt-get install -y nodejs
+# npm install -g node-firestore-import-export;
+firestore-export --backupFile db.json --prettyPrint;
+./jq-win64.exe --sort-keys . db.json > db1.json; rm db.json; mv db1.json db.json;
\ No newline at end of file
diff --git a/database/db.json b/database/db.json
new file mode 100644
index 00000000..63564fb1
--- /dev/null
+++ b/database/db.json
@@ -0,0 +1,6072 @@
+{
+ "__collections__": {
+ "instruments": {
+ "acoustic_guitar": {
+ "__collections__": {},
+ "id": "acoustic_guitar",
+ "maxNote": 84,
+ "minNote": 31,
+ "soundFiles": {
+ "31": "sounds/acoustic-guitar/31.mp3",
+ "33": "sounds/acoustic-guitar/33.mp3",
+ "36": "sounds/acoustic-guitar/36.mp3",
+ "39": "sounds/acoustic-guitar/39.mp3",
+ "43": "sounds/acoustic-guitar/43.mp3",
+ "45": "sounds/acoustic-guitar/45.mp3",
+ "48": "sounds/acoustic-guitar/48.mp3",
+ "52": "sounds/acoustic-guitar/52.mp3",
+ "54": "sounds/acoustic-guitar/54.mp3",
+ "57": "sounds/acoustic-guitar/57.mp3",
+ "60": "sounds/acoustic-guitar/60.mp3",
+ "63": "sounds/acoustic-guitar/63.mp3",
+ "66": "sounds/acoustic-guitar/66.mp3",
+ "69": "sounds/acoustic-guitar/69.mp3",
+ "72": "sounds/acoustic-guitar/72.mp3",
+ "75": "sounds/acoustic-guitar/75.mp3",
+ "78": "sounds/acoustic-guitar/78.mp3",
+ "81": "sounds/acoustic-guitar/81.mp3",
+ "84": "sounds/acoustic-guitar/84.mp3"
+ },
+ "soundNotes": {
+ "31": {
+ "note": 31,
+ "pitch": 1
+ },
+ "32": {
+ "note": 31,
+ "pitch": 1.0594631
+ },
+ "33": {
+ "note": 33,
+ "pitch": 1
+ },
+ "34": {
+ "note": 33,
+ "pitch": 1.0594631
+ },
+ "35": {
+ "note": 33,
+ "pitch": 1.122462
+ },
+ "36": {
+ "note": 36,
+ "pitch": 1
+ },
+ "37": {
+ "note": 36,
+ "pitch": 1.0594631
+ },
+ "38": {
+ "note": 36,
+ "pitch": 1.122462
+ },
+ "39": {
+ "note": 39,
+ "pitch": 1
+ },
+ "40": {
+ "note": 39,
+ "pitch": 1.0594631
+ },
+ "41": {
+ "note": 39,
+ "pitch": 1.122462
+ },
+ "42": {
+ "note": 43,
+ "pitch": 0.9438743
+ },
+ "43": {
+ "note": 43,
+ "pitch": 1
+ },
+ "44": {
+ "note": 43,
+ "pitch": 1.0594631
+ },
+ "45": {
+ "note": 45,
+ "pitch": 1
+ },
+ "46": {
+ "note": 45,
+ "pitch": 1.0594631
+ },
+ "47": {
+ "note": 45,
+ "pitch": 1.122462
+ },
+ "48": {
+ "note": 48,
+ "pitch": 1
+ },
+ "49": {
+ "note": 48,
+ "pitch": 1.0594631
+ },
+ "50": {
+ "note": 48,
+ "pitch": 1.122462
+ },
+ "51": {
+ "note": 52,
+ "pitch": 0.9438743
+ },
+ "52": {
+ "note": 52,
+ "pitch": 1
+ },
+ "53": {
+ "note": 52,
+ "pitch": 1.0594631
+ },
+ "54": {
+ "note": 54,
+ "pitch": 1
+ },
+ "55": {
+ "note": 54,
+ "pitch": 1.0594631
+ },
+ "56": {
+ "note": 54,
+ "pitch": 1.122462
+ },
+ "57": {
+ "note": 57,
+ "pitch": 1
+ },
+ "58": {
+ "note": 57,
+ "pitch": 1.0594631
+ },
+ "59": {
+ "note": 57,
+ "pitch": 1.122462
+ },
+ "60": {
+ "note": 60,
+ "pitch": 1
+ },
+ "61": {
+ "note": 60,
+ "pitch": 1.0594631
+ },
+ "62": {
+ "note": 60,
+ "pitch": 1.122462
+ },
+ "63": {
+ "note": 63,
+ "pitch": 1
+ },
+ "64": {
+ "note": 63,
+ "pitch": 1.0594631
+ },
+ "65": {
+ "note": 63,
+ "pitch": 1.122462
+ },
+ "66": {
+ "note": 66,
+ "pitch": 1
+ },
+ "67": {
+ "note": 66,
+ "pitch": 1.0594631
+ },
+ "68": {
+ "note": 66,
+ "pitch": 1.122462
+ },
+ "69": {
+ "note": 69,
+ "pitch": 1
+ },
+ "70": {
+ "note": 69,
+ "pitch": 1.0594631
+ },
+ "71": {
+ "note": 69,
+ "pitch": 1.122462
+ },
+ "72": {
+ "note": 72,
+ "pitch": 1
+ },
+ "73": {
+ "note": 72,
+ "pitch": 1.0594631
+ },
+ "74": {
+ "note": 72,
+ "pitch": 1.122462
+ },
+ "75": {
+ "note": 75,
+ "pitch": 1
+ },
+ "76": {
+ "note": 75,
+ "pitch": 1.0594631
+ },
+ "77": {
+ "note": 75,
+ "pitch": 1.122462
+ },
+ "78": {
+ "note": 78,
+ "pitch": 1
+ },
+ "79": {
+ "note": 78,
+ "pitch": 1.0594631
+ },
+ "80": {
+ "note": 78,
+ "pitch": 1.122462
+ },
+ "81": {
+ "note": 81,
+ "pitch": 1
+ },
+ "82": {
+ "note": 81,
+ "pitch": 1.0594631
+ },
+ "83": {
+ "note": 81,
+ "pitch": 1.122462
+ },
+ "84": {
+ "note": 84,
+ "pitch": 1
+ }
+ },
+ "volume": 0.5
+ },
+ "electric_guitar": {
+ "__collections__": {},
+ "id": "electric_guitar",
+ "maxNote": 84,
+ "minNote": 40,
+ "soundFiles": {
+ "40": "sounds/electric-guitar/40.mp3",
+ "44": "sounds/electric-guitar/44.mp3",
+ "48": "sounds/electric-guitar/48.mp3",
+ "52": "sounds/electric-guitar/52.mp3",
+ "56": "sounds/electric-guitar/56.mp3",
+ "60": "sounds/electric-guitar/60.mp3",
+ "65": "sounds/electric-guitar/65.mp3",
+ "68": "sounds/electric-guitar/68.mp3",
+ "72": "sounds/electric-guitar/72.mp3",
+ "76": "sounds/electric-guitar/76.mp3",
+ "80": "sounds/electric-guitar/80.mp3",
+ "84": "sounds/electric-guitar/84.mp3"
+ },
+ "soundNotes": {
+ "40": {
+ "note": 40,
+ "pitch": 1
+ },
+ "41": {
+ "note": 40,
+ "pitch": 1.0594631
+ },
+ "42": {
+ "note": 40,
+ "pitch": 1.122462
+ },
+ "43": {
+ "note": 44,
+ "pitch": 0.9438743
+ },
+ "44": {
+ "note": 44,
+ "pitch": 1
+ },
+ "45": {
+ "note": 44,
+ "pitch": 1.0594631
+ },
+ "46": {
+ "note": 44,
+ "pitch": 1.122462
+ },
+ "47": {
+ "note": 48,
+ "pitch": 0.9438743
+ },
+ "48": {
+ "note": 48,
+ "pitch": 1
+ },
+ "49": {
+ "note": 48,
+ "pitch": 1.0594631
+ },
+ "50": {
+ "note": 48,
+ "pitch": 1.122462
+ },
+ "51": {
+ "note": 52,
+ "pitch": 0.9438743
+ },
+ "52": {
+ "note": 52,
+ "pitch": 1
+ },
+ "53": {
+ "note": 52,
+ "pitch": 1.0594631
+ },
+ "54": {
+ "note": 52,
+ "pitch": 1.122462
+ },
+ "55": {
+ "note": 56,
+ "pitch": 0.9438743
+ },
+ "56": {
+ "note": 56,
+ "pitch": 1
+ },
+ "57": {
+ "note": 56,
+ "pitch": 1.0594631
+ },
+ "58": {
+ "note": 56,
+ "pitch": 1.122462
+ },
+ "59": {
+ "note": 60,
+ "pitch": 0.9438743
+ },
+ "60": {
+ "note": 60,
+ "pitch": 1
+ },
+ "61": {
+ "note": 60,
+ "pitch": 1.0594631
+ },
+ "62": {
+ "note": 60,
+ "pitch": 1.122462
+ },
+ "63": {
+ "note": 65,
+ "pitch": 0.8908987
+ },
+ "64": {
+ "note": 65,
+ "pitch": 0.9438743
+ },
+ "65": {
+ "note": 65,
+ "pitch": 1
+ },
+ "66": {
+ "note": 65,
+ "pitch": 1.0594631
+ },
+ "67": {
+ "note": 65,
+ "pitch": 1.122462
+ },
+ "68": {
+ "note": 68,
+ "pitch": 1
+ },
+ "69": {
+ "note": 68,
+ "pitch": 1.0594631
+ },
+ "70": {
+ "note": 68,
+ "pitch": 1.122462
+ },
+ "71": {
+ "note": 72,
+ "pitch": 0.9438743
+ },
+ "72": {
+ "note": 72,
+ "pitch": 1
+ },
+ "73": {
+ "note": 72,
+ "pitch": 1.0594631
+ },
+ "74": {
+ "note": 72,
+ "pitch": 1.122462
+ },
+ "75": {
+ "note": 76,
+ "pitch": 0.9438743
+ },
+ "76": {
+ "note": 76,
+ "pitch": 1
+ },
+ "77": {
+ "note": 76,
+ "pitch": 1.0594631
+ },
+ "78": {
+ "note": 76,
+ "pitch": 1.122462
+ },
+ "79": {
+ "note": 80,
+ "pitch": 0.9438743
+ },
+ "80": {
+ "note": 80,
+ "pitch": 1
+ },
+ "81": {
+ "note": 80,
+ "pitch": 1.0594631
+ },
+ "82": {
+ "note": 80,
+ "pitch": 1.122462
+ },
+ "83": {
+ "note": 84,
+ "pitch": 0.9438743
+ },
+ "84": {
+ "note": 84,
+ "pitch": 1
+ }
+ },
+ "volume": 0.3
+ },
+ "piano": {
+ "__collections__": {},
+ "id": "piano",
+ "maxNote": 108,
+ "minNote": 24,
+ "soundFiles": {
+ "102": "sounds/piano/102.mp3",
+ "105": "sounds/piano/105.mp3",
+ "107": "sounds/piano/107.mp3",
+ "24": "sounds/piano/24.mp3",
+ "30": "sounds/piano/30.mp3",
+ "35": "sounds/piano/35.mp3",
+ "39": "sounds/piano/39.mp3",
+ "42": "sounds/piano/42.mp3",
+ "47": "sounds/piano/47.mp3",
+ "51": "sounds/piano/51.mp3",
+ "54": "sounds/piano/54.mp3",
+ "57": "sounds/piano/57.mp3",
+ "60": "sounds/piano/60.mp3",
+ "63": "sounds/piano/63.mp3",
+ "66": "sounds/piano/66.mp3",
+ "69": "sounds/piano/69.mp3",
+ "72": "sounds/piano/72.mp3",
+ "75": "sounds/piano/75.mp3",
+ "78": "sounds/piano/78.mp3",
+ "81": "sounds/piano/81.mp3",
+ "84": "sounds/piano/84.mp3",
+ "87": "sounds/piano/87.mp3",
+ "90": "sounds/piano/90.mp3",
+ "93": "sounds/piano/93.mp3",
+ "96": "sounds/piano/96.mp3",
+ "99": "sounds/piano/99.mp3"
+ },
+ "soundNotes": {
+ "100": {
+ "note": 99,
+ "pitch": 1.0594631
+ },
+ "101": {
+ "note": 99,
+ "pitch": 1.122462
+ },
+ "102": {
+ "note": 102,
+ "pitch": 1
+ },
+ "103": {
+ "note": 102,
+ "pitch": 1.0594631
+ },
+ "104": {
+ "note": 102,
+ "pitch": 1.122462
+ },
+ "105": {
+ "note": 105,
+ "pitch": 1
+ },
+ "106": {
+ "note": 105,
+ "pitch": 1.0594631
+ },
+ "107": {
+ "note": 107,
+ "pitch": 1
+ },
+ "108": {
+ "note": 107,
+ "pitch": 1.0594631
+ },
+ "24": {
+ "note": 24,
+ "pitch": 1
+ },
+ "25": {
+ "note": 24,
+ "pitch": 1.0594631
+ },
+ "26": {
+ "note": 24,
+ "pitch": 1.122462
+ },
+ "27": {
+ "note": 30,
+ "pitch": 0.8408964
+ },
+ "28": {
+ "note": 30,
+ "pitch": 0.8908987
+ },
+ "29": {
+ "note": 30,
+ "pitch": 0.9438743
+ },
+ "30": {
+ "note": 30,
+ "pitch": 1
+ },
+ "31": {
+ "note": 30,
+ "pitch": 1.0594631
+ },
+ "32": {
+ "note": 30,
+ "pitch": 1.122462
+ },
+ "33": {
+ "note": 35,
+ "pitch": 0.8908987
+ },
+ "34": {
+ "note": 35,
+ "pitch": 0.9438743
+ },
+ "35": {
+ "note": 35,
+ "pitch": 1
+ },
+ "36": {
+ "note": 35,
+ "pitch": 1.0594631
+ },
+ "37": {
+ "note": 35,
+ "pitch": 1.122462
+ },
+ "38": {
+ "note": 39,
+ "pitch": 0.9438743
+ },
+ "39": {
+ "note": 39,
+ "pitch": 1
+ },
+ "40": {
+ "note": 39,
+ "pitch": 1.0594631
+ },
+ "41": {
+ "note": 39,
+ "pitch": 1.122462
+ },
+ "42": {
+ "note": 42,
+ "pitch": 1
+ },
+ "43": {
+ "note": 42,
+ "pitch": 1.0594631
+ },
+ "44": {
+ "note": 42,
+ "pitch": 1.122462
+ },
+ "45": {
+ "note": 47,
+ "pitch": 0.8908987
+ },
+ "46": {
+ "note": 47,
+ "pitch": 0.9438743
+ },
+ "47": {
+ "note": 47,
+ "pitch": 1
+ },
+ "48": {
+ "note": 47,
+ "pitch": 1.0594631
+ },
+ "49": {
+ "note": 47,
+ "pitch": 1.122462
+ },
+ "50": {
+ "note": 51,
+ "pitch": 0.9438743
+ },
+ "51": {
+ "note": 51,
+ "pitch": 1
+ },
+ "52": {
+ "note": 51,
+ "pitch": 1.0594631
+ },
+ "53": {
+ "note": 51,
+ "pitch": 1.122462
+ },
+ "54": {
+ "note": 54,
+ "pitch": 1
+ },
+ "55": {
+ "note": 54,
+ "pitch": 1.0594631
+ },
+ "56": {
+ "note": 54,
+ "pitch": 1.122462
+ },
+ "57": {
+ "note": 57,
+ "pitch": 1
+ },
+ "58": {
+ "note": 57,
+ "pitch": 1.0594631
+ },
+ "59": {
+ "note": 57,
+ "pitch": 1.122462
+ },
+ "60": {
+ "note": 60,
+ "pitch": 1
+ },
+ "61": {
+ "note": 60,
+ "pitch": 1.0594631
+ },
+ "62": {
+ "note": 60,
+ "pitch": 1.122462
+ },
+ "63": {
+ "note": 63,
+ "pitch": 1
+ },
+ "64": {
+ "note": 63,
+ "pitch": 1.0594631
+ },
+ "65": {
+ "note": 63,
+ "pitch": 1.122462
+ },
+ "66": {
+ "note": 66,
+ "pitch": 1
+ },
+ "67": {
+ "note": 66,
+ "pitch": 1.0594631
+ },
+ "68": {
+ "note": 66,
+ "pitch": 1.122462
+ },
+ "69": {
+ "note": 69,
+ "pitch": 1
+ },
+ "70": {
+ "note": 69,
+ "pitch": 1.0594631
+ },
+ "71": {
+ "note": 69,
+ "pitch": 1.122462
+ },
+ "72": {
+ "note": 72,
+ "pitch": 1
+ },
+ "73": {
+ "note": 72,
+ "pitch": 1.0594631
+ },
+ "74": {
+ "note": 72,
+ "pitch": 1.122462
+ },
+ "75": {
+ "note": 75,
+ "pitch": 1
+ },
+ "76": {
+ "note": 75,
+ "pitch": 1.0594631
+ },
+ "77": {
+ "note": 75,
+ "pitch": 1.122462
+ },
+ "78": {
+ "note": 78,
+ "pitch": 1
+ },
+ "79": {
+ "note": 78,
+ "pitch": 1.0594631
+ },
+ "80": {
+ "note": 78,
+ "pitch": 1.122462
+ },
+ "81": {
+ "note": 81,
+ "pitch": 1
+ },
+ "82": {
+ "note": 81,
+ "pitch": 1.0594631
+ },
+ "83": {
+ "note": 81,
+ "pitch": 1.122462
+ },
+ "84": {
+ "note": 84,
+ "pitch": 1
+ },
+ "85": {
+ "note": 84,
+ "pitch": 1.0594631
+ },
+ "86": {
+ "note": 84,
+ "pitch": 1.122462
+ },
+ "87": {
+ "note": 87,
+ "pitch": 1
+ },
+ "88": {
+ "note": 87,
+ "pitch": 1.0594631
+ },
+ "89": {
+ "note": 87,
+ "pitch": 1.122462
+ },
+ "90": {
+ "note": 90,
+ "pitch": 1
+ },
+ "91": {
+ "note": 90,
+ "pitch": 1.0594631
+ },
+ "92": {
+ "note": 90,
+ "pitch": 1.122462
+ },
+ "93": {
+ "note": 93,
+ "pitch": 1
+ },
+ "94": {
+ "note": 93,
+ "pitch": 1.0594631
+ },
+ "95": {
+ "note": 93,
+ "pitch": 1.122462
+ },
+ "96": {
+ "note": 96,
+ "pitch": 1
+ },
+ "97": {
+ "note": 96,
+ "pitch": 1.0594631
+ },
+ "98": {
+ "note": 96,
+ "pitch": 1.122462
+ },
+ "99": {
+ "note": 99,
+ "pitch": 1
+ }
+ },
+ "volume": 0.5
+ }
+ },
+ "songs": {
+ "Alan_Walker-Alone": {
+ "__collections__": {},
+ "artist": "Alan Walker",
+ "bpm": 194,
+ "duration": [
+ 363436426,
+ 272577320,
+ 218061856
+ ],
+ "id": "Alan_Walker-Alone",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 740,
+ 951,
+ 1067
+ ],
+ "title": "Alone",
+ "url": "songs/pop/Alan_Walker-Alone.mid"
+ },
+ "Alan_Walker-Faded": {
+ "__collections__": {},
+ "artist": "Alan Walker",
+ "bpm": 89,
+ "duration": [
+ 318202247,
+ 238651685,
+ 190921348
+ ],
+ "id": "Alan_Walker-Faded",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 829,
+ 865,
+ 882
+ ],
+ "title": "Faded",
+ "url": "songs/pop/Alan_Walker-Faded.mid"
+ },
+ "Alan_Walker-Spectre": {
+ "__collections__": {},
+ "artist": "Alan Walker",
+ "bpm": 128,
+ "duration": [
+ 299895833,
+ 224921875,
+ 179937500
+ ],
+ "id": "Alan_Walker-Spectre",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 1226,
+ 1255,
+ 1255
+ ],
+ "title": "Spectre",
+ "url": "songs/pop/Alan_Walker-Spectre.mid"
+ },
+ "Aram_Khachaturian-Masquerade_Waltz": {
+ "__collections__": {},
+ "artist": "Aram Khachaturian",
+ "bpm": 150,
+ "duration": [
+ 419466667,
+ 314600000,
+ 251680000
+ ],
+ "id": "Aram_Khachaturian-Masquerade_Waltz",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1787,
+ 2310,
+ 2573
+ ],
+ "title": "Masquerade Waltz",
+ "url": "songs/classic/Aram_Khachaturian-Masquerade_Waltz.mid"
+ },
+ "Aram_Khachaturian-Sabr_Dance": {
+ "__collections__": {},
+ "artist": "Aram Khachaturian",
+ "bpm": 150,
+ "duration": [
+ 305392593,
+ 229044444,
+ 183235556
+ ],
+ "id": "Aram_Khachaturian-Sabr_Dance",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1642,
+ 2110,
+ 2347
+ ],
+ "title": "Sabr Dance",
+ "url": "songs/classic/Aram_Khachaturian-Sabr_Dance.mid"
+ },
+ "BTS-Black_Swan": {
+ "__collections__": {},
+ "artist": "BTS",
+ "bpm": 147,
+ "duration": [
+ 290884354,
+ 218163265,
+ 174530612
+ ],
+ "id": "BTS-Black_Swan",
+ "tags": [
+ "kpop"
+ ],
+ "tilesCount": [
+ 1165,
+ 1226,
+ 1262
+ ],
+ "title": "Black Swan",
+ "url": "songs/kpop/BTS-Black_Swan.mid"
+ },
+ "BTS-DNA": {
+ "__collections__": {},
+ "artist": "BTS",
+ "bpm": 130,
+ "duration": [
+ 161025641,
+ 120769231,
+ 96615385
+ ],
+ "id": "BTS-DNA",
+ "tags": [
+ "kpop"
+ ],
+ "tilesCount": [
+ 664,
+ 846,
+ 962
+ ],
+ "title": "DNA",
+ "url": "songs/kpop/BTS-DNA.mid"
+ },
+ "BTS-Dynamite": {
+ "__collections__": {},
+ "artist": "BTS",
+ "bpm": 115,
+ "duration": [
+ 470260870,
+ 352695652,
+ 282156522
+ ],
+ "id": "BTS-Dynamite",
+ "tags": [
+ "kpop"
+ ],
+ "tilesCount": [
+ 1397,
+ 1757,
+ 1929
+ ],
+ "title": "Dynamite",
+ "url": "songs/kpop/BTS-Dynamite.mid"
+ },
+ "BTS-I_Need_U": {
+ "__collections__": {},
+ "artist": "BTS",
+ "bpm": 150,
+ "duration": [
+ 325729630,
+ 244297222,
+ 195437778
+ ],
+ "id": "BTS-I_Need_U",
+ "tags": [
+ "kpop"
+ ],
+ "tilesCount": [
+ 1332,
+ 1586,
+ 1748
+ ],
+ "title": "I Need U",
+ "url": "songs/kpop/BTS-I_Need_U.mid"
+ },
+ "BTS-Life_Goes_On": {
+ "__collections__": {},
+ "artist": "BTS",
+ "bpm": 74,
+ "duration": [
+ 293963964,
+ 220472973,
+ 176378378
+ ],
+ "id": "BTS-Life_Goes_On",
+ "tags": [
+ "kpop"
+ ],
+ "tilesCount": [
+ 1125,
+ 1361,
+ 1432
+ ],
+ "title": "Life Goes On",
+ "url": "songs/kpop/BTS-Life_Goes_On.mid"
+ },
+ "Bach-Air_On_The_G_String": {
+ "__collections__": {},
+ "artist": "Bach",
+ "bpm": 45,
+ "duration": [
+ 276691358,
+ 207518519,
+ 166014815
+ ],
+ "id": "Bach-Air_On_The_G_String",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 650,
+ 669,
+ 674
+ ],
+ "title": "Air On The G String",
+ "url": "songs/classic/Bach-Air_On_The_G_String.mid"
+ },
+ "Bach-Badinerie": {
+ "__collections__": {},
+ "artist": "Bach",
+ "bpm": 75,
+ "duration": [
+ 184355556,
+ 138266667,
+ 110613333
+ ],
+ "id": "Bach-Badinerie",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 914,
+ 1160,
+ 1310
+ ],
+ "title": "Badinerie",
+ "url": "songs/classic/Bach-Badinerie.mid"
+ },
+ "Bach-Bourree_In_E_Minor": {
+ "__collections__": {},
+ "artist": "Bach",
+ "bpm": 107,
+ "duration": [
+ 150031153,
+ 112523364,
+ 90018692
+ ],
+ "id": "Bach-Bourree_In_E_Minor",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 472,
+ 474,
+ 476
+ ],
+ "title": "Bourree In E Minor",
+ "url": "songs/classic/Bach-Bourree_In_E_Minor.mid"
+ },
+ "Bach-Cello_Suite": {
+ "__collections__": {},
+ "artist": "Bach",
+ "bpm": 69,
+ "duration": [
+ 190628019,
+ 142971014,
+ 114376812
+ ],
+ "id": "Bach-Cello_Suite",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1096,
+ 1111,
+ 1126
+ ],
+ "title": "Cello Suite",
+ "url": "songs/classic/Bach-Cello_Suite.mid"
+ },
+ "Bach-Joy": {
+ "__collections__": {},
+ "artist": "Bach",
+ "bpm": 64,
+ "duration": [
+ 276157407,
+ 207118056,
+ 165694444
+ ],
+ "id": "Bach-Joy",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 866,
+ 993,
+ 1018
+ ],
+ "title": "Joy",
+ "url": "songs/classic/Bach-Joy.mid"
+ },
+ "Bach-Minuet_In_G": {
+ "__collections__": {},
+ "artist": "Bach",
+ "bpm": 120,
+ "duration": [
+ 148888889,
+ 111666667,
+ 89333333
+ ],
+ "id": "Bach-Minuet_In_G",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 422,
+ 428,
+ 432
+ ],
+ "title": "Minuet In G",
+ "url": "songs/classic/Bach-Minuet_In_G.mid"
+ },
+ "Bach-Toccata_and_Fugue": {
+ "__collections__": {},
+ "artist": "Bach",
+ "bpm": 60,
+ "duration": [
+ 953722222,
+ 715291667,
+ 572233333
+ ],
+ "id": "Bach-Toccata_and_Fugue",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 3500,
+ 3794,
+ 3942
+ ],
+ "title": "Toccata and Fugue",
+ "url": "songs/classic/Bach-Toccata_and_Fugue.mid"
+ },
+ "Bach-Well_Tempered_Clavier": {
+ "__collections__": {},
+ "artist": "Bach",
+ "bpm": 72,
+ "duration": [
+ 147129630,
+ 110347222,
+ 88277778
+ ],
+ "id": "Bach-Well_Tempered_Clavier",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 530,
+ 531,
+ 532
+ ],
+ "title": "Well Tempered Clavier",
+ "url": "songs/classic/Bach-Well_Tempered_Clavier.mid"
+ },
+ "Beethoven-Appasionata_Sonate": {
+ "__collections__": {},
+ "artist": "Beethoven",
+ "bpm": 72,
+ "duration": [
+ 423148148,
+ 317361111,
+ 253888889
+ ],
+ "id": "Beethoven-Appasionata_Sonate",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 774,
+ 929,
+ 989
+ ],
+ "title": "Appasionata Sonate",
+ "url": "songs/classic/Beethoven-Appasionata_Sonate.mid"
+ },
+ "Beethoven-Fur_Elise": {
+ "__collections__": {},
+ "artist": "Beethoven",
+ "bpm": 53,
+ "duration": [
+ 315471698,
+ 236603774,
+ 189283019
+ ],
+ "id": "Beethoven-Fur_Elise",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 940,
+ 999,
+ 1035
+ ],
+ "title": "Fur Elise",
+ "url": "songs/classic/Beethoven-Fur_Elise.mid"
+ },
+ "Beethoven-Moonlight_Sonata_Mvt._1": {
+ "__collections__": {},
+ "artist": "Beethoven",
+ "bpm": 48,
+ "duration": [
+ 474320988,
+ 355740741,
+ 284592593
+ ],
+ "id": "Beethoven-Moonlight_Sonata_Mvt._1",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 959,
+ 1065,
+ 1132
+ ],
+ "title": "Moonlight Sonata Mvt. 1",
+ "url": "songs/classic/Beethoven-Moonlight_Sonata_Mvt._1.mid"
+ },
+ "Beethoven-Moonlight_Sonata_Mvt._3": {
+ "__collections__": {},
+ "artist": "Beethoven",
+ "bpm": 75,
+ "duration": [
+ 1189562963,
+ 892172222,
+ 713737778
+ ],
+ "id": "Beethoven-Moonlight_Sonata_Mvt._3",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 5245,
+ 5912,
+ 6346
+ ],
+ "title": "Moonlight Sonata Mvt. 3",
+ "url": "songs/classic/Beethoven-Moonlight_Sonata_Mvt._3.mid"
+ },
+ "Beethoven-String_Quartet": {
+ "__collections__": {},
+ "artist": "Beethoven",
+ "bpm": 75,
+ "duration": [
+ 969037037,
+ 726777778,
+ 581422222
+ ],
+ "id": "Beethoven-String_Quartet",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 3011,
+ 3466,
+ 3803
+ ],
+ "title": "String Quartet",
+ "url": "songs/classic/Beethoven-String_Quartet.mid"
+ },
+ "Beethoven-Symphony_No._3_Mvt._1": {
+ "__collections__": {},
+ "artist": "Beethoven",
+ "bpm": 150,
+ "duration": [
+ 397037037,
+ 297777778,
+ 238222222
+ ],
+ "id": "Beethoven-Symphony_No._3_Mvt._1",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1542,
+ 1992,
+ 2264
+ ],
+ "title": "Symphony No. 3 Mvt. 1",
+ "url": "songs/classic/Beethoven-Symphony_No._3_Mvt._1.mid"
+ },
+ "Beethoven-Symphony_No._5_Mvt._1": {
+ "__collections__": {},
+ "artist": "Beethoven",
+ "bpm": 149,
+ "duration": [
+ 159731544,
+ 119798658,
+ 95838926
+ ],
+ "id": "Beethoven-Symphony_No._5_Mvt._1",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 695,
+ 889,
+ 1001
+ ],
+ "title": "Symphony No. 5 Mvt. 1",
+ "url": "songs/classic/Beethoven-Symphony_No._5_Mvt._1.mid"
+ },
+ "Beethoven-Symphony_No._5_Mvt._3": {
+ "__collections__": {},
+ "artist": "Beethoven",
+ "bpm": 149,
+ "duration": [
+ 633944817,
+ 475458613,
+ 380366890
+ ],
+ "id": "Beethoven-Symphony_No._5_Mvt._3",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 2059,
+ 2559,
+ 2932
+ ],
+ "title": "Symphony No. 5 Mvt. 3",
+ "url": "songs/classic/Beethoven-Symphony_No._5_Mvt._3.mid"
+ },
+ "Beethoven-Symphony_No._6": {
+ "__collections__": {},
+ "artist": "Beethoven",
+ "bpm": 75,
+ "duration": [
+ 772051852,
+ 579038889,
+ 463231111
+ ],
+ "id": "Beethoven-Symphony_No._6",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 3426,
+ 4215,
+ 4675
+ ],
+ "title": "Symphony No. 6",
+ "url": "songs/classic/Beethoven-Symphony_No._6.mid"
+ },
+ "Beethoven-Symphony_No._7_Mvt._2": {
+ "__collections__": {},
+ "artist": "Beethoven",
+ "bpm": 72,
+ "duration": [
+ 668888889,
+ 501666667,
+ 401333333
+ ],
+ "id": "Beethoven-Symphony_No._7_Mvt._2",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 2468,
+ 3019,
+ 3361
+ ],
+ "title": "Symphony No. 7 Mvt. 2",
+ "url": "songs/classic/Beethoven-Symphony_No._7_Mvt._2.mid"
+ },
+ "Beethoven-Symphony_No._9_Mvt._2": {
+ "__collections__": {},
+ "artist": "Beethoven",
+ "bpm": 299,
+ "duration": [
+ 2230635452,
+ 1672976589,
+ 1338381271
+ ],
+ "id": "Beethoven-Symphony_No._9_Mvt._2",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 8557,
+ 11600,
+ 13686
+ ],
+ "title": "Symphony No. 9 Mvt. 2",
+ "url": "songs/classic/Beethoven-Symphony_No._9_Mvt._2.mid"
+ },
+ "Beethoven-Symphony_No._9_Mvt._4": {
+ "__collections__": {},
+ "artist": "Beethoven",
+ "bpm": 123,
+ "duration": [
+ 169539295,
+ 127154472,
+ 101723577
+ ],
+ "id": "Beethoven-Symphony_No._9_Mvt._4",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 208,
+ 266,
+ 266
+ ],
+ "title": "Symphony No. 9 Mvt. 4",
+ "url": "songs/classic/Beethoven-Symphony_No._9_Mvt._4.mid"
+ },
+ "Beethoven-Turkish_March": {
+ "__collections__": {},
+ "artist": "Beethoven",
+ "bpm": 120,
+ "duration": [
+ 375166667,
+ 281375000,
+ 225100000
+ ],
+ "id": "Beethoven-Turkish_March",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1178,
+ 1613,
+ 1916
+ ],
+ "title": "Turkish March",
+ "url": "songs/classic/Beethoven-Turkish_March.mid"
+ },
+ "Billie_Eilish,_Khalid-Lovely": {
+ "__collections__": {},
+ "artist": "Billie Eilish, Khalid",
+ "bpm": 115,
+ "duration": [
+ 278067633,
+ 208550725,
+ 166840580
+ ],
+ "id": "Billie_Eilish,_Khalid-Lovely",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 542,
+ 629,
+ 713
+ ],
+ "title": "Lovely",
+ "url": "songs/other/Billie_Eilish,_Khalid-Lovely.mid"
+ },
+ "Blackpink-How_You_Like_That": {
+ "__collections__": {},
+ "artist": "Blackpink",
+ "bpm": 130,
+ "duration": [
+ 325435897,
+ 244076923,
+ 195261538
+ ],
+ "id": "Blackpink-How_You_Like_That",
+ "tags": [
+ "kpop"
+ ],
+ "tilesCount": [
+ 1146,
+ 1369,
+ 1445
+ ],
+ "title": "How You Like That",
+ "url": "songs/kpop/Blackpink-How_You_Like_That.mid"
+ },
+ "Blackpink-Kill_This_Love_Piano": {
+ "__collections__": {},
+ "artist": "Blackpink",
+ "bpm": 100,
+ "duration": [
+ 342992593,
+ 257244444,
+ 205795556
+ ],
+ "id": "Blackpink-Kill_This_Love_Piano",
+ "tags": [
+ "kpop"
+ ],
+ "tilesCount": [
+ 1217,
+ 1452,
+ 1485
+ ],
+ "title": "Kill This Love Piano",
+ "url": "songs/kpop/Blackpink-Kill_This_Love_Piano.mid"
+ },
+ "Blackpink-You_Never_Know": {
+ "__collections__": {},
+ "artist": "Blackpink",
+ "bpm": 75,
+ "duration": [
+ 292000000,
+ 219000000,
+ 175200000
+ ],
+ "id": "Blackpink-You_Never_Know",
+ "tags": [
+ "kpop"
+ ],
+ "tilesCount": [
+ 1149,
+ 1320,
+ 1371
+ ],
+ "title": "You Never Know",
+ "url": "songs/kpop/Blackpink-You_Never_Know.mid"
+ },
+ "Brad_Breeck-Gravity_Falls,_Opening_Theme": {
+ "__collections__": {},
+ "artist": "Brad Breeck",
+ "bpm": 150,
+ "duration": [
+ 57422222,
+ 43066667,
+ 34453333
+ ],
+ "id": "Brad_Breeck-Gravity_Falls,_Opening_Theme",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 271,
+ 332,
+ 338
+ ],
+ "title": "Gravity Falls, Opening Theme",
+ "url": "songs/other/Brad_Breeck-Gravity_Falls,_Opening_Theme.mid"
+ },
+ "Bruno_Mars-When_I_Was_Your_Man": {
+ "__collections__": {},
+ "artist": "Bruno Mars",
+ "bpm": 73,
+ "duration": [
+ 299178082,
+ 224383562,
+ 179506849
+ ],
+ "id": "Bruno_Mars-When_I_Was_Your_Man",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 598,
+ 831,
+ 940
+ ],
+ "title": "When I Was Your Man",
+ "url": "songs/pop/Bruno_Mars-When_I_Was_Your_Man.mid"
+ },
+ "Carl_Orff-O_Fortuna": {
+ "__collections__": {},
+ "artist": "Carl Orff",
+ "bpm": 120,
+ "duration": [
+ 209111111,
+ 156833333,
+ 125466667
+ ],
+ "id": "Carl_Orff-O_Fortuna",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 835,
+ 939,
+ 958
+ ],
+ "title": "O Fortuna",
+ "url": "songs/classic/Carl_Orff-O_Fortuna.mid"
+ },
+ "Celine_Dion-My_Heart_Will_Go_On": {
+ "__collections__": {},
+ "artist": "Celine Dion",
+ "bpm": 89,
+ "duration": [
+ 444093633,
+ 333070225,
+ 266456180
+ ],
+ "id": "Celine_Dion-My_Heart_Will_Go_On",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 1188,
+ 1358,
+ 1402
+ ],
+ "title": "My Heart Will Go On",
+ "url": "songs/pop/Celine_Dion-My_Heart_Will_Go_On.mid"
+ },
+ "Charlie_Puth-See_You_Again": {
+ "__collections__": {},
+ "artist": "Charlie Puth",
+ "bpm": 74,
+ "duration": [
+ 380630631,
+ 285472973,
+ 228378378
+ ],
+ "id": "Charlie_Puth-See_You_Again",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 1215,
+ 1481,
+ 1647
+ ],
+ "title": "See You Again",
+ "url": "songs/pop/Charlie_Puth-See_You_Again.mid"
+ },
+ "Chinese_Folk-Jasmine_Flower": {
+ "__collections__": {},
+ "artist": "Chinese Folk",
+ "bpm": 75,
+ "duration": [
+ 58044444,
+ 43533333,
+ 34826667
+ ],
+ "id": "Chinese_Folk-Jasmine_Flower",
+ "tags": [
+ "folk"
+ ],
+ "tilesCount": [
+ 284,
+ 284,
+ 284
+ ],
+ "title": "Jasmine Flower",
+ "url": "songs/folk/Chinese_Folk-Jasmine_Flower.mid"
+ },
+ "Chopin-Etude_Op._10_No._3": {
+ "__collections__": {},
+ "artist": "Chopin",
+ "bpm": 34,
+ "duration": [
+ 380392157,
+ 285294118,
+ 228235294
+ ],
+ "id": "Chopin-Etude_Op._10_No._3",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1212,
+ 1586,
+ 1777
+ ],
+ "title": "Etude Op. 10 No. 3",
+ "url": "songs/classic/Chopin-Etude_Op._10_No._3.mid"
+ },
+ "Chopin-Fantaisie_Impromptu": {
+ "__collections__": {},
+ "artist": "Chopin",
+ "bpm": 75,
+ "duration": [
+ 840700000,
+ 630525000,
+ 504420000
+ ],
+ "id": "Chopin-Fantaisie_Impromptu",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 3027,
+ 3063,
+ 3067
+ ],
+ "title": "Fantaisie Impromptu",
+ "url": "songs/classic/Chopin-Fantaisie_Impromptu.mid"
+ },
+ "Chopin-Funeral_March": {
+ "__collections__": {},
+ "artist": "Chopin",
+ "bpm": 50,
+ "duration": [
+ 440000000,
+ 330000000,
+ 264000000
+ ],
+ "id": "Chopin-Funeral_March",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 504,
+ 576,
+ 591
+ ],
+ "title": "Funeral March",
+ "url": "songs/classic/Chopin-Funeral_March.mid"
+ },
+ "Chopin-Heroic_Polonaise": {
+ "__collections__": {},
+ "artist": "Chopin",
+ "bpm": 75,
+ "duration": [
+ 596414815,
+ 447311111,
+ 357848889
+ ],
+ "id": "Chopin-Heroic_Polonaise",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 2723,
+ 3265,
+ 3471
+ ],
+ "title": "Heroic Polonaise",
+ "url": "songs/classic/Chopin-Heroic_Polonaise.mid"
+ },
+ "Chopin-Minute_Waltz": {
+ "__collections__": {},
+ "artist": "Chopin",
+ "bpm": 150,
+ "duration": [
+ 269155556,
+ 201866667,
+ 161493333
+ ],
+ "id": "Chopin-Minute_Waltz",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1121,
+ 1323,
+ 1423
+ ],
+ "title": "Minute Waltz",
+ "url": "songs/classic/Chopin-Minute_Waltz.mid"
+ },
+ "Chopin-Nocturne_Op._9_No._2": {
+ "__collections__": {},
+ "artist": "Chopin",
+ "bpm": 60,
+ "duration": [
+ 734888889,
+ 551166667,
+ 440933333
+ ],
+ "id": "Chopin-Nocturne_Op._9_No._2",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 991,
+ 1199,
+ 1286
+ ],
+ "title": "Nocturne Op. 9 No. 2",
+ "url": "songs/classic/Chopin-Nocturne_Op._9_No._2.mid"
+ },
+ "Chopin-Revolutionary_Etude": {
+ "__collections__": {},
+ "artist": "Chopin",
+ "bpm": 75,
+ "duration": [
+ 365644444,
+ 274233333,
+ 219386667
+ ],
+ "id": "Chopin-Revolutionary_Etude",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1696,
+ 1881,
+ 2002
+ ],
+ "title": "Revolutionary Etude",
+ "url": "songs/classic/Chopin-Revolutionary_Etude.mid"
+ },
+ "Chopin-Waltz_Op._64_No._2": {
+ "__collections__": {},
+ "artist": "Chopin",
+ "bpm": 120,
+ "duration": [
+ 429444444,
+ 322083333,
+ 257666667
+ ],
+ "id": "Chopin-Waltz_Op._64_No._2",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1488,
+ 1769,
+ 1856
+ ],
+ "title": "Waltz Op. 64 No. 2",
+ "url": "songs/classic/Chopin-Waltz_Op._64_No._2.mid"
+ },
+ "Chopin-Waltz_in_A_Minor": {
+ "__collections__": {},
+ "artist": "Chopin",
+ "bpm": 120,
+ "duration": [
+ 234888889,
+ 176166667,
+ 140933333
+ ],
+ "id": "Chopin-Waltz_in_A_Minor",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 669,
+ 816,
+ 937
+ ],
+ "title": "Waltz in A Minor",
+ "url": "songs/classic/Chopin-Waltz_in_A_Minor.mid"
+ },
+ "Christina_Perri-A_Thousand_Years": {
+ "__collections__": {},
+ "artist": "Christina Perri",
+ "bpm": 75,
+ "duration": [
+ 2547200000,
+ 1910400000,
+ 1528320000
+ ],
+ "id": "Christina_Perri-A_Thousand_Years",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 560,
+ 764,
+ 912
+ ],
+ "title": "A Thousand Years",
+ "url": "songs/pop/Christina_Perri-A_Thousand_Years.mid"
+ },
+ "Deaf_Kev-Invincible": {
+ "__collections__": {},
+ "artist": "Deaf Kev",
+ "bpm": 100,
+ "duration": [
+ 862711111,
+ 647033333,
+ 517626667
+ ],
+ "id": "Deaf_Kev-Invincible",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 1766,
+ 2217,
+ 2579
+ ],
+ "title": "Invincible",
+ "url": "songs/pop/Deaf_Kev-Invincible.mid"
+ },
+ "Debussy-Clair_De_Lune": {
+ "__collections__": {},
+ "artist": "Debussy",
+ "bpm": 54,
+ "duration": [
+ 486181070,
+ 364635802,
+ 291708642
+ ],
+ "id": "Debussy-Clair_De_Lune",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1092,
+ 1263,
+ 1352
+ ],
+ "title": "Clair De Lune",
+ "url": "songs/classic/Debussy-Clair_De_Lune.mid"
+ },
+ "Delibes-Flower_Duet": {
+ "__collections__": {},
+ "artist": "Delibes",
+ "bpm": 75,
+ "duration": [
+ 163748148,
+ 122811111,
+ 98248889
+ ],
+ "id": "Delibes-Flower_Duet",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 756,
+ 888,
+ 888
+ ],
+ "title": "Flower Duet",
+ "url": "songs/classic/Delibes-Flower_Duet.mid"
+ },
+ "Delibes-Pizzicato": {
+ "__collections__": {},
+ "artist": "Delibes",
+ "bpm": 75,
+ "duration": [
+ 170437037,
+ 127827778,
+ 102262222
+ ],
+ "id": "Delibes-Pizzicato",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 708,
+ 774,
+ 821
+ ],
+ "title": "Pizzicato",
+ "url": "songs/classic/Delibes-Pizzicato.mid"
+ },
+ "Different_Heaven,_EHDE-My_Heart": {
+ "__collections__": {},
+ "artist": "Different Heaven, EHDE",
+ "bpm": 150,
+ "duration": [
+ 431111111,
+ 323333333,
+ 258666667
+ ],
+ "id": "Different_Heaven,_EHDE-My_Heart",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 1943,
+ 2223,
+ 2373
+ ],
+ "title": "My Heart",
+ "url": "songs/pop/Different_Heaven,_EHDE-My_Heart.mid"
+ },
+ "Disfigure-Blank": {
+ "__collections__": {},
+ "artist": "Disfigure",
+ "bpm": 130,
+ "duration": [
+ 237333333,
+ 178000000,
+ 142400000
+ ],
+ "id": "Disfigure-Blank",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 1228,
+ 1476,
+ 1634
+ ],
+ "title": "Blank",
+ "url": "songs/pop/Disfigure-Blank.mid"
+ },
+ "Disney_Pixar's_Up-Married_Life": {
+ "__collections__": {},
+ "artist": "Disney Pixar's Up",
+ "bpm": 150,
+ "duration": [
+ 210462963,
+ 157847222,
+ 126277778
+ ],
+ "id": "Disney_Pixar's_Up-Married_Life",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 661,
+ 762,
+ 800
+ ],
+ "title": "Married Life",
+ "url": "songs/pop/Disney_Pixar's_Up-Married_Life.mid"
+ },
+ "Dmitri_Shostakovich-Second_Waltz": {
+ "__collections__": {},
+ "artist": "Dmitri Shostakovich",
+ "bpm": 170,
+ "duration": [
+ 530509804,
+ 397882353,
+ 318305882
+ ],
+ "id": "Dmitri_Shostakovich-Second_Waltz",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1415,
+ 1936,
+ 2245
+ ],
+ "title": "Second Waltz",
+ "url": "songs/classic/Dmitri_Shostakovich-Second_Waltz.mid"
+ },
+ "Dvorak-Humoresque_No._7": {
+ "__collections__": {},
+ "artist": "Dvorak",
+ "bpm": 72,
+ "duration": [
+ 181450617,
+ 136087963,
+ 108870370
+ ],
+ "id": "Dvorak-Humoresque_No._7",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 751,
+ 930,
+ 998
+ ],
+ "title": "Humoresque No. 7",
+ "url": "songs/classic/Dvorak-Humoresque_No._7.mid"
+ },
+ "Dvorak-Slavonic_Dance_No._2": {
+ "__collections__": {},
+ "artist": "Dvorak",
+ "bpm": 50,
+ "duration": [
+ 480044444,
+ 360033333,
+ 288026667
+ ],
+ "id": "Dvorak-Slavonic_Dance_No._2",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1646,
+ 2164,
+ 2521
+ ],
+ "title": "Slavonic Dance No. 2",
+ "url": "songs/classic/Dvorak-Slavonic_Dance_No._2.mid"
+ },
+ "Dvorak-Symphony_No._9_Mvt._4": {
+ "__collections__": {},
+ "artist": "Dvorak",
+ "bpm": 100,
+ "duration": [
+ 456177778,
+ 342133333,
+ 273706667
+ ],
+ "id": "Dvorak-Symphony_No._9_Mvt._4",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1389,
+ 1726,
+ 1914
+ ],
+ "title": "Symphony No. 9 Mvt. 4",
+ "url": "songs/classic/Dvorak-Symphony_No._9_Mvt._4.mid"
+ },
+ "Ed_Sheeran-I_See_Fire": {
+ "__collections__": {},
+ "artist": "Ed Sheeran",
+ "bpm": 160,
+ "duration": [
+ 781833333,
+ 586375000,
+ 469100000
+ ],
+ "id": "Ed_Sheeran-I_See_Fire",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 1026,
+ 1242,
+ 1353
+ ],
+ "title": "I See Fire",
+ "url": "songs/pop/Ed_Sheeran-I_See_Fire.mid"
+ },
+ "Ed_Sheeran-Perfect": {
+ "__collections__": {},
+ "artist": "Ed Sheeran",
+ "bpm": 94,
+ "duration": [
+ 285957447,
+ 214468085,
+ 171574468
+ ],
+ "id": "Ed_Sheeran-Perfect",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 808,
+ 961,
+ 1018
+ ],
+ "title": "Perfect",
+ "url": "songs/pop/Ed_Sheeran-Perfect.mid"
+ },
+ "Ed_Sheeran-Photograph": {
+ "__collections__": {},
+ "artist": "Ed Sheeran",
+ "bpm": 120,
+ "duration": [
+ 378521296,
+ 283890972,
+ 227112778
+ ],
+ "id": "Ed_Sheeran-Photograph",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 1261,
+ 1472,
+ 1552
+ ],
+ "title": "Photograph",
+ "url": "songs/pop/Ed_Sheeran-Photograph.mid"
+ },
+ "Ed_Sheeran-Shape_Of_You": {
+ "__collections__": {},
+ "artist": "Ed Sheeran",
+ "bpm": 149,
+ "duration": [
+ 400357942,
+ 300268456,
+ 240214765
+ ],
+ "id": "Ed_Sheeran-Shape_Of_You",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 1527,
+ 1885,
+ 1922
+ ],
+ "title": "Shape Of You",
+ "url": "songs/pop/Ed_Sheeran-Shape_Of_You.mid"
+ },
+ "Ed_Sheeran-Thinking_Out_Loud": {
+ "__collections__": {},
+ "artist": "Ed Sheeran",
+ "bpm": 74,
+ "duration": [
+ 397199700,
+ 297899775,
+ 238319820
+ ],
+ "id": "Ed_Sheeran-Thinking_Out_Loud",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 916,
+ 1072,
+ 1113
+ ],
+ "title": "Thinking Out Loud",
+ "url": "songs/pop/Ed_Sheeran-Thinking_Out_Loud.mid"
+ },
+ "Edvard_Grieg-Anitras_Dance": {
+ "__collections__": {},
+ "artist": "Edvard Grieg",
+ "bpm": 72,
+ "duration": [
+ 724938272,
+ 543703704,
+ 434962963
+ ],
+ "id": "Edvard_Grieg-Anitras_Dance",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1455,
+ 1741,
+ 1904
+ ],
+ "title": "Anitras Dance",
+ "url": "songs/classic/Edvard_Grieg-Anitras_Dance.mid"
+ },
+ "Edvard_Grieg-Morning_Mood": {
+ "__collections__": {},
+ "artist": "Edvard Grieg",
+ "bpm": 75,
+ "duration": [
+ 114874074,
+ 86155556,
+ 68924444
+ ],
+ "id": "Edvard_Grieg-Morning_Mood",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 440,
+ 536,
+ 571
+ ],
+ "title": "Morning Mood",
+ "url": "songs/classic/Edvard_Grieg-Morning_Mood.mid"
+ },
+ "Edvard_Grieg-Piano_Concerto": {
+ "__collections__": {},
+ "artist": "Edvard Grieg",
+ "bpm": 37,
+ "duration": [
+ 637717718,
+ 478288288,
+ 382630631
+ ],
+ "id": "Edvard_Grieg-Piano_Concerto",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1818,
+ 2170,
+ 2411
+ ],
+ "title": "Piano Concerto",
+ "url": "songs/classic/Edvard_Grieg-Piano_Concerto.mid"
+ },
+ "Edward_Elgar-Pomp_And_Circumstance_March": {
+ "__collections__": {},
+ "artist": "Edward Elgar",
+ "bpm": 75,
+ "duration": [
+ 686281481,
+ 514711111,
+ 411768889
+ ],
+ "id": "Edward_Elgar-Pomp_And_Circumstance_March",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 2299,
+ 2845,
+ 3168
+ ],
+ "title": "Pomp And Circumstance March",
+ "url": "songs/classic/Edward_Elgar-Pomp_And_Circumstance_March.mid"
+ },
+ "Elektronomia-Sky_High": {
+ "__collections__": {},
+ "artist": "Elektronomia",
+ "bpm": 75,
+ "duration": [
+ 332711111,
+ 249533333,
+ 199626667
+ ],
+ "id": "Elektronomia-Sky_High",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 1183,
+ 1362,
+ 1474
+ ],
+ "title": "Sky High",
+ "url": "songs/pop/Elektronomia-Sky_High.mid"
+ },
+ "English_Folk-Greensleeves": {
+ "__collections__": {},
+ "artist": "English Folk",
+ "bpm": 72,
+ "duration": [
+ 146296296,
+ 109722222,
+ 87777778
+ ],
+ "id": "English_Folk-Greensleeves",
+ "tags": [
+ "folk"
+ ],
+ "tilesCount": [
+ 304,
+ 320,
+ 320
+ ],
+ "title": "Greensleeves",
+ "url": "songs/folk/English_Folk-Greensleeves.mid"
+ },
+ "English_Folk-Twinkle_Twinkle_Little_Star": {
+ "__collections__": {},
+ "artist": "English Folk",
+ "bpm": 120,
+ "duration": [
+ 134666667,
+ 101000000,
+ 80800000
+ ],
+ "id": "English_Folk-Twinkle_Twinkle_Little_Star",
+ "tags": [
+ "folk"
+ ],
+ "tilesCount": [
+ 423,
+ 441,
+ 458
+ ],
+ "title": "Twinkle Twinkle Little Star",
+ "url": "songs/folk/English_Folk-Twinkle_Twinkle_Little_Star.mid"
+ },
+ "English_Folk-We_Wish_You_A_Marry_Christmas": {
+ "__collections__": {},
+ "artist": "English Folk",
+ "bpm": 120,
+ "duration": [
+ 118444444,
+ 88833333,
+ 71066667
+ ],
+ "id": "English_Folk-We_Wish_You_A_Marry_Christmas",
+ "tags": [
+ "folk"
+ ],
+ "tilesCount": [
+ 166,
+ 246,
+ 313
+ ],
+ "title": "We Wish You A Marry Christmas",
+ "url": "songs/folk/English_Folk-We_Wish_You_A_Marry_Christmas.mid"
+ },
+ "English_Folk-We_Wish_You_A_Merry_Christmas": {
+ "__collections__": {},
+ "artist": "English Folk",
+ "bpm": 150,
+ "duration": [
+ 99422222,
+ 74566667,
+ 59653333
+ ],
+ "id": "English_Folk-We_Wish_You_A_Merry_Christmas",
+ "tags": [
+ "folk"
+ ],
+ "tilesCount": [
+ 443,
+ 552,
+ 600
+ ],
+ "title": "We Wish You A Merry Christmas",
+ "url": "songs/folk/English_Folk-We_Wish_You_A_Merry_Christmas.mid"
+ },
+ "Erik_Satie-Gnossienne": {
+ "__collections__": {},
+ "artist": "Erik Satie",
+ "bpm": 102,
+ "duration": [
+ 904572985,
+ 678429739,
+ 542743791
+ ],
+ "id": "Erik_Satie-Gnossienne",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 627,
+ 787,
+ 815
+ ],
+ "title": "Gnossienne",
+ "url": "songs/other/Erik_Satie-Gnossienne.mid"
+ },
+ "Erik_Satie-Gymnopedie": {
+ "__collections__": {},
+ "artist": "Erik Satie",
+ "bpm": 54,
+ "duration": [
+ 352098765,
+ 264074074,
+ 211259259
+ ],
+ "id": "Erik_Satie-Gymnopedie",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 316,
+ 394,
+ 437
+ ],
+ "title": "Gymnopedie",
+ "url": "songs/other/Erik_Satie-Gymnopedie.mid"
+ },
+ "Eugen_Doga-My_Sweet_And_Tender_Beast": {
+ "__collections__": {},
+ "artist": "Eugen Doga",
+ "bpm": 120,
+ "duration": [
+ 171555556,
+ 128666667,
+ 102933333
+ ],
+ "id": "Eugen_Doga-My_Sweet_And_Tender_Beast",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 728,
+ 831,
+ 901
+ ],
+ "title": "My Sweet And Tender Beast",
+ "url": "songs/classic/Eugen_Doga-My_Sweet_And_Tender_Beast.mid"
+ },
+ "Eva_Cassidy-Autumn_Leaves": {
+ "__collections__": {},
+ "artist": "Eva Cassidy",
+ "bpm": 120,
+ "duration": [
+ 284888889,
+ 213666667,
+ 170933333
+ ],
+ "id": "Eva_Cassidy-Autumn_Leaves",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 813,
+ 858,
+ 861
+ ],
+ "title": "Autumn Leaves",
+ "url": "songs/other/Eva_Cassidy-Autumn_Leaves.mid"
+ },
+ "Frank_Sinatra-Fly_Me_To_The_Moon": {
+ "__collections__": {},
+ "artist": "Frank Sinatra",
+ "bpm": 139,
+ "duration": [
+ 699721823,
+ 524791367,
+ 419833094
+ ],
+ "id": "Frank_Sinatra-Fly_Me_To_The_Moon",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 581,
+ 668,
+ 694
+ ],
+ "title": "Fly Me To The Moon",
+ "url": "songs/other/Frank_Sinatra-Fly_Me_To_The_Moon.mid"
+ },
+ "Franz_Liszt-Hungarian_Rhapsody_No._2": {
+ "__collections__": {},
+ "artist": "Franz Liszt",
+ "bpm": 240,
+ "duration": [
+ 313333333,
+ 235000000,
+ 188000000
+ ],
+ "id": "Franz_Liszt-Hungarian_Rhapsody_No._2",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 786,
+ 938,
+ 944
+ ],
+ "title": "Hungarian Rhapsody No. 2",
+ "url": "songs/classic/Franz_Liszt-Hungarian_Rhapsody_No._2.mid"
+ },
+ "Franz_Xaver_Gruber-Silent_Night": {
+ "__collections__": {},
+ "artist": "Franz Xaver Gruber",
+ "bpm": 40,
+ "duration": [
+ 171000000,
+ 128250000,
+ 102600000
+ ],
+ "id": "Franz_Xaver_Gruber-Silent_Night",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 172,
+ 218,
+ 258
+ ],
+ "title": "Silent Night",
+ "url": "songs/other/Franz_Xaver_Gruber-Silent_Night.mid"
+ },
+ "Gary_Jules-Mad_World": {
+ "__collections__": {},
+ "artist": "Gary Jules",
+ "bpm": 67,
+ "duration": [
+ 302885572,
+ 227164179,
+ 181731343
+ ],
+ "id": "Gary_Jules-Mad_World",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 651,
+ 718,
+ 719
+ ],
+ "title": "Mad World",
+ "url": "songs/other/Gary_Jules-Mad_World.mid"
+ },
+ "George_Gershwin-Rhapsody_In_Blue": {
+ "__collections__": {},
+ "artist": "George Gershwin",
+ "bpm": 80,
+ "duration": [
+ 964888889,
+ 723666667,
+ 578933333
+ ],
+ "id": "George_Gershwin-Rhapsody_In_Blue",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1637,
+ 2015,
+ 2288
+ ],
+ "title": "Rhapsody In Blue",
+ "url": "songs/classic/George_Gershwin-Rhapsody_In_Blue.mid"
+ },
+ "Georges_Bizet-Habanera": {
+ "__collections__": {},
+ "artist": "Georges Bizet",
+ "bpm": 120,
+ "duration": [
+ 114666667,
+ 86000000,
+ 68800000
+ ],
+ "id": "Georges_Bizet-Habanera",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 374,
+ 477,
+ 526
+ ],
+ "title": "Habanera",
+ "url": "songs/classic/Georges_Bizet-Habanera.mid"
+ },
+ "Georges_Bizet-Les_Toradors": {
+ "__collections__": {},
+ "artist": "Georges Bizet",
+ "bpm": 75,
+ "duration": [
+ 366118519,
+ 274588889,
+ 219671111
+ ],
+ "id": "Georges_Bizet-Les_Toradors",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1278,
+ 1568,
+ 1676
+ ],
+ "title": "Les Toradors",
+ "url": "songs/classic/Georges_Bizet-Les_Toradors.mid"
+ },
+ "Georges_Bizet-March_Of_The_Toreadors": {
+ "__collections__": {},
+ "artist": "Georges Bizet",
+ "bpm": 120,
+ "duration": [
+ 146537037,
+ 109902778,
+ 87922222
+ ],
+ "id": "Georges_Bizet-March_Of_The_Toreadors",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 375,
+ 504,
+ 591
+ ],
+ "title": "March Of The Toreadors",
+ "url": "songs/classic/Georges_Bizet-March_Of_The_Toreadors.mid"
+ },
+ "Giacomo_Puccini-Madama_Butterfly": {
+ "__collections__": {},
+ "artist": "Giacomo Puccini",
+ "bpm": 75,
+ "duration": [
+ 259992593,
+ 194994444,
+ 155995556
+ ],
+ "id": "Giacomo_Puccini-Madama_Butterfly",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 979,
+ 1147,
+ 1249
+ ],
+ "title": "Madama Butterfly",
+ "url": "songs/classic/Giacomo_Puccini-Madama_Butterfly.mid"
+ },
+ "Giacomo_Puccini-Nessun_Dorma": {
+ "__collections__": {},
+ "artist": "Giacomo Puccini",
+ "bpm": 60,
+ "duration": [
+ 703555556,
+ 527666667,
+ 422133333
+ ],
+ "id": "Giacomo_Puccini-Nessun_Dorma",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 702,
+ 842,
+ 959
+ ],
+ "title": "Nessun Dorma",
+ "url": "songs/classic/Giacomo_Puccini-Nessun_Dorma.mid"
+ },
+ "Giuseppe_Verdi-Drinking_Song": {
+ "__collections__": {},
+ "artist": "Giuseppe Verdi",
+ "bpm": 75,
+ "duration": [
+ 226844444,
+ 170133333,
+ 136106667
+ ],
+ "id": "Giuseppe_Verdi-Drinking_Song",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 365,
+ 365,
+ 365
+ ],
+ "title": "Drinking Song",
+ "url": "songs/classic/Giuseppe_Verdi-Drinking_Song.mid"
+ },
+ "Giuseppe_Verdi-La_Donna_E_Mobile": {
+ "__collections__": {},
+ "artist": "Giuseppe Verdi",
+ "bpm": 100,
+ "duration": [
+ 360533333,
+ 270400000,
+ 216320000
+ ],
+ "id": "Giuseppe_Verdi-La_Donna_E_Mobile",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 343,
+ 384,
+ 385
+ ],
+ "title": "La Donna E Mobile",
+ "url": "songs/classic/Giuseppe_Verdi-La_Donna_E_Mobile.mid"
+ },
+ "Giuseppe_Verdi-Triumphal_March": {
+ "__collections__": {},
+ "artist": "Giuseppe Verdi",
+ "bpm": 109,
+ "duration": [
+ 286238532,
+ 214678899,
+ 171743119
+ ],
+ "id": "Giuseppe_Verdi-Triumphal_March",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 218,
+ 301,
+ 357
+ ],
+ "title": "Triumphal March",
+ "url": "songs/classic/Giuseppe_Verdi-Triumphal_March.mid"
+ },
+ "Gounod-Funeral_March_Of_A_Marionette": {
+ "__collections__": {},
+ "artist": "Gounod",
+ "bpm": 109,
+ "duration": [
+ 167584098,
+ 125688073,
+ 100550459
+ ],
+ "id": "Gounod-Funeral_March_Of_A_Marionette",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 441,
+ 513,
+ 547
+ ],
+ "title": "Funeral March Of A Marionette",
+ "url": "songs/classic/Gounod-Funeral_March_Of_A_Marionette.mid"
+ },
+ "Gustav_Mahler-Symphony_No._5_Mvt._4": {
+ "__collections__": {},
+ "artist": "Gustav Mahler",
+ "bpm": 48,
+ "duration": [
+ 666203704,
+ 499652778,
+ 399722222
+ ],
+ "id": "Gustav_Mahler-Symphony_No._5_Mvt._4",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 648,
+ 750,
+ 796
+ ],
+ "title": "Symphony No. 5 Mvt. 4",
+ "url": "songs/classic/Gustav_Mahler-Symphony_No._5_Mvt._4.mid"
+ },
+ "Handel-Arrival_Of_The_Queen_Of_Sheba": {
+ "__collections__": {},
+ "artist": "Handel",
+ "bpm": 74,
+ "duration": [
+ 384234234,
+ 288175676,
+ 230540541
+ ],
+ "id": "Handel-Arrival_Of_The_Queen_Of_Sheba",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1671,
+ 1831,
+ 1832
+ ],
+ "title": "Arrival Of The Queen Of Sheba",
+ "url": "songs/classic/Handel-Arrival_Of_The_Queen_Of_Sheba.mid"
+ },
+ "Handel-Hallelujah_Chorus": {
+ "__collections__": {},
+ "artist": "Handel",
+ "bpm": 104,
+ "duration": [
+ 422820513,
+ 317115385,
+ 253692308
+ ],
+ "id": "Handel-Hallelujah_Chorus",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1184,
+ 1603,
+ 1934
+ ],
+ "title": "Hallelujah Chorus",
+ "url": "songs/classic/Handel-Hallelujah_Chorus.mid"
+ },
+ "Handel-La_Rejouissance": {
+ "__collections__": {},
+ "artist": "Handel",
+ "bpm": 120,
+ "duration": [
+ 169555556,
+ 127166667,
+ 101733333
+ ],
+ "id": "Handel-La_Rejouissance",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 284,
+ 284,
+ 284
+ ],
+ "title": "La Rejouissance",
+ "url": "songs/classic/Handel-La_Rejouissance.mid"
+ },
+ "Handel-Ombra_Mai_Fu": {
+ "__collections__": {},
+ "artist": "Handel",
+ "bpm": 55,
+ "duration": [
+ 552727273,
+ 414545455,
+ 331636364
+ ],
+ "id": "Handel-Ombra_Mai_Fu",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 274,
+ 383,
+ 451
+ ],
+ "title": "Ombra Mai Fu",
+ "url": "songs/classic/Handel-Ombra_Mai_Fu.mid"
+ },
+ "Handel-Sarabande": {
+ "__collections__": {},
+ "artist": "Handel",
+ "bpm": 120,
+ "duration": [
+ 195111111,
+ 146333333,
+ 117066667
+ ],
+ "id": "Handel-Sarabande",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 384,
+ 461,
+ 502
+ ],
+ "title": "Sarabande",
+ "url": "songs/classic/Handel-Sarabande.mid"
+ },
+ "Handel-Zadok_The_Priest": {
+ "__collections__": {},
+ "artist": "Handel",
+ "bpm": 72,
+ "duration": [
+ 53518519,
+ 40138889,
+ 32111111
+ ],
+ "id": "Handel-Zadok_The_Priest",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 281,
+ 370,
+ 459
+ ],
+ "title": "Zadok The Priest",
+ "url": "songs/classic/Handel-Zadok_The_Priest.mid"
+ },
+ "Handel_Halvorsen-Passacaglia": {
+ "__collections__": {},
+ "artist": "Handel Halvorsen",
+ "bpm": 130,
+ "duration": [
+ 180717949,
+ 135538462,
+ 108430769
+ ],
+ "id": "Handel_Halvorsen-Passacaglia",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 1022,
+ 1030,
+ 1030
+ ],
+ "title": "Passacaglia",
+ "url": "songs/other/Handel_Halvorsen-Passacaglia.mid"
+ },
+ "Hans_Zimmer-Interstellar,_Main_Theme": {
+ "__collections__": {},
+ "artist": "Hans Zimmer",
+ "bpm": 74,
+ "duration": [
+ 489099099,
+ 366824324,
+ 293459459
+ ],
+ "id": "Hans_Zimmer-Interstellar,_Main_Theme",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 1864,
+ 1967,
+ 1973
+ ],
+ "title": "Interstellar, Main Theme",
+ "url": "songs/other/Hans_Zimmer-Interstellar,_Main_Theme.mid"
+ },
+ "Haydn-The_Nutcracker_-_Russian_Dance": {
+ "__collections__": {},
+ "artist": "Haydn",
+ "bpm": 150,
+ "duration": [
+ 197422222,
+ 148066667,
+ 118453333
+ ],
+ "id": "Haydn-The_Nutcracker_-_Russian_Dance",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 738,
+ 1017,
+ 1212
+ ],
+ "title": "The Nutcracker - Russian Dance",
+ "url": "songs/classic/Haydn-The_Nutcracker_-_Russian_Dance.mid"
+ },
+ "Haydn-Trumpet_Concerto": {
+ "__collections__": {},
+ "artist": "Haydn",
+ "bpm": 120,
+ "duration": [
+ 771444444,
+ 578583333,
+ 462866667
+ ],
+ "id": "Haydn-Trumpet_Concerto",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 2375,
+ 2973,
+ 3328
+ ],
+ "title": "Trumpet Concerto",
+ "url": "songs/classic/Haydn-Trumpet_Concerto.mid"
+ },
+ "Imagine_Dragons-Believer": {
+ "__collections__": {},
+ "artist": "Imagine Dragons",
+ "bpm": 150,
+ "duration": [
+ 318488889,
+ 238866667,
+ 191093333
+ ],
+ "id": "Imagine_Dragons-Believer",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 1262,
+ 1441,
+ 1468
+ ],
+ "title": "Believer",
+ "url": "songs/pop/Imagine_Dragons-Believer.mid"
+ },
+ "Italian_Folk-Bella_Ciao": {
+ "__collections__": {},
+ "artist": "Italian Folk",
+ "bpm": 120,
+ "duration": [
+ 153333333,
+ 115000000,
+ 92000000
+ ],
+ "id": "Italian_Folk-Bella_Ciao",
+ "tags": [
+ "folk"
+ ],
+ "tilesCount": [
+ 843,
+ 1095,
+ 1178
+ ],
+ "title": "Bella Ciao",
+ "url": "songs/folk/Italian_Folk-Bella_Ciao.mid"
+ },
+ "Jacques_Offenbach-Barcarolle": {
+ "__collections__": {},
+ "artist": "Jacques Offenbach",
+ "bpm": 80,
+ "duration": [
+ 565333333,
+ 424000000,
+ 339200000
+ ],
+ "id": "Jacques_Offenbach-Barcarolle",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 823,
+ 896,
+ 912
+ ],
+ "title": "Barcarolle",
+ "url": "songs/classic/Jacques_Offenbach-Barcarolle.mid"
+ },
+ "Jacques_Offenbach-Orpheus_In_The_Underworld": {
+ "__collections__": {},
+ "artist": "Jacques Offenbach",
+ "bpm": 145,
+ "duration": [
+ 173977011,
+ 130482759,
+ 104386207
+ ],
+ "id": "Jacques_Offenbach-Orpheus_In_The_Underworld",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 996,
+ 1454,
+ 1780
+ ],
+ "title": "Orpheus In The Underworld",
+ "url": "songs/classic/Jacques_Offenbach-Orpheus_In_The_Underworld.mid"
+ },
+ "Janji-Heroes_Tonight": {
+ "__collections__": {},
+ "artist": "Janji",
+ "bpm": 120,
+ "duration": [
+ 291222222,
+ 218416667,
+ 174733333
+ ],
+ "id": "Janji-Heroes_Tonight",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 1064,
+ 1311,
+ 1420
+ ],
+ "title": "Heroes Tonight",
+ "url": "songs/pop/Janji-Heroes_Tonight.mid"
+ },
+ "Japanese_Folk-Sakura_Sakura": {
+ "__collections__": {},
+ "artist": "Japanese Folk",
+ "bpm": 120,
+ "duration": [
+ 73111111,
+ 54833333,
+ 43866667
+ ],
+ "id": "Japanese_Folk-Sakura_Sakura",
+ "tags": [
+ "folk"
+ ],
+ "tilesCount": [
+ 222,
+ 240,
+ 240
+ ],
+ "title": "Sakura Sakura",
+ "url": "songs/folk/Japanese_Folk-Sakura_Sakura.mid"
+ },
+ "Joe_Hisaishi-Innocent": {
+ "__collections__": {},
+ "artist": "Joe Hisaishi",
+ "bpm": 100,
+ "duration": [
+ 220594444,
+ 165445833,
+ 132356667
+ ],
+ "id": "Joe_Hisaishi-Innocent",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 403,
+ 453,
+ 485
+ ],
+ "title": "Innocent",
+ "url": "songs/other/Joe_Hisaishi-Innocent.mid"
+ },
+ "Joe_Hisaishi-Merry_Go_Round_of_Life,_Howls_Moving_Castle": {
+ "__collections__": {},
+ "artist": "Joe Hisaishi",
+ "bpm": 100,
+ "duration": [
+ 2071789444,
+ 1553842083,
+ 1243073667
+ ],
+ "id": "Joe_Hisaishi-Merry_Go_Round_of_Life,_Howls_Moving_Castle",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 1578,
+ 1988,
+ 2247
+ ],
+ "title": "Merry Go Round of Life, Howls Moving Castle",
+ "url": "songs/other/Joe_Hisaishi-Merry_Go_Round_of_Life,_Howls_Moving_Castle.mid"
+ },
+ "Joe_Hisaishi-One_Summers_Day,_Spirited_Away": {
+ "__collections__": {},
+ "artist": "Joe Hisaishi",
+ "bpm": 77,
+ "duration": [
+ 478229437,
+ 358672078,
+ 286937662
+ ],
+ "id": "Joe_Hisaishi-One_Summers_Day,_Spirited_Away",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 731,
+ 866,
+ 913
+ ],
+ "title": "One Summers Day, Spirited Away",
+ "url": "songs/other/Joe_Hisaishi-One_Summers_Day,_Spirited_Away.mid"
+ },
+ "Joe_Hisaishi-Summer": {
+ "__collections__": {},
+ "artist": "Joe Hisaishi",
+ "bpm": 75,
+ "duration": [
+ 233177778,
+ 174883333,
+ 139906667
+ ],
+ "id": "Joe_Hisaishi-Summer",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 930,
+ 1096,
+ 1201
+ ],
+ "title": "Summer",
+ "url": "songs/other/Joe_Hisaishi-Summer.mid"
+ },
+ "Johann_Strauss-Blue_Danube_Waltz": {
+ "__collections__": {},
+ "artist": "Johann Strauss",
+ "bpm": 115,
+ "duration": [
+ 1082579710,
+ 811934783,
+ 649547826
+ ],
+ "id": "Johann_Strauss-Blue_Danube_Waltz",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 3035,
+ 3934,
+ 4413
+ ],
+ "title": "Blue Danube Waltz",
+ "url": "songs/classic/Johann_Strauss-Blue_Danube_Waltz.mid"
+ },
+ "Johann_Strauss-Die_Fledermaus": {
+ "__collections__": {},
+ "artist": "Johann Strauss",
+ "bpm": 150,
+ "duration": [
+ 981155556,
+ 735866667,
+ 588693333
+ ],
+ "id": "Johann_Strauss-Die_Fledermaus",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 3633,
+ 4355,
+ 4686
+ ],
+ "title": "Die Fledermaus",
+ "url": "songs/classic/Johann_Strauss-Die_Fledermaus.mid"
+ },
+ "Johann_Strauss-Emperor_Waltz": {
+ "__collections__": {},
+ "artist": "Johann Strauss",
+ "bpm": 120,
+ "duration": [
+ 178444444,
+ 133833333,
+ 107066667
+ ],
+ "id": "Johann_Strauss-Emperor_Waltz",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 257,
+ 270,
+ 272
+ ],
+ "title": "Emperor Waltz",
+ "url": "songs/classic/Johann_Strauss-Emperor_Waltz.mid"
+ },
+ "Johann_Strauss-Radetzky_March": {
+ "__collections__": {},
+ "artist": "Johann Strauss",
+ "bpm": 200,
+ "duration": [
+ 850933333,
+ 638200000,
+ 510560000
+ ],
+ "id": "Johann_Strauss-Radetzky_March",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1201,
+ 1457,
+ 1649
+ ],
+ "title": "Radetzky March",
+ "url": "songs/classic/Johann_Strauss-Radetzky_March.mid"
+ },
+ "Johann_Strauss-Tritsch_Tratsch_Polka": {
+ "__collections__": {},
+ "artist": "Johann Strauss",
+ "bpm": 150,
+ "duration": [
+ 268503704,
+ 201377778,
+ 161102222
+ ],
+ "id": "Johann_Strauss-Tritsch_Tratsch_Polka",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1128,
+ 1343,
+ 1418
+ ],
+ "title": "Tritsch Tratsch Polka",
+ "url": "songs/classic/Johann_Strauss-Tritsch_Tratsch_Polka.mid"
+ },
+ "Johann_Strauss-Voices_Of_Spring": {
+ "__collections__": {},
+ "artist": "Johann Strauss",
+ "bpm": 149,
+ "duration": [
+ 1196420582,
+ 897315436,
+ 717852349
+ ],
+ "id": "Johann_Strauss-Voices_Of_Spring",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 3661,
+ 4507,
+ 4875
+ ],
+ "title": "Voices Of Spring",
+ "url": "songs/classic/Johann_Strauss-Voices_Of_Spring.mid"
+ },
+ "Johann_Strauss-Wiener_Blut": {
+ "__collections__": {},
+ "artist": "Johann Strauss",
+ "bpm": 120,
+ "duration": [
+ 180444444,
+ 135333333,
+ 108266667
+ ],
+ "id": "Johann_Strauss-Wiener_Blut",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 370,
+ 370,
+ 370
+ ],
+ "title": "Wiener Blut",
+ "url": "songs/classic/Johann_Strauss-Wiener_Blut.mid"
+ },
+ "Johannes_Brahms-Hungarian_Dance_No_5": {
+ "__collections__": {},
+ "artist": "Johannes Brahms",
+ "bpm": 130,
+ "duration": [
+ 296102564,
+ 222076923,
+ 177661538
+ ],
+ "id": "Johannes_Brahms-Hungarian_Dance_No_5",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1118,
+ 1450,
+ 1621
+ ],
+ "title": "Hungarian Dance No 5",
+ "url": "songs/classic/Johannes_Brahms-Hungarian_Dance_No_5.mid"
+ },
+ "Johannes_Brahms-Hungarian_Dance_No_6": {
+ "__collections__": {},
+ "artist": "Johannes Brahms",
+ "bpm": 80,
+ "duration": [
+ 563000000,
+ 422250000,
+ 337800000
+ ],
+ "id": "Johannes_Brahms-Hungarian_Dance_No_6",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1362,
+ 1846,
+ 2198
+ ],
+ "title": "Hungarian Dance No 6",
+ "url": "songs/classic/Johannes_Brahms-Hungarian_Dance_No_6.mid"
+ },
+ "John_Legend-All_Of_Me": {
+ "__collections__": {},
+ "artist": "John Legend",
+ "bpm": 120,
+ "duration": [
+ 382444444,
+ 286833333,
+ 229466667
+ ],
+ "id": "John_Legend-All_Of_Me",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 1026,
+ 1238,
+ 1296
+ ],
+ "title": "All Of Me",
+ "url": "songs/pop/John_Legend-All_Of_Me.mid"
+ },
+ "John_Lennon-Imagine": {
+ "__collections__": {},
+ "artist": "John Lennon",
+ "bpm": 70,
+ "duration": [
+ 345333333,
+ 259000000,
+ 207200000
+ ],
+ "id": "John_Lennon-Imagine",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 742,
+ 827,
+ 831
+ ],
+ "title": "Imagine",
+ "url": "songs/pop/John_Lennon-Imagine.mid"
+ },
+ "John_Philip_Sousa-Liberty_Bell": {
+ "__collections__": {},
+ "artist": "John Philip Sousa",
+ "bpm": 150,
+ "duration": [
+ 348918519,
+ 261688889,
+ 209351111
+ ],
+ "id": "John_Philip_Sousa-Liberty_Bell",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1724,
+ 2347,
+ 2740
+ ],
+ "title": "Liberty Bell",
+ "url": "songs/classic/John_Philip_Sousa-Liberty_Bell.mid"
+ },
+ "John_Philip_Sousa-Semper_Fidelis": {
+ "__collections__": {},
+ "artist": "John Philip Sousa",
+ "bpm": 149,
+ "duration": [
+ 216435496,
+ 162326622,
+ 129861298
+ ],
+ "id": "John_Philip_Sousa-Semper_Fidelis",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1065,
+ 1418,
+ 1672
+ ],
+ "title": "Semper Fidelis",
+ "url": "songs/classic/John_Philip_Sousa-Semper_Fidelis.mid"
+ },
+ "John_Philip_Sousa-Stars_And_Stripes_Forever": {
+ "__collections__": {},
+ "artist": "John Philip Sousa",
+ "bpm": 240,
+ "duration": [
+ 480888889,
+ 360666667,
+ 288533333
+ ],
+ "id": "John_Philip_Sousa-Stars_And_Stripes_Forever",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1687,
+ 2205,
+ 2402
+ ],
+ "title": "Stars And Stripes Forever",
+ "url": "songs/classic/John_Philip_Sousa-Stars_And_Stripes_Forever.mid"
+ },
+ "Jose_Feliciano-Feliz_Navidad": {
+ "__collections__": {},
+ "artist": "Jose Feliciano",
+ "bpm": 120,
+ "duration": [
+ 89444444,
+ 67083333,
+ 53666667
+ ],
+ "id": "Jose_Feliciano-Feliz_Navidad",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 245,
+ 297,
+ 320
+ ],
+ "title": "Feliz Navidad",
+ "url": "songs/other/Jose_Feliciano-Feliz_Navidad.mid"
+ },
+ "Julius_Fucik-Entry_Of_The_Gladiators": {
+ "__collections__": {},
+ "artist": "Julius Fucik",
+ "bpm": 200,
+ "duration": [
+ 1730933333,
+ 1298200000,
+ 1038560000
+ ],
+ "id": "Julius_Fucik-Entry_Of_The_Gladiators",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1933,
+ 2507,
+ 2788
+ ],
+ "title": "Entry Of The Gladiators",
+ "url": "songs/classic/Julius_Fucik-Entry_Of_The_Gladiators.mid"
+ },
+ "Juventino_Rosas-Over_The_Waves": {
+ "__collections__": {},
+ "artist": "Juventino Rosas",
+ "bpm": 120,
+ "duration": [
+ 765777778,
+ 574333333,
+ 459466667
+ ],
+ "id": "Juventino_Rosas-Over_The_Waves",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1750,
+ 2124,
+ 2152
+ ],
+ "title": "Over The Waves",
+ "url": "songs/classic/Juventino_Rosas-Over_The_Waves.mid"
+ },
+ "Karl_Jenkins-Palladio": {
+ "__collections__": {},
+ "artist": "Karl Jenkins",
+ "bpm": 75,
+ "duration": [
+ 323644444,
+ 242733333,
+ 194186667
+ ],
+ "id": "Karl_Jenkins-Palladio",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1778,
+ 2264,
+ 2635
+ ],
+ "title": "Palladio",
+ "url": "songs/classic/Karl_Jenkins-Palladio.mid"
+ },
+ "Kazumi_Totaka-Mii_Channel": {
+ "__collections__": {},
+ "artist": "Kazumi Totaka",
+ "bpm": 113,
+ "duration": [
+ 142064897,
+ 106548673,
+ 85238938
+ ],
+ "id": "Kazumi_Totaka-Mii_Channel",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 452,
+ 538,
+ 578
+ ],
+ "title": "Mii Channel",
+ "url": "songs/other/Kazumi_Totaka-Mii_Channel.mid"
+ },
+ "Ketelbey-In_A_Persian_Market": {
+ "__collections__": {},
+ "artist": "Ketelbey",
+ "bpm": 89,
+ "duration": [
+ 1234019975,
+ 925514981,
+ 740411985
+ ],
+ "id": "Ketelbey-In_A_Persian_Market",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 2175,
+ 2611,
+ 2883
+ ],
+ "title": "In A Persian Market",
+ "url": "songs/classic/Ketelbey-In_A_Persian_Market.mid"
+ },
+ "Kimi_No_Na_Wa-Sparkle": {
+ "__collections__": {},
+ "artist": "Kimi No Na Wa",
+ "bpm": 150,
+ "duration": [
+ 674592222,
+ 505944167,
+ 404755333
+ ],
+ "id": "Kimi_No_Na_Wa-Sparkle",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 2472,
+ 2751,
+ 2894
+ ],
+ "title": "Sparkle",
+ "url": "songs/other/Kimi_No_Na_Wa-Sparkle.mid"
+ },
+ "Korean_Folk-Arirang": {
+ "__collections__": {},
+ "artist": "Korean Folk",
+ "bpm": 80,
+ "duration": [
+ 142666667,
+ 107000000,
+ 85600000
+ ],
+ "id": "Korean_Folk-Arirang",
+ "tags": [
+ "folk"
+ ],
+ "tilesCount": [
+ 408,
+ 408,
+ 408
+ ],
+ "title": "Arirang",
+ "url": "songs/folk/Korean_Folk-Arirang.mid"
+ },
+ "Leonard_Cohen-Hallelujah": {
+ "__collections__": {},
+ "artist": "Leonard Cohen",
+ "bpm": 89,
+ "duration": [
+ 165425094,
+ 124068820,
+ 99255056
+ ],
+ "id": "Leonard_Cohen-Hallelujah",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 482,
+ 508,
+ 508
+ ],
+ "title": "Hallelujah",
+ "url": "songs/other/Leonard_Cohen-Hallelujah.mid"
+ },
+ "Lewis_Capaldi-Someone_You_Loved": {
+ "__collections__": {},
+ "artist": "Lewis Capaldi",
+ "bpm": 109,
+ "duration": [
+ 239755352,
+ 179816514,
+ 143853211
+ ],
+ "id": "Lewis_Capaldi-Someone_You_Loved",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 1051,
+ 1281,
+ 1419
+ ],
+ "title": "Someone You Loved",
+ "url": "songs/other/Lewis_Capaldi-Someone_You_Loved.mid"
+ },
+ "Liszt-Etude_In_G_Minor_La_Campanella": {
+ "__collections__": {},
+ "artist": "Liszt",
+ "bpm": 75,
+ "duration": [
+ 734718519,
+ 551038889,
+ 440831111
+ ],
+ "id": "Liszt-Etude_In_G_Minor_La_Campanella",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 3150,
+ 3647,
+ 3964
+ ],
+ "title": "Etude In G Minor La Campanella",
+ "url": "songs/classic/Liszt-Etude_In_G_Minor_La_Campanella.mid"
+ },
+ "Liszt-Liebestraum_No._3": {
+ "__collections__": {},
+ "artist": "Liszt",
+ "bpm": 149,
+ "duration": [
+ 341953766,
+ 256465324,
+ 205172260
+ ],
+ "id": "Liszt-Liebestraum_No._3",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1578,
+ 1786,
+ 1860
+ ],
+ "title": "Liebestraum No. 3",
+ "url": "songs/classic/Liszt-Liebestraum_No._3.mid"
+ },
+ "Ludovico_Einaudi-Experience": {
+ "__collections__": {},
+ "artist": "Ludovico Einaudi",
+ "bpm": 72,
+ "duration": [
+ 499351852,
+ 374513889,
+ 299611111
+ ],
+ "id": "Ludovico_Einaudi-Experience",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 2534,
+ 2572,
+ 2597
+ ],
+ "title": "Experience",
+ "url": "songs/other/Ludovico_Einaudi-Experience.mid"
+ },
+ "Ludovico_Einaudi-Una_Mattina": {
+ "__collections__": {},
+ "artist": "Ludovico Einaudi",
+ "bpm": 60,
+ "duration": [
+ 267222222,
+ 200416667,
+ 160333333
+ ],
+ "id": "Ludovico_Einaudi-Una_Mattina",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 776,
+ 776,
+ 776
+ ],
+ "title": "Una Mattina",
+ "url": "songs/other/Ludovico_Einaudi-Una_Mattina.mid"
+ },
+ "Ludovico_Einoudi-Nuvole_Bianche": {
+ "__collections__": {},
+ "artist": "Ludovico Einoudi",
+ "bpm": 40,
+ "duration": [
+ 1204777778,
+ 903583333,
+ 722866667
+ ],
+ "id": "Ludovico_Einoudi-Nuvole_Bianche",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 1656,
+ 1707,
+ 1740
+ ],
+ "title": "Nuvole Bianche",
+ "url": "songs/other/Ludovico_Einoudi-Nuvole_Bianche.mid"
+ },
+ "Luigi_Denza-Funiculi_Funicula": {
+ "__collections__": {},
+ "artist": "Luigi Denza",
+ "bpm": 150,
+ "duration": [
+ 119911111,
+ 89933333,
+ 71946667
+ ],
+ "id": "Luigi_Denza-Funiculi_Funicula",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 164,
+ 216,
+ 244
+ ],
+ "title": "Funiculi Funicula",
+ "url": "songs/classic/Luigi_Denza-Funiculi_Funicula.mid"
+ },
+ "Luis_Fonsi_ft_Daddy_Yankee-Despacito": {
+ "__collections__": {},
+ "artist": "Luis Fonsi ft Daddy Yankee",
+ "bpm": 75,
+ "duration": [
+ 367274074,
+ 275455556,
+ 220364444
+ ],
+ "id": "Luis_Fonsi_ft_Daddy_Yankee-Despacito",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 1537,
+ 1723,
+ 1747
+ ],
+ "title": "Despacito",
+ "url": "songs/pop/Luis_Fonsi_ft_Daddy_Yankee-Despacito.mid"
+ },
+ "Mariah_Carey-All_I_Want_For_Christmas_Is_You": {
+ "__collections__": {},
+ "artist": "Mariah Carey",
+ "bpm": 100,
+ "duration": [
+ 472613333,
+ 354460000,
+ 283568000
+ ],
+ "id": "Mariah_Carey-All_I_Want_For_Christmas_Is_You",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 1758,
+ 2224,
+ 2443
+ ],
+ "title": "All I Want For Christmas Is You",
+ "url": "songs/pop/Mariah_Carey-All_I_Want_For_Christmas_Is_You.mid"
+ },
+ "Megalovania-Undertale": {
+ "__collections__": {},
+ "artist": "Megalovania",
+ "bpm": 75,
+ "duration": [
+ 338970370,
+ 254227778,
+ 203382222
+ ],
+ "id": "Megalovania-Undertale",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 1506,
+ 1711,
+ 1811
+ ],
+ "title": "Undertale",
+ "url": "songs/other/Megalovania-Undertale.mid"
+ },
+ "Mendelssohn_Bartholdy-Wedding_March": {
+ "__collections__": {},
+ "artist": "Mendelssohn Bartholdy",
+ "bpm": 37,
+ "duration": [
+ 344009009,
+ 258006757,
+ 206405405
+ ],
+ "id": "Mendelssohn_Bartholdy-Wedding_March",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 414,
+ 515,
+ 559
+ ],
+ "title": "Wedding March",
+ "url": "songs/classic/Mendelssohn_Bartholdy-Wedding_March.mid"
+ },
+ "Meredith_Willson-It's_Beginning_To_Look_A_Lot_Like_Christmas": {
+ "__collections__": {},
+ "artist": "Meredith Willson",
+ "bpm": 120,
+ "duration": [
+ 64000000,
+ 48000000,
+ 38400000
+ ],
+ "id": "Meredith_Willson-It's_Beginning_To_Look_A_Lot_Like_Christmas",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 173,
+ 192,
+ 200
+ ],
+ "title": "It's Beginning To Look A Lot Like Christmas",
+ "url": "songs/other/Meredith_Willson-It's_Beginning_To_Look_A_Lot_Like_Christmas.mid"
+ },
+ "Michael_Buble-White_Christmas": {
+ "__collections__": {},
+ "artist": "Michael Buble",
+ "bpm": 120,
+ "duration": [
+ 94666667,
+ 71000000,
+ 56800000
+ ],
+ "id": "Michael_Buble-White_Christmas",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 153,
+ 165,
+ 165
+ ],
+ "title": "White Christmas",
+ "url": "songs/other/Michael_Buble-White_Christmas.mid"
+ },
+ "Mozart-Aria_Queen_Of_The_Night": {
+ "__collections__": {},
+ "artist": "Mozart",
+ "bpm": 140,
+ "duration": [
+ 326619048,
+ 244964286,
+ 195971429
+ ],
+ "id": "Mozart-Aria_Queen_Of_The_Night",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1166,
+ 1296,
+ 1311
+ ],
+ "title": "Aria Queen Of The Night",
+ "url": "songs/classic/Mozart-Aria_Queen_Of_The_Night.mid"
+ },
+ "Mozart-Eine_Kleine_Nachtmusik": {
+ "__collections__": {},
+ "artist": "Mozart",
+ "bpm": 150,
+ "duration": [
+ 211822222,
+ 158866667,
+ 127093333
+ ],
+ "id": "Mozart-Eine_Kleine_Nachtmusik",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 840,
+ 1074,
+ 1155
+ ],
+ "title": "Eine Kleine Nachtmusik",
+ "url": "songs/classic/Mozart-Eine_Kleine_Nachtmusik.mid"
+ },
+ "Mozart-Lacrimosa": {
+ "__collections__": {},
+ "artist": "Mozart",
+ "bpm": 120,
+ "duration": [
+ 117111111,
+ 87833333,
+ 70266667
+ ],
+ "id": "Mozart-Lacrimosa",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 491,
+ 603,
+ 679
+ ],
+ "title": "Lacrimosa",
+ "url": "songs/classic/Mozart-Lacrimosa.mid"
+ },
+ "Mozart-Marriage_Of_Figaro": {
+ "__collections__": {},
+ "artist": "Mozart",
+ "bpm": 150,
+ "duration": [
+ 661511111,
+ 496133333,
+ 396906667
+ ],
+ "id": "Mozart-Marriage_Of_Figaro",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 2473,
+ 3107,
+ 3506
+ ],
+ "title": "Marriage Of Figaro",
+ "url": "songs/classic/Mozart-Marriage_Of_Figaro.mid"
+ },
+ "Mozart-Piano_Sonata_No._11": {
+ "__collections__": {},
+ "artist": "Mozart",
+ "bpm": 75,
+ "duration": [
+ 573488889,
+ 430116667,
+ 344093333
+ ],
+ "id": "Mozart-Piano_Sonata_No._11",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 2346,
+ 2787,
+ 2823
+ ],
+ "title": "Piano Sonata No. 11",
+ "url": "songs/classic/Mozart-Piano_Sonata_No._11.mid"
+ },
+ "Mozart-Requiem": {
+ "__collections__": {},
+ "artist": "Mozart",
+ "bpm": 64,
+ "duration": [
+ 219583333,
+ 164687500,
+ 131750000
+ ],
+ "id": "Mozart-Requiem",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 491,
+ 603,
+ 679
+ ],
+ "title": "Requiem",
+ "url": "songs/classic/Mozart-Requiem.mid"
+ },
+ "Mozart-Rondo_Alla_Turca": {
+ "__collections__": {},
+ "artist": "Mozart",
+ "bpm": 75,
+ "duration": [
+ 571333333,
+ 428500000,
+ 342800000
+ ],
+ "id": "Mozart-Rondo_Alla_Turca",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 2344,
+ 2785,
+ 2823
+ ],
+ "title": "Rondo Alla Turca",
+ "url": "songs/classic/Mozart-Rondo_Alla_Turca.mid"
+ },
+ "Mozart-Symphony_No._25": {
+ "__collections__": {},
+ "artist": "Mozart",
+ "bpm": 150,
+ "duration": [
+ 854844444,
+ 641133333,
+ 512906667
+ ],
+ "id": "Mozart-Symphony_No._25",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 3537,
+ 4311,
+ 4551
+ ],
+ "title": "Symphony No. 25",
+ "url": "songs/classic/Mozart-Symphony_No._25.mid"
+ },
+ "Mozart-Symphony_No._40_in_G": {
+ "__collections__": {},
+ "artist": "Mozart",
+ "bpm": 149,
+ "duration": [
+ 442953020,
+ 332214765,
+ 265771812
+ ],
+ "id": "Mozart-Symphony_No._40_in_G",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 2074,
+ 2394,
+ 2480
+ ],
+ "title": "Symphony No. 40 in G",
+ "url": "songs/classic/Mozart-Symphony_No._40_in_G.mid"
+ },
+ "Mozart-Turkish_March": {
+ "__collections__": {},
+ "artist": "Mozart",
+ "bpm": 75,
+ "duration": [
+ 565488889,
+ 424116667,
+ 339293333
+ ],
+ "id": "Mozart-Turkish_March",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 2324,
+ 2767,
+ 2803
+ ],
+ "title": "Turkish March",
+ "url": "songs/classic/Mozart-Turkish_March.mid"
+ },
+ "Mr_Grinch-You're_A_Mean_One": {
+ "__collections__": {},
+ "artist": "Mr Grinch",
+ "bpm": 150,
+ "duration": [
+ 405870370,
+ 304402778,
+ 243522222
+ ],
+ "id": "Mr_Grinch-You're_A_Mean_One",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 1245,
+ 1582,
+ 1775
+ ],
+ "title": "You're A Mean One",
+ "url": "songs/other/Mr_Grinch-You're_A_Mean_One.mid"
+ },
+ "Mykola_Leontovych-Carol_Of_The_Bells": {
+ "__collections__": {},
+ "artist": "Mykola Leontovych",
+ "bpm": 150,
+ "duration": [
+ 196566667,
+ 147425000,
+ 117940000
+ ],
+ "id": "Mykola_Leontovych-Carol_Of_The_Bells",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 974,
+ 1138,
+ 1234
+ ],
+ "title": "Carol Of The Bells",
+ "url": "songs/other/Mykola_Leontovych-Carol_Of_The_Bells.mid"
+ },
+ "One_Republic-Counting_Stars": {
+ "__collections__": {},
+ "artist": "One Republic",
+ "bpm": 120,
+ "duration": [
+ 42555556,
+ 31916667,
+ 25533333
+ ],
+ "id": "One_Republic-Counting_Stars",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 102,
+ 110,
+ 110
+ ],
+ "title": "Counting Stars",
+ "url": "songs/pop/One_Republic-Counting_Stars.mid"
+ },
+ "Pachelbel-Canon_In_D": {
+ "__collections__": {},
+ "artist": "Pachelbel",
+ "bpm": 100,
+ "duration": [
+ 482688333,
+ 362016250,
+ 289613000
+ ],
+ "id": "Pachelbel-Canon_In_D",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 1490,
+ 1566,
+ 1589
+ ],
+ "title": "Canon In D",
+ "url": "songs/other/Pachelbel-Canon_In_D.mid"
+ },
+ "Paganini-Caprice_No._24": {
+ "__collections__": {},
+ "artist": "Paganini",
+ "bpm": 75,
+ "duration": [
+ 237474074,
+ 178105556,
+ 142484444
+ ],
+ "id": "Paganini-Caprice_No._24",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 961,
+ 1168,
+ 1238
+ ],
+ "title": "Caprice No. 24",
+ "url": "songs/classic/Paganini-Caprice_No._24.mid"
+ },
+ "Paul_De_Senneville-Mariage_d'Amour": {
+ "__collections__": {},
+ "artist": "Paul De Senneville",
+ "bpm": 75,
+ "duration": [
+ 363037778,
+ 272278333,
+ 217822667
+ ],
+ "id": "Paul_De_Senneville-Mariage_d'Amour",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 1618,
+ 1623,
+ 1625
+ ],
+ "title": "Mariage d'Amour",
+ "url": "songs/other/Paul_De_Senneville-Mariage_d'Amour.mid"
+ },
+ "Ponchielli-Dance_Of_The_Hours": {
+ "__collections__": {},
+ "artist": "Ponchielli",
+ "bpm": 149,
+ "duration": [
+ 300134228,
+ 225100671,
+ 180080537
+ ],
+ "id": "Ponchielli-Dance_Of_The_Hours",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1251,
+ 1605,
+ 1770
+ ],
+ "title": "Dance Of The Hours",
+ "url": "songs/classic/Ponchielli-Dance_Of_The_Hours.mid"
+ },
+ "Purcell-Funeral_Of_Queen_Mary": {
+ "__collections__": {},
+ "artist": "Purcell",
+ "bpm": 75,
+ "duration": [
+ 136977778,
+ 102733333,
+ 82186667
+ ],
+ "id": "Purcell-Funeral_Of_Queen_Mary",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 150,
+ 179,
+ 207
+ ],
+ "title": "Funeral Of Queen Mary",
+ "url": "songs/classic/Purcell-Funeral_Of_Queen_Mary.mid"
+ },
+ "Rachmaninoff-Prelude_In_C_Sharp_Minor": {
+ "__collections__": {},
+ "artist": "Rachmaninoff",
+ "bpm": 40,
+ "duration": [
+ 495851852,
+ 371888889,
+ 297511111
+ ],
+ "id": "Rachmaninoff-Prelude_In_C_Sharp_Minor",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 932,
+ 1197,
+ 1346
+ ],
+ "title": "Prelude In C Sharp Minor",
+ "url": "songs/classic/Rachmaninoff-Prelude_In_C_Sharp_Minor.mid"
+ },
+ "Rachmaninoff-Rhapsody_On_A_Theme_Of_Paganini": {
+ "__collections__": {},
+ "artist": "Rachmaninoff",
+ "bpm": 60,
+ "duration": [
+ 218641975,
+ 163981481,
+ 131185185
+ ],
+ "id": "Rachmaninoff-Rhapsody_On_A_Theme_Of_Paganini",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 652,
+ 831,
+ 961
+ ],
+ "title": "Rhapsody On A Theme Of Paganini",
+ "url": "songs/classic/Rachmaninoff-Rhapsody_On_A_Theme_Of_Paganini.mid"
+ },
+ "Ramin_Djawadi-Game_Of_Thrones": {
+ "__collections__": {},
+ "artist": "Ramin Djawadi",
+ "bpm": 150,
+ "duration": [
+ 146488889,
+ 109866667,
+ 87893333
+ ],
+ "id": "Ramin_Djawadi-Game_Of_Thrones",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 523,
+ 646,
+ 661
+ ],
+ "title": "Game Of Thrones",
+ "url": "songs/other/Ramin_Djawadi-Game_Of_Thrones.mid"
+ },
+ "Respighi-Pines_Of_The_Appian_Way": {
+ "__collections__": {},
+ "artist": "Respighi",
+ "bpm": 120,
+ "duration": [
+ 105888889,
+ 79416667,
+ 63533333
+ ],
+ "id": "Respighi-Pines_Of_The_Appian_Way",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 317,
+ 366,
+ 385
+ ],
+ "title": "Pines Of The Appian Way",
+ "url": "songs/classic/Respighi-Pines_Of_The_Appian_Way.mid"
+ },
+ "Richard_Clayderman-Ballade_Pour_Adeline": {
+ "__collections__": {},
+ "artist": "Richard Clayderman",
+ "bpm": 60,
+ "duration": [
+ 257733333,
+ 193300000,
+ 154640000
+ ],
+ "id": "Richard_Clayderman-Ballade_Pour_Adeline",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 782,
+ 883,
+ 894
+ ],
+ "title": "Ballade Pour Adeline",
+ "url": "songs/other/Richard_Clayderman-Ballade_Pour_Adeline.mid"
+ },
+ "Richard_Wagner-Tristan_And_Isolde": {
+ "__collections__": {},
+ "artist": "Richard Wagner",
+ "bpm": 37,
+ "duration": [
+ 707042042,
+ 530281532,
+ 424225225
+ ],
+ "id": "Richard_Wagner-Tristan_And_Isolde",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 2480,
+ 2814,
+ 2943
+ ],
+ "title": "Tristan And Isolde",
+ "url": "songs/classic/Richard_Wagner-Tristan_And_Isolde.mid"
+ },
+ "Rick_Astley-Never_Gonna_Give_You_Up": {
+ "__collections__": {},
+ "artist": "Rick Astley",
+ "bpm": 113,
+ "duration": [
+ 452743363,
+ 339557522,
+ 271646018
+ ],
+ "id": "Rick_Astley-Never_Gonna_Give_You_Up",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 1133,
+ 1353,
+ 1519
+ ],
+ "title": "Never Gonna Give You Up",
+ "url": "songs/other/Rick_Astley-Never_Gonna_Give_You_Up.mid"
+ },
+ "Ricketts-Colonel_Bogey_March": {
+ "__collections__": {},
+ "artist": "Ricketts",
+ "bpm": 207,
+ "duration": [
+ 819838969,
+ 614879227,
+ 491903382
+ ],
+ "id": "Ricketts-Colonel_Bogey_March",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 2081,
+ 2675,
+ 3016
+ ],
+ "title": "Colonel Bogey March",
+ "url": "songs/classic/Ricketts-Colonel_Bogey_March.mid"
+ },
+ "Rimsky_Korsakov-Flight_Of_The_Bumblebee": {
+ "__collections__": {},
+ "artist": "Rimsky Korsakov",
+ "bpm": 75,
+ "duration": [
+ 220888889,
+ 165666667,
+ 132533333
+ ],
+ "id": "Rimsky_Korsakov-Flight_Of_The_Bumblebee",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 981,
+ 1069,
+ 1125
+ ],
+ "title": "Flight Of The Bumblebee",
+ "url": "songs/classic/Rimsky_Korsakov-Flight_Of_The_Bumblebee.mid"
+ },
+ "Rossini-Barber_Of_Seville_-_Overture": {
+ "__collections__": {},
+ "artist": "Rossini",
+ "bpm": 80,
+ "duration": [
+ 136000000,
+ 102000000,
+ 81600000
+ ],
+ "id": "Rossini-Barber_Of_Seville_-_Overture",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 281,
+ 353,
+ 383
+ ],
+ "title": "Barber Of Seville - Overture",
+ "url": "songs/classic/Rossini-Barber_Of_Seville_-_Overture.mid"
+ },
+ "Rossini-William_Tell_Overture_Finale": {
+ "__collections__": {},
+ "artist": "Rossini",
+ "bpm": 75,
+ "duration": [
+ 477511111,
+ 358133333,
+ 286506667
+ ],
+ "id": "Rossini-William_Tell_Overture_Finale",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 2153,
+ 2748,
+ 3018
+ ],
+ "title": "William Tell Overture Finale",
+ "url": "songs/classic/Rossini-William_Tell_Overture_Finale.mid"
+ },
+ "Russian_Folk-Kalinka": {
+ "__collections__": {},
+ "artist": "Russian Folk",
+ "bpm": 120,
+ "duration": [
+ 75111111,
+ 56333333,
+ 45066667
+ ],
+ "id": "Russian_Folk-Kalinka",
+ "tags": [
+ "folk"
+ ],
+ "tilesCount": [
+ 210,
+ 269,
+ 328
+ ],
+ "title": "Kalinka",
+ "url": "songs/folk/Russian_Folk-Kalinka.mid"
+ },
+ "Russian_Folk-Katyusha": {
+ "__collections__": {},
+ "artist": "Russian Folk",
+ "bpm": 120,
+ "duration": [
+ 151555556,
+ 113666667,
+ 90933333
+ ],
+ "id": "Russian_Folk-Katyusha",
+ "tags": [
+ "folk"
+ ],
+ "tilesCount": [
+ 653,
+ 794,
+ 855
+ ],
+ "title": "Katyusha",
+ "url": "songs/folk/Russian_Folk-Katyusha.mid"
+ },
+ "Russian_Folk-Korobeiniki": {
+ "__collections__": {},
+ "artist": "Russian Folk",
+ "bpm": 125,
+ "duration": [
+ 104320000,
+ 78240000,
+ 62592000
+ ],
+ "id": "Russian_Folk-Korobeiniki",
+ "tags": [
+ "folk"
+ ],
+ "tilesCount": [
+ 473,
+ 483,
+ 493
+ ],
+ "title": "Korobeiniki",
+ "url": "songs/folk/Russian_Folk-Korobeiniki.mid"
+ },
+ "Russian_Folk-Oy,_Da_Ne_Vecher": {
+ "__collections__": {},
+ "artist": "Russian Folk",
+ "bpm": 60,
+ "duration": [
+ 105851852,
+ 79388889,
+ 63511111
+ ],
+ "id": "Russian_Folk-Oy,_Da_Ne_Vecher",
+ "tags": [
+ "folk"
+ ],
+ "tilesCount": [
+ 213,
+ 220,
+ 221
+ ],
+ "title": "Oy, Da Ne Vecher",
+ "url": "songs/folk/Russian_Folk-Oy,_Da_Ne_Vecher.mid"
+ },
+ "Russian_Folk-Polyushka_Polye": {
+ "__collections__": {},
+ "artist": "Russian Folk",
+ "bpm": 120,
+ "duration": [
+ 101777778,
+ 76333333,
+ 61066667
+ ],
+ "id": "Russian_Folk-Polyushka_Polye",
+ "tags": [
+ "folk"
+ ],
+ "tilesCount": [
+ 221,
+ 222,
+ 222
+ ],
+ "title": "Polyushka Polye",
+ "url": "songs/folk/Russian_Folk-Polyushka_Polye.mid"
+ },
+ "Russian_Folk-Smuglyanka": {
+ "__collections__": {},
+ "artist": "Russian Folk",
+ "bpm": 120,
+ "duration": [
+ 258666667,
+ 194000000,
+ 155200000
+ ],
+ "id": "Russian_Folk-Smuglyanka",
+ "tags": [
+ "folk"
+ ],
+ "tilesCount": [
+ 799,
+ 1012,
+ 1117
+ ],
+ "title": "Smuglyanka",
+ "url": "songs/folk/Russian_Folk-Smuglyanka.mid"
+ },
+ "Russian_Folk-Troika": {
+ "__collections__": {},
+ "artist": "Russian Folk",
+ "bpm": 69,
+ "duration": [
+ 855966184,
+ 641974638,
+ 513579710
+ ],
+ "id": "Russian_Folk-Troika",
+ "tags": [
+ "folk"
+ ],
+ "tilesCount": [
+ 1174,
+ 1318,
+ 1337
+ ],
+ "title": "Troika",
+ "url": "songs/folk/Russian_Folk-Troika.mid"
+ },
+ "Russian_Folk-Tumbalalaika": {
+ "__collections__": {},
+ "artist": "Russian Folk",
+ "bpm": 180,
+ "duration": [
+ 188148148,
+ 141111111,
+ 112888889
+ ],
+ "id": "Russian_Folk-Tumbalalaika",
+ "tags": [
+ "folk"
+ ],
+ "tilesCount": [
+ 396,
+ 523,
+ 543
+ ],
+ "title": "Tumbalalaika",
+ "url": "songs/folk/Russian_Folk-Tumbalalaika.mid"
+ },
+ "SHINee-Hello": {
+ "__collections__": {},
+ "artist": "SHINee",
+ "bpm": 120,
+ "duration": [
+ 296000000,
+ 222000000,
+ 177600000
+ ],
+ "id": "SHINee-Hello",
+ "tags": [
+ "kpop"
+ ],
+ "tilesCount": [
+ 1120,
+ 1229,
+ 1298
+ ],
+ "title": "Hello",
+ "url": "songs/kpop/SHINee-Hello.mid"
+ },
+ "Saint_Saens-Carnival_Of_The_Animals_-_Aquarium": {
+ "__collections__": {},
+ "artist": "Saint Saens",
+ "bpm": 37,
+ "duration": [
+ 359729730,
+ 269797297,
+ 215837838
+ ],
+ "id": "Saint_Saens-Carnival_Of_The_Animals_-_Aquarium",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1698,
+ 1744,
+ 1759
+ ],
+ "title": "Carnival Of The Animals - Aquarium",
+ "url": "songs/classic/Saint_Saens-Carnival_Of_The_Animals_-_Aquarium.mid"
+ },
+ "Saint_Saens-Carnival_Of_The_Animals_-_Finale": {
+ "__collections__": {},
+ "artist": "Saint Saens",
+ "bpm": 74,
+ "duration": [
+ 440090090,
+ 330067568,
+ 264054054
+ ],
+ "id": "Saint_Saens-Carnival_Of_The_Animals_-_Finale",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1902,
+ 2395,
+ 2756
+ ],
+ "title": "Carnival Of The Animals - Finale",
+ "url": "songs/classic/Saint_Saens-Carnival_Of_The_Animals_-_Finale.mid"
+ },
+ "Saint_Saens-Danse_Macabre": {
+ "__collections__": {},
+ "artist": "Saint Saens",
+ "bpm": 149,
+ "duration": [
+ 216733781,
+ 162550336,
+ 130040268
+ ],
+ "id": "Saint_Saens-Danse_Macabre",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1020,
+ 1360,
+ 1622
+ ],
+ "title": "Danse Macabre",
+ "url": "songs/classic/Saint_Saens-Danse_Macabre.mid"
+ },
+ "Saint_Saens-Symphony_No._3": {
+ "__collections__": {},
+ "artist": "Saint Saens",
+ "bpm": 75,
+ "duration": [
+ 204800000,
+ 153600000,
+ 122880000
+ ],
+ "id": "Saint_Saens-Symphony_No._3",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 482,
+ 632,
+ 769
+ ],
+ "title": "Symphony No. 3",
+ "url": "songs/classic/Saint_Saens-Symphony_No._3.mid"
+ },
+ "Samuel_Barber-Adagio_For_Strings": {
+ "__collections__": {},
+ "artist": "Samuel Barber",
+ "bpm": 83,
+ "duration": [
+ 542329317,
+ 406746988,
+ 325397590
+ ],
+ "id": "Samuel_Barber-Adagio_For_Strings",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 597,
+ 693,
+ 750
+ ],
+ "title": "Adagio For Strings",
+ "url": "songs/classic/Samuel_Barber-Adagio_For_Strings.mid"
+ },
+ "Schubert-Ave_Maria": {
+ "__collections__": {},
+ "artist": "Schubert",
+ "bpm": 37,
+ "duration": [
+ 258903904,
+ 194177928,
+ 155342342
+ ],
+ "id": "Schubert-Ave_Maria",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1070,
+ 1073,
+ 1074
+ ],
+ "title": "Ave Maria",
+ "url": "songs/classic/Schubert-Ave_Maria.mid"
+ },
+ "Scott_Joplin-Maple_Leaf_Rag": {
+ "__collections__": {},
+ "artist": "Scott Joplin",
+ "bpm": 74,
+ "duration": [
+ 311261261,
+ 233445946,
+ 186756757
+ ],
+ "id": "Scott_Joplin-Maple_Leaf_Rag",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 1703,
+ 2121,
+ 2409
+ ],
+ "title": "Maple Leaf Rag",
+ "url": "songs/other/Scott_Joplin-Maple_Leaf_Rag.mid"
+ },
+ "Shakira-Waka_Waka_(This_Time_For_Africa)": {
+ "__collections__": {},
+ "artist": "Shakira",
+ "bpm": 75,
+ "duration": [
+ 410607407,
+ 307955556,
+ 246364444
+ ],
+ "id": "Shakira-Waka_Waka_(This_Time_For_Africa)",
+ "tags": [
+ "pop"
+ ],
+ "tilesCount": [
+ 1632,
+ 1796,
+ 1820
+ ],
+ "title": "Waka Waka (This Time For Africa)",
+ "url": "songs/pop/Shakira-Waka_Waka_(This_Time_For_Africa).mid"
+ },
+ "Shostakovich-Waltz_No._2": {
+ "__collections__": {},
+ "artist": "Shostakovich",
+ "bpm": 169,
+ "duration": [
+ 533648915,
+ 400236686,
+ 320189349
+ ],
+ "id": "Shostakovich-Waltz_No._2",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 1415,
+ 1934,
+ 2236
+ ],
+ "title": "Waltz No. 2",
+ "url": "songs/other/Shostakovich-Waltz_No._2.mid"
+ },
+ "Smetana-Moldau_River": {
+ "__collections__": {},
+ "artist": "Smetana",
+ "bpm": 120,
+ "duration": [
+ 151111111,
+ 113333333,
+ 90666667
+ ],
+ "id": "Smetana-Moldau_River",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 559,
+ 711,
+ 805
+ ],
+ "title": "Moldau River",
+ "url": "songs/classic/Smetana-Moldau_River.mid"
+ },
+ "Stravinsky-Firebird_Suite_-_Finale": {
+ "__collections__": {},
+ "artist": "Stravinsky",
+ "bpm": 75,
+ "duration": [
+ 486844444,
+ 365133333,
+ 292106667
+ ],
+ "id": "Stravinsky-Firebird_Suite_-_Finale",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1520,
+ 1708,
+ 1794
+ ],
+ "title": "Firebird Suite - Finale",
+ "url": "songs/classic/Stravinsky-Firebird_Suite_-_Finale.mid"
+ },
+ "Takahiro_Obata-Isabella's_Lullaby": {
+ "__collections__": {},
+ "artist": "Takahiro Obata",
+ "bpm": 112,
+ "duration": [
+ 198893849,
+ 149170387,
+ 119336310
+ ],
+ "id": "Takahiro_Obata-Isabella's_Lullaby",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 502,
+ 615,
+ 656
+ ],
+ "title": "Isabella's Lullaby",
+ "url": "songs/other/Takahiro_Obata-Isabella's_Lullaby.mid"
+ },
+ "Tchaikovsky-Dance_Of_The_Little_Swans": {
+ "__collections__": {},
+ "artist": "Tchaikovsky",
+ "bpm": 100,
+ "duration": [
+ 203200000,
+ 152400000,
+ 121920000
+ ],
+ "id": "Tchaikovsky-Dance_Of_The_Little_Swans",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 598,
+ 763,
+ 810
+ ],
+ "title": "Dance Of The Little Swans",
+ "url": "songs/classic/Tchaikovsky-Dance_Of_The_Little_Swans.mid"
+ },
+ "Tchaikovsky-Dance_Of_The_Reed_Flutes": {
+ "__collections__": {},
+ "artist": "Tchaikovsky",
+ "bpm": 69,
+ "duration": [
+ 206932367,
+ 155199275,
+ 124159420
+ ],
+ "id": "Tchaikovsky-Dance_Of_The_Reed_Flutes",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 993,
+ 1288,
+ 1416
+ ],
+ "title": "Dance Of The Reed Flutes",
+ "url": "songs/classic/Tchaikovsky-Dance_Of_The_Reed_Flutes.mid"
+ },
+ "Tchaikovsky-Dance_Of_The_Sugar_Plum_Fairy": {
+ "__collections__": {},
+ "artist": "Tchaikovsky",
+ "bpm": 18,
+ "duration": [
+ 469004630,
+ 351753472,
+ 281402778
+ ],
+ "id": "Tchaikovsky-Dance_Of_The_Sugar_Plum_Fairy",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 727,
+ 914,
+ 1018
+ ],
+ "title": "Dance Of The Sugar Plum Fairy",
+ "url": "songs/classic/Tchaikovsky-Dance_Of_The_Sugar_Plum_Fairy.mid"
+ },
+ "Tchaikovsky-Marche_Slave": {
+ "__collections__": {},
+ "artist": "Tchaikovsky",
+ "bpm": 72,
+ "duration": [
+ 220000000,
+ 165000000,
+ 132000000
+ ],
+ "id": "Tchaikovsky-Marche_Slave",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 383,
+ 464,
+ 470
+ ],
+ "title": "Marche Slave",
+ "url": "songs/classic/Tchaikovsky-Marche_Slave.mid"
+ },
+ "Tchaikovsky-Nutcracker_-_March": {
+ "__collections__": {},
+ "artist": "Tchaikovsky",
+ "bpm": 75,
+ "duration": [
+ 435881481,
+ 326911111,
+ 261528889
+ ],
+ "id": "Tchaikovsky-Nutcracker_-_March",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1675,
+ 2061,
+ 2345
+ ],
+ "title": "Nutcracker - March",
+ "url": "songs/classic/Tchaikovsky-Nutcracker_-_March.mid"
+ },
+ "Tchaikovsky-Piano_Concerto_No._1": {
+ "__collections__": {},
+ "artist": "Tchaikovsky",
+ "bpm": 120,
+ "duration": [
+ 419648148,
+ 314736111,
+ 251788889
+ ],
+ "id": "Tchaikovsky-Piano_Concerto_No._1",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1007,
+ 1258,
+ 1369
+ ],
+ "title": "Piano Concerto No. 1",
+ "url": "songs/classic/Tchaikovsky-Piano_Concerto_No._1.mid"
+ },
+ "Tchaikovsky-Romeo_And_Juliet_Fantasy_Overture": {
+ "__collections__": {},
+ "artist": "Tchaikovsky",
+ "bpm": 37,
+ "duration": [
+ 956929429,
+ 717697072,
+ 574157658
+ ],
+ "id": "Tchaikovsky-Romeo_And_Juliet_Fantasy_Overture",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1227,
+ 1411,
+ 1558
+ ],
+ "title": "Romeo And Juliet Fantasy Overture",
+ "url": "songs/classic/Tchaikovsky-Romeo_And_Juliet_Fantasy_Overture.mid"
+ },
+ "Tchaikovsky-Sleeping_Beauty": {
+ "__collections__": {},
+ "artist": "Tchaikovsky",
+ "bpm": 150,
+ "duration": [
+ 477688889,
+ 358266667,
+ 286613333
+ ],
+ "id": "Tchaikovsky-Sleeping_Beauty",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 2110,
+ 2670,
+ 2918
+ ],
+ "title": "Sleeping Beauty",
+ "url": "songs/classic/Tchaikovsky-Sleeping_Beauty.mid"
+ },
+ "Tchaikovsky-Swan_Lake": {
+ "__collections__": {},
+ "artist": "Tchaikovsky",
+ "bpm": 100,
+ "duration": [
+ 86133333,
+ 64600000,
+ 51680000
+ ],
+ "id": "Tchaikovsky-Swan_Lake",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 326,
+ 326,
+ 326
+ ],
+ "title": "Swan Lake",
+ "url": "songs/classic/Tchaikovsky-Swan_Lake.mid"
+ },
+ "Tchaikovsky-Symphony_No._6": {
+ "__collections__": {},
+ "artist": "Tchaikovsky",
+ "bpm": 143,
+ "duration": [
+ 617435897,
+ 463076923,
+ 370461538
+ ],
+ "id": "Tchaikovsky-Symphony_No._6",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 512,
+ 620,
+ 683
+ ],
+ "title": "Symphony No. 6",
+ "url": "songs/classic/Tchaikovsky-Symphony_No._6.mid"
+ },
+ "Tchaikovsky-Waltz_Of_The_Flowers": {
+ "__collections__": {},
+ "artist": "Tchaikovsky",
+ "bpm": 182,
+ "duration": [
+ 389597070,
+ 292197802,
+ 233758242
+ ],
+ "id": "Tchaikovsky-Waltz_Of_The_Flowers",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 602,
+ 784,
+ 889
+ ],
+ "title": "Waltz Of The Flowers",
+ "url": "songs/classic/Tchaikovsky-Waltz_Of_The_Flowers.mid"
+ },
+ "The_Scientist-Coldplay": {
+ "__collections__": {},
+ "artist": "The Scientist",
+ "bpm": 75,
+ "duration": [
+ 402311111,
+ 301733333,
+ 241386667
+ ],
+ "id": "The_Scientist-Coldplay",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 949,
+ 1283,
+ 1460
+ ],
+ "title": "Coldplay",
+ "url": "songs/other/The_Scientist-Coldplay.mid"
+ },
+ "Tokyo_Ghoul-Unravel": {
+ "__collections__": {},
+ "artist": "Tokyo Ghoul",
+ "bpm": 75,
+ "duration": [
+ 614366667,
+ 460775000,
+ 368620000
+ ],
+ "id": "Tokyo_Ghoul-Unravel",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 2595,
+ 3016,
+ 3243
+ ],
+ "title": "Unravel",
+ "url": "songs/other/Tokyo_Ghoul-Unravel.mid"
+ },
+ "Vaughan_Williams-Fantasia_On_Greensleeves": {
+ "__collections__": {},
+ "artist": "Vaughan Williams",
+ "bpm": 120,
+ "duration": [
+ 341555556,
+ 256166667,
+ 204933333
+ ],
+ "id": "Vaughan_Williams-Fantasia_On_Greensleeves",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 791,
+ 911,
+ 948
+ ],
+ "title": "Fantasia On Greensleeves",
+ "url": "songs/classic/Vaughan_Williams-Fantasia_On_Greensleeves.mid"
+ },
+ "Vietnamese_Folk-A_Test_Song": {
+ "__collections__": {},
+ "artist": "Vietnamese Folk",
+ "bpm": 80,
+ "duration": [
+ 1074074,
+ 805556,
+ 644444
+ ],
+ "id": "Vietnamese_Folk-A_Test_Song",
+ "tags": [
+ "songs"
+ ],
+ "tilesCount": [
+ 2,
+ 2,
+ 2
+ ],
+ "title": "A Test Song",
+ "url": "songs/Vietnamese_Folk-A_Test_Song.mid"
+ },
+ "Vietnamese_Folk-Bac_Kim_Thang": {
+ "__collections__": {},
+ "artist": "Vietnamese Folk",
+ "bpm": 120,
+ "duration": [
+ 127555556,
+ 95666667,
+ 76533333
+ ],
+ "id": "Vietnamese_Folk-Bac_Kim_Thang",
+ "tags": [
+ "folk"
+ ],
+ "tilesCount": [
+ 148,
+ 148,
+ 148
+ ],
+ "title": "Bac Kim Thang",
+ "url": "songs/folk/Vietnamese_Folk-Bac_Kim_Thang.mid"
+ },
+ "Vietnamese_Folk-Beo_Dat_May_Troi": {
+ "__collections__": {},
+ "artist": "Vietnamese Folk",
+ "bpm": 120,
+ "duration": [
+ 265666667,
+ 199250000,
+ 159400000
+ ],
+ "id": "Vietnamese_Folk-Beo_Dat_May_Troi",
+ "tags": [
+ "folk"
+ ],
+ "tilesCount": [
+ 741,
+ 751,
+ 759
+ ],
+ "title": "Beo Dat May Troi",
+ "url": "songs/folk/Vietnamese_Folk-Beo_Dat_May_Troi.mid"
+ },
+ "Vietnamese_Folk-Cay_Truc_Xinh": {
+ "__collections__": {},
+ "artist": "Vietnamese Folk",
+ "bpm": 60,
+ "duration": [
+ 65333333,
+ 49000000,
+ 39200000
+ ],
+ "id": "Vietnamese_Folk-Cay_Truc_Xinh",
+ "tags": [
+ "folk"
+ ],
+ "tilesCount": [
+ 54,
+ 54,
+ 54
+ ],
+ "title": "Cay Truc Xinh",
+ "url": "songs/folk/Vietnamese_Folk-Cay_Truc_Xinh.mid"
+ },
+ "Vietnamese_Folk-Co_La": {
+ "__collections__": {},
+ "artist": "Vietnamese Folk",
+ "bpm": 60,
+ "duration": [
+ 80000000,
+ 60000000,
+ 48000000
+ ],
+ "id": "Vietnamese_Folk-Co_La",
+ "tags": [
+ "folk"
+ ],
+ "tilesCount": [
+ 59,
+ 59,
+ 59
+ ],
+ "title": "Co La",
+ "url": "songs/folk/Vietnamese_Folk-Co_La.mid"
+ },
+ "Vietnamese_Folk-Ly_Cay_Bong": {
+ "__collections__": {},
+ "artist": "Vietnamese Folk",
+ "bpm": 120,
+ "duration": [
+ 88888889,
+ 66666667,
+ 53333333
+ ],
+ "id": "Vietnamese_Folk-Ly_Cay_Bong",
+ "tags": [
+ "folk"
+ ],
+ "tilesCount": [
+ 58,
+ 58,
+ 58
+ ],
+ "title": "Ly Cay Bong",
+ "url": "songs/folk/Vietnamese_Folk-Ly_Cay_Bong.mid"
+ },
+ "Vietnamese_Folk-Nguoi_O_Dung_Ve": {
+ "__collections__": {},
+ "artist": "Vietnamese Folk",
+ "bpm": 60,
+ "duration": [
+ 117796296,
+ 88347222,
+ 70677778
+ ],
+ "id": "Vietnamese_Folk-Nguoi_O_Dung_Ve",
+ "tags": [
+ "folk"
+ ],
+ "tilesCount": [
+ 159,
+ 159,
+ 159
+ ],
+ "title": "Nguoi O Dung Ve",
+ "url": "songs/folk/Vietnamese_Folk-Nguoi_O_Dung_Ve.mid"
+ },
+ "Vietnamese_Folk-Trong_Com": {
+ "__collections__": {},
+ "artist": "Vietnamese Folk",
+ "bpm": 60,
+ "duration": [
+ 154666667,
+ 116000000,
+ 92800000
+ ],
+ "id": "Vietnamese_Folk-Trong_Com",
+ "tags": [
+ "folk"
+ ],
+ "tilesCount": [
+ 233,
+ 233,
+ 233
+ ],
+ "title": "Trong Com",
+ "url": "songs/folk/Vietnamese_Folk-Trong_Com.mid"
+ },
+ "Vince_Guaraldi-Linus_And_Lucy": {
+ "__collections__": {},
+ "artist": "Vince Guaraldi",
+ "bpm": 150,
+ "duration": [
+ 239792593,
+ 179844444,
+ 143875556
+ ],
+ "id": "Vince_Guaraldi-Linus_And_Lucy",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 1050,
+ 1242,
+ 1282
+ ],
+ "title": "Linus And Lucy",
+ "url": "songs/other/Vince_Guaraldi-Linus_And_Lucy.mid"
+ },
+ "Vittorio_Monti-Czardas": {
+ "__collections__": {},
+ "artist": "Vittorio Monti",
+ "bpm": 55,
+ "duration": [
+ 685010101,
+ 513757576,
+ 411006061
+ ],
+ "id": "Vittorio_Monti-Czardas",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1849,
+ 2152,
+ 2342
+ ],
+ "title": "Czardas",
+ "url": "songs/classic/Vittorio_Monti-Czardas.mid"
+ },
+ "Vivaldi-Four_Seasons_-_Spring": {
+ "__collections__": {},
+ "artist": "Vivaldi",
+ "bpm": 37,
+ "duration": [
+ 682072072,
+ 511554054,
+ 409243243
+ ],
+ "id": "Vivaldi-Four_Seasons_-_Spring",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 2023,
+ 2249,
+ 2419
+ ],
+ "title": "Four Seasons - Spring",
+ "url": "songs/classic/Vivaldi-Four_Seasons_-_Spring.mid"
+ },
+ "Vivaldi-Four_Seasons_-_Summer": {
+ "__collections__": {},
+ "artist": "Vivaldi",
+ "bpm": 75,
+ "duration": [
+ 406844444,
+ 305133333,
+ 244106667
+ ],
+ "id": "Vivaldi-Four_Seasons_-_Summer",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 2362,
+ 2548,
+ 2553
+ ],
+ "title": "Four Seasons - Summer",
+ "url": "songs/classic/Vivaldi-Four_Seasons_-_Summer.mid"
+ },
+ "Vivaldi-Four_Seasons_-_Winter": {
+ "__collections__": {},
+ "artist": "Vivaldi",
+ "bpm": 75,
+ "duration": [
+ 305822222,
+ 229366667,
+ 183493333
+ ],
+ "id": "Vivaldi-Four_Seasons_-_Winter",
+ "tags": [
+ "classic"
+ ],
+ "tilesCount": [
+ 1064,
+ 1187,
+ 1204
+ ],
+ "title": "Four Seasons - Winter",
+ "url": "songs/classic/Vivaldi-Four_Seasons_-_Winter.mid"
+ },
+ "Wham!-Last_Christmas-": {
+ "__collections__": {},
+ "artist": "Wham!",
+ "bpm": 106,
+ "duration": [
+ 340743711,
+ 255557783,
+ 204446226
+ ],
+ "id": "Wham!-Last_Christmas-",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 1255,
+ 1366,
+ 1416
+ ],
+ "title": "Last Christmas-",
+ "url": "songs/other/Wham!-Last_Christmas-.mid"
+ },
+ "Yann_Tiersen-Comptine_Dune_Autre_Ete": {
+ "__collections__": {},
+ "artist": "Yann Tiersen",
+ "bpm": 74,
+ "duration": [
+ 399385886,
+ 299539414,
+ 239631532
+ ],
+ "id": "Yann_Tiersen-Comptine_Dune_Autre_Ete",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 1722,
+ 1993,
+ 2036
+ ],
+ "title": "Comptine Dune Autre Ete",
+ "url": "songs/other/Yann_Tiersen-Comptine_Dune_Autre_Ete.mid"
+ },
+ "Yann_Tiersen-La_Valse_dAmelie": {
+ "__collections__": {},
+ "artist": "Yann Tiersen",
+ "bpm": 100,
+ "duration": [
+ 309600000,
+ 232200000,
+ 185760000
+ ],
+ "id": "Yann_Tiersen-La_Valse_dAmelie",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 1039,
+ 1139,
+ 1164
+ ],
+ "title": "La Valse dAmelie",
+ "url": "songs/other/Yann_Tiersen-La_Valse_dAmelie.mid"
+ },
+ "Yiruma-Kiss_The_Rain": {
+ "__collections__": {},
+ "artist": "Yiruma",
+ "bpm": 58,
+ "duration": [
+ 533793103,
+ 400344828,
+ 320275862
+ ],
+ "id": "Yiruma-Kiss_The_Rain",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 938,
+ 1069,
+ 1103
+ ],
+ "title": "Kiss The Rain",
+ "url": "songs/other/Yiruma-Kiss_The_Rain.mid"
+ },
+ "Yiruma-River_Flows_In_You": {
+ "__collections__": {},
+ "artist": "Yiruma",
+ "bpm": 64,
+ "duration": [
+ 240078125,
+ 180058594,
+ 144046875
+ ],
+ "id": "Yiruma-River_Flows_In_You",
+ "tags": [
+ "other"
+ ],
+ "tilesCount": [
+ 824,
+ 839,
+ 840
+ ],
+ "title": "River Flows In You",
+ "url": "songs/other/Yiruma-River_Flows_In_You.mid"
+ }
+ },
+ "users": {
+ "0BeeRxhV6Vf5NXKrZq0lgkn79CS2": {
+ "__collections__": {},
+ "id": "0BeeRxhV6Vf5NXKrZq0lgkn79CS2",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "0EdvbPVXEzQ5BeJYShnIvuuYj8Q2": {
+ "__collections__": {},
+ "id": "0EdvbPVXEzQ5BeJYShnIvuuYj8Q2",
+ "instrumentId": "piano",
+ "notificationTokens": [
+ "cBVI8cziQ9G5puJevwcWBD:APA91bEi9nkH0zFZAo7Uc4O1VGcTPk1x4d8iGrT3Y80Up41JVjt6Xzj_Uo7n3SE2oHWRP2uDw1L5YP_G_lyAYLTHbm_mCC_ltb9k7-XFo7dmYCUBWyvPM9-4UpSq8O-s8cfjsUjWUThV"
+ ],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "0ukyCEXiKCcCYyqZrpDcCNN3CbJ3": {
+ "__collections__": {},
+ "id": "0ukyCEXiKCcCYyqZrpDcCNN3CbJ3",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "2XmAJsBkacSr6F2IXWF9BaUXDrh1": {
+ "__collections__": {},
+ "id": "2XmAJsBkacSr6F2IXWF9BaUXDrh1",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "2qGH4o4qUaUze3uElDGMiF4SeiM2": {
+ "__collections__": {},
+ "id": "2qGH4o4qUaUze3uElDGMiF4SeiM2",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "43cIyyVCwSQYcq3j4a2p3PjzpoJ3": {
+ "__collections__": {},
+ "id": "43cIyyVCwSQYcq3j4a2p3PjzpoJ3",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "7LS8zAa6KJPbgm06pAwGVgLCrrP2": {
+ "__collections__": {},
+ "id": "7LS8zAa6KJPbgm06pAwGVgLCrrP2",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "8xr2UR0zImfxs1tPtgjoJEMBko92": {
+ "__collections__": {},
+ "id": "8xr2UR0zImfxs1tPtgjoJEMBko92",
+ "instrumentId": "piano",
+ "notificationTokens": [
+ "cVA2tcziSzOhIzhYIfV4pf:APA91bHJ7oiUTwA3zrNn5_660XO6Wz2QTVBCasHPVXwEdnj993U3UlDx86StTqP0RnjkFWXgNf0r-3VPYDUTBzVZL8qmbYmSjcLIhrEawF0XfzuQaeSp8sDf2sDg4UJ_Fv-BtFSzTtKs"
+ ],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "9biTHhe3NPe68NUryBPazmPP4sR2": {
+ "__collections__": {},
+ "id": "9biTHhe3NPe68NUryBPazmPP4sR2",
+ "instrumentId": "piano",
+ "notificationTokens": [
+ "fzfOU613SnK53Ss-GnZojg:APA91bGjWYrEs_LYd1Myj9R0Mky3cWdZWz4bdfrBws0ujnnkDu50iqV8PMEsYpzGUtFK3cRNO2UupYUcAZI1i_DxSPO-jSQ-gGc84dPyGVM3qQ1Fc-41t-Eeqjwyyfttrl0cX5qWOjyp"
+ ],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "BLiLbBiWOEZOZnlBPnK2OFbUfou2": {
+ "__collections__": {},
+ "id": "BLiLbBiWOEZOZnlBPnK2OFbUfou2",
+ "instrumentId": "piano",
+ "notificationTokens": [
+ "cSvkk9y0RkuGQYoJYtW9bD:APA91bFQ58J2AnP7B-dq_AXpuDI0zgEssAq1YABpcfWArPA7xMiV96HocF3a9jGvutajM2zOHOIYorLg7sjKzpmxGYYGiax68i_rrdt29SHysq5ZUvkzyzlT9n3DfGSsshAJEvTF3eGF"
+ ],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "F59WYp7vBZQiQ9oiAsKTHg9yIaw1": {
+ "__collections__": {},
+ "id": "F59WYp7vBZQiQ9oiAsKTHg9yIaw1",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "HKngqjDpyTdTDsbB4UmEeoiWDqN2": {
+ "__collections__": {},
+ "id": "HKngqjDpyTdTDsbB4UmEeoiWDqN2",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "Iii5bdHWs3ek6i70LGwiQ5PTzys2": {
+ "__collections__": {},
+ "id": "Iii5bdHWs3ek6i70LGwiQ5PTzys2",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "KW8wIcA0QhVaXV1WzDgn8xjv6GC3": {
+ "__collections__": {},
+ "id": "KW8wIcA0QhVaXV1WzDgn8xjv6GC3",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "LUhrzNDdKXPMvnMLYdxcgMudEIp1": {
+ "__collections__": {},
+ "id": "LUhrzNDdKXPMvnMLYdxcgMudEIp1",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "QrpJSR0YUPRGAOrMgND5mmeyBQh1": {
+ "__collections__": {},
+ "id": "QrpJSR0YUPRGAOrMgND5mmeyBQh1",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "R1VmLwjtC5dypJQIylIHnpIlXg62": {
+ "__collections__": {},
+ "id": "R1VmLwjtC5dypJQIylIHnpIlXg62",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "RKGxgBzeHhOSWdI3sCRR3FoS45H2": {
+ "__collections__": {},
+ "id": "RKGxgBzeHhOSWdI3sCRR3FoS45H2",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "RhE7WvqGbCU7hkFNDuexQELJ8Id2": {
+ "__collections__": {},
+ "id": "RhE7WvqGbCU7hkFNDuexQELJ8Id2",
+ "instrumentId": "piano",
+ "notificationTokens": [
+ "cTdpYM-2QUCQJKboanScyq:APA91bGkXocGn-l036qhUHOKYd8A_Ozq5w98qEBwSidKnDpjYd3HXMk6NQ9ZBW8UtSDH99h1ugl64TLyjqMHB2bUihacXHKDnviCs-1U6_aKqZ-Eb_hMt2x3r1tJyZAr-jRIE_euzQTM"
+ ],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "Rij0NF9i9cYmd1CG3ip9wUbLc3d2": {
+ "__collections__": {},
+ "id": "Rij0NF9i9cYmd1CG3ip9wUbLc3d2",
+ "instrumentId": "piano",
+ "notificationTokens": [
+ "f1XZ3E4hQceHYoLxhC0IQL:APA91bEYQz4UZuKGpZrz_rvW563wIZv90Pk8H_Lf4pok-k4G0CjUooPCqLVuTWBR2GgntmmeNQMAz777YqMU4asWX9467U7giTkfdFGg6RLR4_CbPq-GG4qxQcVXDgFNWSIYsVavdeA4",
+ "eRUVaaMGQ9GKLSsf_yslMB:APA91bG7ef5hatqqtOBI5_QYNcgbuarl6dtTLBzdAWeOrXFaAzs0igK3flrH2fku_2zlF6oqISChGbhjMfbZ63vbnPWfIVE_ZoJfySpJ9P4Y6G1zwwNtqxR7HBT9i23T4ycXm0zlfkQj"
+ ],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "XvSB2o0VdIWugUUOlGvIxaHQSiv1": {
+ "__collections__": {},
+ "id": "XvSB2o0VdIWugUUOlGvIxaHQSiv1",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "aKdLkus3mKRQaODMcKzSuJgowT72": {
+ "__collections__": {},
+ "id": "aKdLkus3mKRQaODMcKzSuJgowT72",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "amgaWgNeWSYqSM6SNzpOfc5Nkyz2": {
+ "__collections__": {},
+ "id": "amgaWgNeWSYqSM6SNzpOfc5Nkyz2",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "gIRFROQsoGQzyMxA6XRU9X3L89D2": {
+ "__collections__": {},
+ "id": "gIRFROQsoGQzyMxA6XRU9X3L89D2",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "jXc2Vn4VNshi2eINGJG3I8c2iV43": {
+ "__collections__": {},
+ "id": "jXc2Vn4VNshi2eINGJG3I8c2iV43",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "lm0XcaXECOXOEPJhTLSQTBz7weY2": {
+ "__collections__": {},
+ "id": "lm0XcaXECOXOEPJhTLSQTBz7weY2",
+ "instrumentId": "piano",
+ "notificationTokens": [
+ "daAm4JyMQs2a25AukiVRdd:APA91bEsNWE0VQmrNLZmGqZcXBHk8uTyvK8F0-d_mijAv8NVAvbyoDEMj2ZZA0ptvYFRRHlqm-kjnphgCkFIaZTuFw7gmmvBLcvtMPuyN_U0xklgInCtQT5PY7fs3k-kPGHdqNkGdb1R"
+ ],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "lpxDXty32iXKLKWJ1BOn1jmaEaN2": {
+ "__collections__": {},
+ "id": "lpxDXty32iXKLKWJ1BOn1jmaEaN2",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "nVVup1IcJBNtkrPjIWnOgo3t77v2": {
+ "__collections__": {},
+ "id": "nVVup1IcJBNtkrPjIWnOgo3t77v2",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "o0iXwocdbLeLmxscbf2jSpEWOUH3": {
+ "__collections__": {},
+ "id": "o0iXwocdbLeLmxscbf2jSpEWOUH3",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "oSF8ug2viyNgIieFNrCDHdacYTR2": {
+ "__collections__": {},
+ "id": "oSF8ug2viyNgIieFNrCDHdacYTR2",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "put8pZHQ8RfpVo1bDoyW7iyk9Xo2": {
+ "__collections__": {},
+ "id": "put8pZHQ8RfpVo1bDoyW7iyk9Xo2",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "s1aSboU0bEfvA4CReOTkKhY0cmF3": {
+ "__collections__": {},
+ "id": "s1aSboU0bEfvA4CReOTkKhY0cmF3",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "saxyGwgvMsS1FhnImc0lHWXo6h33": {
+ "__collections__": {},
+ "id": "saxyGwgvMsS1FhnImc0lHWXo6h33",
+ "instrumentId": "piano",
+ "notificationTokens": [
+ "f5BBRlgjTt2WftUdnSdMqW:APA91bFZZNRYjODOVAY2XJ9VaXJDHxCbxo1s6tpG2rSOYdt-jO98864mlMrOl7apSsMkZ3quBwGtxwuSfXd_Gn3TG6Wfz_T2O39gPdh8mywFv187_QEqjwZ9UBlUw-B_NFHsf09v7xMg"
+ ],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "vNpikramyRU7UnCGkZfc956Cr9v1": {
+ "__collections__": {},
+ "id": "vNpikramyRU7UnCGkZfc956Cr9v1",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "ygnHbB5EwthSbdGhllKQeW4YlXM2": {
+ "__collections__": {},
+ "id": "ygnHbB5EwthSbdGhllKQeW4YlXM2",
+ "instrumentId": "piano",
+ "notificationTokens": [],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ },
+ "ymeeqn0mHHZkWmyk1mvRuWjj1J32": {
+ "__collections__": {},
+ "id": "ymeeqn0mHHZkWmyk1mvRuWjj1J32",
+ "instrumentId": "piano",
+ "notificationTokens": [
+ "eD7WBAKnQ02anvf7jNd8uq:APA91bHGGbtw33ubbGT-AHuBKr8XliH0ITrcyd47g1zUk9cX78eo3eYPVclIb35Bz7EiVANV16xu2W-Syrf6nw3YrGl4axyOwerJ5mdApMwQLAhkRT4U0hRuX-jNLiQimGcaaRXw1OlM"
+ ],
+ "playedNotes": 0,
+ "playedTime": 0,
+ "stars": 0
+ }
+ }
+ }
+}
diff --git a/database/jq-win64.exe b/database/jq-win64.exe
new file mode 100644
index 00000000..cf9f37e4
Binary files /dev/null and b/database/jq-win64.exe differ
diff --git a/database/restore-firestore.sh b/database/restore-firestore.sh
new file mode 100644
index 00000000..cb51600f
--- /dev/null
+++ b/database/restore-firestore.sh
@@ -0,0 +1,4 @@
+# curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
+# sudo apt-get install -y nodejs
+# npm install -g node-firestore-import-export;
+firestore-import --backupFile db.json --yes
\ No newline at end of file
diff --git a/fastlane/Appfile b/fastlane/Appfile
new file mode 100644
index 00000000..9c329c63
--- /dev/null
+++ b/fastlane/Appfile
@@ -0,0 +1,2 @@
+json_key_file("service_account.json") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one
+package_name("com.chaomao.hittick")
diff --git a/fastlane/metadata/en-US/changelogs/19.txt b/fastlane/metadata/en-US/changelogs/19.txt
new file mode 100644
index 00000000..86309c81
--- /dev/null
+++ b/fastlane/metadata/en-US/changelogs/19.txt
@@ -0,0 +1 @@
+Fix bugs and improve performance
\ No newline at end of file
diff --git a/fastlane/metadata/en-US/full_description.txt b/fastlane/metadata/en-US/full_description.txt
new file mode 100644
index 00000000..d6f24b18
--- /dev/null
+++ b/fastlane/metadata/en-US/full_description.txt
@@ -0,0 +1,11 @@
+Even you don't have any basic knowledge of music instruments, you still can play with falling notes in the game!
+
+* Hundreds of songs, from classics of Beethoven, Chopin, Mozart, or Schubert, folk songs to POP/EDM. Will be updated continuously
+* Adjustable difficulty level
+* Adjustable tempo
+* Many game modes
+* Many musical instruments
+* Play offline without the internet
+* Upload your song and play your way
+
+Just select your favorites songs, hit music notes, and listen.
\ No newline at end of file
diff --git a/fastlane/metadata/en-US/images/featureGraphic.png b/fastlane/metadata/en-US/images/featureGraphic.png
new file mode 100644
index 00000000..10b4904b
Binary files /dev/null and b/fastlane/metadata/en-US/images/featureGraphic.png differ
diff --git a/fastlane/metadata/en-US/images/icon.png b/fastlane/metadata/en-US/images/icon.png
new file mode 100644
index 00000000..6c4f8257
Binary files /dev/null and b/fastlane/metadata/en-US/images/icon.png differ
diff --git a/fastlane/metadata/en-US/images/phoneScreenshots/1_en-US.png b/fastlane/metadata/en-US/images/phoneScreenshots/1_en-US.png
new file mode 100644
index 00000000..df96c326
Binary files /dev/null and b/fastlane/metadata/en-US/images/phoneScreenshots/1_en-US.png differ
diff --git a/fastlane/metadata/en-US/images/phoneScreenshots/2_en-US.png b/fastlane/metadata/en-US/images/phoneScreenshots/2_en-US.png
new file mode 100644
index 00000000..3740d0fa
Binary files /dev/null and b/fastlane/metadata/en-US/images/phoneScreenshots/2_en-US.png differ
diff --git a/fastlane/metadata/en-US/images/phoneScreenshots/3_en-US.png b/fastlane/metadata/en-US/images/phoneScreenshots/3_en-US.png
new file mode 100644
index 00000000..6808e243
Binary files /dev/null and b/fastlane/metadata/en-US/images/phoneScreenshots/3_en-US.png differ
diff --git a/fastlane/metadata/en-US/short_description.txt b/fastlane/metadata/en-US/short_description.txt
new file mode 100644
index 00000000..bf0f9fa6
--- /dev/null
+++ b/fastlane/metadata/en-US/short_description.txt
@@ -0,0 +1 @@
+Play Piano, Guitar and more. Hit falling notes to play hundreds of songs easily.
\ No newline at end of file
diff --git a/fastlane/metadata/en-US/title.txt b/fastlane/metadata/en-US/title.txt
new file mode 100644
index 00000000..7e05b5c2
--- /dev/null
+++ b/fastlane/metadata/en-US/title.txt
@@ -0,0 +1 @@
+Hit Notes - Play instruments
\ No newline at end of file
diff --git a/fastlane/metadata/en-US/video.txt b/fastlane/metadata/en-US/video.txt
new file mode 100644
index 00000000..1e634847
--- /dev/null
+++ b/fastlane/metadata/en-US/video.txt
@@ -0,0 +1 @@
+https://www.youtube.com/watch?v=dzI4UCgvdQc
\ No newline at end of file
diff --git a/fastlane/metadata/vi/changelogs/19.txt b/fastlane/metadata/vi/changelogs/19.txt
new file mode 100644
index 00000000..11b4ee78
--- /dev/null
+++ b/fastlane/metadata/vi/changelogs/19.txt
@@ -0,0 +1 @@
+Sửa lỗi và cải thiện hiệu suất
\ No newline at end of file
diff --git a/fastlane/metadata/vi/full_description.txt b/fastlane/metadata/vi/full_description.txt
new file mode 100644
index 00000000..549d34b3
--- /dev/null
+++ b/fastlane/metadata/vi/full_description.txt
@@ -0,0 +1,11 @@
+Ngay cả khi bạn không có bất kỳ kiến thức cơ bản nào về nhạc cụ, bạn vẫn có thể chơi với các nốt nhạc đang rơi trong trò chơi!
+
+* Hàng trăm bản nhạc, từ các tác phẩm kinh điển của Beethoven, Chopin, Mozart hoặc Schubert, các bản nhạc dân gian đến POP / EDM. Sẽ được cập nhật liên tục
+* Mức độ khó có thể điều chỉnh
+* Nhịp độ có thể điều chỉnh
+* Nhiều chế độ chơi
+* Nhiều nhạc cụ
+* Chơi ngoại tuyến mà không cần kết nối mạng
+* Tải lên bản nhạc của bạn và chơi theo cách của bạn
+
+Chỉ cần chọn các bản nhạc yêu thích của bạn, nhấn các nốt nhạc và lắng nghe.
\ No newline at end of file
diff --git a/fastlane/metadata/vi/images/featureGraphic.png b/fastlane/metadata/vi/images/featureGraphic.png
new file mode 100644
index 00000000..b9a0ed05
Binary files /dev/null and b/fastlane/metadata/vi/images/featureGraphic.png differ
diff --git a/fastlane/metadata/vi/images/icon.png b/fastlane/metadata/vi/images/icon.png
new file mode 100644
index 00000000..6c4f8257
Binary files /dev/null and b/fastlane/metadata/vi/images/icon.png differ
diff --git a/fastlane/metadata/vi/images/phoneScreenshots/1_vi.png b/fastlane/metadata/vi/images/phoneScreenshots/1_vi.png
new file mode 100644
index 00000000..69ac1070
Binary files /dev/null and b/fastlane/metadata/vi/images/phoneScreenshots/1_vi.png differ
diff --git a/fastlane/metadata/vi/images/phoneScreenshots/2_vi.png b/fastlane/metadata/vi/images/phoneScreenshots/2_vi.png
new file mode 100644
index 00000000..4abf5747
Binary files /dev/null and b/fastlane/metadata/vi/images/phoneScreenshots/2_vi.png differ
diff --git a/fastlane/metadata/vi/images/phoneScreenshots/3_vi.png b/fastlane/metadata/vi/images/phoneScreenshots/3_vi.png
new file mode 100644
index 00000000..56ce38e6
Binary files /dev/null and b/fastlane/metadata/vi/images/phoneScreenshots/3_vi.png differ
diff --git a/fastlane/metadata/vi/short_description.txt b/fastlane/metadata/vi/short_description.txt
new file mode 100644
index 00000000..b3995df8
--- /dev/null
+++ b/fastlane/metadata/vi/short_description.txt
@@ -0,0 +1 @@
+Chơi Piano, Guitar và hơn nữa. Chạm vào các nốt để chơi hàng trăm bản nhạc.
\ No newline at end of file
diff --git a/fastlane/metadata/vi/title.txt b/fastlane/metadata/vi/title.txt
new file mode 100644
index 00000000..f464e663
--- /dev/null
+++ b/fastlane/metadata/vi/title.txt
@@ -0,0 +1 @@
+Hit Notes - Piano Guitar Việt
\ No newline at end of file
diff --git a/fastlane/metadata/vi/video.txt b/fastlane/metadata/vi/video.txt
new file mode 100644
index 00000000..1e634847
--- /dev/null
+++ b/fastlane/metadata/vi/video.txt
@@ -0,0 +1 @@
+https://www.youtube.com/watch?v=dzI4UCgvdQc
\ No newline at end of file
diff --git a/functions/.gitignore b/functions/.gitignore
new file mode 100644
index 00000000..24b8f589
--- /dev/null
+++ b/functions/.gitignore
@@ -0,0 +1,9 @@
+.firebaserc
+/.vs/
+/.vscode/
+/functions/lib/
+*.log
+/functions/node_modules/
+
+## Google api key to access play console
+/functions/service_account.json
diff --git a/functions/firebase.json b/functions/firebase.json
new file mode 100644
index 00000000..8a2d399e
--- /dev/null
+++ b/functions/firebase.json
@@ -0,0 +1,16 @@
+{
+ "firestore": {
+ "rules": "firestore.rules",
+ "indexes": "firestore.indexes.json"
+ },
+ "functions": {
+ "predeploy": [
+ "npm --prefix ./functions run lint",
+ "npm --prefix ./functions run build"
+ ],
+ "source": "functions"
+ },
+ "storage": {
+ "rules": "storage.rules"
+ }
+}
\ No newline at end of file
diff --git a/functions/firestore.indexes.json b/functions/firestore.indexes.json
new file mode 100644
index 00000000..dd713580
--- /dev/null
+++ b/functions/firestore.indexes.json
@@ -0,0 +1,19 @@
+{
+ "indexes": [
+ {
+ "collectionGroup": "songs",
+ "queryScope": "COLLECTION",
+ "fields": [
+ {
+ "fieldPath": "tags",
+ "arrayConfig": "CONTAINS"
+ },
+ {
+ "fieldPath": "title",
+ "order": "ASCENDING"
+ }
+ ]
+ }
+ ],
+ "fieldOverrides": []
+}
diff --git a/functions/firestore.rules b/functions/firestore.rules
new file mode 100644
index 00000000..dd00fee7
--- /dev/null
+++ b/functions/firestore.rules
@@ -0,0 +1,38 @@
+rules_version = '2';
+service cloud.firestore {
+ match /databases/{database}/documents {
+ function isSignedIn() {
+ return request.auth.uid != null;
+ }
+ function isItMine() {
+ return request.auth.uid == resource.data.id;
+ }
+ match /users/{userId} {
+ allow read: if isSignedIn();
+ allow create: if false;
+ allow update: if isItMine() && isUpdatingInstrumentId();
+ allow delete: if false;
+ function isUpdatingInstrumentId() {
+ return request.resource.data.diff(resource.data).affectedKeys().hasOnly(['instrumentId', 'notificationTokens']);
+ }
+ }
+ match /songs/{songId} {
+ allow read: if isSignedIn();
+ allow create: if false;
+ allow update: if false;
+ allow delete: if false;
+ }
+ match /instruments/{instrumentId} {
+ allow read: if isSignedIn();
+ allow create: if false;
+ allow update: if false;
+ allow delete: if false;
+ }
+ match /games/{gameId} {
+ allow read: if false;
+ allow create: if request.auth.uid == gameId;
+ allow update: if false;
+ allow delete: if false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/functions/functions/package-lock.json b/functions/functions/package-lock.json
new file mode 100644
index 00000000..05ab504a
--- /dev/null
+++ b/functions/functions/package-lock.json
@@ -0,0 +1,2235 @@
+{
+ "name": "functions",
+ "requires": true,
+ "lockfileVersion": 1,
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
+ "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.8.3"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz",
+ "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==",
+ "dev": true
+ },
+ "@babel/highlight": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz",
+ "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.9.0",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@fimbul/bifrost": {
+ "version": "0.21.0",
+ "resolved": "https://registry.npmjs.org/@fimbul/bifrost/-/bifrost-0.21.0.tgz",
+ "integrity": "sha512-ou8VU+nTmOW1jeg+FT+sn+an/M0Xb9G16RucrfhjXGWv1Q97kCoM5CG9Qj7GYOSdu7km72k7nY83Eyr53Bkakg==",
+ "dev": true,
+ "requires": {
+ "@fimbul/ymir": "^0.21.0",
+ "get-caller-file": "^2.0.0",
+ "tslib": "^1.8.1",
+ "tsutils": "^3.5.0"
+ },
+ "dependencies": {
+ "tsutils": {
+ "version": "3.17.1",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz",
+ "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.8.1"
+ }
+ }
+ }
+ },
+ "@fimbul/ymir": {
+ "version": "0.21.0",
+ "resolved": "https://registry.npmjs.org/@fimbul/ymir/-/ymir-0.21.0.tgz",
+ "integrity": "sha512-T/y7WqPsm4n3zhT08EpB5sfdm2Kvw3gurAxr2Lr5dQeLi8ZsMlNT/Jby+ZmuuAAd1PnXYzKp+2SXgIkQIIMCUg==",
+ "dev": true,
+ "requires": {
+ "inversify": "^5.0.0",
+ "reflect-metadata": "^0.1.12",
+ "tslib": "^1.8.1"
+ }
+ },
+ "@firebase/app-types": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.6.1.tgz",
+ "integrity": "sha512-L/ZnJRAq7F++utfuoTKX4CLBG5YR7tFO3PLzG1/oXXKEezJ0kRL3CMRoueBEmTCzVb/6SIs2Qlaw++uDgi5Xyg=="
+ },
+ "@firebase/auth-interop-types": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.5.tgz",
+ "integrity": "sha512-88h74TMQ6wXChPA6h9Q3E1Jg6TkTHep2+k63OWg3s0ozyGVMeY+TTOti7PFPzq5RhszQPQOoCi59es4MaRvgCw=="
+ },
+ "@firebase/component": {
+ "version": "0.1.19",
+ "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.1.19.tgz",
+ "integrity": "sha512-L0S3g8eqaerg8y0zox3oOHSTwn/FE8RbcRHiurnbESvDViZtP5S5WnhuAPd7FnFxa8ElWK0z1Tr3ikzWDv1xdQ==",
+ "requires": {
+ "@firebase/util": "0.3.2",
+ "tslib": "^1.11.1"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+ }
+ }
+ },
+ "@firebase/database": {
+ "version": "0.6.13",
+ "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.6.13.tgz",
+ "integrity": "sha512-NommVkAPzU7CKd1gyehmi3lz0K78q0KOfiex7Nfy7MBMwknLm7oNqKovXSgQV1PCLvKXvvAplDSFhDhzIf9obA==",
+ "requires": {
+ "@firebase/auth-interop-types": "0.1.5",
+ "@firebase/component": "0.1.19",
+ "@firebase/database-types": "0.5.2",
+ "@firebase/logger": "0.2.6",
+ "@firebase/util": "0.3.2",
+ "faye-websocket": "0.11.3",
+ "tslib": "^1.11.1"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+ }
+ }
+ },
+ "@firebase/database-types": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.5.2.tgz",
+ "integrity": "sha512-ap2WQOS3LKmGuVFKUghFft7RxXTyZTDr0Xd8y2aqmWsbJVjgozi0huL/EUMgTjGFrATAjcf2A7aNs8AKKZ2a8g==",
+ "requires": {
+ "@firebase/app-types": "0.6.1"
+ }
+ },
+ "@firebase/logger": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.2.6.tgz",
+ "integrity": "sha512-KIxcUvW/cRGWlzK9Vd2KB864HlUnCfdTH0taHE0sXW5Xl7+W68suaeau1oKNEqmc3l45azkd4NzXTCWZRZdXrw=="
+ },
+ "@firebase/util": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.3.2.tgz",
+ "integrity": "sha512-Dqs00++c8rwKky6KCKLLY2T1qYO4Q+X5t+lF7DInXDNF4ae1Oau35bkD+OpJ9u7l1pEv7KHowP6CUKuySCOc8g==",
+ "requires": {
+ "tslib": "^1.11.1"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+ }
+ }
+ },
+ "@google-cloud/common": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-3.5.0.tgz",
+ "integrity": "sha512-10d7ZAvKhq47L271AqvHEd8KzJqGU45TY+rwM2Z3JHuB070FeTi7oJJd7elfrnKaEvaktw3hH2wKnRWxk/3oWQ==",
+ "optional": true,
+ "requires": {
+ "@google-cloud/projectify": "^2.0.0",
+ "@google-cloud/promisify": "^2.0.0",
+ "arrify": "^2.0.1",
+ "duplexify": "^4.1.1",
+ "ent": "^2.2.0",
+ "extend": "^3.0.2",
+ "google-auth-library": "^6.1.1",
+ "retry-request": "^4.1.1",
+ "teeny-request": "^7.0.0"
+ }
+ },
+ "@google-cloud/firestore": {
+ "version": "4.8.0",
+ "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-4.8.0.tgz",
+ "integrity": "sha512-cBPo7QQG+aUhS7AIr6fDlA9KIX0/U26rKZyL2K/L68LArDQzgBk1/xOiMoflHRNDQARwCQ0PAZmw8V8CXg7vTg==",
+ "optional": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "functional-red-black-tree": "^1.0.1",
+ "google-gax": "^2.9.2"
+ }
+ },
+ "@google-cloud/paginator": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-3.0.5.tgz",
+ "integrity": "sha512-N4Uk4BT1YuskfRhKXBs0n9Lg2YTROZc6IMpkO/8DIHODtm5s3xY8K5vVBo23v/2XulY3azwITQlYWgT4GdLsUw==",
+ "optional": true,
+ "requires": {
+ "arrify": "^2.0.0",
+ "extend": "^3.0.2"
+ }
+ },
+ "@google-cloud/projectify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-2.0.1.tgz",
+ "integrity": "sha512-ZDG38U/Yy6Zr21LaR3BTiiLtpJl6RkPS/JwoRT453G+6Q1DhlV0waNf8Lfu+YVYGIIxgKnLayJRfYlFJfiI8iQ==",
+ "optional": true
+ },
+ "@google-cloud/promisify": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.3.tgz",
+ "integrity": "sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw==",
+ "optional": true
+ },
+ "@google-cloud/storage": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-5.6.0.tgz",
+ "integrity": "sha512-nLcym8IuCzy1O7tNTXNFuMHfX900sTM3kSTqbKe7oFSoKUiaIM+FHuuuDimMMlieY6StA1xYNPRFFHz57Nv8YQ==",
+ "optional": true,
+ "requires": {
+ "@google-cloud/common": "^3.5.0",
+ "@google-cloud/paginator": "^3.0.0",
+ "@google-cloud/promisify": "^2.0.0",
+ "arrify": "^2.0.0",
+ "compressible": "^2.0.12",
+ "date-and-time": "^0.14.0",
+ "duplexify": "^4.0.0",
+ "extend": "^3.0.2",
+ "gaxios": "^4.0.0",
+ "gcs-resumable-upload": "^3.1.0",
+ "get-stream": "^6.0.0",
+ "hash-stream-validation": "^0.2.2",
+ "mime": "^2.2.0",
+ "mime-types": "^2.0.8",
+ "onetime": "^5.1.0",
+ "p-limit": "^3.0.1",
+ "pumpify": "^2.0.0",
+ "snakeize": "^0.1.0",
+ "stream-events": "^1.0.1",
+ "xdg-basedir": "^4.0.0"
+ },
+ "dependencies": {
+ "p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "optional": true,
+ "requires": {
+ "yocto-queue": "^0.1.0"
+ }
+ }
+ }
+ },
+ "@grpc/grpc-js": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.1.8.tgz",
+ "integrity": "sha512-64hg5rmEm6F/NvlWERhHmmgxbWU8nD2TMWE+9TvG7/WcOrFT3fzg/Uu631pXRFwmJ4aWO/kp9vVSlr8FUjBDLA==",
+ "optional": true,
+ "requires": {
+ "@grpc/proto-loader": "^0.6.0-pre14",
+ "@types/node": "^12.12.47",
+ "google-auth-library": "^6.0.0",
+ "semver": "^6.2.0"
+ },
+ "dependencies": {
+ "@grpc/proto-loader": {
+ "version": "0.6.0-pre9",
+ "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.0-pre9.tgz",
+ "integrity": "sha512-oM+LjpEjNzW5pNJjt4/hq1HYayNeQT+eGrOPABJnYHv7TyNPDNzkQ76rDYZF86X5swJOa4EujEMzQ9iiTdPgww==",
+ "optional": true,
+ "requires": {
+ "@types/long": "^4.0.1",
+ "lodash.camelcase": "^4.3.0",
+ "long": "^4.0.0",
+ "protobufjs": "^6.9.0",
+ "yargs": "^15.3.1"
+ }
+ },
+ "@types/node": {
+ "version": "12.19.8",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.8.tgz",
+ "integrity": "sha512-D4k2kNi0URNBxIRCb1khTnkWNHv8KSL1owPmS/K5e5t8B2GzMReY7AsJIY1BnP5KdlgC4rj9jk2IkDMasIE7xg==",
+ "optional": true
+ }
+ }
+ },
+ "@grpc/proto-loader": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.5.5.tgz",
+ "integrity": "sha512-WwN9jVNdHRQoOBo9FDH7qU+mgfjPc8GygPYms3M+y3fbQLfnCe/Kv/E01t7JRgnrsOHH8euvSbed3mIalXhwqQ==",
+ "optional": true,
+ "requires": {
+ "lodash.camelcase": "^4.3.0",
+ "protobufjs": "^6.8.6"
+ }
+ },
+ "@protobufjs/aspromise": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+ "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=",
+ "optional": true
+ },
+ "@protobufjs/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
+ "optional": true
+ },
+ "@protobufjs/codegen": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+ "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
+ "optional": true
+ },
+ "@protobufjs/eventemitter": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+ "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=",
+ "optional": true
+ },
+ "@protobufjs/fetch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+ "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=",
+ "optional": true,
+ "requires": {
+ "@protobufjs/aspromise": "^1.1.1",
+ "@protobufjs/inquire": "^1.1.0"
+ }
+ },
+ "@protobufjs/float": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+ "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=",
+ "optional": true
+ },
+ "@protobufjs/inquire": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+ "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=",
+ "optional": true
+ },
+ "@protobufjs/path": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+ "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=",
+ "optional": true
+ },
+ "@protobufjs/pool": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+ "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=",
+ "optional": true
+ },
+ "@protobufjs/utf8": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+ "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=",
+ "optional": true
+ },
+ "@tootallnate/once": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+ "optional": true
+ },
+ "@types/body-parser": {
+ "version": "1.19.0",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz",
+ "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==",
+ "requires": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/connect": {
+ "version": "3.4.33",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
+ "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==",
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/express": {
+ "version": "4.17.3",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.3.tgz",
+ "integrity": "sha512-I8cGRJj3pyOLs/HndoP+25vOqhqWkAZsWMEmq1qXy/b/M3ppufecUwaK2/TVDVxcV61/iSdhykUjQQ2DLSrTdg==",
+ "requires": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "*",
+ "@types/serve-static": "*"
+ }
+ },
+ "@types/express-serve-static-core": {
+ "version": "4.17.14",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.14.tgz",
+ "integrity": "sha512-uFTLwu94TfUFMToXNgRZikwPuZdOtDgs3syBtAIr/OXorL1kJqUJT9qCLnRZ5KBOWfZQikQ2xKgR2tnDj1OgDA==",
+ "requires": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*"
+ }
+ },
+ "@types/long": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
+ "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==",
+ "optional": true
+ },
+ "@types/mime": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz",
+ "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q=="
+ },
+ "@types/node": {
+ "version": "10.17.48",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.48.tgz",
+ "integrity": "sha512-Agl6xbYP6FOMDeAsr3QVZ+g7Yzg0uhPHWx0j5g4LFdUBHVtqtU+gH660k/lCEe506jJLOGbEzsnqPDTZGJQLag=="
+ },
+ "@types/qs": {
+ "version": "6.9.5",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz",
+ "integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ=="
+ },
+ "@types/range-parser": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
+ "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA=="
+ },
+ "@types/serve-static": {
+ "version": "1.13.8",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.8.tgz",
+ "integrity": "sha512-MoJhSQreaVoL+/hurAZzIm8wafFR6ajiTM1m4A0kv6AGeVBl4r4pOV8bGFrjjq1sGxDTnCoF8i22o0/aE5XCyA==",
+ "requires": {
+ "@types/mime": "*",
+ "@types/node": "*"
+ }
+ },
+ "abort-controller": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "optional": true,
+ "requires": {
+ "event-target-shim": "^5.0.0"
+ }
+ },
+ "accepts": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
+ "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
+ "requires": {
+ "mime-types": "~2.1.24",
+ "negotiator": "0.6.2"
+ }
+ },
+ "agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "optional": true,
+ "requires": {
+ "debug": "4"
+ }
+ },
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "optional": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
+ },
+ "arrify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
+ "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
+ "optional": true
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "optional": true
+ },
+ "bignumber.js": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz",
+ "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==",
+ "optional": true
+ },
+ "body-parser": {
+ "version": "1.19.0",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
+ "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
+ "requires": {
+ "bytes": "3.1.0",
+ "content-type": "~1.0.4",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "http-errors": "1.7.2",
+ "iconv-lite": "0.4.24",
+ "on-finished": "~2.3.0",
+ "qs": "6.7.0",
+ "raw-body": "2.4.0",
+ "type-is": "~1.6.17"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ }
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
+ },
+ "builtin-modules": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+ "dev": true
+ },
+ "bytes": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
+ "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "optional": true
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "dependencies": {
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
+ "cliui": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+ "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "optional": true,
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^6.2.0"
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "compressible": {
+ "version": "2.0.18",
+ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+ "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+ "optional": true,
+ "requires": {
+ "mime-db": ">= 1.43.0 < 2"
+ }
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "configstore": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz",
+ "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==",
+ "optional": true,
+ "requires": {
+ "dot-prop": "^5.2.0",
+ "graceful-fs": "^4.1.2",
+ "make-dir": "^3.0.0",
+ "unique-string": "^2.0.0",
+ "write-file-atomic": "^3.0.0",
+ "xdg-basedir": "^4.0.0"
+ }
+ },
+ "content-disposition": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
+ "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
+ "requires": {
+ "safe-buffer": "5.1.2"
+ },
+ "dependencies": {
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ }
+ }
+ },
+ "content-type": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
+ },
+ "cookie": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
+ "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
+ },
+ "cookie-signature": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+ },
+ "cors": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+ "requires": {
+ "object-assign": "^4",
+ "vary": "^1"
+ }
+ },
+ "crypto-random-string": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
+ "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
+ "optional": true
+ },
+ "date-and-time": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-0.14.1.tgz",
+ "integrity": "sha512-M4RggEH5OF2ZuCOxgOU67R6Z9ohjKbxGvAQz48vj53wLmL0bAgumkBvycR32f30pK+Og9pIR+RFDyChbaE4oLA==",
+ "optional": true
+ },
+ "debug": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+ "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+ "optional": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+ "optional": true
+ },
+ "depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
+ },
+ "destroy": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+ },
+ "dicer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz",
+ "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==",
+ "requires": {
+ "streamsearch": "0.1.2"
+ }
+ },
+ "doctrine": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-0.7.2.tgz",
+ "integrity": "sha1-fLhgNZujvpDgQLJrcpzkv6ZUxSM=",
+ "dev": true,
+ "requires": {
+ "esutils": "^1.1.6",
+ "isarray": "0.0.1"
+ },
+ "dependencies": {
+ "esutils": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.1.6.tgz",
+ "integrity": "sha1-wBzKqa5LiXxtDD4hCuUvPHqEQ3U=",
+ "dev": true
+ }
+ }
+ },
+ "dot-prop": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
+ "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==",
+ "optional": true,
+ "requires": {
+ "is-obj": "^2.0.0"
+ }
+ },
+ "duplexify": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz",
+ "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==",
+ "optional": true,
+ "requires": {
+ "end-of-stream": "^1.4.1",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1",
+ "stream-shift": "^1.0.0"
+ }
+ },
+ "ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "optional": true
+ },
+ "encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
+ },
+ "end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "optional": true,
+ "requires": {
+ "once": "^1.4.0"
+ }
+ },
+ "ent": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz",
+ "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=",
+ "optional": true
+ },
+ "escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
+ },
+ "event-target-shim": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+ "optional": true
+ },
+ "express": {
+ "version": "4.17.1",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
+ "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
+ "requires": {
+ "accepts": "~1.3.7",
+ "array-flatten": "1.1.1",
+ "body-parser": "1.19.0",
+ "content-disposition": "0.5.3",
+ "content-type": "~1.0.4",
+ "cookie": "0.4.0",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "~1.1.2",
+ "fresh": "0.5.2",
+ "merge-descriptors": "1.0.1",
+ "methods": "~1.1.2",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "0.1.7",
+ "proxy-addr": "~2.0.5",
+ "qs": "6.7.0",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.1.2",
+ "send": "0.17.1",
+ "serve-static": "1.14.1",
+ "setprototypeof": "1.1.1",
+ "statuses": "~1.5.0",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ }
+ }
+ },
+ "extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "optional": true
+ },
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "optional": true
+ },
+ "fast-text-encoding": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz",
+ "integrity": "sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig==",
+ "optional": true
+ },
+ "faye-websocket": {
+ "version": "0.11.3",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz",
+ "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==",
+ "requires": {
+ "websocket-driver": ">=0.5.1"
+ }
+ },
+ "finalhandler": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+ "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+ "requires": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.3",
+ "statuses": "~1.5.0",
+ "unpipe": "~1.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ }
+ }
+ },
+ "find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "optional": true,
+ "requires": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "firebase-admin": {
+ "version": "9.4.1",
+ "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-9.4.1.tgz",
+ "integrity": "sha512-y9r2Mz2x1WTr60YrCDqz8Lw70DlwIvRIieVltP+UdRogkVpfnvyd+bi4D0KPlujW3teqcFPmxuzsXB+DP5vGfQ==",
+ "requires": {
+ "@firebase/database": "^0.6.10",
+ "@firebase/database-types": "^0.5.2",
+ "@google-cloud/firestore": "^4.5.0",
+ "@google-cloud/storage": "^5.3.0",
+ "@types/node": "^10.10.0",
+ "dicer": "^0.3.0",
+ "jsonwebtoken": "^8.5.1",
+ "node-forge": "^0.10.0"
+ }
+ },
+ "firebase-functions": {
+ "version": "3.12.0",
+ "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-3.12.0.tgz",
+ "integrity": "sha512-C6XfFlnL9JPo2tRNpncxkXeXIpViLzKxBsfNTMB3M2i33mJFvkrLhxp9rhsCRSrcwxraBKmpeSvyh3FJkka54w==",
+ "requires": {
+ "@types/express": "4.17.3",
+ "cors": "^2.8.5",
+ "express": "^4.17.1",
+ "lodash": "^4.17.14"
+ }
+ },
+ "forwarded": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
+ "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
+ },
+ "fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+ "optional": true
+ },
+ "gaxios": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.0.1.tgz",
+ "integrity": "sha512-jOin8xRZ/UytQeBpSXFqIzqU7Fi5TqgPNLlUsSB8kjJ76+FiGBfImF8KJu++c6J4jOldfJUtt0YmkRj2ZpSHTQ==",
+ "optional": true,
+ "requires": {
+ "abort-controller": "^3.0.0",
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^5.0.0",
+ "is-stream": "^2.0.0",
+ "node-fetch": "^2.3.0"
+ }
+ },
+ "gcp-metadata": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.2.1.tgz",
+ "integrity": "sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw==",
+ "optional": true,
+ "requires": {
+ "gaxios": "^4.0.0",
+ "json-bigint": "^1.0.0"
+ }
+ },
+ "gcs-resumable-upload": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-3.1.1.tgz",
+ "integrity": "sha512-RS1osvAicj9+MjCc6jAcVL1Pt3tg7NK2C2gXM5nqD1Gs0klF2kj5nnAFSBy97JrtslMIQzpb7iSuxaG8rFWd2A==",
+ "optional": true,
+ "requires": {
+ "abort-controller": "^3.0.0",
+ "configstore": "^5.0.0",
+ "extend": "^3.0.2",
+ "gaxios": "^3.0.0",
+ "google-auth-library": "^6.0.0",
+ "pumpify": "^2.0.0",
+ "stream-events": "^1.0.4"
+ },
+ "dependencies": {
+ "gaxios": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-3.2.0.tgz",
+ "integrity": "sha512-+6WPeVzPvOshftpxJwRi2Ozez80tn/hdtOUag7+gajDHRJvAblKxTFSSMPtr2hmnLy7p0mvYz0rMXLBl8pSO7Q==",
+ "optional": true,
+ "requires": {
+ "abort-controller": "^3.0.0",
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^5.0.0",
+ "is-stream": "^2.0.0",
+ "node-fetch": "^2.3.0"
+ }
+ }
+ }
+ },
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
+ },
+ "get-stream": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz",
+ "integrity": "sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==",
+ "optional": true
+ },
+ "glob": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "google-auth-library": {
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.1.3.tgz",
+ "integrity": "sha512-m9mwvY3GWbr7ZYEbl61isWmk+fvTmOt0YNUfPOUY2VH8K5pZlAIWJjxEi0PqR3OjMretyiQLI6GURMrPSwHQ2g==",
+ "optional": true,
+ "requires": {
+ "arrify": "^2.0.0",
+ "base64-js": "^1.3.0",
+ "ecdsa-sig-formatter": "^1.0.11",
+ "fast-text-encoding": "^1.0.0",
+ "gaxios": "^4.0.0",
+ "gcp-metadata": "^4.2.0",
+ "gtoken": "^5.0.4",
+ "jws": "^4.0.0",
+ "lru-cache": "^6.0.0"
+ }
+ },
+ "google-gax": {
+ "version": "2.9.2",
+ "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-2.9.2.tgz",
+ "integrity": "sha512-Pve4osEzNKpBZqFXMfGKBbKCtgnHpUe5IQMh5Ou+Xtg8nLcba94L3gF0xgM5phMdGRRqJn0SMjcuEVmOYu7EBg==",
+ "optional": true,
+ "requires": {
+ "@grpc/grpc-js": "~1.1.1",
+ "@grpc/proto-loader": "^0.5.1",
+ "@types/long": "^4.0.0",
+ "abort-controller": "^3.0.0",
+ "duplexify": "^4.0.0",
+ "google-auth-library": "^6.1.3",
+ "is-stream-ended": "^0.1.4",
+ "node-fetch": "^2.6.1",
+ "protobufjs": "^6.9.0",
+ "retry-request": "^4.0.0"
+ }
+ },
+ "google-p12-pem": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.3.tgz",
+ "integrity": "sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA==",
+ "optional": true,
+ "requires": {
+ "node-forge": "^0.10.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.4",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+ "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+ "optional": true
+ },
+ "gtoken": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.1.0.tgz",
+ "integrity": "sha512-4d8N6Lk8TEAHl9vVoRVMh9BNOKWVgl2DdNtr3428O75r3QFrF/a5MMu851VmK0AA8+iSvbwRv69k5XnMLURGhg==",
+ "optional": true,
+ "requires": {
+ "gaxios": "^4.0.0",
+ "google-p12-pem": "^3.0.3",
+ "jws": "^4.0.0",
+ "mime": "^2.2.0"
+ }
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "hash-stream-validation": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/hash-stream-validation/-/hash-stream-validation-0.2.4.tgz",
+ "integrity": "sha512-Gjzu0Xn7IagXVkSu9cSFuK1fqzwtLwFhNhVL8IFJijRNMgUttFbBSIAzKuSIrsFMO1+g1RlsoN49zPIbwPDMGQ==",
+ "optional": true
+ },
+ "http-errors": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
+ "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
+ "requires": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.1",
+ "statuses": ">= 1.5.0 < 2",
+ "toidentifier": "1.0.0"
+ },
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ }
+ }
+ },
+ "http-parser-js": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.2.tgz",
+ "integrity": "sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ=="
+ },
+ "http-proxy-agent": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+ "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+ "optional": true,
+ "requires": {
+ "@tootallnate/once": "1",
+ "agent-base": "6",
+ "debug": "4"
+ }
+ },
+ "https-proxy-agent": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
+ "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
+ "optional": true,
+ "requires": {
+ "agent-base": "6",
+ "debug": "4"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+ "optional": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "inversify": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/inversify/-/inversify-5.0.1.tgz",
+ "integrity": "sha512-Ieh06s48WnEYGcqHepdsJUIJUXpwH5o5vodAX+DK2JA/gjy4EbEcQZxw+uFfzysmKjiLXGYwNG3qDZsKVMcINQ==",
+ "dev": true
+ },
+ "ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "optional": true
+ },
+ "is-obj": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
+ "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
+ "optional": true
+ },
+ "is-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
+ "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==",
+ "optional": true
+ },
+ "is-stream-ended": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz",
+ "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==",
+ "optional": true
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+ "optional": true
+ },
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+ "dev": true
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "json-bigint": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
+ "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+ "optional": true,
+ "requires": {
+ "bignumber.js": "^9.0.0"
+ }
+ },
+ "jsonwebtoken": {
+ "version": "8.5.1",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
+ "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
+ "requires": {
+ "jws": "^3.2.2",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^5.6.0"
+ },
+ "dependencies": {
+ "jwa": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+ "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+ "requires": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "jws": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "requires": {
+ "jwa": "^1.4.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+ }
+ }
+ },
+ "jwa": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
+ "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
+ "optional": true,
+ "requires": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "jws": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
+ "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+ "optional": true,
+ "requires": {
+ "jwa": "^2.0.0",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "optional": true,
+ "requires": {
+ "p-locate": "^4.1.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
+ },
+ "lodash.camelcase": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+ "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=",
+ "optional": true
+ },
+ "lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
+ },
+ "lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
+ },
+ "lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
+ },
+ "lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
+ },
+ "lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
+ },
+ "lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
+ },
+ "lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
+ },
+ "long": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+ "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
+ "optional": true
+ },
+ "lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "optional": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
+ "make-dir": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "optional": true,
+ "requires": {
+ "semver": "^6.0.0"
+ }
+ },
+ "media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
+ },
+ "merge-descriptors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
+ },
+ "methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
+ },
+ "mime": {
+ "version": "2.4.6",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz",
+ "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==",
+ "optional": true
+ },
+ "mime-db": {
+ "version": "1.45.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz",
+ "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==",
+ "optional": true
+ },
+ "mime-types": {
+ "version": "2.1.27",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
+ "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
+ "requires": {
+ "mime-db": "1.44.0"
+ },
+ "dependencies": {
+ "mime-db": {
+ "version": "1.44.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
+ "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
+ }
+ }
+ },
+ "mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "optional": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "negotiator": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
+ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
+ },
+ "node-fetch": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
+ "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==",
+ "optional": true
+ },
+ "node-forge": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
+ "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA=="
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+ },
+ "on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "optional": true,
+ "requires": {
+ "mimic-fn": "^2.1.0"
+ }
+ },
+ "p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "optional": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "optional": true,
+ "requires": {
+ "p-limit": "^2.2.0"
+ }
+ },
+ "p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "optional": true
+ },
+ "parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "optional": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+ "dev": true
+ },
+ "path-to-regexp": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+ },
+ "protobufjs": {
+ "version": "6.10.2",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.2.tgz",
+ "integrity": "sha512-27yj+04uF6ya9l+qfpH187aqEzfCF4+Uit0I9ZBQVqK09hk/SQzKa2MUqUpXaVa7LOFRg1TSSr3lVxGOk6c0SQ==",
+ "optional": true,
+ "requires": {
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.4",
+ "@protobufjs/eventemitter": "^1.1.0",
+ "@protobufjs/fetch": "^1.1.0",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/inquire": "^1.1.0",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.0",
+ "@types/long": "^4.0.1",
+ "@types/node": "^13.7.0",
+ "long": "^4.0.0"
+ },
+ "dependencies": {
+ "@types/node": {
+ "version": "13.13.34",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.34.tgz",
+ "integrity": "sha512-g8D1HF2dMDKYSDl5+79izRwRgNPsSynmWMbj50mj7GZ0b7Lv4p8EmZjbo3h0h+6iLr6YmVz9VnF6XVZ3O6V1Ug==",
+ "optional": true
+ }
+ }
+ },
+ "proxy-addr": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
+ "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
+ "requires": {
+ "forwarded": "~0.1.2",
+ "ipaddr.js": "1.9.1"
+ }
+ },
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "optional": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "pumpify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz",
+ "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==",
+ "optional": true,
+ "requires": {
+ "duplexify": "^4.1.1",
+ "inherits": "^2.0.3",
+ "pump": "^3.0.0"
+ }
+ },
+ "qs": {
+ "version": "6.7.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+ "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
+ },
+ "range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
+ },
+ "raw-body": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
+ "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
+ "requires": {
+ "bytes": "3.1.0",
+ "http-errors": "1.7.2",
+ "iconv-lite": "0.4.24",
+ "unpipe": "1.0.0"
+ }
+ },
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "optional": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ },
+ "reflect-metadata": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
+ "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==",
+ "dev": true
+ },
+ "require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+ "optional": true
+ },
+ "require-main-filename": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "optional": true
+ },
+ "resolve": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+ "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ },
+ "retry-request": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.1.3.tgz",
+ "integrity": "sha512-QnRZUpuPNgX0+D1xVxul6DbJ9slvo4Rm6iV/dn63e048MvGbUZiKySVt6Tenp04JqmchxjiLltGerOJys7kJYQ==",
+ "optional": true,
+ "requires": {
+ "debug": "^4.1.1"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "optional": true
+ },
+ "send": {
+ "version": "0.17.1",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
+ "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
+ "requires": {
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "destroy": "~1.0.4",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "~1.7.2",
+ "mime": "1.6.0",
+ "ms": "2.1.1",
+ "on-finished": "~2.3.0",
+ "range-parser": "~1.2.1",
+ "statuses": "~1.5.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "requires": {
+ "ms": "2.0.0"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ }
+ }
+ },
+ "mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+ }
+ }
+ },
+ "serve-static": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
+ "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
+ "requires": {
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "0.17.1"
+ }
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+ "optional": true
+ },
+ "setprototypeof": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
+ "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
+ },
+ "signal-exit": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
+ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
+ "optional": true
+ },
+ "snakeize": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/snakeize/-/snakeize-0.1.0.tgz",
+ "integrity": "sha1-EMCI2LWOsHazIpu1oE4jLOEmQi0=",
+ "optional": true
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
+ },
+ "stream-events": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz",
+ "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==",
+ "optional": true,
+ "requires": {
+ "stubs": "^3.0.0"
+ }
+ },
+ "stream-shift": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
+ "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
+ "optional": true
+ },
+ "streamsearch": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
+ "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
+ },
+ "string-width": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+ "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+ "optional": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "optional": true,
+ "requires": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "optional": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ },
+ "stubs": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz",
+ "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=",
+ "optional": true
+ },
+ "teeny-request": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.0.1.tgz",
+ "integrity": "sha512-sasJmQ37klOlplL4Ia/786M5YlOcoLGQyq2TE4WHSRupbAuDaQW0PfVxV4MtdBtRJ4ngzS+1qim8zP6Zp35qCw==",
+ "optional": true,
+ "requires": {
+ "http-proxy-agent": "^4.0.0",
+ "https-proxy-agent": "^5.0.0",
+ "node-fetch": "^2.6.1",
+ "stream-events": "^1.0.5",
+ "uuid": "^8.0.0"
+ }
+ },
+ "toidentifier": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
+ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
+ },
+ "tslib": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
+ "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
+ "dev": true
+ },
+ "tslint": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.2.tgz",
+ "integrity": "sha512-UyNrLdK3E0fQG/xWNqAFAC5ugtFyPO4JJR1KyyfQAyzR8W0fTRrC91A8Wej4BntFzcvETdCSDa/4PnNYJQLYiA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "builtin-modules": "^1.1.1",
+ "chalk": "^2.3.0",
+ "commander": "^2.12.1",
+ "diff": "^4.0.1",
+ "glob": "^7.1.1",
+ "js-yaml": "^3.13.1",
+ "minimatch": "^3.0.4",
+ "mkdirp": "^0.5.3",
+ "resolve": "^1.3.2",
+ "semver": "^5.3.0",
+ "tslib": "^1.10.0",
+ "tsutils": "^2.29.0"
+ },
+ "dependencies": {
+ "diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "tslint-config-airbnb": {
+ "version": "5.11.2",
+ "resolved": "https://registry.npmjs.org/tslint-config-airbnb/-/tslint-config-airbnb-5.11.2.tgz",
+ "integrity": "sha512-mUpHPTeeCFx8XARGG/kzYP4dPSOgoCqNiYbGHh09qTH8q+Y1ghsOgaeZKYYQT7IyxMos523z/QBaiv2zKNBcow==",
+ "dev": true,
+ "requires": {
+ "tslint-consistent-codestyle": "^1.14.1",
+ "tslint-eslint-rules": "^5.4.0",
+ "tslint-microsoft-contrib": "~5.2.1"
+ }
+ },
+ "tslint-consistent-codestyle": {
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/tslint-consistent-codestyle/-/tslint-consistent-codestyle-1.16.0.tgz",
+ "integrity": "sha512-ebR/xHyMEuU36hGNOgCfjGBNYxBPixf0yU1Yoo6s3BrpBRFccjPOmIVaVvQsWAUAMdmfzHOCihVkcaMfimqvHw==",
+ "dev": true,
+ "requires": {
+ "@fimbul/bifrost": "^0.21.0",
+ "tslib": "^1.7.1",
+ "tsutils": "^2.29.0"
+ }
+ },
+ "tslint-eslint-rules": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/tslint-eslint-rules/-/tslint-eslint-rules-5.4.0.tgz",
+ "integrity": "sha512-WlSXE+J2vY/VPgIcqQuijMQiel+UtmXS+4nvK4ZzlDiqBfXse8FAvkNnTcYhnQyOTW5KFM+uRRGXxYhFpuBc6w==",
+ "dev": true,
+ "requires": {
+ "doctrine": "0.7.2",
+ "tslib": "1.9.0",
+ "tsutils": "^3.0.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz",
+ "integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==",
+ "dev": true
+ },
+ "tsutils": {
+ "version": "3.17.1",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz",
+ "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.8.1"
+ }
+ }
+ }
+ },
+ "tslint-microsoft-contrib": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/tslint-microsoft-contrib/-/tslint-microsoft-contrib-5.2.1.tgz",
+ "integrity": "sha512-PDYjvpo0gN9IfMULwKk0KpVOPMhU6cNoT9VwCOLeDl/QS8v8W2yspRpFFuUS7/c5EIH/n8ApMi8TxJAz1tfFUA==",
+ "dev": true,
+ "requires": {
+ "tsutils": "^2.27.2 <2.29.0"
+ },
+ "dependencies": {
+ "tsutils": {
+ "version": "2.28.0",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.28.0.tgz",
+ "integrity": "sha512-bh5nAtW0tuhvOJnx1GLRn5ScraRLICGyJV5wJhtRWOLsxW70Kk5tZtpK3O/hW6LDnqKS9mlUMPZj9fEMJ0gxqA==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.8.1"
+ }
+ }
+ }
+ },
+ "tsutils": {
+ "version": "2.29.0",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
+ "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.8.1"
+ }
+ },
+ "type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "requires": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ }
+ },
+ "typedarray-to-buffer": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
+ "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
+ "optional": true,
+ "requires": {
+ "is-typedarray": "^1.0.0"
+ }
+ },
+ "typescript": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.2.tgz",
+ "integrity": "sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==",
+ "dev": true
+ },
+ "unique-string": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
+ "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==",
+ "optional": true,
+ "requires": {
+ "crypto-random-string": "^2.0.0"
+ }
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "optional": true
+ },
+ "utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
+ },
+ "uuid": {
+ "version": "8.3.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz",
+ "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==",
+ "optional": true
+ },
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
+ },
+ "websocket-driver": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
+ "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
+ "requires": {
+ "http-parser-js": ">=0.5.1",
+ "safe-buffer": ">=5.1.0",
+ "websocket-extensions": ">=0.1.1"
+ }
+ },
+ "websocket-extensions": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg=="
+ },
+ "which-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+ "optional": true
+ },
+ "wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "optional": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "optional": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "optional": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "optional": true
+ }
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ },
+ "write-file-atomic": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
+ "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
+ "optional": true,
+ "requires": {
+ "imurmurhash": "^0.1.4",
+ "is-typedarray": "^1.0.0",
+ "signal-exit": "^3.0.2",
+ "typedarray-to-buffer": "^3.1.5"
+ }
+ },
+ "xdg-basedir": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz",
+ "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==",
+ "optional": true
+ },
+ "y18n": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz",
+ "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==",
+ "optional": true
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "optional": true
+ },
+ "yargs": {
+ "version": "15.4.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
+ "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+ "optional": true,
+ "requires": {
+ "cliui": "^6.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^4.1.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^4.2.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^18.1.2"
+ }
+ },
+ "yargs-parser": {
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "optional": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ },
+ "yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "optional": true
+ }
+ }
+}
diff --git a/functions/functions/package.json b/functions/functions/package.json
new file mode 100644
index 00000000..bd7b0cfe
--- /dev/null
+++ b/functions/functions/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "functions",
+ "scripts": {
+ "lint": "tslint --fix --project tsconfig.json",
+ "build": "tsc",
+ "serve": "npm run build && firebase serve --only functions",
+ "shell": "npm run build && firebase functions:shell",
+ "start": "npm run shell",
+ "deploy": "firebase deploy --only functions",
+ "logs": "firebase functions:log",
+ "test": "npm run build && mocha -r ts-node/register test/**/*.spec.ts --timeout 5000"
+ },
+ "main": "lib/index.js",
+ "dependencies": {
+ "firebase-admin": "^9.4.1",
+ "firebase-functions": "^3.12.0"
+ },
+ "devDependencies": {
+ "tslint": "^6.1.2",
+ "tslint-config-airbnb": "^5.11.2",
+ "typescript": "^4.1.2"
+ },
+ "engines": {
+ "node": "10"
+ },
+ "private": true
+}
diff --git a/functions/functions/src/firebase-path.ts b/functions/functions/src/firebase-path.ts
new file mode 100644
index 00000000..41f67e0c
--- /dev/null
+++ b/functions/functions/src/firebase-path.ts
@@ -0,0 +1,5 @@
+export class FirebasePath {
+ /* This file is synchronized with client code */
+ public static readonly FIREBASE_PATH_USERS = 'users';
+ public static readonly FIREBASE_PATH_SONGS = 'songs';
+}
diff --git a/functions/functions/src/game/game-manager.ts b/functions/functions/src/game/game-manager.ts
new file mode 100644
index 00000000..1e4b6f03
--- /dev/null
+++ b/functions/functions/src/game/game-manager.ts
@@ -0,0 +1,77 @@
+import {
+ HttpsError,
+ CallableContext,
+} from 'firebase-functions/lib/providers/https';
+import { FirebasePath } from '../firebase-path';
+import * as firebase from 'firebase-admin';
+import { getUserId } from '../shared';
+import { Song } from '../model/song';
+import { GameReward } from '../model/game-reward';
+import { User } from '../model/user';
+
+export async function getGameReward(
+ data: any,
+ context: CallableContext,
+): Promise {
+ const songId = data.songId;
+ const difficulty = data.difficulty;
+ const speed = data.speed;
+ const errorCount = data.errorCount;
+
+ const userId = getUserId(context);
+ console.log(`User ${userId} is getting game reward with songId ${songId}, difficulty ${difficulty}, speed ${speed}, errorCount ${errorCount}`);
+ const firestore = firebase.firestore();
+ if (
+ !(
+ Number.isSafeInteger(errorCount) &&
+ errorCount >= 0
+ )
+ ) {
+ console.log('Invalid game information');
+ throw new HttpsError('invalid-argument', 'Invalid game information');
+ }
+ return firestore.runTransaction(async (transaction): Promise => {
+ const userRef = firestore
+ .collection(FirebasePath.FIREBASE_PATH_USERS)
+ .doc(userId);
+ const userSnapshot = await transaction.get(userRef);
+ if (!userSnapshot.exists) {
+ console.log('Cannot retrieve user information');
+ throw new HttpsError('unavailable', 'Cannot retrieve user information');
+ }
+ const user = userSnapshot.data() as User;
+ const songRef = firestore.collection(FirebasePath.FIREBASE_PATH_SONGS).doc(songId);
+ const songSnapshot = await transaction.get(songRef);
+ if (!songSnapshot.exists) {
+ console.log('Cannot retrieve played song information');
+ throw new HttpsError(
+ 'unavailable',
+ 'Cannot retrieve played song information',
+ );
+ }
+ const song: Song = songSnapshot.data() as Song;
+
+ let stars = 0;
+ const tilesCount = song.tilesCount[difficulty];
+ const errorPercent = errorCount / tilesCount;
+ if (errorPercent <= 0.05) {
+ stars = 3;
+ } else if (errorPercent <= 0.2) {
+ stars = 2;
+ } else {
+ stars = 1;
+ }
+
+ user.playedNotes += tilesCount;
+ user.stars += tilesCount - errorCount;
+ user.playedTime += song.duration[speed];
+
+ transaction.set(userRef, user);
+ const reward: GameReward = {
+ stars,
+ playedNotes: tilesCount,
+ };
+ console.log(`Reward of the user is ${JSON.stringify(reward)}`);
+ return reward;
+ });
+}
diff --git a/functions/functions/src/index.ts b/functions/functions/src/index.ts
new file mode 100644
index 00000000..fc46ddde
--- /dev/null
+++ b/functions/functions/src/index.ts
@@ -0,0 +1,26 @@
+import * as functions from 'firebase-functions';
+import * as firebase from 'firebase-admin';
+import { onUserSignUp, onUserDeleted } from './user/user';
+import { getGameReward } from './game/game-manager';
+
+const runtimeOpts: functions.RuntimeOptions = {
+ timeoutSeconds: 540,
+ memory: '128MB',
+};
+firebase.initializeApp();
+
+if (!process.env.FUNCTION_NAME || process.env.FUNCTION_NAME === 'onUserSignUp') {
+ exports.onUserSignUp = functions.runWith(runtimeOpts).auth
+ .user()
+ .onCreate(onUserSignUp);
+}
+
+if (!process.env.FUNCTION_NAME || process.env.FUNCTION_NAME === 'onUserDeleted') {
+ exports.onUserDeleted = functions.runWith(runtimeOpts).auth
+ .user()
+ .onDelete(onUserDeleted);
+}
+
+if (!process.env.FUNCTION_NAME || process.env.FUNCTION_NAME === 'getGameReward') {
+ exports.getGameReward = functions.runWith(runtimeOpts).https.onCall(getGameReward);
+}
diff --git a/functions/functions/src/model/game-reward.ts b/functions/functions/src/model/game-reward.ts
new file mode 100644
index 00000000..8c9da9b3
--- /dev/null
+++ b/functions/functions/src/model/game-reward.ts
@@ -0,0 +1,4 @@
+export interface GameReward {
+ stars: number;
+ playedNotes: number;
+}
diff --git a/functions/functions/src/model/song.ts b/functions/functions/src/model/song.ts
new file mode 100644
index 00000000..c48cf59d
--- /dev/null
+++ b/functions/functions/src/model/song.ts
@@ -0,0 +1,9 @@
+export interface Song {
+ id: string;
+ title: string;
+ artist: string;
+ url: string;
+ bpm: number;
+ tilesCount: number[];
+ duration: number[];
+}
diff --git a/functions/functions/src/model/user.ts b/functions/functions/src/model/user.ts
new file mode 100644
index 00000000..399bc445
--- /dev/null
+++ b/functions/functions/src/model/user.ts
@@ -0,0 +1,9 @@
+
+export interface User {
+ id: string;
+ playedNotes: number;
+ stars: number;
+ playedTime: number;
+ instrumentId: string;
+ notificationTokens: string[];
+}
diff --git a/functions/functions/src/shared.ts b/functions/functions/src/shared.ts
new file mode 100644
index 00000000..6d5e0a29
--- /dev/null
+++ b/functions/functions/src/shared.ts
@@ -0,0 +1,14 @@
+import * as functions from 'firebase-functions';
+import { CallableContext } from 'firebase-functions/lib/providers/https';
+
+export function getUserId(context: CallableContext): string {
+ if (!context.auth) {
+ console.log('Unauthorized Access');
+ throw new functions.https.HttpsError(
+ 'unauthenticated',
+ 'Unauthorized Access',
+ );
+ } else {
+ return context.auth.uid;
+ }
+}
diff --git a/functions/functions/src/user/user.ts b/functions/functions/src/user/user.ts
new file mode 100644
index 00000000..32aa3566
--- /dev/null
+++ b/functions/functions/src/user/user.ts
@@ -0,0 +1,42 @@
+import * as firebase from 'firebase-admin';
+import { User } from '../model/user';
+import { FirebasePath } from '../firebase-path';
+import { EventContext } from 'firebase-functions';
+import UserRecord = firebase.auth.UserRecord;
+import { HttpsError } from 'firebase-functions/lib/providers/https';
+
+export function onUserSignUp(user: UserRecord, _: EventContext): Promise {
+ return firebase.firestore().runTransaction(async (transaction): Promise => {
+ const userRef = firebase
+ .firestore()
+ .collection(FirebasePath.FIREBASE_PATH_USERS)
+ .doc(user.uid);
+ const newUser: User = {
+ id: user.uid,
+ playedNotes: 0,
+ stars: 0,
+ playedTime: 0,
+ instrumentId: 'piano',
+ notificationTokens: [],
+ };
+ transaction.set(userRef, newUser);
+ console.log(`Create profile success ${user.uid}`);
+ });
+}
+
+export function onUserDeleted(userRecord: UserRecord): Promise {
+ return firebase.firestore().runTransaction(async (transaction): Promise => {
+ const userRef = firebase
+ .firestore()
+ .collection(FirebasePath.FIREBASE_PATH_USERS)
+ .doc(userRecord.uid);
+ const userSnapshot = await transaction.get(userRef);
+ if (!userSnapshot.exists) {
+ console.log('Cannot retrieve user information');
+ throw new HttpsError('unavailable', 'Cannot retrieve user information');
+ }
+ const user = userSnapshot.data();
+ transaction.delete(userRef);
+ console.log(`Deleted profile ${user.id}`);
+ });
+}
diff --git a/functions/functions/tsconfig.json b/functions/functions/tsconfig.json
new file mode 100644
index 00000000..14a4e6b9
--- /dev/null
+++ b/functions/functions/tsconfig.json
@@ -0,0 +1,33 @@
+{
+ "compilerOptions": {
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noImplicitAny": true,
+ "removeComments": true,
+ "strict": true,
+ "strictNullChecks": true,
+ "noImplicitThis": true,
+ "alwaysStrict": true,
+ "noImplicitReturns": true,
+ "noFallthroughCasesInSwitch": true,
+ "strictFunctionTypes": true,
+ "lib": [
+ "es2017"
+ ],
+ "module": "commonjs",
+ "outDir": "lib",
+ "sourceMap": true,
+ "resolveJsonModule": true,
+ "target": "es2017"
+ },
+ "compileOnSave": true,
+ "include": [
+ "src"
+ ],
+ "files": [
+ "node_modules/typescript/lib/lib.es6.d.ts"
+ ],
+ "exclude": [
+ "node_modules"
+ ]
+}
\ No newline at end of file
diff --git a/functions/functions/tslint.json b/functions/functions/tslint.json
new file mode 100644
index 00000000..9d157d09
--- /dev/null
+++ b/functions/functions/tslint.json
@@ -0,0 +1,11 @@
+{
+ "extends": "tslint-config-airbnb",
+ "rules": {
+ "max-line-length": false,
+ "typedef": [
+ true,
+ "call-signature",
+ "arrow-call-signature"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/functions/storage.rules b/functions/storage.rules
new file mode 100644
index 00000000..9ec1d96e
--- /dev/null
+++ b/functions/storage.rules
@@ -0,0 +1,9 @@
+rules_version = '2';
+service firebase.storage {
+ match /b/{bucket}/o {
+ match /{allPaths=**} {
+ allow read: if request.auth != null;
+ allow write: if false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ios/.gitignore b/ios/.gitignore
new file mode 100644
index 00000000..e96ef602
--- /dev/null
+++ b/ios/.gitignore
@@ -0,0 +1,32 @@
+*.mode1v3
+*.mode2v3
+*.moved-aside
+*.pbxuser
+*.perspectivev3
+**/*sync/
+.sconsign.dblite
+.tags*
+**/.vagrant/
+**/DerivedData/
+Icon?
+**/Pods/
+**/.symlinks/
+profile
+xcuserdata
+**/.generated/
+Flutter/App.framework
+Flutter/Flutter.framework
+Flutter/Flutter.podspec
+Flutter/Generated.xcconfig
+Flutter/app.flx
+Flutter/app.zip
+Flutter/flutter_assets/
+Flutter/flutter_export_environment.sh
+ServiceDefinitions.json
+Runner/GeneratedPluginRegistrant.*
+
+# Exceptions to above rules.
+!default.mode1v3
+!default.mode2v3
+!default.pbxuser
+!default.perspectivev3
diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 00000000..f2872cf4
--- /dev/null
+++ b/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ App
+ CFBundleIdentifier
+ io.flutter.flutter.app
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ App
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ MinimumOSVersion
+ 9.0
+
+
diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig
new file mode 100644
index 00000000..592ceee8
--- /dev/null
+++ b/ios/Flutter/Debug.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig
new file mode 100644
index 00000000..592ceee8
--- /dev/null
+++ b/ios/Flutter/Release.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..31f9995e
--- /dev/null
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,495 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
+ 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 97C146EB1CF9000F007C117D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 9740EEB11CF90186004384FC /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */,
+ );
+ name = Flutter;
+ sourceTree = "";
+ };
+ 97C146E51CF9000F007C117D = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB11CF90186004384FC /* Flutter */,
+ 97C146F01CF9000F007C117D /* Runner */,
+ 97C146EF1CF9000F007C117D /* Products */,
+ );
+ sourceTree = "";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 97C146ED1CF9000F007C117D /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 9740EEB61CF901F6004384FC /* Run Script */,
+ 97C146EA1CF9000F007C117D /* Sources */,
+ 97C146EB1CF9000F007C117D /* Frameworks */,
+ 97C146EC1CF9000F007C117D /* Resources */,
+ 9705A1C41CF9048500538489 /* Embed Frameworks */,
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 97C146E61CF9000F007C117D /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1020;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 97C146ED1CF9000F007C117D = {
+ CreatedOnToolsVersion = 7.3.1;
+ LastSwiftMigration = 1100;
+ };
+ };
+ };
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 97C146E51CF9000F007C117D;
+ productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 97C146ED1CF9000F007C117D /* Runner */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 97C146EC1CF9000F007C117D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+ };
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 97C146EA1CF9000F007C117D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C146FB1CF9000F007C117D /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "";
+ };
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C147001CF9000F007C117D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 249021D3217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Profile;
+ };
+ 249021D4217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.chaomao.hitnotes;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Profile;
+ };
+ 97C147031CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 97C147041CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 97C147061CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.chaomao.hitnotes;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Debug;
+ };
+ 97C147071CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.chaomao.hitnotes;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147031CF9000F007C117D /* Debug */,
+ 97C147041CF9000F007C117D /* Release */,
+ 249021D3217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147061CF9000F007C117D /* Debug */,
+ 97C147071CF9000F007C117D /* Release */,
+ 249021D4217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 97C146E61CF9000F007C117D /* Project object */;
+}
diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..1d526a16
--- /dev/null
+++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 00000000..f9b0d7c5
--- /dev/null
+++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 00000000..a28140cf
--- /dev/null
+++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..1d526a16
--- /dev/null
+++ b/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 00000000..f9b0d7c5
--- /dev/null
+++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift
new file mode 100644
index 00000000..70693e4a
--- /dev/null
+++ b/ios/Runner/AppDelegate.swift
@@ -0,0 +1,13 @@
+import UIKit
+import Flutter
+
+@UIApplicationMain
+@objc class AppDelegate: FlutterAppDelegate {
+ override func application(
+ _ application: UIApplication,
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
+ ) -> Bool {
+ GeneratedPluginRegistrant.register(with: self)
+ return super.application(application, didFinishLaunchingWithOptions: launchOptions)
+ }
+}
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000..e882ab98
--- /dev/null
+++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,122 @@
+{
+ "images": [
+ {
+ "size": "20x20",
+ "idiom": "iphone",
+ "filename": "Icon-App-20x20@2x.png",
+ "scale": "2x"
+ },
+ {
+ "size": "20x20",
+ "idiom": "iphone",
+ "filename": "Icon-App-20x20@3x.png",
+ "scale": "3x"
+ },
+ {
+ "size": "29x29",
+ "idiom": "iphone",
+ "filename": "Icon-App-29x29@1x.png",
+ "scale": "1x"
+ },
+ {
+ "size": "29x29",
+ "idiom": "iphone",
+ "filename": "Icon-App-29x29@2x.png",
+ "scale": "2x"
+ },
+ {
+ "size": "29x29",
+ "idiom": "iphone",
+ "filename": "Icon-App-29x29@3x.png",
+ "scale": "3x"
+ },
+ {
+ "size": "40x40",
+ "idiom": "iphone",
+ "filename": "Icon-App-40x40@2x.png",
+ "scale": "2x"
+ },
+ {
+ "size": "40x40",
+ "idiom": "iphone",
+ "filename": "Icon-App-40x40@3x.png",
+ "scale": "3x"
+ },
+ {
+ "size": "60x60",
+ "idiom": "iphone",
+ "filename": "Icon-App-60x60@2x.png",
+ "scale": "2x"
+ },
+ {
+ "size": "60x60",
+ "idiom": "iphone",
+ "filename": "Icon-App-60x60@3x.png",
+ "scale": "3x"
+ },
+ {
+ "size": "20x20",
+ "idiom": "ipad",
+ "filename": "Icon-App-20x20@1x.png",
+ "scale": "1x"
+ },
+ {
+ "size": "20x20",
+ "idiom": "ipad",
+ "filename": "Icon-App-20x20@2x.png",
+ "scale": "2x"
+ },
+ {
+ "size": "29x29",
+ "idiom": "ipad",
+ "filename": "Icon-App-29x29@1x.png",
+ "scale": "1x"
+ },
+ {
+ "size": "29x29",
+ "idiom": "ipad",
+ "filename": "Icon-App-29x29@2x.png",
+ "scale": "2x"
+ },
+ {
+ "size": "40x40",
+ "idiom": "ipad",
+ "filename": "Icon-App-40x40@1x.png",
+ "scale": "1x"
+ },
+ {
+ "size": "40x40",
+ "idiom": "ipad",
+ "filename": "Icon-App-40x40@2x.png",
+ "scale": "2x"
+ },
+ {
+ "size": "76x76",
+ "idiom": "ipad",
+ "filename": "Icon-App-76x76@1x.png",
+ "scale": "1x"
+ },
+ {
+ "size": "76x76",
+ "idiom": "ipad",
+ "filename": "Icon-App-76x76@2x.png",
+ "scale": "2x"
+ },
+ {
+ "size": "83.5x83.5",
+ "idiom": "ipad",
+ "filename": "Icon-App-83.5x83.5@2x.png",
+ "scale": "2x"
+ },
+ {
+ "size": "1024x1024",
+ "idiom": "ios-marketing",
+ "filename": "Icon-App-1024x1024@1x.png",
+ "scale": "1x"
+ }
+ ],
+ "info": {
+ "version": 1,
+ "author": "xcode"
+ }
+}
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
new file mode 100644
index 00000000..dc9ada47
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 00000000..28c6bf03
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 00000000..2ccbfd96
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 00000000..f091b6b0
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 00000000..4cde1211
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 00000000..d0ef06e7
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 00000000..dcdc2306
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 00000000..2ccbfd96
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 00000000..c8f9ed8f
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 00000000..a6d6b860
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 00000000..a6d6b860
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 00000000..75b2d164
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 00000000..c4df70d3
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 00000000..6a84f41e
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 00000000..d0e1f585
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 00000000..781d7cdc
--- /dev/null
+++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images": [
+ {
+ "idiom": "universal",
+ "filename": "LaunchImage.png",
+ "scale": "1x"
+ },
+ {
+ "idiom": "universal",
+ "filename": "LaunchImage@2x.png",
+ "scale": "2x"
+ },
+ {
+ "idiom": "universal",
+ "filename": "LaunchImage@3x.png",
+ "scale": "3x"
+ }
+ ],
+ "info": {
+ "version": 1,
+ "author": "xcode"
+ }
+}
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 00000000..9da19eac
Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 00000000..9da19eac
Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 00000000..9da19eac
Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 00000000..89c2725b
--- /dev/null
+++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
@@ -0,0 +1,5 @@
+# Launch Screen Assets
+
+You can customize the launch screen with your own desired assets by replacing the image files in this directory.
+
+You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 00000000..f2e259c7
--- /dev/null
+++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 00000000..f3c28516
--- /dev/null
+++ b/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
new file mode 100644
index 00000000..b504c7ab
--- /dev/null
+++ b/ios/Runner/Info.plist
@@ -0,0 +1,45 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ hitnotes
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
+
+
diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h
new file mode 100644
index 00000000..308a2a56
--- /dev/null
+++ b/ios/Runner/Runner-Bridging-Header.h
@@ -0,0 +1 @@
+#import "GeneratedPluginRegistrant.h"
diff --git a/lib/authentication/authentication_bloc.dart b/lib/authentication/authentication_bloc.dart
new file mode 100644
index 00000000..c1926ca2
--- /dev/null
+++ b/lib/authentication/authentication_bloc.dart
@@ -0,0 +1,90 @@
+import 'dart:async';
+
+import 'package:bloc/bloc.dart';
+import 'package:firebase_auth/firebase_auth.dart';
+import 'package:firebase_core/firebase_core.dart';
+import 'package:flutter_facebook_login/flutter_facebook_login.dart';
+import 'package:google_sign_in/google_sign_in.dart';
+
+import '../user/user_repository.dart';
+import 'authentication_event.dart';
+import 'authentication_state.dart';
+
+class AuthenticationBloc
+ extends Bloc {
+ AuthenticationBloc(this._userRepository) : super(Uninitialized());
+ final _googleSignIn = GoogleSignIn();
+ final _facebookLogin = FacebookLogin();
+ final UserRepository _userRepository;
+
+ @override
+ Stream mapEventToState(
+ AuthenticationEvent event,
+ ) async* {
+ if (event is SignInAnonymouslyEvent) {
+ yield* _mapSignInAnonymouslyEventToState();
+ } else if (event is SignInWithGoogleEvent) {
+ yield* _mapSignInWithGoogleEventToState();
+ } else if (event is SignInWithFacebookEvent) {
+ yield* _mapSignInWithFacebookEventToState();
+ }
+ }
+
+ Stream _mapSignInAnonymouslyEventToState() async* {
+ try {
+ await Firebase.initializeApp();
+ final currentUser = FirebaseAuth.instance.currentUser;
+ if (currentUser == null) {
+ await FirebaseAuth.instance.signInAnonymously();
+ }
+ _userRepository.changUser();
+ yield Authenticated('Anonymous');
+ } catch (_) {
+ yield Unauthenticated();
+ }
+ }
+
+ Stream _mapSignInWithGoogleEventToState() async* {
+ try {
+ final googleUser = await _googleSignIn.signIn();
+ final googleAuth = await googleUser.authentication;
+ final credential = GoogleAuthProvider.credential(
+ accessToken: googleAuth.accessToken,
+ idToken: googleAuth.idToken,
+ );
+ await _tryToLinkWithCurrentUser(credential);
+ _userRepository.changUser();
+ yield Authenticated('Google');
+ } on Exception {
+ yield Unauthenticated();
+ }
+ }
+
+ Stream _mapSignInWithFacebookEventToState() async* {
+ try {
+ final result = await _facebookLogin.logIn(['email', 'public_profile']);
+ switch (result.status) {
+ case FacebookLoginStatus.loggedIn:
+ final credential =
+ FacebookAuthProvider.credential(result.accessToken.token);
+ await _tryToLinkWithCurrentUser(credential);
+ _userRepository.changUser();
+ yield Authenticated('Facebook');
+ break;
+ default:
+ yield Unauthenticated();
+ }
+ } on Exception {
+ yield Unauthenticated();
+ }
+ }
+
+ Future _tryToLinkWithCurrentUser(OAuthCredential authCredential) async {
+ try {
+ await FirebaseAuth.instance.currentUser
+ .linkWithCredential(authCredential);
+ } on Exception {
+ await FirebaseAuth.instance.signInWithCredential(authCredential);
+ }
+ }
+}
diff --git a/lib/authentication/authentication_event.dart b/lib/authentication/authentication_event.dart
new file mode 100644
index 00000000..6b428b53
--- /dev/null
+++ b/lib/authentication/authentication_event.dart
@@ -0,0 +1,18 @@
+import 'package:equatable/equatable.dart';
+
+abstract class AuthenticationEvent extends Equatable {}
+
+class SignInAnonymouslyEvent extends AuthenticationEvent {
+ @override
+ List