essential-actions

Clone or download

Modified Files

A .DS_Store
+- −-
Binary files /dev/null and b/.DS_Store differ
M .gitignore
+5 −0
--- 'a/.gitignore'
+++ b/.gitignore
@@ -2,3 +2,8 @@
.idea/
target/
+PreBuiltPlugins/
+playmusicclipaction/LICENSE
+playmusicclipaction/LICENSE
+*.xml
+playmusicclipaction/LICENSE
--- 'a/DemoCustomNormalAction/src/main/java/com/stream_pi/democustomnormalaction/DemoCustomNormalAction.java'
+++ b/DemoCustomNormalAction/src/main/java/com/stream_pi/democustomnormalaction/DemoCustomNormalAction.java
@@ -1,11 +1,11 @@
package com.stream_pi.democustomnormalaction;
-import com.stream_pi.action_api.actionproperty.property.ControlType;
import com.stream_pi.action_api.actionproperty.property.Property;
import com.stream_pi.action_api.actionproperty.property.Type;
-import com.stream_pi.action_api.normalaction.NormalAction;
+import com.stream_pi.action_api.externalplugin.NormalAction;
+import com.stream_pi.util.alert.StreamPiAlert;
import com.stream_pi.util.version.Version;
-import java.util.ArrayList;
+import javafx.scene.control.Button;
public class DemoCustomNormalAction extends NormalAction
{
@@ -13,8 +13,8 @@ public class DemoCustomNormalAction exte
public DemoCustomNormalAction()
{
setName("Demo Action");
- setAuthor("dubbadhar");
- setHelpLink("https://github.com/Stream-Pi/DemoCustomNormalAction");
+ setAuthor("rnayabed");
+ setHelpLink("https://github.com/stream-pi/");
setVersion(new Version(1,0,0));
setCategory("Custom Actions");
@@ -25,79 +25,58 @@ public class DemoCustomNormalAction exte
public void initProperties() throws Exception {
//Called First
- Property privateProperty = new Property("InvisibleProperty", Type.STRING);
- privateProperty.setVisible(false);
-
- Property sliderPropertyInt = new Property("SliderPropertyInt", Type.INTEGER);
- sliderPropertyInt.setControlType(ControlType.SLIDER_INTEGER);
- sliderPropertyInt.setMaxIntValue(5);
-
- Property sliderPropertyDouble= new Property("SliderPropertyDouble", Type.DOUBLE);
- sliderPropertyInt.setControlType(ControlType.SLIDER_DOUBLE);
-
- Property comboBoxProperty = new Property("ComboBoxProperty", Type.LIST);
- ArrayList<String> arrayList = new ArrayList<>();
- arrayList.add("Choice 1");
- arrayList.add("Choice 2");
-
- comboBoxProperty.setListValue(arrayList);
-
- Property property = new Property("ServerProperty2", Type.STRING);
- property.setDisplayName("ServerProperty2 [Required]");
- property.setDefaultValueStr("test");
- property.setCanBeBlank(false);
-
- Property property1 = new Property("ServerProperty1", Type.STRING);
+ Property property1 = new Property("ClientServerProperty1", Type.STRING);
property1.setDefaultValueStr("23");
- Property booleanProperty = new Property("ServerBooleanProperty1", Type.BOOLEAN);
- booleanProperty.setDefaultValueBoolean(true);
-
- addServerProperties(
- property1,
- property,
- booleanProperty,
- privateProperty,
- comboBoxProperty,
- sliderPropertyDouble,
- sliderPropertyInt
- );
-
- Property clientProperty1 = new Property("ClientProperty1", Type.STRING);
- clientProperty1.setDefaultValueStr("test");
- clientProperty1.setCanBeBlank(false);
-
- Property clientProperty2 = new Property("ClientProperty2", Type.STRING);
- clientProperty2.setDefaultValueStr("Default Prop");
addClientProperties(
- clientProperty1,
- clientProperty2,
- new Property("ClientBooleanProperty1", Type.BOOLEAN),
- privateProperty,
- comboBoxProperty,
- sliderPropertyDouble,
- sliderPropertyInt
+ property1
);
}
@Override
public void initAction() {
- // This is called after initProperties()
+
}
@Override
- public void onActionClicked()
+ public void initClientActionSettingsButtonBar()
{
- //Called when action is clicked
- System.out.println("Action Called!");
+ Button b1 = new Button("Test Alert");
+ b1.setOnAction(actionEvent -> {
+ new StreamPiAlert("Hi","Hello").show();
+ });
+
+ Button b2 = new Button("Modify Test");
+ b2.setOnAction(actionEvent -> {
+ try
+ {
+ System.out.println("AAAAAAAAAAAAAAAAAAAAAAA : "+getProfileID());
+ System.out.println("BBBBBBBBBBb : "+getID());
+ getClientProperties().getSingleProperty("ClientServerProperty1")
+ .setStringValue("This property was dynamically modified");
+
+ setDisplayText("Dynamic");
+
+ saveClientAction();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ });
+
+
+ setClientActionSettingsButtonBar(b1,b2);
}
@Override
- public void onShutDown() throws Exception {
- // TODO Auto-generated method stub
+ public void onActionClicked()
+ {
+ //Called when action is clicked
+ System.out.println("Action Called!");
}
}
--- 'a/DemoCustomNormalAction/src/main/java/module-info.java'
+++ b/DemoCustomNormalAction/src/main/java/module-info.java
@@ -1,5 +1,6 @@
-module com.stream_pi.democustomnormalaction{
+module com.stream_pi.democustomnormalaction
+{
requires com.stream_pi.action_api;
requires org.kordamp.ikonli.javafx;
- provides com.stream_pi.action_api.normalaction.NormalAction with com.stream_pi.democustomnormalaction.DemoCustomNormalAction;
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with com.stream_pi.democustomnormalaction.DemoCustomNormalAction;
}
\ No newline at end of file
--- /dev/null
+++ b/DemoCustomToggleAction/.gitignore
@@ -0,0 +1,9 @@
+.idea/
+DemoCustomToggleAction.iml
+target/
+
+
+.settings/
+.project
+.factorypath
+.classpath
--- /dev/null
+++ b/DemoCustomToggleAction/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 <https://www.gnu.org/licenses/>.
+
+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:
+
+ <program> Copyright (C) <year> <name of author>
+ 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
+<https://www.gnu.org/licenses/>.
+
+ 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
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
\ No newline at end of file
--- /dev/null
+++ b/DemoCustomToggleAction/pom.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>com.stream-pi</groupId>
+ <artifactId>DemoCustomToggleAction</artifactId>
+ <version>1.0.0</version>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>3.1.0</version>
+ <executions>
+ <execution>
+ <id>test-jar</id>
+ <phase>package</phase>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.8.1</version>
+ <configuration>
+ <release>11</release>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <properties>
+ <ActionAPIVersion>1.0.0-SNAPSHOT</ActionAPIVersion>
+
+ <JavaFXVersion>16</JavaFXVersion>
+
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <maven.compiler.source>11</maven.compiler.source>
+ <maven.compiler.target>11</maven.compiler.target>
+ </properties>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.openjfx</groupId>
+ <artifactId>javafx-controls</artifactId>
+ <version>${JavaFXVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openjfx</groupId>
+ <artifactId>javafx-graphics</artifactId>
+ <version>${JavaFXVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openjfx</groupId>
+ <artifactId>javafx-base</artifactId>
+ <version>${JavaFXVersion}</version>
+ </dependency>
+
+
+
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>action-api</artifactId>
+ <version>${ActionAPIVersion}</version>
+ </dependency>
+
+
+
+
+ <dependency>
+ <groupId>org.kordamp.ikonli</groupId>
+ <artifactId>ikonli-material-pack</artifactId>
+ <version>11.5.0</version>
+ </dependency>
+
+
+ </dependencies>
+
+</project>
--- /dev/null
+++ b/DemoCustomToggleAction/src/main/java/com/stream_pi/democustomtoggleaction/DemoCustomToggleAction.java
@@ -0,0 +1,74 @@
+package com.stream_pi.democustomtoggleaction;
+
+import com.stream_pi.action_api.action.DisplayTextAlignment;
+import com.stream_pi.action_api.actionproperty.property.ControlType;
+import com.stream_pi.action_api.actionproperty.property.Property;
+import com.stream_pi.action_api.actionproperty.property.Type;
+import com.stream_pi.action_api.externalplugin.ToggleAction;
+import com.stream_pi.util.alert.StreamPiAlert;
+import com.stream_pi.util.version.Version;
+import java.util.ArrayList;
+
+public class DemoCustomToggleAction extends ToggleAction
+{
+
+ public DemoCustomToggleAction()
+ {
+ setName("Demo Toggle Action");
+ setAuthor("rnayabed");
+ setHelpLink("https://github.com/Stream-Pi/");
+ setVersion(new Version(1,0,0));
+
+ setCategory("Custom Actions");
+
+ }
+
+ @Override
+ public void onToggleOn() throws Exception
+ {
+ setDisplayText("ON");
+ saveClientAction();
+ }
+
+ @Override
+ public void onActionCreate()
+ {
+ //setDisplayText("Hi");
+ setDisplayTextAlignment(DisplayTextAlignment.BOTTOM);
+
+ try
+ {
+ setToggleOnIcon(getClass().getResource("streamdeck_key.png").openStream().readAllBytes());
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onToggleOff() throws Exception {
+ setDisplayText("OFF");
+ saveClientAction();
+ }
+
+ @Override
+ public void initProperties() throws Exception {
+ //Called First
+
+
+ }
+
+
+ @Override
+ public void initAction() {
+ // This is called after initProperties()
+ }
+
+
+ @Override
+ public void onShutDown() throws Exception {
+ // TODO Auto-generated method stub
+
+ }
+}
--- /dev/null
+++ b/DemoCustomToggleAction/src/main/java/module-info.java
@@ -0,0 +1,6 @@
+module com.stream_pi.democustomtoggleaction
+{
+ requires com.stream_pi.action_api;
+ requires org.kordamp.ikonli.javafx;
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with com.stream_pi.democustomtoggleaction.DemoCustomToggleAction;
+}
Binary files /dev/null and b/DemoCustomToggleAction/src/main/resources/com/stream_pi/democustomtoggleaction/streamdeck_key.png differ
Binary files /dev/null and b/Dependencies/Java-Twirk-0.6.3.jar differ
Binary files /dev/null and b/Dependencies/jetty-client-9.4.35.v20201120.jar differ
Binary files /dev/null and b/Dependencies/jetty-http-9.4.35.v20201120.jar differ
Binary files /dev/null and b/Dependencies/jetty-io-9.4.35.v20201120.jar differ
Binary files /dev/null and b/Dependencies/jetty-util-9.4.35.v20201120.jar differ
Binary files /dev/null and b/Dependencies/obs-websocket-java-1.2.0.jar differ
Binary files /dev/null and b/Dependencies/slf4j-api-1.7.30.jar differ
Binary files /dev/null and b/Dependencies/slf4j-simple-1.7.30.jar differ
Binary files /dev/null and b/Dependencies/twitter4j-core-4.0.7.jar differ
Binary files /dev/null and b/Dependencies/websocket-api-9.4.35.v20201120.jar differ
Binary files /dev/null and b/Dependencies/websocket-client-9.4.35.v20201120.jar differ
Binary files /dev/null and b/Dependencies/websocket-common-9.4.35.v20201120.jar differ
A Makefile
+29 −0
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,29 @@
+build-all:
+ ./build.sh
+
+build-hotkey-action:
+ ./build.sh hotkey
+
+build-media-key-action:
+ ./build.sh mediakey
+
+build-obs-suite-action:
+ ./build.sh obssuite
+
+build-play-audio-clip-action:
+ ./build.sh playaudioclip
+
+build-run-command-action:
+ ./build.sh runcommand
+
+build-text-block-action:
+ ./build.sh textblock
+
+build-twitch-chat-action:
+ ./build.sh twitchchat
+
+build-twitter-action:
+ ./build.sh twitter
+
+build-website-action:
+ ./build.sh website
Binary files /dev/null and b/PreBuiltPlugins.zip differ
Binary files 'a/PreBuiltPlugins/Java-Twirk-0.6.3.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/hotkeyaction.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/jetty-client-9.4.35.v20201120.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/jetty-http-9.4.35.v20201120.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/jetty-io-9.4.35.v20201120.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/jetty-util-9.4.35.v20201120.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/mediakeyaction.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/obs-websocket-java-1.2.0.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/obssuite_motheraction.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/obssuite_setcurrentprofileaction.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/obssuite_setcurrentsceneaction.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/obssuite_setcurrenttransitionaction.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/obssuite_setmuteaction.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/obssuite_setpreviewsceneaction.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/obssuite_setrecordingaction.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/obssuite_setreplaybufferaction.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/obssuite_setstreamingaction.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/obssuite_setstudiomodeaction.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/obssuite_setvolumeaction.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/playaudioclipaction.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/runcommandaction.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/slf4j-api-1.7.30.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/slf4j-simple-1.7.30.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/textblockaction.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/twitch-chat-connect.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/twitch-send-channel-msg.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/twitter4j-core-4.0.7.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/twitteraction.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/websiteaction.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/websocket-api-9.4.35.v20201120.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/websocket-client-9.4.35.v20201120.jar' and /dev/null differ
Binary files 'a/PreBuiltPlugins/websocket-common-9.4.35.v20201120.jar' and /dev/null differ
M README.md
+9 −20
--- 'a/README.md'
+++ b/README.md
@@ -10,36 +10,25 @@ Set of trusted, pre-bundled actions and
## List of Actions
- Hotkey
-- Website
-- Twitter
+- Media File
+- Media Key
- OBS Actions
- Run Command
- Text Block
-- Media File
-- Media Key
-- Twitch
+- Twitch Chat
+- Twitter
+- Website
## Actions Help Guide
-### Twitch Chat Integration
-
-The first step is to acquire an OAuth token from https://twitchapps.com/tmi/, the generated OAuth token should look something like `oauth:xxxxx`.
-
-In the Stream-Pi Server's Plugin page enter your Twitch username, and the generated token then click on `Save Twitch chat credentials` button. You should then be able to use the pre-bundled Twitch chat actions.
-
-### Supported actions
-
-- Send channel message
-
-### Running locally
-
-Copy the `twitch-chat-connect`, `twitch-send-channel-msg`, and `Java-Twirk` jar files from the `PreBuiltPlugins` directory to your Stream-Pi server plugins' directory.
+- [Hotkey](hotkeyaction/README.md)
+- [Twitch Chat](twitch/README.md)
---
## Quick Start
-Build actions by executing `./build.sh` from the command line.
+Build all actions by executing `make build-all` from the command line or specific actions i.e. `make build-twitch-chat-action`, see [Makefile](Makefile) for complete list.
-To test these actions out in your local environment you'll need to run the [Stream-Pi Server](https://github.com/stream-pi/server) and copy the contents of `PreBuiltPlugins/` to the server's
+To test these actions out in your local environment you'll need to run the [Stream-Pi Server](https://github.com/stream-pi/server) and copy the contents of `PreBuiltPlugins` to the server's
Plugins directory (`data/Plugins` by default), especially if you're writing your own custom action / integration.
Binary files /dev/null and b/airplayStreamPi.jar differ
M build.sh
+170 −61
--- 'a/build.sh'
+++ b/build.sh
@@ -1,72 +1,181 @@
-FOLD=../PreBuiltPlugins
+#!/bin/bash
-pushd . || exit
-cd hotkeyaction && mvn clean package
-mv target/hotkeyaction-1.0.0.jar $FOLD/hotkeyaction.jar
+FOLD=PreBuiltPlugins
+DEPS=Dependencies
-cd ../mediakeyaction && mvn clean package
-mv target/mediakeyaction-1.0.0.jar $FOLD/mediakeyaction.jar
-
-cd ../playaudioclipaction && mvn clean package
-mv target/playaudioclipaction-1.0.0.jar $FOLD/playaudioclipaction.jar
-
-cd ../runcommandaction && mvn clean package
-mv target/runcommandaction-1.0.0.jar $FOLD/runcommandaction.jar
-
-cd ../textblockaction && mvn clean package
-mv target/textblockaction-1.0.0.jar $FOLD/textblockaction.jar
-
-cd ../twitteraction && mvn clean package
-mv target/twitteraction-1.0.0.jar $FOLD/twitteraction.jar
-
-cd ../websiteaction && mvn clean package
-mv target/websiteaction-1.0.0.jar $FOLD/websiteaction.jar
-popd || exit
-
-## OBS Suite
-pushd . || exit
-cd obssuite/mother && mvn clean install package
-mv target/obssuite_motheraction-1.0.0.jar ../$FOLD/obssuite_motheraction.jar
-
-cd ../setcurrentprofile && mvn clean package
-mv target/obssuite_setcurrentprofileaction-1.0.0.jar ../$FOLD/obssuite_setcurrentprofileaction.jar
-
-cd ../setcurrentscene && mvn clean package
-mv target/obssuite_setcurrentsceneaction-1.0.0.jar ../$FOLD/obssuite_setcurrentsceneaction.jar
-
-cd ../setcurrenttransition && mvn clean package
-mv target/obssuite_setcurrenttransitionaction-1.0.0.jar ../$FOLD/obssuite_setcurrenttransitionaction.jar
-
-cd ../setmute && mvn clean package
-mv target/obssuite_setmuteaction-1.0.0.jar ../$FOLD/obssuite_setmuteaction.jar
-
-cd ../setcurrentprofile && mvn clean package
-mv target/obssuite_setcurrentprofileaction-1.0.0.jar ../$FOLD/obssuite_setcurrentprofileaction.jar
-
-cd ../setpreviewscene && mvn clean package
-mv target/obssuite_setpreviewsceneaction-1.0.0.jar ../$FOLD/obssuite_setpreviewsceneaction.jar
-
-cd ../setrecording && mvn clean package
-mv target/obssuite_setrecordingaction-1.0.0.jar ../$FOLD/obssuite_setrecordingaction.jar
-
-cd ../setreplaybuffer && mvn clean package
-mv target/obssuite_setreplaybufferaction-1.0.0.jar ../$FOLD/obssuite_setreplaybufferaction.jar
-
-cd ../setstreaming && mvn clean package
-mv target/obssuite_setstreamingaction-1.0.0.jar ../$FOLD/obssuite_setstreamingaction.jar
-
-cd ../setstudiomode && mvn clean package
-mv target/obssuite_setstudiomodeaction-1.0.0.jar ../$FOLD/obssuite_setstudiomodeaction.jar
-
-cd ../setvolume && mvn clean package
-mv target/obssuite_setvolumeaction-1.0.0.jar ../$FOLD/obssuite_setvolumeaction.jar
-popd || exit
-
-# Twitch Chat
-pushd . || exit
-cd twitch/twitch-chat-connect && mvn clean install package
-mv target/twitch-chat-connect-1.0.0.jar ../$FOLD/twitch-chat-connect.jar
-
-cd ../send-channel-msg && mvn clean package
-mv target/twitch-send-channel-msg-1.0.0.jar ../$FOLD/twitch-send-channel-msg.jar
-popd || exit
+hotkey() {
+ pushd . || exit
+ cd hotkeyaction && mvn clean -Dmaven.test.skip package
+ mv target/hotkeyaction-*.jar ../$FOLD/hotkeyaction.jar
+ popd || exit
+}
+
+mediakey() {
+ pushd . || exit
+ cd mediakeyaction && mvn clean -Dmaven.test.skip package
+ mv target/mediakeyaction-*.jar ../$FOLD/mediakeyaction.jar
+ popd || exit
+}
+
+obssuite() {
+ pushd . || exit
+ cd obssuite/mother && mvn clean install -Dmaven.test.skip package
+ mv target/obssuite_motheraction-*.jar ../../$FOLD/obssuite_motheraction.jar
+
+ cd ../setcurrentprofile && mvn clean -Dmaven.test.skip package
+ mv target/obssuite_setcurrentprofileaction-*.jar ../../$FOLD/obssuite_setcurrentprofileaction.jar
+
+ cd ../setcurrentscene && mvn clean -Dmaven.test.skip package
+ mv target/obssuite_setcurrentsceneaction-*.jar ../../$FOLD/obssuite_setcurrentsceneaction.jar
+
+ cd ../setcurrenttransition && mvn clean -Dmaven.test.skip package
+ mv target/obssuite_setcurrenttransitionaction-*.jar ../../$FOLD/obssuite_setcurrenttransitionaction.jar
+
+ cd ../setmute && mvn clean -Dmaven.test.skip package
+ mv target/obssuite_setmuteaction-*.jar ../../$FOLD/obssuite_setmuteaction.jar
+
+ cd ../setcurrentprofile && mvn clean -Dmaven.test.skip package
+ mv target/obssuite_setcurrentprofileaction-*.jar ../../$FOLD/obssuite_setcurrentprofileaction.jar
+
+ cd ../setpreviewscene && mvn clean -Dmaven.test.skip package
+ mv target/obssuite_setpreviewsceneaction-*.jar ../../$FOLD/obssuite_setpreviewsceneaction.jar
+
+ cd ../setrecording && mvn clean -Dmaven.test.skip package
+ mv target/obssuite_setrecordingaction-*.jar ../../$FOLD/obssuite_setrecordingaction.jar
+
+ cd ../setreplaybuffer && mvn clean -Dmaven.test.skip package
+ mv target/obssuite_setreplaybufferaction-*.jar ../../$FOLD/obssuite_setreplaybufferaction.jar
+
+ cd ../setstreaming && mvn clean -Dmaven.test.skip package
+ mv target/obssuite_setstreamingaction-*.jar ../../$FOLD/obssuite_setstreamingaction.jar
+
+ cd ../setstudiomode && mvn clean -Dmaven.test.skip package
+ mv target/obssuite_setstudiomodeaction-*.jar ../../$FOLD/obssuite_setstudiomodeaction.jar
+
+ cd ../setvolume && mvn clean -Dmaven.test.skip package
+ mv target/obssuite_setvolumeaction-*.jar ../../$FOLD/obssuite_setvolumeaction.jar
+ popd || exit
+}
+
+playaudioclip() {
+ pushd . || exit
+ cd playaudioclipaction && mvn clean -Dmaven.test.skip package
+ mv target/playaudioclipaction-*.jar ../$FOLD/playaudioclipaction.jar
+ popd || exit
+}
+
+runcommand() {
+ pushd . || exit
+ cd runcommandaction && mvn clean -Dmaven.test.skip package
+ mv target/runcommandaction-*.jar ../$FOLD/runcommandaction.jar
+ popd || exit
+}
+
+textblock() {
+ pushd . || exit
+ cd textblockaction && mvn clean -Dmaven.test.skip package
+ mv target/textblockaction-*.jar ../$FOLD/textblockaction.jar
+ popd || exit
+}
+
+twitter() {
+ pushd . || exit
+ cd twitteraction && mvn clean -Dmaven.test.skip package
+ mv target/twitteraction-*.jar ../$FOLD/twitteraction.jar
+ popd || exit
+}
+
+twitchchat() {
+ pushd . || exit
+ cd twitch/twitch-chat-connect && mvn clean install -Dmaven.test.skip package
+ mv target/twitch-chat-connect-*.jar ../../$FOLD/twitch-chat-connect.jar
+
+ cd ../send-channel-msg && mvn clean -Dmaven.test.skip package
+ mv target/twitch-send-channel-msg-*.jar ../../$FOLD/twitch-send-channel-msg.jar
+
+ cd ../clear-chat && mvn clean -Dmaven.test.skip package
+ mv target/twitch-clear-chat-*.jar ../../$FOLD/twitch-clear-chat.jar
+
+ cd ../set-color && mvn clean -Dmaven.test.skip package
+ mv target/twitch-set-color-*.jar ../../$FOLD/twitch-set-color.jar
+
+ cd ../whisper && mvn clean -Dmaven.test.skip package
+ mv target/twitch-whisper-*.jar ../../$FOLD/twitch-whisper.jar
+
+ cd ../unraid && mvn clean -Dmaven.test.skip package
+ mv target/twitch-unraid-*.jar ../../$FOLD/twitch-unraid.jar
+
+ cd ../unhost && mvn clean -Dmaven.test.skip package
+ mv target/twitch-unhost-*.jar ../../$FOLD/twitch-unhost.jar
+
+ cd ../add-stream-marker && mvn clean -Dmaven.test.skip package
+ mv target/twitch-add-stream-marker-*.jar ../../$FOLD/twitch-add-stream-marker.jar
+
+ cd ../host-channel && mvn clean -Dmaven.test.skip package
+ mv target/twitch-host-channel-*.jar ../../$FOLD/twitch-host-channel.jar
+
+ cd ../raid-channel && mvn clean -Dmaven.test.skip package
+ mv target/twitch-raid-channel-*.jar ../../$FOLD/twitch-raid-channel.jar
+
+ cd ../start-commercial && mvn clean -Dmaven.test.skip package
+ mv target/twitch-start-commercial-*.jar ../../$FOLD/twitch-start-commercial.jar
+ popd || exit
+}
+
+website() {
+ pushd . || exit
+ cd websiteaction && mvn clean -Dmaven.test.skip package
+ mv target/websiteaction-*.jar ../$FOLD/websiteaction.jar
+ popd || exit
+}
+
+
+
+
+mkdir -p $FOLD
+rm -rf "${FOLD:?}/"*
+
+cp $DEPS/* $FOLD/
+
+
+case "$1" in
+hotkey)
+ hotkey
+ ;;
+mediakey)
+ mediakey
+ ;;
+playaudioclip)
+ playaudioclip
+ ;;
+runcommand)
+ runcommand
+ ;;
+textblock)
+ textblock
+ ;;
+twitter)
+ twitter
+ ;;
+website)
+ website
+ ;;
+obssuite)
+ obssuite
+ ;;
+twitchchat)
+ twitchchat
+ ;;
+*)
+ # build all actions as default
+ hotkey
+ mediakey
+ obssuite
+ playaudioclip
+ runcommand
+ textblock
+ twitter
+ twitchchat
+ website
+ ;;
+esac
--- /dev/null
+++ b/documentopen/.gitignore
@@ -0,0 +1,9 @@
+.idea/
+DemoCustomNormalAction.iml
+target/
+
+
+.settings/
+.project
+.factorypath
+.classpath
A documentopen/LICENSE
+674 −0
--- /dev/null
+++ b/documentopen/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 <https://www.gnu.org/licenses/>.
+
+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:
+
+ <program> Copyright (C) <year> <name of author>
+ 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
+<https://www.gnu.org/licenses/>.
+
+ 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
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
\ No newline at end of file
--- /dev/null
+++ b/documentopen/pom.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>com.stream-pi</groupId>
+ <artifactId>DocumentOpen</artifactId>
+ <version>1.0.0</version>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>3.1.0</version>
+ <executions>
+ <execution>
+ <id>test-jar</id>
+ <phase>package</phase>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.8.1</version>
+ <configuration>
+ <release>11</release>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <properties>
+ <ActionAPIVersion>1.0.0-SNAPSHOT</ActionAPIVersion>
+
+ <JavaFXVersion>16-ea+6</JavaFXVersion>
+
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <maven.compiler.source>11</maven.compiler.source>
+ <maven.compiler.target>11</maven.compiler.target>
+ </properties>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.openjfx</groupId>
+ <artifactId>javafx-controls</artifactId>
+ <version>${JavaFXVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openjfx</groupId>
+ <artifactId>javafx-graphics</artifactId>
+ <version>${JavaFXVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openjfx</groupId>
+ <artifactId>javafx-base</artifactId>
+ <version>${JavaFXVersion}</version>
+ </dependency>
+
+
+
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>action-api</artifactId>
+ <version>${ActionAPIVersion}</version>
+ </dependency>
+
+
+
+
+ <dependency>
+ <groupId>org.kordamp.ikonli</groupId>
+ <artifactId>ikonli-material-pack</artifactId>
+ <version>11.5.0</version>
+ </dependency>
+
+
+ </dependencies>
+
+</project>
\ No newline at end of file
--- /dev/null
+++ b/documentopen/src/main/java/com/stream_pi/documentopen/DocumentOpen.java
@@ -0,0 +1,73 @@
+package com.stream_pi.documentopen;
+
+import com.stream_pi.action_api.actionproperty.property.ControlType;
+import com.stream_pi.action_api.actionproperty.property.FileExtensionFilter;
+import com.stream_pi.action_api.actionproperty.property.Property;
+import com.stream_pi.action_api.actionproperty.property.Type;
+import com.stream_pi.action_api.externalplugin.NormalAction;
+import com.stream_pi.util.exception.MinorException;
+import com.stream_pi.util.alert.StreamPiAlert;
+import com.stream_pi.util.alert.StreamPiAlertType;
+import com.stream_pi.util.version.Version;
+
+import java.io.File;
+import java.awt.*;
+import java.net.URI;
+
+public class DocumentOpen extends NormalAction
+{
+
+ public DocumentOpen()
+ {
+ setName("Document Open");
+ setAuthor("quimodotcom");
+ setHelpLink("https://github.com/stream-pi/essentialactions");
+ setVersion(new Version(1,0,0));
+
+ setCategory("Essentials");
+
+ }
+
+ @Override
+ public void initProperties() throws Exception {
+ //Called First
+
+ Property property1 = new Property("documentopen", Type.STRING);
+ property1.setDefaultValueStr("Document.pdf");
+ property1.setControlType(ControlType.FILE_PATH);
+ property1.setDisplayName("Document File Location");
+ property1.setExtensionFilters(
+ new FileExtensionFilter("pdf","*.pdf"),
+ new FileExtensionFilter("docx", "*.docx")
+ );
+
+ addClientProperties(
+ property1
+ );
+ }
+
+
+ @Override
+ public void initAction() {
+
+ }
+
+ public String path = null;
+
+ @Override
+ public void onActionClicked() throws Exception
+ {
+ Property documentloc = getClientProperties().getSingleProperty("documentopen");
+
+ if (documentloc.getStringValue().isBlank())
+ {
+ new StreamPiAlert("Document Open Action", "No file specified", StreamPiAlertType.ERROR).show();
+ return;
+ }
+
+ File file = new File(documentloc.getStringValue());
+
+ //Open PDF file
+ Desktop.getDesktop().open(file);
+ }
+}
--- /dev/null
+++ b/documentopen/src/main/java/module-info.java
@@ -0,0 +1,9 @@
+module com.stream_pi.documentopen
+{
+ requires com.stream_pi.action_api;
+ requires org.kordamp.ikonli.javafx;
+
+ requires java.desktop;
+
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with com.stream_pi.documentopen.DocumentOpen;
+}
\ No newline at end of file
--- /dev/null
+++ b/executeapplication/.gitignore
@@ -0,0 +1,9 @@
+.idea/
+DemoCustomNormalAction.iml
+target/
+
+
+.settings/
+.project
+.factorypath
+.classpath
--- /dev/null
+++ b/executeapplication/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 <https://www.gnu.org/licenses/>.
+
+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:
+
+ <program> Copyright (C) <year> <name of author>
+ 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
+<https://www.gnu.org/licenses/>.
+
+ 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
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
\ No newline at end of file
Binary files /dev/null and b/executeapplication/Plug-In/ExecuteApplication-1.1.2.jar differ
--- /dev/null
+++ b/executeapplication/pom.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>com.quimodotcom</groupId>
+ <artifactId>ExecuteApplication</artifactId>
+ <version>1.1.2</version>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>3.1.0</version>
+ <executions>
+ <execution>
+ <id>test-jar</id>
+ <phase>package</phase>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.8.1</version>
+ <configuration>
+ <release>11</release>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <properties>
+ <ActionAPIVersion>1.0.0-SNAPSHOT</ActionAPIVersion>
+
+ <JavaFXVersion>16-ea+6</JavaFXVersion>
+
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <maven.compiler.source>11</maven.compiler.source>
+ <maven.compiler.target>11</maven.compiler.target>
+ </properties>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.openjfx</groupId>
+ <artifactId>javafx-controls</artifactId>
+ <version>${JavaFXVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openjfx</groupId>
+ <artifactId>javafx-graphics</artifactId>
+ <version>${JavaFXVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openjfx</groupId>
+ <artifactId>javafx-base</artifactId>
+ <version>${JavaFXVersion}</version>
+ </dependency>
+
+
+
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>action-api</artifactId>
+ <version>${ActionAPIVersion}</version>
+ </dependency>
+
+
+
+
+ <dependency>
+ <groupId>org.kordamp.ikonli</groupId>
+ <artifactId>ikonli-material-pack</artifactId>
+ <version>11.5.0</version>
+ </dependency>
+
+
+ </dependencies>
+
+</project>
\ No newline at end of file
--- /dev/null
+++ b/executeapplication/src/main/java/com/stream_pi/executeapplication/ExecuteApplication.java
@@ -0,0 +1,71 @@
+package com.stream_pi.executeapplication;
+
+import com.stream_pi.action_api.actionproperty.property.ControlType;
+import com.stream_pi.action_api.actionproperty.property.FileExtensionFilter;
+import com.stream_pi.action_api.actionproperty.property.Property;
+import com.stream_pi.action_api.actionproperty.property.Type;
+import com.stream_pi.action_api.externalplugin.NormalAction;
+import com.stream_pi.util.alert.StreamPiAlert;
+import com.stream_pi.util.alert.StreamPiAlertType;
+import com.stream_pi.util.version.Version;
+import javafx.scene.control.Button;
+
+import java.awt.*;
+import java.net.URI;
+
+import java.io.File;
+
+public class ExecuteApplication extends NormalAction
+{
+
+ public ExecuteApplication()
+ {
+ setName("Execute Application");
+ setAuthor("quimodotcom");
+ setServerButtonGraphic("fas-paste");
+ setVersion(new Version(1,1,2));
+
+ setCategory("Exclusive Actions");
+
+ }
+
+ @Override
+ public void initProperties() throws Exception {
+ Property applicationProp = new Property("app_location", Type.STRING);
+ applicationProp.setControlType(ControlType.FILE_PATH);
+ applicationProp.setDisplayName("Application File Location");
+ applicationProp.setExtensionFilters(
+ new FileExtensionFilter("EXE","*.exe"),
+ new FileExtensionFilter("MSI","*.msi"),
+ new FileExtensionFilter("BAT","*.bat"),
+ new FileExtensionFilter("CMD","*.cmd"),
+ new FileExtensionFilter("COM","*.com"),
+ new FileExtensionFilter("VB","*.vb", "*.vbs"),
+ new FileExtensionFilter("LNK","*.lnk")
+ );
+
+ addClientProperties(applicationProp);
+ }
+
+
+ @Override
+ public void initAction() {
+
+ }
+
+ @Override
+ public void onActionClicked() throws Exception
+ {
+ Property applicationProp = getClientProperties().getSingleProperty("app_location");
+
+ if (applicationProp.getStringValue().isBlank())
+ {
+ new StreamPiAlert("Execute Application Action", "No file specified", StreamPiAlertType.ERROR).show();
+ return;
+ }
+
+ File file = new File(applicationProp.getStringValue());
+
+ Desktop.getDesktop().open(file);
+ }
+}
--- /dev/null
+++ b/executeapplication/src/main/java/module-info.java
@@ -0,0 +1,9 @@
+module com.stream_pi.executeapplication
+{
+ requires com.stream_pi.action_api;
+ requires org.kordamp.ikonli.javafx;
+
+ requires java.desktop;
+
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with com.stream_pi.executeapplication.ExecuteApplication;
+}
\ No newline at end of file
--- /dev/null
+++ b/hotkeyaction/README.md
@@ -0,0 +1,150 @@
+# Hotkey Action plugin
+
+![version](https://img.shields.io/badge/Version-2.0.0-green)
+
+This plugin allows map and execute keyboard shortcuts
+
+## KeyBoard Mapping Reference to Hotkeyaction
+
+The following table shows the relation between our keyboard and what we have to type on the plugin field to fire the hotkeys we want:
+
+Hotkey mapping plugin | Keyboard Key |
+--------------------- | ------------ |
+ A | <kbd>A</kbd> |
+ B | <kbd>B</kbd> |
+ C | <kbd>C</kbd> |
+ D | <kbd>D</kbd> |
+ E | <kbd>E</kbd> |
+ F | <kbd>F</kbd> |
+ G | <kbd>G</kbd> |
+ H | <kbd>H</kbd> |
+ I | <kbd>I</kbd> |
+ J | <kbd>J</kbd> |
+ K | <kbd>K</kbd> |
+ L | <kbd>L</kbd> |
+ M | <kbd>M</kbd> |
+ N | <kbd>N</kbd> |
+ O | <kbd>O</kbd> |
+ P | <kbd>P</kbd> |
+ Q | <kbd>Q</kbd> |
+ R | <kbd>R</kbd> |
+ S | <kbd>S</kbd> |
+ T | <kbd>T</kbd> |
+ U | <kbd>U</kbd> |
+ V | <kbd>V</kbd> |
+ W | <kbd>W</kbd> |
+ X | <kbd>X</kbd> |
+ Y | <kbd>Y</kbd> |
+ Z | <kbd>Z</kbd> |
+ ` | <kbd>`</kbd> |
+ 0 | <kbd>0</kbd> |
+ 1 | <kbd>1</kbd> |
+ 2 | <kbd>2</kbd> |
+ 3 | <kbd>3</kbd> |
+ 4 | <kbd>4</kbd> |
+ 5 | <kbd>5</kbd> |
+ 6 | <kbd>6</kbd> |
+ 7 | <kbd>7</kbd> |
+ 8 | <kbd>8</kbd> |
+ 9 | <kbd>9</kbd> |
+ \- | <kbd>-</kbd> |
+ = | <kbd>=</kbd> |
+ ~ | <kbd>~</kbd> |
+ ! | <kbd>!</kbd> |
+ @ | <kbd>@</kbd> |
+ \# | <kbd>#</kbd> |
+ $ | <kbd>$</kbd> |
+ ^ | <kbd>^</kbd> |
+ & | <kbd>&</kbd> |
+ \* | <kbd>*</kbd> |
+ ( | <kbd>(</kbd> |
+ ) | <kbd>)</kbd> |
+ \_ | <kbd>_</kbd> |
+ \+ | <kbd>+</kbd> |
+ TAB | <kbd>TAB</kbd> |
+ \[ | <kbd>\[</kbd> |
+ ] | <kbd>]</kbd> |
+ \\ | <kbd>\\</kbd> |
+ { | <kbd>{</kbd> |
+ } | <kbd>}</kbd> |
+ \| | <kbd>\|</kbd> |
+ \; | <kbd>;</kbd> |
+ \: | <kbd>:</kbd> |
+ \ | <kbd>\</kbd> |
+ , | <kbd>,</kbd> |
+ \< | <kbd><</kbd> |
+ \. | <kbd>.</kbd> |
+ \> | <kbd>></kbd> |
+ / | <kbd>/</kbd> |
+SPACE | <kbd>SPACE</kbd> |
+WINDOWS, SUPER, COMMAND, WIN | <kbd>⊞</kbd> <kbd>⌘</kbd> |
+META | <kbd>META</kbd> |
+SHIFT | <kbd>SHIFT</kbd> |
+ALT | <kbd>ALT</kbd> |
+F1 | <kbd>F1</kbd> |
+F2 | <kbd>F2</kbd> |
+F3 | <kbd>F3</kbd> |
+F4 | <kbd>F4</kbd> |
+F5 | <kbd>F5</kbd> |
+F6 | <kbd>F6</kbd> |
+F7 | <kbd>F7</kbd> |
+F8 | <kbd>F8</kbd> |
+F9 | <kbd>F9</kbd> |
+F10 | <kbd>F10</kbd> |
+F11 | <kbd>F11</kbd> |
+F12 | <kbd>F12</kbd> |
+F13 | <kbd>F13</kbd> |
+F14 | <kbd>F14</kbd> |
+F15 | <kbd>F15</kbd> |
+F16 | <kbd>F16</kbd> |
+F17 | <kbd>F17</kbd> |
+F18 | <kbd>F18</kbd> |
+F19 | <kbd>F19</kbd> |
+F20 | <kbd>F20</kbd> |
+F21 | <kbd>F21</kbd> |
+F22 | <kbd>F22</kbd> |
+F23 | <kbd>F23</kbd> |
+F24 | <kbd>F24</kbd> |
+NUMPAD 0, NUM 0, N1 | <kbd>0<br/>Ins</kbd> |
+NUMPAD 1, NUM 1, N1 | <kbd>1<br/>End</kbd> |
+NUMPAD 2, NUM 2, N2 | <kbd>2<br/>🠗</kbd> |
+NUMPAD 3, NUM 3, N3 | <kbd>3<br/>PgDn</kbd> |
+NUMPAD 4, NUM 4, N4 | <kbd>4<br/>🠔</kbd> |
+NUMPAD 5, NUM 5, N5 | <kbd>5<br/></kbd> |
+NUMPAD 6, NUM 6, N6 | <kbd>6<br/>🠖</kbd> |
+NUMPAD 7, NUM 7, N7 | <kbd>7<br/>Home</kbd> |
+NUMPAD 8, NUM 8, N8 | <kbd>8<br/>🠕</kbd> |
+NUMPAD 9, NUM 9, N9 | <kbd>9<br/>PgUp</kbd> |
+HOME | <kbd>HOME</kbd> |
+INSERT | <kbd>Insert</kbd> |
+PAGE UP | <kbd>Page<br/>Up</kbd> |
+PAGE DOWN | <kbd>Page<br/>Up</kbd> |
+END | <kbd>End</kbd> |
+ENTER | <kbd>Enter</kbd> |
+DELETE | <kbd>Delete</kbd> |
+SCROLL LOCK | <kbd>Scroll<br/>Lock</kbd> |
+PRINT SCREEN | <kbd>Print<br/>Screen</kbd> |
+PAUSE | <kbd>Pause</kbd> |
+NUM LOCK | <kbd>Num<br/>Lock</kbd> |
+CONTROL | <kbd>Ctrl</kbd> |
+CAPS LOCK | <kbd>Caps Lock</kbd> |
+NUM UP | <kbd>8<br/>🠕</kbd> |
+UP | <kbd>🠕</kbd> |
+NUM LEFT | <kbd>4<br/>🠔</kbd> |
+LEFT | <kbd>🠔</kbd> |
+ALT GRAPH | <kbd>ALT GRAPH</kbd> |
+NUM RIGHT | <kbd>6<br/>🠖</kbd> |
+RIGHT | <kbd>🠖</kbd> |
+NUM DOWN | <kbd>2<br/>🠗</kbd> |
+DOWN | <kbd>🠗</kbd> |
+ESC, ESCAPE | <kbd>ESC</kbd> |
+
+## Usage
+
+To create a new hotkey, just drag the hotkey feature to the desired button and in the field `Key combination (separate using comma)` just type your combination.
+
+Example: in ubuntu 20.04 for opening terminal just type `CONTROL,ALT,T`
+
+
+## Known issues:
+- `WINDOWS` or `SUPER` or `META` key in Linux distros does not work.
--- 'a/hotkeyaction/pom.xml'
+++ b/hotkeyaction/pom.xml
@@ -6,7 +6,7 @@
<groupId>com.stream-pi</groupId>
<artifactId>hotkeyaction</artifactId>
- <version>1.0.0</version>
+ <version>2.0.0</version>
<build>
<plugins>
@@ -40,7 +40,7 @@
<ActionAPIVersion>1.0.0-SNAPSHOT</ActionAPIVersion>
<UtilVersion>1.0.0-SNAPSHOT</UtilVersion>
- <JavaFXVersion>16-ea+6</JavaFXVersion>
+ <JavaFXVersion>16</JavaFXVersion>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
--- 'a/hotkeyaction/src/main/java/com/stream_pi/hotkeyaction/HotkeyAction.java'
+++ b/hotkeyaction/src/main/java/com/stream_pi/hotkeyaction/HotkeyAction.java
@@ -2,9 +2,7 @@ package com.stream_pi.hotkeyaction;
import com.stream_pi.action_api.actionproperty.property.Property;
import com.stream_pi.action_api.actionproperty.property.Type;
-import com.stream_pi.action_api.normalaction.NormalAction;
-import com.stream_pi.util.alert.StreamPiAlert;
-import com.stream_pi.util.alert.StreamPiAlertType;
+import com.stream_pi.action_api.externalplugin.NormalAction;
import com.stream_pi.util.exception.MinorException;
import com.stream_pi.util.version.Version;
@@ -24,8 +22,8 @@ public class HotkeyAction extends Normal
setCategory("Essentials");
setAuthor("rnayabed");
setServerButtonGraphic("far-keyboard");
- setHelpLink("https://github.com/Stream-Pi/EssentialActions");
- setVersion(new Version(1,0,0));
+ setHelpLink("https://github.com/stream-pi/essentialactions/blob/master/hotkeyaction/README.md");
+ setVersion(new Version(2,0,0));
}
@Override
@@ -57,6 +55,7 @@ public class HotkeyAction extends Normal
press(keyCombination.getStringValue()
.toUpperCase()
.replace("?","SHIFT,/")
+ .replace("|","SHIFT,\\")
.split(",")
);
}
@@ -145,19 +144,18 @@ public class HotkeyAction extends Normal
case "TAB": return TAB;
case "[": return OPEN_BRACKET;
case "]": return CLOSE_BRACKET;
- case "\\": return BACK_SLASH;
case "{": return OPEN_BRACKET;
- case "}": return CLOSE_BRACKET;
- case "|": return BACK_SLASH;
+ case "}": return CLOSE_BRACKET;
case ";": return SEMICOLON;
case ":": return COLON;
- case "\"": return BACK_SLASH;
+ case "\\": return BACK_SLASH;
+ case "\"": return QUOTE;
case ",": return COMMA;
case "<": return LESS;
case ".": return PERIOD;
case ">": return GREATER;
case "/": return SLASH;
- case " ": return SPACE;
+ case "SPACE": return SPACE;
case "WIN":
case "WINDOWS":
@@ -198,19 +196,61 @@ public class HotkeyAction extends Normal
case "F23": return F23;
case "F24": return F24;
- case "NUMPAD 0": return NUMPAD0;
- case "NUMPAD 1": return NUMPAD1;
- case "NUMPAD 2": return NUMPAD2;
- case "NUMPAD 3": return NUMPAD3;
- case "NUMPAD 4": return NUMPAD4;
- case "NUMPAD 5": return NUMPAD5;
- case "NUMPAD 6": return NUMPAD6;
- case "NUMPAD 7": return NUMPAD7;
- case "NUMPAD 8": return NUMPAD8;
- case "NUMPAD 9": return NUMPAD9;
+ case "NUMPAD 0":
+ case "NUM 0":
+ case "N0":
+ return NUMPAD0;
+
+ case "NUMPAD 1":
+ case "NUM 1":
+ case "N1":
+ return NUMPAD1;
+
+
+ case "NUMPAD 2":
+ case "NUM 2":
+ case "N2":
+ return NUMPAD2;
+
+
+ case "NUMPAD 3":
+ case "NUM 3":
+ case "N3":
+ return NUMPAD3;
+
+
+ case "NUMPAD 4":
+ case "NUM 4":
+ case "N4":
+ return NUMPAD4;
+
+
+ case "NUMPAD 5":
+ case "NUM 5":
+ case "N5":
+ return NUMPAD5;
+
+
+ case "NUMPAD 6":
+ case "NUM 6":
+ case "N6":
+ return NUMPAD6;
+
+
+ case "NUMPAD 7":
+ case "NUM 7":
+ case "N7":
+ return NUMPAD7;
+
+ case "NUMPAD 8":
+ case "NUM 8":
+ case "N8":
+ return NUMPAD8;
- case "NUMBER SIGN": return NUMBER_SIGN;
-
+ case "NUMPAD 9":
+ case "NUM 9":
+ case "N9":
+ return NUMPAD9;
case "HOME": return HOME;
@@ -231,6 +271,10 @@ public class HotkeyAction extends Normal
case "CONTROL": return CONTROL;
case "CAPS LOCK": return CAPS;
+
+ case "ESCAPE":
+ case "ESC":
+ return ESCAPE;
case "NUM UP": return KP_UP;
case "UP": return UP;
--- 'a/hotkeyaction/src/main/java/module-info.java'
+++ b/hotkeyaction/src/main/java/module-info.java
@@ -8,5 +8,5 @@ module com.stream_pi.hotkeyaction
requires java.desktop;
- provides com.stream_pi.action_api.normalaction.NormalAction with com.stream_pi.hotkeyaction.HotkeyAction;
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with com.stream_pi.hotkeyaction.HotkeyAction;
}
\ No newline at end of file
--- /dev/null
+++ b/mediakeyaction/README.md
@@ -0,0 +1,3 @@
+# Media Key Action
+
+Documentation is WIP
\ No newline at end of file
--- 'a/mediakeyaction/pom.xml'
+++ b/mediakeyaction/pom.xml
@@ -6,7 +6,7 @@
<groupId>com.stream-pi</groupId>
<artifactId>mediakeyaction</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<build>
<plugins>
--- 'a/mediakeyaction/src/main/java/com/stream_pi/mediakeyaction/MediaKeyAction.java'
+++ b/mediakeyaction/src/main/java/com/stream_pi/mediakeyaction/MediaKeyAction.java
@@ -2,9 +2,7 @@ package com.stream_pi.mediakeyaction;
import com.stream_pi.action_api.actionproperty.property.Property;
import com.stream_pi.action_api.actionproperty.property.Type;
-import com.stream_pi.action_api.normalaction.NormalAction;
-import com.stream_pi.util.alert.StreamPiAlert;
-import com.stream_pi.util.alert.StreamPiAlertType;
+import com.stream_pi.action_api.externalplugin.NormalAction;
import com.stream_pi.util.platform.Platform;
import com.stream_pi.util.version.Version;
@@ -20,15 +18,13 @@ public class MediaKeyAction extends Norm
setCategory("Essentials");
setAuthor("rnayabed");
setServerButtonGraphic("fas-volume-up");
- setHelpLink("https://github.com/Stream-Pi/EssentialActions");
- setVersion(new Version(1,0,0));
+ setHelpLink("https://github.com/stream-pi/essentialactions/tree/master/mediakeyaction");
+ setVersion(new Version(1,1,0));
}
- private List<String> states;
-
@Override
public void initProperties() throws Exception {
- states = List.of("Play/Pause", "Previous", "Next", "Vol Up", "Vol Down", "Mute", "Stop");
+ List<String> states = List.of("Play/Pause", "Previous", "Next", "Vol Up", "Vol Down", "Mute", "Stop");
Property mediaKeyTypeProperty = new Property("media_key_type", Type.LIST);
mediaKeyTypeProperty.setListValue(states);
@@ -66,7 +62,7 @@ public class MediaKeyAction extends Norm
}
@Override
- public void onActionClicked() throws Exception
+ public void onActionClicked() throws Exception
{
int state = getClientProperties().getSingleProperty("media_key_type").getSelectedIndex();
@@ -86,16 +82,16 @@ public class MediaKeyAction extends Norm
}
@Override
- public void onShutDown() throws Exception
+ public void onShutDown()
{
}
- private int runCommand(String command) throws Exception
+ private void runCommand(String command) throws Exception
{
Runtime rt = Runtime.getRuntime();
Process pr = rt.exec(command);
- return pr.waitFor();
+ pr.waitFor();
}
private void linuxHandler(int state) throws Exception
@@ -117,45 +113,42 @@ public class MediaKeyAction extends Norm
{
e.printStackTrace();
- new StreamPiAlert("Uh Oh!",
- "It looks like 'xdotool' is not installed in this computer. Please install it and try again.",
- StreamPiAlertType.ERROR
- ).show();
+ throw new UnsupportedOperationException(
+ "It looks like 'xdotool' is not installed in this computer. Please install it and try again."
+ );
}
}
- private void windowsHandler(int state) throws Exception
- {
+ private void windowsHandler(int state) throws Exception {
try
{
+ String exe = "\""+absolutePathToExe+"\"";
switch(state)
{
- case 0: runCommand(absolutePathToExe+" 0xB3"); break;
- case 1: runCommand(absolutePathToExe+" 0xB1"); break;
- case 2: runCommand(absolutePathToExe+" 0xB0"); break;
- case 3: runCommand(absolutePathToExe+" 0xAF"); break;
- case 4: runCommand(absolutePathToExe+" 0xAE"); break;
- case 5: runCommand(absolutePathToExe+" 0xAD"); break;
- case 6: runCommand(absolutePathToExe+" 0xB2"); break;
+ case 0: runCommand(exe+" 0xB3"); break;
+ case 1: runCommand(exe+" 0xB1"); break;
+ case 2: runCommand(exe+" 0xB0"); break;
+ case 3: runCommand(exe+" 0xAF"); break;
+ case 4: runCommand(exe+" 0xAE"); break;
+ case 5: runCommand(exe+" 0xAD"); break;
+ case 6: runCommand(exe+" 0xB2"); break;
}
}
catch (Exception e)
{
e.printStackTrace();
- new StreamPiAlert("Uh Oh!",
- "Internal Error occured. Check logs, stacktrace. Report to us.",
- StreamPiAlertType.ERROR
- ).show();
+ throw new UnsupportedOperationException(
+ "Internal Error occurred. Check logs, stacktrace. Report to us."
+ );
}
}
private void othersHandler()
{
- new StreamPiAlert("Uh Oh!",
- "Media Keys arent supported on this platform yet. Check out the supported platforms by clicking the '?' button on plugins pane/Plugins Settings.",
- StreamPiAlertType.ERROR
- ).show();
+ throw new UnsupportedOperationException(
+ "Media Keys arent supported on this platform yet. Check out the supported platforms by clicking the '?' button on Plugins Pane"
+ );
}
}
--- 'a/mediakeyaction/src/main/java/module-info.java'
+++ b/mediakeyaction/src/main/java/module-info.java
@@ -8,5 +8,5 @@ module com.stream_pi.MediaKeyAction
requires org.kordamp.ikonli.javafx;
requires org.kordamp.ikonli.fontawesome5;
- provides com.stream_pi.action_api.normalaction.NormalAction with MediaKeyAction;
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with MediaKeyAction;
}
\ No newline at end of file
--- 'a/obssuite/mother/pom.xml'
+++ b/obssuite/mother/pom.xml
@@ -6,7 +6,7 @@
<groupId>com.stream-pi</groupId>
<artifactId>obssuite_motheraction</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<build>
<plugins>
@@ -69,7 +69,7 @@
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-client</artifactId>
- <version>9.4.35.v20201120</version>
+ <version>9.4.39.v20210325</version>
</dependency>
<dependency>
--- 'a/obssuite/mother/src/main/java/module-info.java'
+++ b/obssuite/mother/src/main/java/module-info.java
@@ -15,5 +15,5 @@ module com.stream_pi.obssuite.motheracti
requires transitive com.google.gson;
exports mother.motherconnection;
- provides com.stream_pi.action_api.normalaction.NormalAction with mother.Mother;
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with mother.Mother;
}
\ No newline at end of file
--- 'a/obssuite/mother/src/main/java/mother/Mother.java'
+++ b/obssuite/mother/src/main/java/mother/Mother.java
@@ -1,8 +1,9 @@
package mother;
+import com.stream_pi.action_api.actionproperty.property.ControlType;
import com.stream_pi.action_api.actionproperty.property.Property;
import com.stream_pi.action_api.actionproperty.property.Type;
-import com.stream_pi.action_api.normalaction.NormalAction;
+import com.stream_pi.action_api.externalplugin.NormalAction;
import com.stream_pi.util.version.Version;
import com.stream_pi.util.exception.MinorException;
@@ -21,24 +22,32 @@ public class Mother extends NormalAction
setVisibilityInPluginsPane(false);
setAuthor("rnayabed");
setHelpLink("https://github.com/Stream-Pi/EssentialActions");
- setVersion(new Version(1,0,0));
+ setVersion(new Version(1,1,0));
connectDisconnectButton = new Button("Connect");
- setButtonBar(connectDisconnectButton);
+ setServerSettingsButtonBar(connectDisconnectButton);
+ }
+
+ @Override
+ public void onActionClicked()
+ {
+
}
private Button connectDisconnectButton;
@Override
- public void initProperties() throws Exception {
+ public void initProperties() throws Exception
+ {
Property urlProperty = new Property("url", Type.STRING);
urlProperty.setDisplayName("URL");
urlProperty.setDefaultValueStr("ws://localhost:4444");
urlProperty.setCanBeBlank(false);
Property passwordProperty = new Property("pass", Type.STRING);
+ passwordProperty.setControlType(ControlType.TEXT_FIELD_MASKED);
passwordProperty.setDisplayName("Password");
Property connectOnStartupProperty = new Property("connect_on_startup", Type.BOOLEAN);
@@ -101,19 +110,13 @@ public class Mother extends NormalAction
private void connect(String url, String pass)
{
- new Thread(
- new OBSActionConnectionTask(url, pass, connectDisconnectButton)
- ).start();
- }
-
- @Override
- public void onActionClicked() throws Exception {
- // TODO Auto-generated method stub
-
+ MotherConnection.setPass(pass);
+ MotherConnection.setUrl(url);
+ new OBSActionConnectionTask(connectDisconnectButton, true);
}
@Override
- public void onShutDown() throws Exception
+ public void onShutDown()
{
if(MotherConnection.getRemoteController() != null)
{
--- 'a/obssuite/mother/src/main/java/mother/OBSActionConnectionTask.java'
+++ b/obssuite/mother/src/main/java/mother/OBSActionConnectionTask.java
@@ -9,20 +9,31 @@ import net.twasi.obsremotejava.OBSRemote
import com.stream_pi.util.alert.StreamPiAlert;
import com.stream_pi.util.alert.StreamPiAlertType;
-public class OBSActionConnectionTask extends Task<Void> {
+public class OBSActionConnectionTask extends Task<Void>
+{
String url, pass;
Button connectDisconnectButton;
- public OBSActionConnectionTask(String url, String pass, Button connectDisconnectButton)
+ public OBSActionConnectionTask(Button connectDisconnectButton,
+ boolean runAsync)
{
- this.url = url;
- this.pass = pass;
- this.connectDisconnectButton = connectDisconnectButton;
+ this.url = MotherConnection.getUrl();
+ this.pass = MotherConnection.getPass();
+ this.connectDisconnectButton = connectDisconnectButton;
+
+ if(runAsync)
+ {
+ new Thread(this).start();
+ }
+ else
+ {
+ call();
+ }
}
@Override
- protected Void call() throws Exception
+ protected Void call()
{
try
{
@@ -85,11 +96,17 @@ public class OBSActionConnectionTask ext
private void setConnectDisconnectButtonText(String text)
{
+ if(connectDisconnectButton == null)
+ return;
+
Platform.runLater(()-> connectDisconnectButton.setText(text));
}
private void setConnectDisconnectButtonDisable(boolean disable)
{
+ if(connectDisconnectButton == null)
+ return;
+
Platform.runLater(()-> connectDisconnectButton.setDisable(disable));
}
--- 'a/obssuite/mother/src/main/java/mother/motherconnection/MotherConnection.java'
+++ b/obssuite/mother/src/main/java/mother/motherconnection/MotherConnection.java
@@ -3,30 +3,61 @@ package mother.motherconnection;
import com.stream_pi.util.alert.StreamPiAlert;
import com.stream_pi.util.alert.StreamPiAlertType;
+import mother.OBSActionConnectionTask;
import net.twasi.obsremotejava.OBSRemoteController;
import net.twasi.obsremotejava.callbacks.Callback;
-import net.twasi.obsremotejava.requests.ResponseBase;
-
-public class MotherConnection {
+public class MotherConnection
+{
private static OBSRemoteController obsRemoteController = null;
- public static OBSRemoteController getRemoteController() {
+ private static String url = null;
+ private static String pass = null;
+
+ public static void setUrl(String url) {
+ MotherConnection.url = url;
+ }
+
+ public static void setPass(String pass) {
+ MotherConnection.pass = pass;
+ }
+
+ public static String getUrl() {
+ return url;
+ }
+
+ public static String getPass() {
+ return pass;
+ }
+
+ public void connect()
+ {
+ connect(true);
+ }
+
+ public void connect(boolean runAsync)
+ {
+ new OBSActionConnectionTask( null, runAsync);
+ }
+
+
+ public static OBSRemoteController getRemoteController()
+ {
return obsRemoteController;
}
- public static void setRemoteController(OBSRemoteController obsRemoteController) {
+ public static void setRemoteController(OBSRemoteController obsRemoteController)
+ {
MotherConnection.obsRemoteController = obsRemoteController;
}
- public static Callback getDefaultCallBack(String head, String content) {
- return new Callback() {
- @Override
- public void run(ResponseBase response) {
- if(response.getStatus().equals("error"))
- {
- new StreamPiAlert(head, content, StreamPiAlertType.ERROR).show();
- }
+ public static Callback getDefaultCallBack(String head, String content)
+ {
+ return response ->
+ {
+ if(response.getStatus().equals("error"))
+ {
+ new StreamPiAlert(head, content, StreamPiAlertType.ERROR).show();
}
};
}
--- 'a/obssuite/setcurrentprofile/pom.xml'
+++ b/obssuite/setcurrentprofile/pom.xml
@@ -39,7 +39,7 @@
<properties>
<ActionAPIVersion>1.0.0-SNAPSHOT</ActionAPIVersion>
<UtilVersion>1.0.0-SNAPSHOT</UtilVersion>
- <OBSSuiteMotherVersion>1.0.0</OBSSuiteMotherVersion>
+ <OBSSuiteMotherVersion>1.1.0</OBSSuiteMotherVersion>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
--- 'a/obssuite/setcurrentprofile/src/main/java/module-info.java'
+++ b/obssuite/setcurrentprofile/src/main/java/module-info.java
@@ -5,5 +5,5 @@ module com.stream_pi.obssuite.setcurrent
requires com.stream_pi.obssuite.motheraction;
- provides com.stream_pi.action_api.normalaction.NormalAction with setcurrentprofile.SetCurrentProfile;
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with setcurrentprofile.SetCurrentProfile;
}
--- 'a/obssuite/setcurrentprofile/src/main/java/setcurrentprofile/SetCurrentProfile.java'
+++ b/obssuite/setcurrentprofile/src/main/java/setcurrentprofile/SetCurrentProfile.java
@@ -2,7 +2,7 @@ package setcurrentprofile;
import com.stream_pi.action_api.actionproperty.property.Property;
import com.stream_pi.action_api.actionproperty.property.Type;
-import com.stream_pi.action_api.normalaction.NormalAction;
+import com.stream_pi.action_api.externalplugin.NormalAction;
import com.stream_pi.util.alert.StreamPiAlert;
import com.stream_pi.util.alert.StreamPiAlertType;
import com.stream_pi.util.version.Version;
--- 'a/obssuite/setcurrentscene/pom.xml'
+++ b/obssuite/setcurrentscene/pom.xml
@@ -39,11 +39,11 @@
<properties>
<ActionAPIVersion>1.0.0-SNAPSHOT</ActionAPIVersion>
<UtilVersion>1.0.0-SNAPSHOT</UtilVersion>
- <OBSSuiteMotherVersion>1.0.0</OBSSuiteMotherVersion>
+ <OBSSuiteMotherVersion>1.1.0</OBSSuiteMotherVersion>
+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
-
</properties>
<dependencies>
--- 'a/obssuite/setcurrentscene/src/main/java/module-info.java'
+++ b/obssuite/setcurrentscene/src/main/java/module-info.java
@@ -6,5 +6,5 @@ module com.stream_pi.obssuite.setcurrent
requires obs.websocket.java;
requires com.stream_pi.obssuite.motheraction;
- provides com.stream_pi.action_api.normalaction.NormalAction with setcurrentscene.SetCurrentScene;
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with setcurrentscene.SetCurrentScene;
}
--- 'a/obssuite/setcurrentscene/src/main/java/setcurrentscene/SetCurrentScene.java'
+++ b/obssuite/setcurrentscene/src/main/java/setcurrentscene/SetCurrentScene.java
@@ -2,7 +2,7 @@ package setcurrentscene;
import com.stream_pi.action_api.actionproperty.property.Property;
import com.stream_pi.action_api.actionproperty.property.Type;
-import com.stream_pi.action_api.normalaction.NormalAction;
+import com.stream_pi.action_api.externalplugin.NormalAction;
import com.stream_pi.util.alert.StreamPiAlert;
import com.stream_pi.util.alert.StreamPiAlertType;
import com.stream_pi.util.version.Version;
--- 'a/obssuite/setcurrenttransition/pom.xml'
+++ b/obssuite/setcurrenttransition/pom.xml
@@ -39,12 +39,11 @@
<properties>
<ActionAPIVersion>1.0.0-SNAPSHOT</ActionAPIVersion>
<UtilVersion>1.0.0-SNAPSHOT</UtilVersion>
- <OBSSuiteMotherVersion>1.0.0</OBSSuiteMotherVersion>
+ <OBSSuiteMotherVersion>1.1.0</OBSSuiteMotherVersion>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
-
</properties>
<dependencies>
--- 'a/obssuite/setcurrenttransition/src/main/java/module-info.java'
+++ b/obssuite/setcurrenttransition/src/main/java/module-info.java
@@ -6,5 +6,5 @@ module com.stream_pi.obssuite.setcurrent
requires obs.websocket.java;
requires com.stream_pi.obssuite.motheraction;
- provides com.stream_pi.action_api.normalaction.NormalAction with setcurrenttransition.SetCurrentTransition;
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with setcurrenttransition.SetCurrentTransition;
}
--- 'a/obssuite/setcurrenttransition/src/main/java/setcurrenttransition/SetCurrentTransition.java'
+++ b/obssuite/setcurrenttransition/src/main/java/setcurrenttransition/SetCurrentTransition.java
@@ -2,7 +2,7 @@ package setcurrenttransition;
import com.stream_pi.action_api.actionproperty.property.Property;
import com.stream_pi.action_api.actionproperty.property.Type;
-import com.stream_pi.action_api.normalaction.NormalAction;
+import com.stream_pi.action_api.externalplugin.NormalAction;
import com.stream_pi.util.alert.StreamPiAlert;
import com.stream_pi.util.alert.StreamPiAlertType;
import com.stream_pi.util.version.Version;
--- 'a/obssuite/setmute/pom.xml'
+++ b/obssuite/setmute/pom.xml
@@ -39,12 +39,11 @@
<properties>
<ActionAPIVersion>1.0.0-SNAPSHOT</ActionAPIVersion>
<UtilVersion>1.0.0-SNAPSHOT</UtilVersion>
- <OBSSuiteMotherVersion>1.0.0</OBSSuiteMotherVersion>
+ <OBSSuiteMotherVersion>1.1.0</OBSSuiteMotherVersion>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
-
</properties>
<dependencies>
--- 'a/obssuite/setmute/src/main/java/module-info.java'
+++ b/obssuite/setmute/src/main/java/module-info.java
@@ -6,5 +6,5 @@ module com.stream_pi.obssuite.setmuteact
requires obs.websocket.java;
requires com.stream_pi.obssuite.motheraction;
- provides com.stream_pi.action_api.normalaction.NormalAction with setmute.SetMute;
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with setmute.SetMute;
}
--- 'a/obssuite/setmute/src/main/java/setmute/SetMute.java'
+++ b/obssuite/setmute/src/main/java/setmute/SetMute.java
@@ -2,7 +2,7 @@ package setmute;
import com.stream_pi.action_api.actionproperty.property.Property;
import com.stream_pi.action_api.actionproperty.property.Type;
-import com.stream_pi.action_api.normalaction.NormalAction;
+import com.stream_pi.action_api.externalplugin.NormalAction;
import com.stream_pi.util.alert.StreamPiAlert;
import com.stream_pi.util.alert.StreamPiAlertType;
import com.stream_pi.util.version.Version;
--- 'a/obssuite/setpreviewscene/pom.xml'
+++ b/obssuite/setpreviewscene/pom.xml
@@ -39,12 +39,11 @@
<properties>
<ActionAPIVersion>1.0.0-SNAPSHOT</ActionAPIVersion>
<UtilVersion>1.0.0-SNAPSHOT</UtilVersion>
- <OBSSuiteMotherVersion>1.0.0</OBSSuiteMotherVersion>
+ <OBSSuiteMotherVersion>1.1.0</OBSSuiteMotherVersion>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
-
</properties>
<dependencies>
--- 'a/obssuite/setpreviewscene/src/main/java/module-info.java'
+++ b/obssuite/setpreviewscene/src/main/java/module-info.java
@@ -6,5 +6,5 @@ module com.stream_pi.obssuite.setpreview
requires obs.websocket.java;
requires com.stream_pi.obssuite.motheraction;
- provides com.stream_pi.action_api.normalaction.NormalAction with setpreviewscene.SetPreviewScene;
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with setpreviewscene.SetPreviewScene;
}
--- 'a/obssuite/setpreviewscene/src/main/java/setpreviewscene/SetPreviewScene.java'
+++ b/obssuite/setpreviewscene/src/main/java/setpreviewscene/SetPreviewScene.java
@@ -2,7 +2,7 @@ package setpreviewscene;
import com.stream_pi.action_api.actionproperty.property.Property;
import com.stream_pi.action_api.actionproperty.property.Type;
-import com.stream_pi.action_api.normalaction.NormalAction;
+import com.stream_pi.action_api.externalplugin.NormalAction;
import com.stream_pi.util.alert.StreamPiAlert;
import com.stream_pi.util.alert.StreamPiAlertType;
import com.stream_pi.util.version.Version;
--- 'a/obssuite/setrecording/pom.xml'
+++ b/obssuite/setrecording/pom.xml
@@ -39,12 +39,11 @@
<properties>
<ActionAPIVersion>1.0.0-SNAPSHOT</ActionAPIVersion>
<UtilVersion>1.0.0-SNAPSHOT</UtilVersion>
- <OBSSuiteMotherVersion>1.0.0</OBSSuiteMotherVersion>
+ <OBSSuiteMotherVersion>1.1.0</OBSSuiteMotherVersion>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
-
</properties>
<dependencies>
--- 'a/obssuite/setrecording/src/main/java/module-info.java'
+++ b/obssuite/setrecording/src/main/java/module-info.java
@@ -6,5 +6,5 @@ module com.stream_pi.obssuite.setrecordi
requires obs.websocket.java;
requires com.stream_pi.obssuite.motheraction;
- provides com.stream_pi.action_api.normalaction.NormalAction with setrecording.SetRecording;
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with setrecording.SetRecording;
}
--- 'a/obssuite/setrecording/src/main/java/setrecording/SetRecording.java'
+++ b/obssuite/setrecording/src/main/java/setrecording/SetRecording.java
@@ -4,7 +4,7 @@ import java.util.ArrayList;
import com.stream_pi.action_api.actionproperty.property.Property;
import com.stream_pi.action_api.actionproperty.property.Type;
-import com.stream_pi.action_api.normalaction.NormalAction;
+import com.stream_pi.action_api.externalplugin.NormalAction;
import com.stream_pi.util.alert.StreamPiAlert;
import com.stream_pi.util.alert.StreamPiAlertType;
import com.stream_pi.util.version.Version;
--- 'a/obssuite/setreplaybuffer/pom.xml'
+++ b/obssuite/setreplaybuffer/pom.xml
@@ -39,12 +39,11 @@
<properties>
<ActionAPIVersion>1.0.0-SNAPSHOT</ActionAPIVersion>
<UtilVersion>1.0.0-SNAPSHOT</UtilVersion>
- <OBSSuiteMotherVersion>1.0.0</OBSSuiteMotherVersion>
+ <OBSSuiteMotherVersion>1.1.0</OBSSuiteMotherVersion>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
-
</properties>
<dependencies>
--- 'a/obssuite/setreplaybuffer/src/main/java/module-info.java'
+++ b/obssuite/setreplaybuffer/src/main/java/module-info.java
@@ -6,5 +6,5 @@ module com.stream_pi.obssuite.setreplayb
requires obs.websocket.java;
requires com.stream_pi.obssuite.motheraction;
- provides com.stream_pi.action_api.normalaction.NormalAction with setreplaybuffer.SetReplayBuffer;
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with setreplaybuffer.SetReplayBuffer;
}
--- 'a/obssuite/setreplaybuffer/src/main/java/setreplaybuffer/SetReplayBuffer.java'
+++ b/obssuite/setreplaybuffer/src/main/java/setreplaybuffer/SetReplayBuffer.java
@@ -4,7 +4,7 @@ import java.util.ArrayList;
import com.stream_pi.action_api.actionproperty.property.Property;
import com.stream_pi.action_api.actionproperty.property.Type;
-import com.stream_pi.action_api.normalaction.NormalAction;
+import com.stream_pi.action_api.externalplugin.NormalAction;
import com.stream_pi.util.alert.StreamPiAlert;
import com.stream_pi.util.alert.StreamPiAlertType;
import com.stream_pi.util.version.Version;
@@ -12,7 +12,8 @@ import com.stream_pi.util.version.Versio
import mother.motherconnection.MotherConnection;
import net.twasi.obsremotejava.OBSRemoteController;
-public class SetReplayBuffer extends NormalAction {
+public class SetReplayBuffer extends NormalAction
+{
public SetReplayBuffer() {
setName("Set Replay Buffer");
--- 'a/obssuite/setstreaming/pom.xml'
+++ b/obssuite/setstreaming/pom.xml
@@ -39,12 +39,11 @@
<properties>
<ActionAPIVersion>1.0.0-SNAPSHOT</ActionAPIVersion>
<UtilVersion>1.0.0-SNAPSHOT</UtilVersion>
- <OBSSuiteMotherVersion>1.0.0</OBSSuiteMotherVersion>
+ <OBSSuiteMotherVersion>1.1.0</OBSSuiteMotherVersion>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
-
</properties>
<dependencies>
--- 'a/obssuite/setstreaming/src/main/java/module-info.java'
+++ b/obssuite/setstreaming/src/main/java/module-info.java
@@ -6,5 +6,5 @@ module com.stream_pi.obssuite.setstreami
requires obs.websocket.java;
requires com.stream_pi.obssuite.motheraction;
- provides com.stream_pi.action_api.normalaction.NormalAction with setstreaming.SetStreaming;
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with setstreaming.SetStreaming;
}
--- 'a/obssuite/setstreaming/src/main/java/setstreaming/SetStreaming.java'
+++ b/obssuite/setstreaming/src/main/java/setstreaming/SetStreaming.java
@@ -4,7 +4,7 @@ import java.util.ArrayList;
import com.stream_pi.action_api.actionproperty.property.Property;
import com.stream_pi.action_api.actionproperty.property.Type;
-import com.stream_pi.action_api.normalaction.NormalAction;
+import com.stream_pi.action_api.externalplugin.NormalAction;
import com.stream_pi.util.alert.StreamPiAlert;
import com.stream_pi.util.alert.StreamPiAlertType;
import com.stream_pi.util.version.Version;
--- 'a/obssuite/setstudiomode/pom.xml'
+++ b/obssuite/setstudiomode/pom.xml
@@ -39,12 +39,11 @@
<properties>
<ActionAPIVersion>1.0.0-SNAPSHOT</ActionAPIVersion>
<UtilVersion>1.0.0-SNAPSHOT</UtilVersion>
- <OBSSuiteMotherVersion>1.0.0</OBSSuiteMotherVersion>
+ <OBSSuiteMotherVersion>1.1.0</OBSSuiteMotherVersion>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
-
</properties>
<dependencies>
--- 'a/obssuite/setstudiomode/src/main/java/SetStudioMode/SetStudioMode.java'
+++ b/obssuite/setstudiomode/src/main/java/SetStudioMode/SetStudioMode.java
@@ -2,7 +2,7 @@ package SetStudioMode;
import com.stream_pi.action_api.actionproperty.property.Property;
import com.stream_pi.action_api.actionproperty.property.Type;
-import com.stream_pi.action_api.normalaction.NormalAction;
+import com.stream_pi.action_api.externalplugin.NormalAction;
import com.stream_pi.util.alert.StreamPiAlert;
import com.stream_pi.util.alert.StreamPiAlertType;
import com.stream_pi.util.version.Version;
--- 'a/obssuite/setstudiomode/src/main/java/module-info.java'
+++ b/obssuite/setstudiomode/src/main/java/module-info.java
@@ -6,5 +6,5 @@ module com.stream_pi.obssuite.setstudiom
requires obs.websocket.java;
requires com.stream_pi.obssuite.motheraction;
- provides com.stream_pi.action_api.normalaction.NormalAction with SetStudioMode.SetStudioMode;
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with SetStudioMode.SetStudioMode;
}
--- 'a/obssuite/setvolume/pom.xml'
+++ b/obssuite/setvolume/pom.xml
@@ -39,12 +39,11 @@
<properties>
<ActionAPIVersion>1.0.0-SNAPSHOT</ActionAPIVersion>
<UtilVersion>1.0.0-SNAPSHOT</UtilVersion>
- <OBSSuiteMotherVersion>1.0.0</OBSSuiteMotherVersion>
+ <OBSSuiteMotherVersion>1.1.0</OBSSuiteMotherVersion>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
-
</properties>
<dependencies>
--- 'a/obssuite/setvolume/src/main/java/module-info.java'
+++ b/obssuite/setvolume/src/main/java/module-info.java
@@ -6,5 +6,5 @@ module com.stream_pi.obssuite.setvolumea
requires obs.websocket.java;
requires com.stream_pi.obssuite.motheraction;
- provides com.stream_pi.action_api.normalaction.NormalAction with setvolume.SetVolume;
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with setvolume.SetVolume;
}
--- 'a/obssuite/setvolume/src/main/java/setvolume/SetVolume.java'
+++ b/obssuite/setvolume/src/main/java/setvolume/SetVolume.java
@@ -2,7 +2,7 @@ package setvolume;
import com.stream_pi.action_api.actionproperty.property.Property;
import com.stream_pi.action_api.actionproperty.property.Type;
-import com.stream_pi.action_api.normalaction.NormalAction;
+import com.stream_pi.action_api.externalplugin.NormalAction;
import com.stream_pi.util.alert.StreamPiAlert;
import com.stream_pi.util.alert.StreamPiAlertType;
import com.stream_pi.util.version.Version;
Binary files /dev/null and b/playaudioclipaction/.DS_Store differ
--- 'a/playaudioclipaction/PlayAudioClipAction.iml'
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
- <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_11">
- <output url="file://$MODULE_DIR$/target/classes" />
- <output-test url="file://$MODULE_DIR$/target/test-classes" />
- <content url="file://$MODULE_DIR$">
- <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
- <excludeFolder url="file://$MODULE_DIR$/target" />
- </content>
- <orderEntry type="inheritedJdk" />
- <orderEntry type="sourceFolder" forTests="false" />
- <orderEntry type="library" name="Maven: com.stream-pi:util:1.0.0-SNAPSHOT" level="project" />
- <orderEntry type="library" name="Maven: org.openjfx:javafx-controls:16-ea+6" level="project" />
- <orderEntry type="library" name="Maven: org.openjfx:javafx-controls:linux:16-ea+6" level="project" />
- <orderEntry type="library" name="Maven: org.openjfx:javafx-base:16-ea+6" level="project" />
- <orderEntry type="library" name="Maven: org.openjfx:javafx-base:linux:16-ea+6" level="project" />
- <orderEntry type="library" name="Maven: com.stream-pi:action-api:1.0.0-SNAPSHOT" level="project" />
- <orderEntry type="library" name="Maven: org.openjfx:javafx-media:16-ea+6" level="project" />
- <orderEntry type="library" name="Maven: org.openjfx:javafx-media:linux:16-ea+6" level="project" />
- <orderEntry type="library" name="Maven: org.openjfx:javafx-graphics:16-ea+6" level="project" />
- <orderEntry type="library" name="Maven: org.openjfx:javafx-graphics:linux:16-ea+6" level="project" />
- <orderEntry type="library" name="Maven: org.kordamp.ikonli:ikonli-fontawesome5-pack:11.5.0" level="project" />
- <orderEntry type="library" name="Maven: org.kordamp.ikonli:ikonli-core:11.5.0" level="project" />
- <orderEntry type="library" name="Maven: org.kordamp.ikonli:ikonli-javafx:11.5.0" level="project" />
- </component>
-</module>
\ No newline at end of file
--- 'a/playaudioclipaction/pom.xml'
+++ b/playaudioclipaction/pom.xml
@@ -6,7 +6,7 @@
<groupId>com.stream-pi</groupId>
<artifactId>playaudioclipaction</artifactId>
- <version>1.0.0</version>
+ <version>2.0.0</version>
<build>
<plugins>
@@ -40,7 +40,7 @@
<ActionAPIVersion>1.0.0-SNAPSHOT</ActionAPIVersion>
<UtilVersion>1.0.0-SNAPSHOT</UtilVersion>
- <JavaFXVersion>16-ea+6</JavaFXVersion>
+ <JavaFXVersion>16</JavaFXVersion>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
Binary files /dev/null and b/playaudioclipaction/src/.DS_Store differ
Binary files /dev/null and b/playaudioclipaction/src/main/.DS_Store differ
Binary files /dev/null and b/playaudioclipaction/src/main/java/.DS_Store differ
Binary files /dev/null and b/playaudioclipaction/src/main/java/com/.DS_Store differ
Binary files /dev/null and b/playaudioclipaction/src/main/java/com/stream_pi/.DS_Store differ
--- 'a/playaudioclipaction/src/main/java/com/stream_pi/playaudioclipaction/PlayAudioClipAction.java'
+++ b/playaudioclipaction/src/main/java/com/stream_pi/playaudioclipaction/PlayAudioClipAction.java
@@ -1,14 +1,16 @@
package com.stream_pi.playaudioclipaction;
+import com.stream_pi.action_api.actionproperty.property.ControlType;
+import com.stream_pi.action_api.actionproperty.property.FileExtensionFilter;
import com.stream_pi.action_api.actionproperty.property.Property;
import com.stream_pi.action_api.actionproperty.property.Type;
-import com.stream_pi.action_api.normalaction.NormalAction;
+import com.stream_pi.action_api.externalplugin.NormalAction;
import com.stream_pi.util.alert.StreamPiAlert;
import com.stream_pi.util.alert.StreamPiAlertType;
import com.stream_pi.util.version.Version;
-import javafx.scene.media.Media;
-import javafx.scene.media.MediaPlayer;
+import javafx.application.Platform;
+import javafx.scene.media.AudioClip;
import java.io.File;
@@ -21,40 +23,85 @@ public class PlayAudioClipAction extends
setAuthor("rnayabed");
setServerButtonGraphic("fas-volume-up");
setHelpLink("https://github.com/Stream-Pi/EssentialActions");
- setVersion(new Version(1,0,0));
+ setVersion(new Version(2,0,0));
}
@Override
- public void initProperties() throws Exception {
+ public void initProperties() throws Exception
+ {
Property audioFileLocationProperty = new Property("audio_location", Type.STRING);
+ audioFileLocationProperty.setControlType(ControlType.FILE_PATH);
audioFileLocationProperty.setDisplayName("Audio File Location");
+ audioFileLocationProperty.setExtensionFilters(
+ new FileExtensionFilter("MP3","*.mp3"),
+ new FileExtensionFilter("MP4","*.mp4", "*.m4a", "*.m4v"),
+ new FileExtensionFilter("WAV","*.wav"),
+ new FileExtensionFilter("AIFF","*.aif", "*.aiff"),
+ new FileExtensionFilter("FXM","*.fxm"),
+ new FileExtensionFilter("FLV","*.flv"),
+ new FileExtensionFilter("HLS","*.m3u8")
+ );
addClientProperties(audioFileLocationProperty);
}
- @Override
- public void initAction() throws Exception {
- }
+ public AudioClip mediaPlayer = null;
+ public String path = null;
@Override
- public void onActionClicked() throws Exception
+ public void onActionClicked() throws Exception
{
+
Property audioFileLocationProperty = getClientProperties().getSingleProperty("audio_location");
- if(audioFileLocationProperty.getStringValue().isBlank())
+ if (audioFileLocationProperty.getStringValue().isBlank())
{
new StreamPiAlert("Media Action", "No file specified", StreamPiAlertType.ERROR).show();
return;
}
- MediaPlayer mediaPlayer = new MediaPlayer(new Media(new File(audioFileLocationProperty.getStringValue()).toURI().toString()));
- mediaPlayer.setOnReady(mediaPlayer::play);
+ if(mediaPlayer != null)
+ {
+ if(mediaPlayer.isPlaying())
+ {
+ Platform.runLater(mediaPlayer::stop);
+ return;
+ }
+ }
+
+ if(!audioFileLocationProperty.getStringValue().equals(path))
+ {
+ path = audioFileLocationProperty.getStringValue();
+ mediaPlayer = new AudioClip(new File(path).toURI().toString());
+ }
+
+ Platform.runLater(mediaPlayer::play);
+
}
@Override
- public void onShutDown() throws Exception {
- // TODO Auto-generated method stub
+ public void onShutDown()
+ {
+ shutDown();
+ }
+
+ public void onActionDeleted()
+ {
+ shutDown();
+ }
+
+ public void onClientDisconnected()
+ {
+ shutDown();
+ }
+ private void shutDown()
+ {
+ if(mediaPlayer != null)
+ {
+ if(mediaPlayer.isPlaying())
+ Platform.runLater(mediaPlayer::stop);
+ }
}
}
--- 'a/playaudioclipaction/src/main/java/module-info.java'
+++ b/playaudioclipaction/src/main/java/module-info.java
@@ -6,6 +6,6 @@ module com.stream_pi.playaudioclipaction
requires javafx.media;
requires org.kordamp.ikonli.javafx;
requires org.kordamp.ikonli.fontawesome5;
-
- provides com.stream_pi.action_api.normalaction.NormalAction with com.stream_pi.playaudioclipaction.PlayAudioClipAction;
+
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with com.stream_pi.playaudioclipaction.PlayAudioClipAction;
}
\ No newline at end of file
Binary files /dev/null and b/playmusicclipaction/.DS_Store differ
--- /dev/null
+++ b/playmusicclipaction/.gitignore
@@ -0,0 +1,7 @@
+.idea/
+target/
+WebsiteAction.iml
+.classpath
+.factorypath
+.project
+.settings/
Binary files /dev/null and b/playmusicclipaction/src/.DS_Store differ
Binary files /dev/null and b/playmusicclipaction/src/main/.DS_Store differ
Binary files /dev/null and b/playmusicclipaction/src/main/java/.DS_Store differ
Binary files /dev/null and b/playmusicclipaction/src/main/java/com/.DS_Store differ
Binary files /dev/null and b/playmusicclipaction/src/main/java/com/stream_pi/.DS_Store differ
--- /dev/null
+++ b/playmusicclipaction/src/main/java/com/stream_pi/playmusicclipaction/PlayMusicClipAction.java
@@ -0,0 +1,62 @@
+package com.stream_pi.playmusicclipaction;
+
+import java.util.ArrayList;
+
+import com.stream_pi.action_api.actionproperty.property.ControlType;
+import com.stream_pi.action_api.actionproperty.property.FileExtensionFilter;
+import com.stream_pi.action_api.actionproperty.property.Property;
+import com.stream_pi.action_api.actionproperty.property.Type;
+import com.stream_pi.action_api.externalplugin.NormalAction;
+import com.stream_pi.util.alert.StreamPiAlert;
+import com.stream_pi.util.alert.StreamPiAlertType;
+import com.stream_pi.util.version.Version;
+
+import java.awt.*;
+import java.net.URI;
+
+import java.io.File;
+
+public class PlayMusicClipAction extends NormalAction {
+
+ public PlayMusicClipAction()
+ {
+ setName("Play Music Clip");
+ setCategory("Essentials");
+ setAuthor("quimodotcom");
+ setServerButtonGraphic("fas-volume-up");
+ setHelpLink("https://github.com/Stream-Pi/EssentialActions");
+ setVersion(new Version(1,0,0));
+ }
+
+ @Override
+ public void initProperties() throws Exception
+ {
+ Property audioFileLocationProperty = new Property("audio_location", Type.STRING);
+ audioFileLocationProperty.setControlType(ControlType.FILE_PATH);
+ audioFileLocationProperty.setDisplayName("Audio File Location");
+ audioFileLocationProperty.setExtensionFilters(
+ new FileExtensionFilter("MP3","*.mp3"),
+ new FileExtensionFilter("MP4","*.mp4", "*.m4a", "*.m4v"),
+ new FileExtensionFilter("WAV","*.wav"),
+ new FileExtensionFilter("AIFF","*.aif", "*.aiff"),
+ new FileExtensionFilter("FXM","*.fxm"),
+ new FileExtensionFilter("FLV","*.flv"),
+ new FileExtensionFilter("HLS","*.m3u8")
+ );
+
+ addClientProperties(audioFileLocationProperty);
+ }
+
+
+ @Override
+ public void onActionClicked() throws Exception
+ {
+
+ Property audioFileLocationProperty1 = getClientProperties().getSingleProperty("audio_location");
+
+ File file = new File(audioFileLocationProperty1.getStringValue());
+
+ Desktop.getDesktop().open(file);
+
+ }
+}
--- /dev/null
+++ b/playmusicclipaction/src/main/java/module-info.java
@@ -0,0 +1,12 @@
+module com.stream_pi.playmusicclipaction
+{
+ requires com.stream_pi.action_api;
+ requires com.stream_pi.util;
+
+ requires org.kordamp.ikonli.javafx;
+ requires org.kordamp.ikonli.fontawesome5;
+
+ requires java.desktop;
+
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with com.stream_pi.playmusicclipaction.PlayMusicClipAction;
+}
\ No newline at end of file
Binary files /dev/null and b/playmusicclipaction/target/classes/com/stream_pi/playmusicclipaction/PlayMusicClipAction.class differ
Binary files /dev/null and b/playmusicclipaction/target/classes/module-info.class differ
--- /dev/null
+++ b/playmusicclipaction/target/maven-archiver/pom.properties
@@ -0,0 +1,4 @@
+#Created by Apache Maven 3.6.3
+groupId=com.stream-pi
+artifactId=playmusicclipaction
+version=1.0.0
--- /dev/null
+++ b/playmusicclipaction/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
@@ -0,0 +1,2 @@
+module-info.class
+com\stream_pi\playmusicclipaction\PlayMusicClipAction.class
--- /dev/null
+++ b/playmusicclipaction/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
@@ -0,0 +1,2 @@
+C:\Users\Evan Donald\Downloads\essentialactions-master\playmusicclipaction\src\main\java\module-info.java
+C:\Users\Evan Donald\Downloads\essentialactions-master\playmusicclipaction\src\main\java\com\stream_pi\playmusicclipaction\PlayMusicClipAction.java
Binary files /dev/null and b/playmusicclipaction/target/playmusicclipaction-1.0.0-tests.jar differ
Binary files /dev/null and b/playmusicclipaction/target/playmusicclipaction-1.0.0.jar differ
--- /dev/null
+++ b/runcommandaction/README.md
@@ -0,0 +1,5 @@
+# Run Command Action
+
+![version](https://img.shields.io/badge/Version-1.1.1-green)
+
+Documentation is WIP
\ No newline at end of file
--- 'a/runcommandaction/pom.xml'
+++ b/runcommandaction/pom.xml
@@ -6,7 +6,7 @@
<groupId>com.stream-pi</groupId>
<artifactId>runcommandaction</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<build>
<plugins>
--- 'a/runcommandaction/src/main/java/com/stream_pi/runcommandaction/RunCommandAction.java'
+++ b/runcommandaction/src/main/java/com/stream_pi/runcommandaction/RunCommandAction.java
@@ -2,7 +2,7 @@ package com.stream_pi.runcommandaction;
import com.stream_pi.action_api.actionproperty.property.Property;
import com.stream_pi.action_api.actionproperty.property.Type;
-import com.stream_pi.action_api.normalaction.NormalAction;
+import com.stream_pi.action_api.externalplugin.NormalAction;
import com.stream_pi.util.version.Version;
@@ -14,8 +14,8 @@ public class RunCommandAction extends No
setCategory("Essentials");
setAuthor("rnayabed");
setServerButtonGraphic("fas-terminal");
- setHelpLink("https://github.com/Stream-Pi/EssentialActions");
- setVersion(new Version(1,0,0));
+ setHelpLink("https://github.com/stream-pi/essentialactions/blob/master/runcommandaction/README.md");
+ setVersion(new Version(1,1,1));
}
@Override
@@ -35,9 +35,7 @@ public class RunCommandAction extends No
@Override
public void onActionClicked() throws Exception
{
- int rv = runCommand(getClientProperties().getSingleProperty("command").getStringValue());
-
- System.out.println(rv);
+ runCommand(getClientProperties().getSingleProperty("command").getStringValue());
}
@Override
@@ -46,10 +44,9 @@ public class RunCommandAction extends No
}
- private int runCommand(String command) throws Exception
+ private void runCommand(String command) throws Exception
{
Runtime rt = Runtime.getRuntime();
- Process pr = rt.exec(command);
- return pr.waitFor();
+ rt.exec(command);
}
}
--- 'a/runcommandaction/src/main/java/module-info.java'
+++ b/runcommandaction/src/main/java/module-info.java
@@ -7,6 +7,6 @@ module com.stream_pi.runcommandaction
requires org.kordamp.ikonli.javafx;
requires org.kordamp.ikonli.fontawesome5;
- provides com.stream_pi.action_api.normalaction.NormalAction with com.stream_pi.runcommandaction.RunCommandAction;
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with com.stream_pi.runcommandaction.RunCommandAction;
}
\ No newline at end of file
A spotify/.gitignore
+9 −0
--- /dev/null
+++ b/spotify/.gitignore
@@ -0,0 +1,9 @@
+.idea/
+DemoCustomNormalAction.iml
+target/
+
+
+.settings/
+.project
+.factorypath
+.classpath
A spotify/LICENSE
+674 −0
--- /dev/null
+++ b/spotify/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 <https://www.gnu.org/licenses/>.
+
+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:
+
+ <program> Copyright (C) <year> <name of author>
+ 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
+<https://www.gnu.org/licenses/>.
+
+ 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
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
\ No newline at end of file
A spotify/pom.xml
+95 −0
--- /dev/null
+++ b/spotify/pom.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>com.stream-pi</groupId>
+ <artifactId>Spotify</artifactId>
+ <version>1.0.0</version>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>3.1.0</version>
+ <executions>
+ <execution>
+ <id>test-jar</id>
+ <phase>package</phase>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.8.1</version>
+ <configuration>
+ <release>11</release>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <properties>
+ <ActionAPIVersion>1.0.0-SNAPSHOT</ActionAPIVersion>
+
+ <JavaFXVersion>16-ea+6</JavaFXVersion>
+
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <maven.compiler.source>11</maven.compiler.source>
+ <maven.compiler.target>11</maven.compiler.target>
+ </properties>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>com.github.thelinmichael</groupId>
+ <artifactId>spotify-web-api-java</artifactId>
+ <version>master-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openjfx</groupId>
+ <artifactId>javafx-controls</artifactId>
+ <version>${JavaFXVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openjfx</groupId>
+ <artifactId>javafx-graphics</artifactId>
+ <version>${JavaFXVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openjfx</groupId>
+ <artifactId>javafx-base</artifactId>
+ <version>${JavaFXVersion}</version>
+ </dependency>
+
+
+
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>action-api</artifactId>
+ <version>${ActionAPIVersion}</version>
+ </dependency>
+
+
+
+
+ <dependency>
+ <groupId>org.kordamp.ikonli</groupId>
+ <artifactId>ikonli-material-pack</artifactId>
+ <version>11.5.0</version>
+ </dependency>
+
+
+ </dependencies>
+
+</project>
\ No newline at end of file
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/Base64.java
@@ -0,0 +1,100 @@
+package com.wrapper.spotify;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Source: https://gist.github.com/EmilHernvall/953733#file-base64-java
+ * Due to Java version support issues with Datatypeconverter (&lt;=1.7) class and Base64 (&gt;=1.8) class.
+ */
+public class Base64 {
+ public static String encode(byte[] data) {
+ char[] tbl = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
+
+ StringBuilder buffer = new StringBuilder();
+ int pad = 0;
+ for (int i = 0; i < data.length; i += 3) {
+
+ int b = ((data[i] & 0xFF) << 16) & 0xFFFFFF;
+ if (i + 1 < data.length) {
+ b |= (data[i + 1] & 0xFF) << 8;
+ } else {
+ pad++;
+ }
+ if (i + 2 < data.length) {
+ b |= (data[i + 2] & 0xFF);
+ } else {
+ pad++;
+ }
+
+ for (int j = 0; j < 4 - pad; j++) {
+ int c = (b & 0xFC0000) >> 18;
+ buffer.append(tbl[c]);
+ b <<= 6;
+ }
+ }
+ for (int j = 0; j < pad; j++) {
+ buffer.append("=");
+ }
+
+ return buffer.toString();
+ }
+
+ public static byte[] decode(String data) {
+ int[] tbl = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30,
+ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
+ byte[] bytes = data.getBytes();
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ for (int i = 0; i < bytes.length; ) {
+ int b;
+ if (tbl[bytes[i]] != -1) {
+ b = (tbl[bytes[i]] & 0xFF) << 18;
+ }
+ // skip unknown characters
+ else {
+ i++;
+ continue;
+ }
+
+ int num = 0;
+ if (i + 1 < bytes.length && tbl[bytes[i + 1]] != -1) {
+ b = b | ((tbl[bytes[i + 1]] & 0xFF) << 12);
+ num++;
+ }
+ if (i + 2 < bytes.length && tbl[bytes[i + 2]] != -1) {
+ b = b | ((tbl[bytes[i + 2]] & 0xFF) << 6);
+ num++;
+ }
+ if (i + 3 < bytes.length && tbl[bytes[i + 3]] != -1) {
+ b = b | (tbl[bytes[i + 3]] & 0xFF);
+ num++;
+ }
+
+ while (num > 0) {
+ int c = (b & 0xFF0000) >> 16;
+ buffer.write((char) c);
+ b <<= 8;
+ num--;
+ }
+ i += 4;
+ }
+ return buffer.toByteArray();
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/IHttpManager.java
@@ -0,0 +1,79 @@
+package com.wrapper.spotify;
+
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+import java.net.URI;
+
+/**
+ * A simple HTTP connection interface.
+ */
+public interface IHttpManager {
+
+ /**
+ * Perform an HTTP GET request to the specified URL.
+ *
+ * @param uri The GET request's {@link URI}.
+ * @param headers The GET request's {@link Header}s.
+ * @return A string containing the GET request's response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ * @throws ParseException The response could not be parsed as a string.
+ */
+ String get(URI uri, Header[] headers) throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException;
+
+ /**
+ * Perform an HTTP POST request to the specified URL.
+ *
+ * @param uri The POST request's {@link URI}.
+ * @param headers The POST request's {@link Header}s.
+ * @param body The PUT request's body as a {@link HttpEntity}.
+ * @return A string containing the POST request's response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ * @throws ParseException The response could not be parsed as a string.
+ */
+ String post(URI uri, Header[] headers, HttpEntity body) throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException;
+
+ /**
+ * Perform an HTTP PUT request to the specified URL.
+ *
+ * @param uri The PUT request's {@link URI}.
+ * @param headers The PUT request's {@link Header}s.
+ * @param body The PUT request's body as a {@link HttpEntity}.
+ * @return A string containing the PUT request's response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ * @throws ParseException The response could not be parsed as a string.
+ */
+ String put(URI uri, Header[] headers, HttpEntity body) throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException;
+
+ /**
+ * Perform an HTTP DELETE request to the specified URL.
+ *
+ * @param uri The DELETE request's {@link URI}.
+ * @param headers The DELETE request's {@link Header}s.
+ * @param body The DELETE request's body as a {@link HttpEntity}.
+ * @return A string containing the DELETE request's response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ * @throws ParseException The response could not be parsed as a string.
+ */
+ String delete(URI uri, Header[] headers, HttpEntity body) throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException;
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/Spotify.java
@@ -0,0 +1,81 @@
+package com.stream_pi.spotify;
+
+import com.stream_pi.action_api.actionproperty.property.Property;
+import com.stream_pi.action_api.actionproperty.property.Type;
+import com.stream_pi.action_api.externalplugin.NormalAction;
+import com.stream_pi.util.alert.StreamPiAlert;
+import com.stream_pi.util.version.Version;
+import javafx.scene.control.Button;
+
+import com.wrapper.spotify.SpotifyApi;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.credentials.ClientCredentials;
+import com.wrapper.spotify.requests.authorization.client_credentials.ClientCredentialsRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+
+public class Spotify extends NormalAction
+{
+
+ public Spotify()
+ {
+ setName("Spotify");
+ setAuthor("rnayabed");
+ setHelpLink("https://github.com/stream-pi/");
+ setVersion(new Version(1,0,0));
+
+ setCategory("Spotify");
+
+ }
+
+ @Override
+ public void initProperties() throws Exception {
+ //Called First
+
+ Property clientID = new Property("clientID", Type.STRING);
+ clientID.setDisplayName("Spotify Client ID");
+
+ Property clientSecret = new Property("clientSecret", Type.STRING);
+ clientSecret.setDisplayName("Spotify Client Secret");
+
+
+ addClientProperties(
+ clientID,
+ clientSecret
+
+ );
+ }
+
+ private static final SpotifyApi spotifyApi = new SpotifyApi.Builder()
+ .setClientId(clientId)
+ .setClientSecret(clientSecret)
+ .build();
+ private static final ClientCredentialsRequest clientCredentialsRequest = spotifyApi.clientCredentials()
+ .build();
+ SpotifyAPI spotifyApi1 = new SpotifyApi.Builder();
+ Property id = getClientProperties().getSingleProperty("clientID");
+ Property secret = getClientProperties().getSingleProperty("clientSecret")
+
+ .setClientId(id.getStringValue())
+ .setClientSecret(secret.getStringValue())
+ .build();
+
+
+ @Override
+ public void initAction() {
+
+ }
+
+
+ @Override
+ public void onActionClicked()
+ {
+ spotifyApi.setAccessToken(clientCredentials.getAccessToken());
+
+ System.out.println("Action Called!");
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/SpotifyApi.java
@@ -0,0 +1,2179 @@
+package com.wrapper.spotify;
+
+import com.google.gson.JsonArray;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.requests.authorization.authorization_code.AuthorizationCodeRefreshRequest;
+import com.wrapper.spotify.requests.authorization.authorization_code.AuthorizationCodeRequest;
+import com.wrapper.spotify.requests.authorization.authorization_code.AuthorizationCodeUriRequest;
+import com.wrapper.spotify.requests.authorization.authorization_code.pkce.AuthorizationCodePKCERefreshRequest;
+import com.wrapper.spotify.requests.authorization.authorization_code.pkce.AuthorizationCodePKCERequest;
+import com.wrapper.spotify.requests.authorization.client_credentials.ClientCredentialsRequest;
+import com.wrapper.spotify.requests.data.albums.GetAlbumRequest;
+import com.wrapper.spotify.requests.data.albums.GetAlbumsTracksRequest;
+import com.wrapper.spotify.requests.data.albums.GetSeveralAlbumsRequest;
+import com.wrapper.spotify.requests.data.artists.*;
+import com.wrapper.spotify.requests.data.browse.*;
+import com.wrapper.spotify.requests.data.browse.miscellaneous.GetAvailableGenreSeedsRequest;
+import com.wrapper.spotify.requests.data.episodes.GetEpisodeRequest;
+import com.wrapper.spotify.requests.data.episodes.GetSeveralEpisodesRequest;
+import com.wrapper.spotify.requests.data.follow.*;
+import com.wrapper.spotify.requests.data.library.*;
+import com.wrapper.spotify.requests.data.personalization.GetUsersTopArtistsAndTracksRequest;
+import com.wrapper.spotify.requests.data.personalization.interfaces.IArtistTrackModelObject;
+import com.wrapper.spotify.requests.data.personalization.simplified.GetUsersTopArtistsRequest;
+import com.wrapper.spotify.requests.data.personalization.simplified.GetUsersTopTracksRequest;
+import com.wrapper.spotify.requests.data.player.*;
+import com.wrapper.spotify.requests.data.playlists.*;
+import com.wrapper.spotify.requests.data.search.SearchItemRequest;
+import com.wrapper.spotify.requests.data.search.simplified.*;
+import com.wrapper.spotify.requests.data.search.simplified.special.SearchAlbumsSpecialRequest;
+import com.wrapper.spotify.requests.data.shows.GetSeveralShowsRequest;
+import com.wrapper.spotify.requests.data.shows.GetShowRequest;
+import com.wrapper.spotify.requests.data.shows.GetShowsEpisodesRequest;
+import com.wrapper.spotify.requests.data.tracks.*;
+import com.wrapper.spotify.requests.data.users_profile.GetCurrentUsersProfileRequest;
+import com.wrapper.spotify.requests.data.users_profile.GetUsersProfileRequest;
+
+import java.net.URI;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+import java.util.logging.Logger;
+
+/**
+ * Instances of the SpotifyApi class provide access to the Spotify Web API.
+ */
+public class SpotifyApi {
+
+ /**
+ * The default authentication host of Spotify API calls.
+ */
+ public static final String DEFAULT_AUTHENTICATION_HOST = "accounts.spotify.com";
+
+ /**
+ * The default authentication port of Spotify API calls.
+ */
+ public static final int DEFAULT_AUTHENTICATION_PORT = 443;
+
+ /**
+ * The default authentication http scheme of Spotify API calls.
+ */
+ public static final String DEFAULT_AUTHENTICATION_SCHEME = "https";
+
+ /**
+ * The default host of Spotify API calls.
+ */
+ public static final String DEFAULT_HOST = "api.spotify.com";
+
+ /**
+ * A HttpManager configured with default settings.
+ */
+ public static final IHttpManager DEFAULT_HTTP_MANAGER = new SpotifyHttpManager.Builder().build();
+
+ /**
+ * The default port of Spotify API calls.
+ */
+ public static final int DEFAULT_PORT = 443;
+
+ /**
+ * The default http scheme of Spotify API calls.
+ */
+ public static final String DEFAULT_SCHEME = "https";
+
+ public static final Logger LOGGER = Logger.getLogger(SpotifyApi.class.getName());
+
+ /**
+ * The date format used by the Spotify Web API. It uses the {@code GMT} timezone and the following pattern:
+ * {@code yyyy-MM-dd'T'HH:mm:ss}
+ */
+ private static final ThreadLocal<SimpleDateFormat> SIMPLE_DATE_FORMAT = ThreadLocal.withInitial(() -> makeSimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", "GMT"));
+
+ private final IHttpManager httpManager;
+ private final String scheme;
+ private final String host;
+ private final Integer port;
+ private final String proxyUrl;
+ private final Integer proxyPort;
+ private final Integer proxyUsername;
+ private final Integer proxyPassword;
+ private final String clientId;
+ private final String clientSecret;
+ private final URI redirectUri;
+ private String accessToken;
+ private String refreshToken;
+
+ private SpotifyApi(Builder builder) {
+ assert (builder.httpManager != null);
+
+ this.httpManager = builder.httpManager;
+ this.scheme = builder.scheme;
+ this.host = builder.host;
+ this.port = builder.port;
+ this.proxyUrl = builder.proxyUrl;
+ this.proxyPort = builder.proxyPort;
+ this.proxyUsername = builder.proxyUsername;
+ this.proxyPassword = builder.proxyPassword;
+ this.clientId = builder.clientId;
+ this.clientSecret = builder.clientSecret;
+ this.redirectUri = builder.redirectUri;
+ this.accessToken = builder.accessToken;
+ this.refreshToken = builder.refreshToken;
+ }
+
+ /**
+ * Create a builder for building a new Spotify API instance.
+ *
+ * @return A {@link Builder}.
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * String concatenation helper method.
+ *
+ * @param parts String parts.
+ * @param character Separation character.
+ * @return A string.
+ */
+ public static String concat(String[] parts, char character) {
+ StringBuilder stringBuilder = new StringBuilder();
+
+ for (String part : parts) {
+ stringBuilder.append(part).append(character);
+ }
+
+ stringBuilder.deleteCharAt(stringBuilder.length() - 1);
+
+ return stringBuilder.toString();
+ }
+
+ /**
+ * Parses a date in the default spotify format.
+ *
+ * @param date the input date to parse
+ * @return the pared {@link Date}
+ * @throws ParseException if the date is not in a valid format
+ */
+ public static Date parseDefaultDate(String date) throws ParseException {
+ return SIMPLE_DATE_FORMAT.get().parse(date);
+ }
+
+ /**
+ * Formats a date, using the default spotify format.
+ *
+ * @param date the date to format
+ * @return the formatted date
+ */
+ public static String formatDefaultDate(Date date) {
+ return SIMPLE_DATE_FORMAT.get().format(date);
+ }
+
+ public static SimpleDateFormat makeSimpleDateFormat(String pattern, String id) {
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
+ simpleDateFormat.setTimeZone(TimeZone.getTimeZone(id));
+
+ return simpleDateFormat;
+ }
+
+ /**
+ * Get the {@link IHttpManager} used for API calls.
+ *
+ * @return An {@link IHttpManager}.
+ */
+ public IHttpManager getHttpManager() {
+ return httpManager;
+ }
+
+ /**
+ * Get the scheme used for API calls. Default: {@code https}
+ *
+ * @return A scheme.
+ */
+ public String getScheme() {
+ return scheme;
+ }
+
+ /**
+ * Get the API host used for API calls. Default: {@code "api.spotify.com"}
+ *
+ * @return The host address.
+ */
+ public String getHost() {
+ return host;
+ }
+
+ /**
+ * Get the port used for API calls. Default: {@code 443}
+ *
+ * @return A port.
+ */
+ public Integer getPort() {
+ return port;
+ }
+
+ /**
+ * Get the proxy URL used for API calls.
+ *
+ * @return The proxy URL.
+ */
+ public String getProxyUrl() {
+ return proxyUrl;
+ }
+
+ /**
+ * Get the proxy port used for API calls.
+ *
+ * @return The proxy port.
+ */
+ public Integer getProxyPort() {
+ return proxyPort;
+ }
+
+ /**
+ * Get the proxy username used for API calls.
+ *
+ * @return The proxy username.
+ */
+ public Integer getProxyUsername() {
+ return proxyUsername;
+ }
+
+ /**
+ * Get the proxy password used for API calls.
+ *
+ * @return The proxy password.
+ */
+ public Integer getProxyPassword() {
+ return proxyPassword;
+ }
+
+ /**
+ * Get the application client ID specified in this API object.
+ *
+ * @return Application client ID.
+ */
+ public String getClientId() {
+ return clientId;
+ }
+
+ /**
+ * Get the application client secret specified in this API object.
+ *
+ * @return Application client secret.
+ */
+ public String getClientSecret() {
+ return clientSecret;
+ }
+
+ /**
+ * Get the redirect URI of the application specified in this API object.
+ *
+ * @return Application redirect URI.
+ */
+ public URI getRedirectURI() {
+ return redirectUri;
+ }
+
+ /**
+ * Get the access token specified in the API object, which is used for API calls.
+ *
+ * @return A Spotify Web API access token.
+ */
+ public String getAccessToken() {
+ return accessToken;
+ }
+
+ /**
+ * Set the access token of the API object.
+ *
+ * @param accessToken A Spotify Web API access token.
+ */
+ public void setAccessToken(String accessToken) {
+ this.accessToken = accessToken;
+ }
+
+ /**
+ * Get the refresh token specified in the API object.
+ *
+ * @return A Spotify Web API refresh token.
+ */
+ public String getRefreshToken() {
+ return refreshToken;
+ }
+
+ /**
+ * Set the refresh token of the API object.
+ *
+ * @param refreshToken A Spotify Web API refresh token.
+ */
+ public void setRefreshToken(String refreshToken) {
+ this.refreshToken = refreshToken;
+ }
+
+ /**
+ * Refresh the access token by using authorization code grant. <br>
+ * Requires client ID, client secret, and refresh token to be set.
+ *
+ * @param client_id When you register your application, Spotify provides you a Client ID.
+ * @param client_secret When you register your application, Spotify provides you a Client Secret.
+ * @param refresh_token The refresh token returned from the authorization code exchange.
+ * @return An {@link AuthorizationCodeRequest.Builder}.
+ */
+ public AuthorizationCodeRefreshRequest.Builder authorizationCodeRefresh(String client_id, String client_secret, String refresh_token) {
+ return new AuthorizationCodeRefreshRequest.Builder(client_id, client_secret)
+ .setDefaults(httpManager, scheme, host, port)
+ .grant_type("refresh_token")
+ .refresh_token(refresh_token);
+ }
+
+ /**
+ * Refresh the access token by using authorization code grant.
+ *
+ * @return An {@link AuthorizationCodeRequest.Builder}.
+ */
+ public AuthorizationCodeRefreshRequest.Builder authorizationCodeRefresh() {
+ return new AuthorizationCodeRefreshRequest.Builder(clientId, clientSecret)
+ .setDefaults(httpManager, scheme, host, port)
+ .grant_type("refresh_token")
+ .refresh_token(refreshToken);
+ }
+
+ /**
+ * Refresh the access token by using the authorization code flow with Proof Key for Code Exchange (PKCE). <br>
+ * Requires client ID and refresh token to be set.
+ *
+ * @param client_id When you register your application, Spotify provides you a Client ID.
+ * @param refresh_token The refresh token returned from the authorization code exchange or the last access token refresh.
+ * @return An {@link AuthorizationCodePKCERefreshRequest.Builder}.
+ */
+ public AuthorizationCodePKCERefreshRequest.Builder authorizationCodePKCERefresh(String client_id, String refresh_token) {
+ return new AuthorizationCodePKCERefreshRequest.Builder()
+ .setDefaults(httpManager, scheme, host, port)
+ .client_id(client_id)
+ .grant_type("refresh_token")
+ .refresh_token(refresh_token);
+ }
+
+ /**
+ * Refresh the access token by using the authorization code flow with Proof Key for Code Exchange (PKCE).
+ *
+ * @return An {@link AuthorizationCodePKCERefreshRequest.Builder}.
+ */
+ public AuthorizationCodePKCERefreshRequest.Builder authorizationCodePKCERefresh() {
+ return new AuthorizationCodePKCERefreshRequest.Builder()
+ .setDefaults(httpManager, scheme, host, port)
+ .client_id(clientId)
+ .grant_type("refresh_token")
+ .refresh_token(refreshToken);
+ }
+
+ /**
+ * Returns a builder that can be used to build requests for authorization code grants. <br>
+ * Requires client ID, client secret, authorization code and redirect URI to be set.
+ *
+ * @param client_id When you register your application, Spotify provides you a Client ID.
+ * @param client_secret When you register your application, Spotify provides you a Client Secret.
+ * @param code The authorization code returned from the initial request to the Account /authorize endpoint.
+ * @param redirect_uri This parameter is used for validation only (there is no actual redirection). The
+ * value of this parameter must exactly match the value of redirect_uri supplied when requesting
+ * the authorization code.
+ * @return An {@link AuthorizationCodeRequest.Builder}.
+ */
+ public AuthorizationCodeRequest.Builder authorizationCode(String client_id, String client_secret, String code, URI redirect_uri) {
+ return new AuthorizationCodeRequest.Builder(client_id, client_secret)
+ .setDefaults(httpManager, scheme, host, port)
+ .grant_type("authorization_code")
+ .code(code)
+ .redirect_uri(redirect_uri);
+ }
+
+ /**
+ * Returns a builder that can be used to build requests for authorization code grants. <br>
+ * Requires authorization code to be set.
+ *
+ * @param code The authorization code returned from the initial request to the Account /authorize endpoint.
+ * @return An {@link AuthorizationCodeRequest.Builder}.
+ */
+ public AuthorizationCodeRequest.Builder authorizationCode(String code) {
+ return new AuthorizationCodeRequest.Builder(clientId, clientSecret)
+ .setDefaults(httpManager, scheme, host, port)
+ .grant_type("authorization_code")
+ .code(code)
+ .redirect_uri(redirectUri);
+ }
+ /**
+ * Returns a builder that can be used to build requests for authorization code grants using the Proof Key for Code Exchange (PKCE) flow. <br>
+ * Requires client ID, authorization code, code verifier and redirect URI to be set.
+ *
+ * @param client_id When you register your application, Spotify provides you a Client ID.
+ * @param code The authorization code returned from the initial request to the Account /authorize endpoint.
+ * @param code_verifier The value of this parameter must match the value of the code_verifier that your app generated beforehand.
+ * @param redirect_uri This parameter is used for validation only (there is no actual redirection). The
+ * value of this parameter must exactly match the value of redirect_uri supplied when requesting
+ * the authorization code.
+ * @return An {@link AuthorizationCodePKCERequest.Builder}.
+ * @see <a href="https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow-with-proof-key-for-code-exchange-pkce">
+ * Authorization Code Flow with Proof Key for Code Exchange (PKCE)</a>
+ */
+ public AuthorizationCodePKCERequest.Builder authorizationCodePKCE(String client_id, String code, String code_verifier, URI redirect_uri) {
+ return new AuthorizationCodePKCERequest.Builder()
+ .setDefaults(httpManager, scheme, host, port)
+ .client_id(client_id)
+ .code_verifier(code_verifier)
+ .grant_type("authorization_code")
+ .code(code)
+ .redirect_uri(redirect_uri);
+ }
+
+ /**
+ * Returns a builder that can be used to build requests for authorization code grants using the Proof Key for Code Exchange (PKCE) flow. <br>
+ * Requires authorization code and code verifier to be set.
+ *
+ * @param code The authorization code returned from the initial request to the Account /authorize endpoint.
+ * @param code_verifier The value of this parameter must match the value of the code_verifier that your app generated beforehand.
+ * @return An {@link AuthorizationCodePKCERequest.Builder}.
+ * @see <a href="https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow-with-proof-key-for-code-exchange-pkce">
+ * Authorization Code Flow with Proof Key for Code Exchange (PKCE)</a>
+ */
+ public AuthorizationCodePKCERequest.Builder authorizationCodePKCE(String code, String code_verifier) {
+ return new AuthorizationCodePKCERequest.Builder()
+ .setDefaults(httpManager, scheme, host, port)
+ .client_id(clientId)
+ .code_verifier(code_verifier)
+ .grant_type("authorization_code")
+ .code(code)
+ .redirect_uri(redirectUri);
+ }
+
+ /**
+ * Retrieve an URL where the user can give the application permissions.
+ *
+ * @param client_id When you register your application, Spotify provides you a Client ID.
+ * @param redirect_uri This parameter is used for validation only (there is no actual redirection). The
+ * value of this parameter must exactly match the value of redirect_uri supplied when requesting
+ * the authorization code.
+ * @return An {@link AuthorizationCodeUriRequest.Builder}.
+ */
+ public AuthorizationCodeUriRequest.Builder authorizationCodeUri(String client_id, URI redirect_uri) {
+ return new AuthorizationCodeUriRequest.Builder()
+ .setDefaults(httpManager, scheme, host, port)
+ .client_id(client_id)
+ .response_type("code")
+ .redirect_uri(redirect_uri);
+ }
+
+ /**
+ * Retrieve an URL where the user can give the application permissions.
+ *
+ * @return An {@link AuthorizationCodeUriRequest.Builder}.
+ */
+ public AuthorizationCodeUriRequest.Builder authorizationCodeUri() {
+ return new AuthorizationCodeUriRequest.Builder()
+ .setDefaults(httpManager, scheme, host, port)
+ .client_id(clientId)
+ .response_type("code")
+ .redirect_uri(redirectUri);
+ }
+
+ /**
+ * Retrieve an URL where the user can give the application permissions using the Proof Key for Code Exchange (PKCE) flow.
+ *
+ * @param client_id When you register your application, Spotify provides you a Client ID.
+ * @param code_challenge The code challenge that your app calculated beforehand.
+ * The code challenge is the base64url encoded sha256-hash of the code verifier,
+ * which is a cryptographically random string between 43 and 128 characters in length.
+ * It can contain letters, digits, underscores, periods, hyphens, or tildes and is generated
+ * by your app before each authentication request.
+ * @param redirect_uri This parameter is used for validation only (there is no actual redirection). The
+ * value of this parameter must exactly match the value of redirect_uri supplied when requesting
+ * the authorization code.
+ * @return An {@link AuthorizationCodeUriRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow-with-proof-key-for-code-exchange-pkce">
+ * Authorization Code Flow with Proof Key for Code Exchange (PKCE)</a>
+ */
+ public AuthorizationCodeUriRequest.Builder authorizationCodePKCEUri(String client_id, String code_challenge, URI redirect_uri) {
+ return new AuthorizationCodeUriRequest.Builder()
+ .setDefaults(httpManager, scheme, host, port)
+ .client_id(client_id)
+ .response_type("code")
+ .code_challenge_method("S256")
+ .code_challenge(code_challenge)
+ .redirect_uri(redirect_uri);
+ }
+
+ /**
+ * Retrieve an URL where the user can give the application permissions using the Proof Key for Code Exchange (PKCE) flow.
+ *
+ * @param code_challenge The code challenge that your app calculated beforehand.
+ * The code challenge is the base64url encoded sha256-hash of the code verifier,
+ * which is a cryptographically random string between 43 and 128 characters in length.
+ * It can contain letters, digits, underscores, periods, hyphens, or tildes and is generated
+ *
+ * @return An {@link AuthorizationCodeUriRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow-with-proof-key-for-code-exchange-pkce">
+ * Authorization Code Flow with Proof Key for Code Exchange (PKCE)</a>
+ */
+ public AuthorizationCodeUriRequest.Builder authorizationCodePKCEUri(String code_challenge) {
+ return new AuthorizationCodeUriRequest.Builder()
+ .setDefaults(httpManager, scheme, host, port)
+ .client_id(clientId)
+ .response_type("code")
+ .code_challenge_method("S256")
+ .code_challenge(code_challenge)
+ .redirect_uri(redirectUri);
+ }
+
+ /**
+ * Returns a builder that can be used to build requests for client credential grants. <br>
+ * Requires client ID and client secret to be set.
+ *
+ * @return A {@link ClientCredentialsRequest.Builder}.
+ */
+ public ClientCredentialsRequest.Builder clientCredentials() {
+ return new ClientCredentialsRequest.Builder(clientId, clientSecret)
+ .setDefaults(httpManager, scheme, host, port)
+ .grant_type("client_credentials");
+ }
+
+ /**
+ * Returns an album with the ID given below.
+ *
+ * @param id The Spotify album ID of the album you're trying to retrieve.
+ * @return A {@link GetAlbumRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetAlbumRequest.Builder getAlbum(String id) {
+ return new GetAlbumRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .id(id);
+ }
+
+ /**
+ * Returns the tracks of the album with the ID given below.
+ *
+ * @param id The Spotify ID of the album you're trying to retrieve.
+ * @return A {@link GetAlbumsTracksRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetAlbumsTracksRequest.Builder getAlbumsTracks(String id) {
+ return new GetAlbumsTracksRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .id(id);
+ }
+
+ /**
+ * Get multiple albums.
+ *
+ * @param ids The Spotify IDs of all albums you're trying to retrieve. Maximum: 20 IDs.
+ * @return A {@link GetSeveralAlbumsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetSeveralAlbumsRequest.Builder getSeveralAlbums(String... ids) {
+ return new GetSeveralAlbumsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .ids(concat(ids, ','));
+ }
+
+ /**
+ * Get an artist.
+ *
+ * @param id The Spotify ID of the artist.
+ * @return A {@link GetArtistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetArtistRequest.Builder getArtist(String id) {
+ return new GetArtistRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .id(id);
+ }
+
+ /**
+ * Get the albums of a specific artist.
+ *
+ * @param id The Spotify ID of the artist.
+ * @return A {@link GetArtistsAlbumsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetArtistsAlbumsRequest.Builder getArtistsAlbums(String id) {
+ return new GetArtistsAlbumsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .id(id);
+ }
+
+ /**
+ * Get the top tracks of an artist in a specific country.
+ *
+ * @param id The Spotify ID of the artist.
+ * @param country The ISO 3166-1 alpha-2 country code of the specific country.
+ * @return A {@link GetArtistsTopTracksRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes </a>
+ */
+ public GetArtistsTopTracksRequest.Builder getArtistsTopTracks(String id, CountryCode country) {
+ return new GetArtistsTopTracksRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .id(id)
+ .country(country);
+ }
+
+ /**
+ * Get artists related/similar to an artist.
+ *
+ * @param id The Spotify ID of the artist.
+ * @return A {@link GetArtistsRelatedArtistsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetArtistsRelatedArtistsRequest.Builder getArtistsRelatedArtists(String id) {
+ return new GetArtistsRelatedArtistsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .id(id);
+ }
+
+ /**
+ * Get multiple artists.
+ *
+ * @param ids The Spotify IDs of all artists you're trying to retrieve. Maximum: 50 IDs.
+ * @return A {@link GetSeveralArtistsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetSeveralArtistsRequest.Builder getSeveralArtists(String... ids) {
+ return new GetSeveralArtistsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .ids(concat(ids, ','));
+ }
+
+ /**
+ * Get a category.
+ *
+ * @param category_id The Spotify category ID for the category.
+ * @return A {@link GetCategoryRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetCategoryRequest.Builder getCategory(String category_id) {
+ return new GetCategoryRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .category_id(category_id);
+ }
+
+ /**
+ * Get the playlists from a specific category.
+ *
+ * @param category_id The Spotify category ID for the category.
+ * @return A {@link GetCategorysPlaylistsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetCategorysPlaylistsRequest.Builder getCategorysPlaylists(String category_id) {
+ return new GetCategorysPlaylistsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .category_id(category_id);
+ }
+
+ /**
+ * Get a list of categories.
+ *
+ * @return A {@link GetListOfCategoriesRequest.Builder}.
+ */
+ public GetListOfCategoriesRequest.Builder getListOfCategories() {
+ return new GetListOfCategoriesRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port);
+ }
+
+ /**
+ * Get "Featured Playlists" of different countries which may match a specific language.
+ *
+ * @return A {@link GetListOfFeaturedPlaylistsRequest.Builder}.
+ */
+ public GetListOfFeaturedPlaylistsRequest.Builder getListOfFeaturedPlaylists() {
+ return new GetListOfFeaturedPlaylistsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port);
+ }
+
+ /**
+ * Get the newest releases from a specific country.
+ *
+ * @return A {@link GetListOfNewReleasesRequest.Builder}.
+ */
+ public GetListOfNewReleasesRequest.Builder getListOfNewReleases() {
+ return new GetListOfNewReleasesRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port);
+ }
+
+ /**
+ * Create a playlist-style listening experience based on seed artists, tracks and genres.
+ *
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public GetRecommendationsRequest.Builder getRecommendations() {
+ return new GetRecommendationsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port);
+ }
+
+ /**
+ * Retrieve a list of available genres seed parameter values for recommendations.
+ *
+ * @return A {@link GetAvailableGenreSeedsRequest.Builder}.
+ */
+ public GetAvailableGenreSeedsRequest.Builder getAvailableGenreSeeds() {
+ return new GetAvailableGenreSeedsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port);
+ }
+
+ /**
+ * Get an episode.
+ *
+ * @param id The Spotify ID of the episode.
+ * @return A {@link GetEpisodeRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetEpisodeRequest.Builder getEpisode(String id) {
+ return new GetEpisodeRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .id(id);
+ }
+
+ /**
+ * Get multiple episodes.
+ *
+ * @param ids The Spotify IDs of all episodes you're trying to retrieve. Maximum: 50 IDs.
+ * @return A {@link GetSeveralEpisodesRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetSeveralEpisodesRequest.Builder getSeveralEpisodes(String... ids) {
+ return new GetSeveralEpisodesRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .ids(concat(ids, ','));
+ }
+
+ /**
+ * Check to see if the current user is following one or more artists or other Spotify users.
+ *
+ * @param type The ID type: either artist or user.
+ * @param ids A list of the artist or the user Spotify IDs to check. Maximum: 50 IDs.
+ * @return A {@link CheckCurrentUserFollowsArtistsOrUsersRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public CheckCurrentUserFollowsArtistsOrUsersRequest.Builder checkCurrentUserFollowsArtistsOrUsers(
+ ModelObjectType type, String[] ids) {
+ return new CheckCurrentUserFollowsArtistsOrUsersRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .type(type)
+ .ids(concat(ids, ','));
+ }
+
+ /**
+ * Check to see if one or more Spotify users are following a specified playlist.
+ *
+ * @param owner_id The Spotify User ID of the person who owns the playlist.
+ * @param playlist_id The Spotify ID of the playlist.
+ * @param ids A list of Spotify User IDs; the IDs of the users that you want to check to see if they
+ * follow the playlist. Maximum: 5 IDs.
+ * @return A {@link CheckUsersFollowPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public CheckUsersFollowPlaylistRequest.Builder checkUsersFollowPlaylist(
+ String owner_id, String playlist_id, String[] ids) {
+ return new CheckUsersFollowPlaylistRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .owner_id(owner_id)
+ .playlist_id(playlist_id)
+ .ids(concat(ids, ','));
+ }
+
+ /**
+ * Add the current user as a follower of one or more artists or other Spotify users.
+ *
+ * @param type The ID type: either artist or user.
+ * @param ids A list of the artist or the user Spotify IDs. Maximum: 50 IDs.
+ * @return A {@link FollowArtistsOrUsersRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public FollowArtistsOrUsersRequest.Builder followArtistsOrUsers(ModelObjectType type, String[] ids) {
+ return new FollowArtistsOrUsersRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .type(type)
+ .ids(concat(ids, ','));
+ }
+
+ /**
+ * Add the current user as a follower of one or more artists or other Spotify users.
+ *
+ * @param type The ID type: either artist or user.
+ * @param ids A list of the artist or the user Spotify IDs. Maximum: 50 IDs.
+ * @return A {@link FollowArtistsOrUsersRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public FollowArtistsOrUsersRequest.Builder followArtistsOrUsers(ModelObjectType type, JsonArray ids) {
+ return new FollowArtistsOrUsersRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .type(type)
+ .ids(ids);
+ }
+
+ /**
+ * Add the current user as a follower of a playlist.
+ *
+ * @param owner_id The Spotify user ID of the person who owns the playlist.
+ * @param playlist_id The Spotify ID of the playlist. Any playlist can be followed, regardless of its
+ * public/private status, as long as you know its playlist ID.
+ * @param public_ Default: true. If true the playlist will be included in user's public playlists, if false it
+ * will remain private. To be able to follow playlists privately, the user must have granted the
+ * playlist-modify-private scope.
+ * @return A {@link com.wrapper.spotify.requests.data.follow.legacy.FollowPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public com.wrapper.spotify.requests.data.follow.legacy.FollowPlaylistRequest.Builder followPlaylist(String owner_id, String playlist_id, boolean public_) {
+ return new com.wrapper.spotify.requests.data.follow.legacy.FollowPlaylistRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .owner_id(owner_id)
+ .playlist_id(playlist_id)
+ .public_(public_);
+ }
+
+
+ /**
+ * Add the current user as a follower of a playlist.
+ *
+ * @param playlist_id The Spotify ID of the playlist. Any playlist can be followed, regardless of its
+ * public/private status, as long as you know its playlist ID.
+ * @param public_ Default: true. If true the playlist will be included in user's public playlists, if false it
+ * will remain private. To be able to follow playlists privately, the user must have granted the
+ * playlist-modify-private scope.
+ * @return A {@link com.wrapper.spotify.requests.data.follow.legacy.FollowPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public FollowPlaylistRequest.Builder followPlaylist(String playlist_id, boolean public_) {
+ return new FollowPlaylistRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .playlist_id(playlist_id)
+ .public_(public_);
+ }
+
+
+ /**
+ * Get the current user’s followed artists.
+ *
+ * @param type The ID type: currently only artist is supported.
+ * @return A {@link GetUsersFollowedArtistsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetUsersFollowedArtistsRequest.Builder getUsersFollowedArtists(ModelObjectType type) {
+ return new GetUsersFollowedArtistsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .type(type);
+ }
+
+ /**
+ * Remove the current user as a follower of one or more artists or other Spotify users.
+ *
+ * @param type The ID type: either artist or user.
+ * @param ids A list of the artist or the user Spotify IDs. Maximum: 50 IDs.
+ * @return A {@link UnfollowArtistsOrUsersRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public UnfollowArtistsOrUsersRequest.Builder unfollowArtistsOrUsers(ModelObjectType type, String[] ids) {
+ return new UnfollowArtistsOrUsersRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .type(type)
+ .ids(concat(ids, ','));
+ }
+
+ /**
+ * Remove the current user as a follower of one or more artists or other Spotify users.
+ *
+ * @param type The ID type: either artist or user.
+ * @param ids A JSON array of the artist or the user Spotify IDs. Maximum: 50 IDs.
+ * @return A {@link UnfollowArtistsOrUsersRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public UnfollowArtistsOrUsersRequest.Builder unfollowArtistsOrUsers(ModelObjectType type, JsonArray ids) {
+ return new UnfollowArtistsOrUsersRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .type(type)
+ .ids(ids);
+ }
+
+ /**
+ * Remove the specified user as a follower of a playlist.
+ *
+ * @param owner_id The owners username.
+ * @param playlist_id The playlist's ID.
+ * @return An {@link com.wrapper.spotify.requests.data.follow.legacy.UnfollowPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public com.wrapper.spotify.requests.data.follow.legacy.UnfollowPlaylistRequest.Builder unfollowPlaylist(String owner_id, String playlist_id) {
+ return new com.wrapper.spotify.requests.data.follow.legacy.UnfollowPlaylistRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .owner_id(owner_id)
+ .playlist_id(playlist_id);
+ }
+
+ /**
+ * Remove the current user as a follower of a playlist.
+ *
+ * @param playlist_id The playlist's ID.
+ * @return An {@link UnfollowPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public UnfollowPlaylistRequest.Builder unfollowPlaylist(String playlist_id) {
+ return new UnfollowPlaylistRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .playlist_id(playlist_id);
+ }
+
+ /**
+ * Check if an album is saved in the user's "Your Music" library.
+ *
+ * @param ids The album IDs to check for in the user's Your Music library. Maximum: 50 IDs.
+ * @return A {@link CheckUsersSavedAlbumsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public CheckUsersSavedAlbumsRequest.Builder checkUsersSavedAlbums(String... ids) {
+ return new CheckUsersSavedAlbumsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .ids(concat(ids, ','));
+ }
+
+ /**
+ * Check if a show is saved in the users "Your Music" library.
+ *
+ * @param ids The show IDs to check for in the user's Your Music library. Maximum: 50 IDs.
+ * @return A {@link CheckUsersSavedShowsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public CheckUsersSavedShowsRequest.Builder checkUsersSavedShows(String... ids) {
+ return new CheckUsersSavedShowsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .ids(concat(ids, ','));
+ }
+
+ /**
+ * Check if a track is saved in the user's "Your Music" library.
+ *
+ * @param ids The track IDs to check for in the user's Your Music library. Maximum: 50 IDs.
+ * @return A {@link CheckUsersSavedTracksRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public CheckUsersSavedTracksRequest.Builder checkUsersSavedTracks(String... ids) {
+ return new CheckUsersSavedTracksRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .ids(concat(ids, ','));
+ }
+
+ /**
+ * Get a list of the albums saved in the current Spotify user’s "Your Music" library.
+ *
+ * @return A {@link GetCurrentUsersSavedAlbumsRequest.Builder}.
+ */
+ public GetCurrentUsersSavedAlbumsRequest.Builder getCurrentUsersSavedAlbums() {
+ return new GetCurrentUsersSavedAlbumsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port);
+ }
+
+ /**
+ * Get a list of shows saved in the current Spotify user’s library.
+ *
+ * @return A {@link GetUsersSavedShowsRequest.Builder}.
+ */
+ public GetUsersSavedShowsRequest.Builder getUsersSavedShows() {
+ return new GetUsersSavedShowsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port);
+ }
+
+ /**
+ * Get an user's "Your Music" tracks.
+ *
+ * @return A {@link GetUsersSavedTracksRequest.Builder}.
+ */
+ public GetUsersSavedTracksRequest.Builder getUsersSavedTracks() {
+ return new GetUsersSavedTracksRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port);
+ }
+
+ /**
+ * Remove one or more albums from the current user's "Your Music" library.
+ *
+ * @param ids A list of the Spotify IDs. Maximum: 50 IDs.
+ * @return A {@link RemoveAlbumsForCurrentUserRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public RemoveAlbumsForCurrentUserRequest.Builder removeAlbumsForCurrentUser(String... ids) {
+ return new RemoveAlbumsForCurrentUserRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .ids(concat(ids, ','));
+ }
+
+ /**
+ * Remove one or more albums from the current user's "Your Music" library.
+ *
+ * @param ids The Spotify IDs for the albums to be deleted. Maximum: 50 IDs.
+ * @return A {@link RemoveAlbumsForCurrentUserRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public RemoveAlbumsForCurrentUserRequest.Builder removeAlbumsForCurrentUser(JsonArray ids) {
+ return new RemoveAlbumsForCurrentUserRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .ids(ids);
+ }
+
+ /**
+ * Remove one or more shows from the current users "Your Music" library.
+ *
+ * @param ids The Spotify IDs for the shows to be deleted. Maximum: 50 IDs.
+ * @return A {@link RemoveAlbumsForCurrentUserRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public RemoveUsersSavedShowsRequest.Builder removeUsersSavedShows(String... ids) {
+ return new RemoveUsersSavedShowsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .ids(concat(ids, ','));
+ }
+
+ /**
+ * Remove one or more shows from the current users "Your Music" library.
+ *
+ * @param ids The Spotify IDs for the shows to be deleted. Maximum: 50 IDs.
+ * @return A {@link RemoveAlbumsForCurrentUserRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public RemoveUsersSavedShowsRequest.Builder removeUsersSavedShows(JsonArray ids) {
+ return new RemoveUsersSavedShowsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .ids(ids);
+ }
+
+ /**
+ * Remove a track if saved to the user's "Your Music" library.
+ *
+ * @param ids The track IDs to remove from the user's Your Music library. Maximum: 50 IDs.
+ * @return A {@link RemoveUsersSavedTracksRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public RemoveUsersSavedTracksRequest.Builder removeUsersSavedTracks(String... ids) {
+ return new RemoveUsersSavedTracksRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .ids(concat(ids, ','));
+ }
+
+ /**
+ * Remove a track if saved to the user's "Your Music" library.
+ *
+ * @param ids The track IDs to remove from the user's Your Music library. Maximum: 50 IDs.
+ * @return A {@link RemoveUsersSavedTracksRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public RemoveUsersSavedTracksRequest.Builder removeUsersSavedTracks(JsonArray ids) {
+ return new RemoveUsersSavedTracksRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .ids(ids);
+ }
+
+ /**
+ * Save albums in the user's "Your Music" library.
+ *
+ * @param ids The album IDs to add to the user's library. Maximum: 50 IDs.
+ * @return A {@link SaveAlbumsForCurrentUserRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public SaveAlbumsForCurrentUserRequest.Builder saveAlbumsForCurrentUser(String... ids) {
+ return new SaveAlbumsForCurrentUserRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .ids(concat(ids, ','));
+ }
+
+ /**
+ * Save albums in the user's "Your Music" library.
+ *
+ * @param ids The album IDs to add to the user's library. Maximum: 50 IDs.
+ * @return A {@link SaveAlbumsForCurrentUserRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public SaveAlbumsForCurrentUserRequest.Builder saveAlbumsForCurrentUser(JsonArray ids) {
+ return new SaveAlbumsForCurrentUserRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .ids(ids);
+ }
+
+ /**
+ * Save one or more shows to current Spotify user’s library.
+ *
+ * @param ids The show IDs to add to the users library. Maximum: 50 IDs.
+ * @return A {@link SaveShowsForCurrentUserRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public SaveShowsForCurrentUserRequest.Builder saveShowsForCurrentUser(String... ids) {
+ return new SaveShowsForCurrentUserRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .ids(concat(ids, ','));
+ }
+
+ /**
+ * Save one or more shows to current Spotify user’s library.
+ *
+ * @param ids The show IDs to add to the users library. Maximum: 50 IDs.
+ * @return A {@link SaveShowsForCurrentUserRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public SaveShowsForCurrentUserRequest.Builder saveShowsForCurrentUser(JsonArray ids) {
+ return new SaveShowsForCurrentUserRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .ids(ids);
+ }
+
+ /**
+ * Save tracks in the user's "Your Music" library.
+ *
+ * @param ids The track IDs to add to the user's library. Maximum: 50 IDs.
+ * @return A {@link SaveTracksForUserRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public SaveTracksForUserRequest.Builder saveTracksForUser(String... ids) {
+ return new SaveTracksForUserRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .ids(concat(ids, ','));
+ }
+
+ /**
+ * Save tracks in the user's "Your Music" library.
+ *
+ * @param ids The track IDs to add to the user's library. Maximum: 50 IDs.
+ * @return A {@link SaveTracksForUserRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public SaveTracksForUserRequest.Builder saveTracksForUser(JsonArray ids) {
+ return new SaveTracksForUserRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .ids(ids);
+ }
+
+ /**
+ * Get the current user's top artists or tracks based on calculated affinity. <br><br>
+ * <p>
+ * Affinity is a measure of the expected preference an user has for a particular track or artist. It is based on user
+ * behavior, including play history, but does not include actions made while in incognito mode. Light or infrequent
+ * users of Spotify may not have sufficient play history to generate a full affinity data set.
+ *
+ * @param type The type of entity to return. Valid values: artists or tracks.
+ * @param <T> Either {@link com.wrapper.spotify.model_objects.specification.Artist} or
+ * {@link com.wrapper.spotify.model_objects.specification.Track}
+ * @return A {@link GetUsersTopArtistsAndTracksRequest.Builder}.
+ */
+ public <T extends IArtistTrackModelObject> GetUsersTopArtistsAndTracksRequest.Builder<T> getUsersTopArtistsAndTracks(ModelObjectType type) {
+ return new GetUsersTopArtistsAndTracksRequest.Builder<T>(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .type(type);
+ }
+
+ /**
+ * Get the current user's top artists based on calculated affinity.
+ *
+ * @return A {@link GetUsersTopArtistsRequest.Builder}.
+ * @see #getUsersTopArtistsAndTracks(ModelObjectType)
+ */
+ public GetUsersTopArtistsRequest.Builder getUsersTopArtists() {
+ return new GetUsersTopArtistsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port);
+ }
+
+ /**
+ * Get the current user's top tracks based on calculated affinity.
+ *
+ * @return A {@link GetUsersTopTracksRequest.Builder}.
+ * @see #getUsersTopArtistsAndTracks(ModelObjectType)
+ */
+ public GetUsersTopTracksRequest.Builder getUsersTopTracks() {
+ return new GetUsersTopTracksRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port);
+ }
+
+ /**
+ * Get information about the user's current playback state, including context, track progress, and active device.
+ *
+ * @return A {@link GetInformationAboutUsersCurrentPlaybackRequest.Builder}.
+ */
+ public GetInformationAboutUsersCurrentPlaybackRequest.Builder getInformationAboutUsersCurrentPlayback() {
+ return new GetInformationAboutUsersCurrentPlaybackRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port);
+ }
+
+ /**
+ * Get tracks from the current user's recently played tracks. <br><br>
+ * <p>
+ * Returns the most recent 50 tracks played by an user. Note that a track currently playing will not be visible in play
+ * history until it has completed. A track must be played for more than 30 seconds to be included in play history.
+ * <p>
+ * Any tracks listened to while the user had "Private Session" enabled in their client will not be returned in the
+ * list of recently played tracks.
+ *
+ * @return A {@link GetCurrentUsersRecentlyPlayedTracksRequest.Builder}.
+ */
+ public GetCurrentUsersRecentlyPlayedTracksRequest.Builder getCurrentUsersRecentlyPlayedTracks() {
+ return new GetCurrentUsersRecentlyPlayedTracksRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port);
+ }
+
+ /**
+ * Get information about an user's available devices.
+ *
+ * @return A {@link GetUsersAvailableDevicesRequest.Builder}.
+ */
+ public GetUsersAvailableDevicesRequest.Builder getUsersAvailableDevices() {
+ return new GetUsersAvailableDevicesRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port);
+ }
+
+ /**
+ * Get the object currently being played on the user's Spotify account.
+ *
+ * @return A {@link GetUsersCurrentlyPlayingTrackRequest.Builder}.
+ */
+ public GetUsersCurrentlyPlayingTrackRequest.Builder getUsersCurrentlyPlayingTrack() {
+ return new GetUsersCurrentlyPlayingTrackRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port);
+ }
+
+ /**
+ * Pause playback on the user's account.
+ *
+ * @return A {@link PauseUsersPlaybackRequest.Builder}.
+ */
+ public PauseUsersPlaybackRequest.Builder pauseUsersPlayback() {
+ return new PauseUsersPlaybackRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port);
+ }
+
+ /**
+ * Seeks to the given position in the user's currently playing track.
+ *
+ * @param position_ms The position in milliseconds to seek to. Must be a positive number. Passing in a position that
+ * is greater than the length of the track will cause the player to start playing the next song.
+ * @return A {@link SeekToPositionInCurrentlyPlayingTrackRequest.Builder}.
+ */
+ public SeekToPositionInCurrentlyPlayingTrackRequest.Builder seekToPositionInCurrentlyPlayingTrack(int position_ms) {
+ return new SeekToPositionInCurrentlyPlayingTrackRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .position_ms(position_ms);
+ }
+
+ /**
+ * Set the repeat mode for the user's playback. Options are repeat-track, repeat-context, and off.
+ *
+ * @param state track, context or off. track will repeat the current track. context will repeat the current
+ * context. off will turn repeat off.
+ * @return A {@link SetRepeatModeOnUsersPlaybackRequest.Builder}.
+ */
+ public SetRepeatModeOnUsersPlaybackRequest.Builder setRepeatModeOnUsersPlayback(String state) {
+ return new SetRepeatModeOnUsersPlaybackRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .state(state);
+ }
+
+ /**
+ * Set the volume for the user's current playback device.
+ *
+ * @param volume_percent Integer. The volume to set. Must be a value from 0 to 100 inclusive.
+ * @return A {@link SetVolumeForUsersPlaybackRequest.Builder}.
+ */
+ public SetVolumeForUsersPlaybackRequest.Builder setVolumeForUsersPlayback(int volume_percent) {
+ return new SetVolumeForUsersPlaybackRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .volume_percent(volume_percent);
+ }
+
+ /**
+ * Skips to next track in the user's queue.
+ *
+ * @return A {@link SkipUsersPlaybackToNextTrackRequest.Builder}.
+ */
+ public SkipUsersPlaybackToNextTrackRequest.Builder skipUsersPlaybackToNextTrack() {
+ return new SkipUsersPlaybackToNextTrackRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port);
+ }
+
+ /**
+ * Skips to previous track in the user's queue.
+ * <p>
+ * <b>Note:</b> This will ALWAYS skip to the previous track, regardless of the current track’s progress. Returning to
+ * the start of the current track should be performed using the {@link #seekToPositionInCurrentlyPlayingTrack(int)}
+ * method.
+ *
+ * @return A {@link SkipUsersPlaybackToPreviousTrackRequest.Builder}.
+ */
+ public SkipUsersPlaybackToPreviousTrackRequest.Builder skipUsersPlaybackToPreviousTrack() {
+ return new SkipUsersPlaybackToPreviousTrackRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port);
+ }
+
+ /**
+ * Start a new context or resume current playback on the user's active device.
+ *
+ * @return A {@link StartResumeUsersPlaybackRequest.Builder}.
+ */
+ public StartResumeUsersPlaybackRequest.Builder startResumeUsersPlayback() {
+ return new StartResumeUsersPlaybackRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port);
+ }
+
+ /**
+ * Toggle shuffle on or off for user's playback.
+ *
+ * @param state true: Shuffle user's playback. false: Do not shuffle user's playback.
+ * @return A {@link ToggleShuffleForUsersPlaybackRequest.Builder}.
+ */
+ public ToggleShuffleForUsersPlaybackRequest.Builder toggleShuffleForUsersPlayback(boolean state) {
+ return new ToggleShuffleForUsersPlaybackRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .state(state);
+ }
+
+ /**
+ * Transfer playback to a new device and determine if it should start playing.
+ *
+ * @param device_ids A JSON array containing the ID of the device on which playback should be started/transferred.
+ * <br><b>Note:</b> Although an array is accepted, only a single device_id is currently supported.
+ * @return A {@link TransferUsersPlaybackRequest.Builder}.
+ */
+ public TransferUsersPlaybackRequest.Builder transferUsersPlayback(JsonArray device_ids) {
+ return new TransferUsersPlaybackRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .device_ids(device_ids);
+ }
+
+ /**
+ * Add a track or an episode to the end of the user's current playback queue.
+ *
+ * @param uri The uri of the item to add to the queue. Must be a track or an episode uri.
+ * @return A {@link AddItemToUsersPlaybackQueueRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public AddItemToUsersPlaybackQueueRequest.Builder addItemToUsersPlaybackQueue(String uri) {
+ return new AddItemToUsersPlaybackQueueRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .uri(uri);
+ }
+
+ /**
+ * Add items to a playlist.
+ *
+ * @param user_id The owners username.
+ * @param playlist_id The playlists ID.
+ * @param uris URIs of the tracks or episodes to add. Maximum: 100 item URIs.
+ * @return An {@link AddItemsToPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ * @deprecated Playlist IDs are unique for themselves. This parameter is thus no longer used.
+ * (https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/)
+ */
+ @Deprecated
+ public AddItemsToPlaylistRequest.Builder addItemsToPlaylist(String user_id, String playlist_id, String[] uris) {
+ return new AddItemsToPlaylistRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .user_id(user_id)
+ .playlist_id(playlist_id)
+ .uris(concat(uris, ','));
+ }
+
+ /**
+ * Add items to a playlist.
+ * <p>
+ * <b>Note:</b> If you want to add a large number of items (&gt;50), use {@link #addItemsToPlaylist(String, JsonArray)} to not exceed
+ * the maximum URI length.
+ * @param playlist_id The playlists ID.
+ * @param uris URIs of the tracks or episodes to add. Maximum: 100 item URIs.
+ * @return An {@link AddItemsToPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public AddItemsToPlaylistRequest.Builder addItemsToPlaylist(String playlist_id, String[] uris) {
+ return new AddItemsToPlaylistRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .playlist_id(playlist_id)
+ .uris(concat(uris, ','));
+ }
+
+ /**
+ * Add items to a playlist.
+ *
+ * @param user_id The owners username.
+ * @param playlist_id The playlists ID.
+ * @param uris URIs of the tracks or episodes to add. Maximum: 100 item URIs.
+ * @return An {@link AddItemsToPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ * @deprecated Playlist IDs are unique for themselves. This parameter is thus no longer used.
+ * (https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/)
+ */
+ @Deprecated
+ public AddItemsToPlaylistRequest.Builder addItemsToPlaylist(String user_id, String playlist_id, JsonArray uris) {
+ return new AddItemsToPlaylistRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .user_id(user_id)
+ .playlist_id(playlist_id)
+ .uris(uris);
+ }
+
+ /**
+ * Add items to a playlist.
+ *
+ * @param playlist_id The playlists ID.
+ * @param uris URIs of the tracks or episodes to add. Maximum: 100 item URIs.
+ * @return An {@link AddItemsToPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public AddItemsToPlaylistRequest.Builder addItemsToPlaylist(String playlist_id, JsonArray uris) {
+ return new AddItemsToPlaylistRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .playlist_id(playlist_id)
+ .uris(uris);
+ }
+
+ /**
+ * Update a playlists properties.
+ *
+ * @param user_id The owners username.
+ * @param playlist_id The playlists ID.
+ * @return A {@link ChangePlaylistsDetailsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ * @deprecated Playlist IDs are unique for themselves. This parameter is thus no longer used.
+ * (https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/)
+ */
+ @Deprecated
+ public ChangePlaylistsDetailsRequest.Builder changePlaylistsDetails(String user_id, String playlist_id) {
+ return new ChangePlaylistsDetailsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .user_id(user_id)
+ .playlist_id(playlist_id);
+ }
+
+ /**
+ * Update a playlists properties.
+ *
+ * @param playlist_id The playlists ID.
+ * @return A {@link ChangePlaylistsDetailsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public ChangePlaylistsDetailsRequest.Builder changePlaylistsDetails(String playlist_id) {
+ return new ChangePlaylistsDetailsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .playlist_id(playlist_id);
+ }
+
+ /**
+ * Create a playlist.
+ *
+ * @param user_id The playlists owner.
+ * @param name The name of the playlist.
+ * @return A {@link CreatePlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public CreatePlaylistRequest.Builder createPlaylist(String user_id, String name) {
+ return new CreatePlaylistRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .user_id(user_id)
+ .name(name);
+ }
+
+ /**
+ * Get a list of the playlists owned or followed by the current Spotify user.
+ *
+ * @return A {@link GetListOfCurrentUsersPlaylistsRequest.Builder}.
+ */
+ public GetListOfCurrentUsersPlaylistsRequest.Builder getListOfCurrentUsersPlaylists() {
+ return new GetListOfCurrentUsersPlaylistsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port);
+ }
+
+ /**
+ * Get an user's playlists.
+ *
+ * @param user_id A Spotify ID of the user.
+ * @return A {@link GetListOfUsersPlaylistsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetListOfUsersPlaylistsRequest.Builder getListOfUsersPlaylists(String user_id) {
+ return new GetListOfUsersPlaylistsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .user_id(user_id);
+ }
+
+ /**
+ * Get a playlist.
+ *
+ * @param user_id The playlists owners username.
+ * @param playlist_id The playlists ID.
+ * @return A {@link GetPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ * @deprecated Playlist IDs are unique for themselves. This parameter is thus no longer used.
+ * (https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/)
+ */
+ @Deprecated
+ public GetPlaylistRequest.Builder getPlaylist(String user_id, String playlist_id) {
+ return new GetPlaylistRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .user_id(user_id)
+ .playlist_id(playlist_id);
+ }
+
+ /**
+ * Get a playlist.
+ *
+ * @param playlist_id The playlists ID.
+ * @return A {@link GetPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetPlaylistRequest.Builder getPlaylist(String playlist_id) {
+ return new GetPlaylistRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .playlist_id(playlist_id);
+ }
+
+ /**
+ * Get the image used to represent a specific playlist.
+ *
+ * @param user_id The user's Spotify user ID.
+ * @param playlist_id The Spotify ID for the playlist.
+ * @return A {@link GetPlaylistCoverImageRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ * @deprecated Playlist IDs are unique for themselves. This parameter is thus no longer used.
+ * (https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/)
+ */
+ @Deprecated
+ public GetPlaylistCoverImageRequest.Builder getPlaylistCoverImage(String user_id, String playlist_id) {
+ return new GetPlaylistCoverImageRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .user_id(user_id)
+ .playlist_id(playlist_id);
+ }
+
+ /**
+ * Get the image used to represent a specific playlist.
+ *
+ * @param playlist_id The Spotify ID for the playlist.
+ * @return A {@link GetPlaylistCoverImageRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetPlaylistCoverImageRequest.Builder getPlaylistCoverImage(String playlist_id) {
+ return new GetPlaylistCoverImageRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .playlist_id(playlist_id);
+ }
+
+ /**
+ * Get a playlists items.
+ *
+ * @param user_id The playlists owners username.
+ * @param playlist_id The playlists ID.
+ * @return A {@link GetPlaylistsItemsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ * @deprecated Playlist IDs are unique for themselves. This parameter is thus no longer used.
+ * (https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/)
+ */
+ @Deprecated
+ public GetPlaylistsItemsRequest.Builder getPlaylistsItems(String user_id, String playlist_id) {
+ return new GetPlaylistsItemsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .user_id(user_id)
+ .playlist_id(playlist_id);
+ }
+
+ /**
+ * Get a playlist's items.
+ *
+ * @param playlist_id The playlists ID.
+ * @return A {@link GetPlaylistsItemsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetPlaylistsItemsRequest.Builder getPlaylistsItems(String playlist_id) {
+ return new GetPlaylistsItemsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .playlist_id(playlist_id);
+ }
+
+ /**
+ * Delete items from a playlist
+ *
+ * @param user_id The owners username.
+ * @param playlist_id The playlists ID.
+ * @param tracks URIs of the items to remove. Maximum: 100 track or episode URIs.
+ * @return A {@link RemoveItemsFromPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ * @deprecated Playlist IDs are unique for themselves. This parameter is thus no longer used.
+ * (https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/)
+ */
+ @Deprecated
+ public RemoveItemsFromPlaylistRequest.Builder removeItemsFromPlaylist(
+ String user_id, String playlist_id, JsonArray tracks) {
+ return new RemoveItemsFromPlaylistRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .user_id(user_id)
+ .playlist_id(playlist_id)
+ .tracks(tracks);
+ }
+
+ /**
+ * Delete items from a playlist
+ *
+ * @param playlist_id The playlists ID.
+ * @param tracks URIs of the items to remove. Maximum: 100 track or episode URIs.
+ * @return A {@link RemoveItemsFromPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public RemoveItemsFromPlaylistRequest.Builder removeItemsFromPlaylist(
+ String playlist_id, JsonArray tracks) {
+ return new RemoveItemsFromPlaylistRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .playlist_id(playlist_id)
+ .tracks(tracks);
+ }
+
+ /**
+ * Reorder a item or a group of items in a playlist. <br><br>
+ * <p>
+ * When reordering items, the timestamp indicating when they were added and the user who added them will be kept
+ * untouched. In addition, the users following the playlists won’t be notified about changes in the playlists when the
+ * items are reordered.
+ *
+ * @param user_id The user's Spotify user ID.
+ * @param playlist_id The Spotify ID for the playlist.
+ * @param range_start The position of the first item to be reordered.
+ * @param insert_before The position where the items should be inserted. To reorder the items to the end of the
+ * playlist, simply set insert_before to the position after the last item.
+ * @return A {@link ReorderPlaylistsItemsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ * @deprecated Playlist IDs are unique for themselves. This parameter is thus no longer used.
+ * (https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/)
+ */
+ @Deprecated
+ public ReorderPlaylistsItemsRequest.Builder reorderPlaylistsItems(String user_id, String playlist_id, int range_start, int insert_before) {
+ return new ReorderPlaylistsItemsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .user_id(user_id)
+ .playlist_id(playlist_id)
+ .range_start(range_start)
+ .insert_before(insert_before);
+ }
+
+ /**
+ * Reorder an item or a group of items in a playlist. <br><br>
+ * <p>
+ * When reordering items, the timestamp indicating when they were added and the user who added them will be kept
+ * untouched. In addition, the users following the playlists won’t be notified about changes in the playlists when the
+ * items are reordered.
+ *
+ * @param playlist_id The Spotify ID for the playlist.
+ * @param range_start The position of the first item to be reordered.
+ * @param insert_before The position where the items should be inserted. To reorder the items to the end of the
+ * playlist, simply set insert_before to the position after the last item.
+ * @return A {@link ReorderPlaylistsItemsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public ReorderPlaylistsItemsRequest.Builder reorderPlaylistsItems(String playlist_id, int range_start, int insert_before) {
+ return new ReorderPlaylistsItemsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .playlist_id(playlist_id)
+ .range_start(range_start)
+ .insert_before(insert_before);
+ }
+
+ /**
+ * Replace items in a playlist.
+ *
+ * @param user_id The owners username.
+ * @param playlist_id The playlists ID.
+ * @param uris URIs of the items to add. Maximum: 100 track or episode URIs.
+ * @return A {@link ReplacePlaylistsItemsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ * @deprecated Playlist IDs are unique for themselves. This parameter is thus no longer used.
+ * (https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/)
+ */
+ @Deprecated
+ public ReplacePlaylistsItemsRequest.Builder replacePlaylistsItems(String user_id, String playlist_id, String[] uris) {
+ return new ReplacePlaylistsItemsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .user_id(user_id)
+ .playlist_id(playlist_id)
+ .uris(concat(uris, ','));
+ }
+
+ /**
+ * Replace items in a playlist.
+ *
+ * @param playlist_id The playlists ID.
+ * @param uris URIs of the items to set. Maximum: 100 track or episode URIs.
+ * @return A {@link ReplacePlaylistsItemsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public ReplacePlaylistsItemsRequest.Builder replacePlaylistsItems(String playlist_id, String[] uris) {
+ return new ReplacePlaylistsItemsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .playlist_id(playlist_id)
+ .uris(concat(uris, ','));
+ }
+
+ /**
+ * Replace items in a playlist.
+ *
+ * @param user_id The owners username.
+ * @param playlist_id The playlists ID.
+ * @param uris URIs of the items to add. Maximum: 100 track or episode URIs.
+ * @return A {@link ReplacePlaylistsItemsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ * @deprecated Playlist IDs are unique for themselves. This parameter is thus no longer used.
+ * (https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/)
+ */
+ @Deprecated
+ public ReplacePlaylistsItemsRequest.Builder replacePlaylistsItems(String user_id, String playlist_id, JsonArray uris) {
+ return new ReplacePlaylistsItemsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .user_id(user_id)
+ .playlist_id(playlist_id)
+ .uris(uris);
+ }
+
+ /**
+ * Replace items in a playlist.
+ *
+ * @param playlist_id The playlists ID.
+ * @param uris URIs of the items to add. Maximum: 100 track or episode URIs.
+ * @return A {@link ReplacePlaylistsItemsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public ReplacePlaylistsItemsRequest.Builder replacePlaylistsItems(String playlist_id, JsonArray uris) {
+ return new ReplacePlaylistsItemsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .playlist_id(playlist_id)
+ .uris(uris);
+ }
+
+ /**
+ * Replace the image used to represent a specific playlist.
+ *
+ * @param user_id The user's Spotify user ID.
+ * @param playlist_id The Spotify ID for the playlist.
+ * @return An {@link UploadCustomPlaylistCoverImageRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ * @deprecated Playlist IDs are unique for themselves. This parameter is thus no longer used.
+ * (https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/)
+ */
+ @Deprecated
+ public UploadCustomPlaylistCoverImageRequest.Builder uploadCustomPlaylistCoverImage(String user_id, String playlist_id) {
+ return new UploadCustomPlaylistCoverImageRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .user_id(user_id)
+ .playlist_id(playlist_id);
+ }
+
+ /**
+ * Replace the image used to represent a specific playlist.
+ *
+ * @param playlist_id The Spotify ID for the playlist.
+ * @return An {@link UploadCustomPlaylistCoverImageRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public UploadCustomPlaylistCoverImageRequest.Builder uploadCustomPlaylistCoverImage(String playlist_id) {
+ return new UploadCustomPlaylistCoverImageRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .playlist_id(playlist_id);
+ }
+
+ /**
+ * Get Spotify catalog information about artists, albums, episodes, shows, tracks or playlists that match a keyword string.
+ *
+ * @param q The search query's keywords (and optional field filters and operators).
+ * @param type A comma-separated list of item types to search across. Valid types are: album, artist, episode, show, playlist and
+ * track.
+ * @return A {@link SearchItemRequest.Builder}.
+ */
+ public SearchItemRequest.Builder searchItem(String q, String type) {
+ return new SearchItemRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .q(q)
+ .type(type);
+ }
+
+ /**
+ * Get Spotify catalog information about albums that match a keyword string.
+ *
+ * @param q The search query's keywords (and optional field filters and operators).
+ * @return A {@link SearchAlbumsRequest.Builder}.
+ */
+ public SearchAlbumsRequest.Builder searchAlbums(String q) {
+ return new SearchAlbumsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .q(q);
+ }
+
+ /**
+ * Get Spotify catalog information about albums that match a keyword string.
+ * <p>
+ * This method exists because the searches API returns the undocumented property {@code totalTracks}, which is
+ * included by this method's return type.
+ *
+ * @param q The search query's keywords (and optional field filters and operators).
+ * @return A {@link SearchAlbumsSpecialRequest.Builder}.
+ */
+ public SearchAlbumsSpecialRequest.Builder searchAlbumsSpecial(String q) {
+ return new SearchAlbumsSpecialRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .q(q);
+ }
+
+ /**
+ * Get Spotify catalog information about artists that match a keyword string.
+ *
+ * @param q The search query's keywords (and optional field filters and operators).
+ * @return A {@link SearchArtistsRequest.Builder}.
+ */
+ public SearchArtistsRequest.Builder searchArtists(String q) {
+ return new SearchArtistsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .q(q);
+ }
+
+ /**
+ * Get Spotify catalog information about episodes that match a keyword string.
+ *
+ * @param q The search query's keywords (and optional field filters and operators).
+ * @return A {@link SearchEpisodesRequest.Builder}.
+ */
+ public SearchEpisodesRequest.Builder searchEpisodes(String q) {
+ return new SearchEpisodesRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .q(q);
+ }
+
+ /**
+ * Get Spotify catalog information about playlists that match a keyword string.
+ *
+ * @param q The search query's keywords (and optional field filters and operators).
+ * @return A {@link SearchPlaylistsRequest.Builder}.
+ */
+ public SearchPlaylistsRequest.Builder searchPlaylists(String q) {
+ return new SearchPlaylistsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .q(q);
+ }
+
+ /**
+ * Get Spotify catalog information about shows that match a keyword string.
+ *
+ * @param q The search query's keywords (and optional field filters and operators).
+ * @return A {@link SearchShowsRequest.Builder}.
+ */
+ public SearchShowsRequest.Builder searchShows(String q) {
+ return new SearchShowsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .q(q);
+ }
+
+ /**
+ * Get Spotify catalog information about tracks that match a keyword string.
+ *
+ * @param q The search query's keywords (and optional field filters and operators).
+ * @return A {@link SearchTracksRequest.Builder}.
+ */
+ public SearchTracksRequest.Builder searchTracks(String q) {
+ return new SearchTracksRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .q(q);
+ }
+
+ /**
+ * Get a show.
+ *
+ * @param id The Spotify ID of the show.
+ * @return A {@link GetShowRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetShowRequest.Builder getShow(String id) {
+ return new GetShowRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .id(id);
+ }
+
+ /**
+ * Get multiple shows.
+ *
+ * @param ids The Spotify IDs of all shows you're trying to retrieve. Maximum: 50 IDs.
+ * @return A {@link GetSeveralShowsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetSeveralShowsRequest.Builder getSeveralShows(String... ids) {
+ return new GetSeveralShowsRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .ids(concat(ids, ','));
+ }
+
+ /**
+ * Get Spotify catalog information about an show’s episodes.
+ *
+ * @param id The Spotify ID of the show.
+ * @return A {@link GetShowsEpisodesRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetShowsEpisodesRequest.Builder getShowEpisodes(String id) {
+ return new GetShowsEpisodesRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .id(id);
+ }
+
+ /**
+ * Get a detailed audio analysis for a single track identified by its unique Spotify ID.
+ *
+ * @param id The Spotify ID for the track.
+ * @return A {@link GetAudioAnalysisForTrackRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetAudioAnalysisForTrackRequest.Builder getAudioAnalysisForTrack(String id) {
+ return new GetAudioAnalysisForTrackRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .id(id);
+ }
+
+ /**
+ * Get audio features for a track based on its Spotify ID.
+ *
+ * @param id The Spotify ID of the track.
+ * @return A {@link GetAudioFeaturesForTrackRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetAudioFeaturesForTrackRequest.Builder getAudioFeaturesForTrack(String id) {
+ return new GetAudioFeaturesForTrackRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .id(id);
+ }
+
+ /**
+ * Get audio features for multiple tracks based on their Spotify IDs.
+ *
+ * @param ids A comma-separated list of the Spotify IDs for the tracks. Maximum: 100 IDs.
+ * @return A {@link GetAudioFeaturesForSeveralTracksRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetAudioFeaturesForSeveralTracksRequest.Builder getAudioFeaturesForSeveralTracks(String... ids) {
+ return new GetAudioFeaturesForSeveralTracksRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .ids(concat(ids, ','));
+ }
+
+ /**
+ * Get multiple tracks.
+ *
+ * @param ids The Spotify IDs of all tracks you're trying to retrieve. Maximum: 50 IDs.
+ * @return A {@link GetSeveralTracksRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetSeveralTracksRequest.Builder getSeveralTracks(String... ids) {
+ return new GetSeveralTracksRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .ids(concat(ids, ','));
+ }
+
+ /**
+ * Get a track.
+ *
+ * @param id The Spotify ID of the track.
+ * @return A {@link GetTrackRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetTrackRequest.Builder getTrack(String id) {
+ return new GetTrackRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .id(id);
+ }
+
+ /**
+ * Get detailed profile information about the current user (including the current user’s username).
+ *
+ * @return A {@link GetCurrentUsersProfileRequest.Builder}.
+ */
+ public GetCurrentUsersProfileRequest.Builder getCurrentUsersProfile() {
+ return new GetCurrentUsersProfileRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port);
+ }
+
+ /**
+ * Get public profile information about a Spotify user.
+ *
+ * @param user_id The Spotify ID of the user.
+ * @return A {@link GetUsersProfileRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URLs &amp; IDs</a>
+ */
+ public GetUsersProfileRequest.Builder getUsersProfile(String user_id) {
+ return new GetUsersProfileRequest.Builder(accessToken)
+ .setDefaults(httpManager, scheme, host, port)
+ .user_id(user_id);
+ }
+
+ /**
+ * Builder class for building {@link SpotifyApi} instances.
+ */
+ public static class Builder {
+
+ private IHttpManager httpManager = DEFAULT_HTTP_MANAGER;
+ private String scheme = DEFAULT_SCHEME;
+ private String host = DEFAULT_HOST;
+ private Integer port = DEFAULT_PORT;
+ private String proxyUrl;
+ private Integer proxyPort;
+ private Integer proxyUsername;
+ private Integer proxyPassword;
+ private String clientId;
+ private String clientSecret;
+ private URI redirectUri;
+ private String accessToken;
+ private String refreshToken;
+
+ /**
+ * The HttpManager setter.
+ *
+ * @param httpManager A Spotify HttpManager.
+ * @return A {@link Builder}.
+ */
+ public Builder setHttpManager(IHttpManager httpManager) {
+ this.httpManager = httpManager;
+ return this;
+ }
+
+ /**
+ * The scheme setter.
+ *
+ * @param scheme A HTTP-scheme.
+ * @return A {@link Builder}.
+ */
+ public Builder setScheme(String scheme) {
+ this.scheme = scheme;
+ return this;
+ }
+
+ /**
+ * The Spotify API host setter.
+ *
+ * @param host A Spotify API host.
+ * @return A {@link Builder}.
+ */
+ public Builder setHost(String host) {
+ this.host = host;
+ return this;
+ }
+
+ /**
+ * The Spotify API port setter.
+ *
+ * @param port A Spotify API port.
+ * @return A {@link Builder}.
+ */
+ public Builder setPort(Integer port) {
+ this.port = port;
+ return this;
+ }
+
+ /**
+ * The proxy URL setter.
+ *
+ * @param proxyUrl A proxy URL.
+ * @return A {@link Builder}.
+ */
+ public Builder setProxyUrl(String proxyUrl) {
+ this.proxyUrl = proxyUrl;
+ return this;
+ }
+
+ /**
+ * The proxy port setter.
+ *
+ * @param proxyPort A proxy port.
+ * @return A {@link Builder}.
+ */
+ public Builder setProxyPort(Integer proxyPort) {
+ this.proxyPort = proxyPort;
+ return this;
+ }
+
+ /**
+ * The proxy username setter.
+ *
+ * @param proxyUsername A proxy username.
+ * @return A {@link Builder}.
+ */
+ public Builder setProxyUsername(Integer proxyUsername) {
+ this.proxyUsername = proxyUsername;
+ return this;
+ }
+
+ /**
+ * The proxy password setter.
+ *
+ * @param proxyPassword A proxy password.
+ * @return A {@link Builder}.
+ */
+ public Builder setProxyPassword(Integer proxyPassword) {
+ this.proxyPassword = proxyPassword;
+ return this;
+ }
+
+ /**
+ * The client ID setter.
+ *
+ * @param clientId A client ID of your application.
+ * @return A {@link Builder}.
+ */
+ public Builder setClientId(String clientId) {
+ this.clientId = clientId;
+ return this;
+ }
+
+ /**
+ * The client secret setter.
+ *
+ * @param clientSecret A client secret of your application.
+ * @return A {@link Builder}.
+ */
+ public Builder setClientSecret(String clientSecret) {
+ this.clientSecret = clientSecret;
+ return this;
+ }
+
+ /**
+ * The redirect URI setter.
+ *
+ * @param redirectUri A redirect URI of your application.
+ * @return A {@link Builder}.
+ */
+ public Builder setRedirectUri(URI redirectUri) {
+ this.redirectUri = redirectUri;
+ return this;
+ }
+
+ /**
+ * The access token setter.
+ *
+ * @param accessToken A Spotify API access token.
+ * @return A {@link Builder}.
+ */
+ public Builder setAccessToken(String accessToken) {
+ this.accessToken = accessToken;
+ return this;
+ }
+
+ /**
+ * The refresh token setter.
+ *
+ * @param refreshToken A Spotify API refresh token.
+ * @return A {@link Builder}.
+ */
+ public Builder setRefreshToken(String refreshToken) {
+ this.refreshToken = refreshToken;
+ return this;
+ }
+
+ /**
+ * Build a {@link SpotifyApi} instance with the information given to the builder.
+ *
+ * @return A {@link SpotifyApi} instance.
+ */
+ public SpotifyApi build() {
+ return new SpotifyApi(this);
+ }
+ }
+}
+
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/SpotifyApiThreading.java
@@ -0,0 +1,27 @@
+package com.wrapper.spotify;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class SpotifyApiThreading {
+
+ public static final ExecutorService THREADPOOL = Executors.newCachedThreadPool();
+
+ public static <T> CompletableFuture<T> executeAsync(final Callable<T> callable) {
+ CompletableFuture<T> future = new CompletableFuture<>();
+
+ SpotifyApiThreading.THREADPOOL.execute(() -> {
+ try {
+ future.complete(callable.call());
+ } catch (Exception e) {
+ future.completeExceptionally(e);
+ }
+ });
+
+ return future;
+ }
+
+}
+
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/SpotifyHttpManager.java
@@ -0,0 +1,395 @@
+package com.wrapper.spotify;
+
+import com.google.gson.*;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.exceptions.detailed.*;
+import org.apache.hc.client5.http.auth.AuthScope;
+import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
+import org.apache.hc.client5.http.cache.CacheResponseStatus;
+import org.apache.hc.client5.http.cache.HttpCacheContext;
+import org.apache.hc.client5.http.classic.methods.HttpDelete;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.classic.methods.HttpPut;
+import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.client5.http.cookie.StandardCookieSpec;
+import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
+import org.apache.hc.client5.http.impl.cache.CacheConfig;
+import org.apache.hc.client5.http.impl.cache.CachingHttpClients;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.core5.http.*;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.util.Timeout;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.logging.Level;
+
+public class SpotifyHttpManager implements IHttpManager {
+
+ private static final int DEFAULT_CACHE_MAX_ENTRIES = 1000;
+ private static final int DEFAULT_CACHE_MAX_OBJECT_SIZE = 8192;
+ private static final Gson GSON = new Gson();
+ private final CloseableHttpClient httpClient;
+ private final CloseableHttpClient httpClientCaching;
+ private final HttpHost proxy;
+ private final UsernamePasswordCredentials proxyCredentials;
+ private final Integer cacheMaxEntries;
+ private final Integer cacheMaxObjectSize;
+ private final Integer connectionRequestTimeout;
+ private final Integer connectTimeout;
+ private final Integer socketTimeout;
+
+ /**
+ * Construct a new SpotifyHttpManager instance.
+ *
+ * @param builder The builder.
+ */
+ public SpotifyHttpManager(Builder builder) {
+ this.proxy = builder.proxy;
+ this.proxyCredentials = builder.proxyCredentials;
+ this.cacheMaxEntries = builder.cacheMaxEntries;
+ this.cacheMaxObjectSize = builder.cacheMaxObjectSize;
+ this.connectionRequestTimeout = builder.connectionRequestTimeout;
+ this.connectTimeout = builder.connectTimeout;
+ this.socketTimeout = builder.socketTimeout;
+
+ CacheConfig cacheConfig = CacheConfig.custom()
+ .setMaxCacheEntries(cacheMaxEntries != null ? cacheMaxEntries : DEFAULT_CACHE_MAX_ENTRIES)
+ .setMaxObjectSize(cacheMaxObjectSize != null ? cacheMaxObjectSize : DEFAULT_CACHE_MAX_OBJECT_SIZE)
+ .setSharedCache(false)
+ .build();
+
+ BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+
+ if (proxy != null) {
+ credentialsProvider.setCredentials(
+ new AuthScope(null, proxy.getHostName(), proxy.getPort(), null, proxy.getSchemeName()),
+ proxyCredentials
+ );
+ }
+
+ RequestConfig requestConfig = RequestConfig
+ .custom()
+ .setCookieSpec(StandardCookieSpec.STRICT)
+ .setProxy(proxy)
+ .setConnectionRequestTimeout(builder.connectionRequestTimeout != null
+ ? Timeout.ofMilliseconds(builder.connectionRequestTimeout)
+ : RequestConfig.DEFAULT.getConnectionRequestTimeout())
+ .setConnectTimeout(builder.connectTimeout != null
+ ? Timeout.ofMilliseconds(builder.connectTimeout)
+ : RequestConfig.DEFAULT.getConnectTimeout())
+ .setResponseTimeout(builder.socketTimeout != null
+ ? Timeout.ofMilliseconds(builder.socketTimeout)
+ : RequestConfig.DEFAULT.getResponseTimeout())
+ .build();
+
+ this.httpClient = HttpClients
+ .custom()
+ .setDefaultCredentialsProvider(credentialsProvider)
+ .setDefaultRequestConfig(requestConfig)
+ .disableContentCompression()
+ .build();
+
+ this.httpClientCaching = CachingHttpClients
+ .custom()
+ .setCacheConfig(cacheConfig)
+ .setDefaultCredentialsProvider(credentialsProvider)
+ .setDefaultRequestConfig(requestConfig)
+ .disableContentCompression()
+ .build();
+ }
+
+ public static URI makeUri(String uriString) {
+ try {
+ return new URI(uriString);
+ } catch (URISyntaxException e) {
+ SpotifyApi.LOGGER.log(
+ Level.SEVERE,
+ "URI Syntax Exception for \"" + uriString + "\"");
+ return null;
+ }
+ }
+
+ public HttpHost getProxy() {
+ return proxy;
+ }
+
+ public UsernamePasswordCredentials getProxyCredentials() {
+ return proxyCredentials;
+ }
+
+ public Integer getCacheMaxEntries() {
+ return cacheMaxEntries;
+ }
+
+ public Integer getCacheMaxObjectSize() {
+ return cacheMaxObjectSize;
+ }
+
+ public Integer getConnectionRequestTimeout() {
+ return connectionRequestTimeout;
+ }
+
+ public Integer getConnectTimeout() {
+ return connectTimeout;
+ }
+
+ public Integer getSocketTimeout() {
+ return socketTimeout;
+ }
+
+ @Override
+ public String get(URI uri, Header[] headers) throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ assert (uri != null);
+ assert (!uri.toString().equals(""));
+
+ final HttpGet httpGet = new HttpGet(uri);
+
+ httpGet.setHeaders(headers);
+ SpotifyApi.LOGGER.log(
+ Level.FINE,
+ "GET request uses these headers: " + GSON.toJson(headers));
+
+ String responseBody = getResponseBody(execute(httpClientCaching, httpGet));
+
+ httpGet.reset();
+
+ return responseBody;
+ }
+
+ @Override
+ public String post(URI uri, Header[] headers, HttpEntity body) throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ assert (uri != null);
+ assert (!uri.toString().equals(""));
+
+ final HttpPost httpPost = new HttpPost(uri);
+
+ httpPost.setHeaders(headers);
+ httpPost.setEntity(body);
+ SpotifyApi.LOGGER.log(
+ Level.FINE,
+ "POST request uses these headers: " + GSON.toJson(headers));
+
+ String responseBody = getResponseBody(execute(httpClient, httpPost));
+
+ httpPost.reset();
+
+ return responseBody;
+ }
+
+ @Override
+ public String put(URI uri, Header[] headers, HttpEntity body) throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ assert (uri != null);
+ assert (!uri.toString().equals(""));
+
+ final HttpPut httpPut = new HttpPut(uri);
+
+ httpPut.setHeaders(headers);
+ httpPut.setEntity(body);
+ SpotifyApi.LOGGER.log(
+ Level.FINE,
+ "PUT request uses these headers: " + GSON.toJson(headers));
+
+ String responseBody = getResponseBody(execute(httpClient, httpPut));
+
+ httpPut.reset();
+
+ return responseBody;
+ }
+
+ @Override
+ public String delete(URI uri, Header[] headers, HttpEntity body) throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ assert (uri != null);
+ assert (!uri.toString().equals(""));
+
+ final HttpDelete httpDelete = new HttpDelete(uri);
+
+ httpDelete.setHeaders(headers);
+ httpDelete.setEntity(body);
+ SpotifyApi.LOGGER.log(
+ Level.FINE,
+ "DELETE request uses these headers: " + GSON.toJson(headers));
+
+ String responseBody = getResponseBody(execute(httpClient, httpDelete));
+
+ httpDelete.reset();
+
+ return responseBody;
+ }
+
+ private CloseableHttpResponse execute(CloseableHttpClient httpClient, ClassicHttpRequest method) throws
+ IOException {
+ HttpCacheContext context = HttpCacheContext.create();
+ CloseableHttpResponse response = httpClient.execute(method, context);
+
+ try {
+ CacheResponseStatus responseStatus = context.getCacheResponseStatus();
+
+ if (responseStatus != null) {
+ switch (responseStatus) {
+ case CACHE_HIT:
+ SpotifyApi.LOGGER.log(
+ Level.CONFIG,
+ "A response was generated from the cache with no requests sent upstream");
+ break;
+ case CACHE_MODULE_RESPONSE:
+ SpotifyApi.LOGGER.log(
+ Level.CONFIG,
+ "The response was generated directly by the caching module");
+ break;
+ case CACHE_MISS:
+ SpotifyApi.LOGGER.log(
+ Level.CONFIG,
+ "The response came from an upstream server");
+ break;
+ case VALIDATED:
+ SpotifyApi.LOGGER.log(
+ Level.CONFIG,
+ "The response was generated from the cache after validating the entry with the origin server");
+ break;
+ case FAILURE:
+ SpotifyApi.LOGGER.log(
+ Level.CONFIG,
+ "The response came from an upstream server after a cache failure");
+ break;
+ }
+ }
+ } catch (Exception e) {
+ SpotifyApi.LOGGER.log(Level.SEVERE, e.getMessage());
+ }
+
+ return response;
+ }
+
+ private String getResponseBody(CloseableHttpResponse httpResponse) throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+
+ final String responseBody = httpResponse.getEntity() != null
+ ? EntityUtils.toString(httpResponse.getEntity(), "UTF-8")
+ : null;
+ String errorMessage = httpResponse.getReasonPhrase();
+
+ SpotifyApi.LOGGER.log(
+ Level.FINE,
+ "The http response has body " + responseBody);
+
+ if (responseBody != null && !responseBody.equals("")) {
+ try {
+ final JsonElement jsonElement = JsonParser.parseString(responseBody);
+
+ if (jsonElement.isJsonObject()) {
+ final JsonObject jsonObject = JsonParser.parseString(responseBody).getAsJsonObject();
+
+ if (jsonObject.has("error")) {
+ if (jsonObject.has("error_description")) {
+ errorMessage = jsonObject.get("error_description").getAsString();
+ } else if (jsonObject.get("error").isJsonObject() && jsonObject.getAsJsonObject("error").has("message")) {
+ errorMessage = jsonObject.getAsJsonObject("error").get("message").getAsString();
+ }
+ }
+ }
+ } catch (JsonSyntaxException e) {
+ // Not necessary
+ }
+ }
+
+ SpotifyApi.LOGGER.log(
+ Level.FINE,
+ "The http response has status code " + httpResponse.getCode());
+
+ switch (httpResponse.getCode()) {
+ case HttpStatus.SC_BAD_REQUEST:
+ throw new BadRequestException(errorMessage);
+ case HttpStatus.SC_UNAUTHORIZED:
+ throw new UnauthorizedException(errorMessage);
+ case HttpStatus.SC_FORBIDDEN:
+ throw new ForbiddenException(errorMessage);
+ case HttpStatus.SC_NOT_FOUND:
+ throw new NotFoundException(errorMessage);
+ case 429: // TOO_MANY_REQUESTS (additional status code, RFC 6585)
+ // Sets "Retry-After" header as described at https://beta.developer.spotify.com/documentation/web-api/#rate-limiting
+ Header header = httpResponse.getFirstHeader("Retry-After");
+
+ if (header != null) {
+ throw new TooManyRequestsException(errorMessage, Integer.parseInt(header.getValue()));
+ } else {
+ throw new TooManyRequestsException(errorMessage);
+ }
+ case HttpStatus.SC_INTERNAL_SERVER_ERROR:
+ throw new InternalServerErrorException(errorMessage);
+ case HttpStatus.SC_BAD_GATEWAY:
+ throw new BadGatewayException(errorMessage);
+ case HttpStatus.SC_SERVICE_UNAVAILABLE:
+ throw new ServiceUnavailableException(errorMessage);
+ default:
+ return responseBody;
+ }
+ }
+
+ public static class Builder {
+ private HttpHost proxy;
+ private UsernamePasswordCredentials proxyCredentials;
+ private Integer cacheMaxEntries;
+ private Integer cacheMaxObjectSize;
+ private Integer connectionRequestTimeout;
+ private Integer connectTimeout;
+ private Integer socketTimeout;
+
+ public Builder setProxy(HttpHost proxy) {
+ this.proxy = proxy;
+ return this;
+ }
+
+ public Builder setProxyCredentials(UsernamePasswordCredentials proxyCredentials) {
+ this.proxyCredentials = proxyCredentials;
+ return this;
+ }
+
+ public Builder setCacheMaxEntries(Integer cacheMaxEntries) {
+ this.cacheMaxEntries = cacheMaxEntries;
+ return this;
+ }
+
+ public Builder setCacheMaxObjectSize(Integer cacheMaxObjectSize) {
+ this.cacheMaxObjectSize = cacheMaxObjectSize;
+ return this;
+ }
+
+ public Builder setConnectionRequestTimeout(Integer connectionRequestTimeout) {
+ this.connectionRequestTimeout = connectionRequestTimeout;
+ return this;
+ }
+
+ public Builder setConnectTimeout(Integer connectTimeout) {
+ this.connectTimeout = connectTimeout;
+ return this;
+ }
+
+ public Builder setSocketTimeout(Integer socketTimeout) {
+ this.socketTimeout = socketTimeout;
+ return this;
+ }
+
+ public SpotifyHttpManager build() {
+ return new SpotifyHttpManager(this);
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/enums/Action.java
@@ -0,0 +1,50 @@
+package com.wrapper.spotify.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * An enumeration of all possible {@link Action} types.
+ *
+ * @see <a href="https://developer.spotify.com/web-api/object-model/#disallows-object">Disallows object</a>
+ */
+public enum Action {
+
+ INTERRUPTING_PLAYBACK("interrupting_playback"),
+ PAUSING("pausing"),
+ RESUMING("resuming"),
+ SEEKING("seeking"),
+ SKIPPING_NEXT("skipping_next"),
+ SKIPPING_PREV("skipping_prev"),
+ TOGGLING_REPEAT_CONTEXT("toggling_repeat_context"),
+ TOGGLING_SHUFFLE("toggling_shuffle"),
+ TOGGLING_REPEAT_TRACK("toggling_repeat_track"),
+ TRANSFERRING_PLAYBACK("transferring_playback");
+
+ private static final Map<String, Action> map = new HashMap<>();
+
+ static {
+ for (Action action : Action.values()) {
+ map.put(action.key, action);
+ }
+ }
+
+ public final String key;
+
+ Action(final String key) {
+ this.key = key;
+ }
+
+ public static Action keyOf(String key) {
+ return map.get(key);
+ }
+
+ /**
+ * Get the {@link Action} key as a string.
+ *
+ * @return {@link Action} key as string.
+ */
+ public String getKey() {
+ return key;
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/enums/AlbumGroup.java
@@ -0,0 +1,43 @@
+package com.wrapper.spotify.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * An enumeration of all possible album types.
+ */
+public enum AlbumGroup {
+
+ ALBUM("album"),
+ APPEARS_ON("appears_on"),
+ COMPILATION("compilation"),
+ SINGLE("single");
+
+ private static final Map<String, AlbumGroup> map = new HashMap<>();
+
+ static {
+ for (AlbumGroup albumGroup : AlbumGroup.values()) {
+ map.put(albumGroup.group, albumGroup);
+ }
+ }
+
+ public final String group;
+
+ AlbumGroup(final String group) {
+ this.group = group;
+ }
+
+ public static AlbumGroup keyOf(String type) {
+ return map.get(type);
+ }
+
+ /**
+ * Get the album group as a string.
+ *
+ * @return Album group as string.
+ */
+ public String getGroup() {
+ return group;
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/enums/AlbumType.java
@@ -0,0 +1,42 @@
+package com.wrapper.spotify.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * An enumeration of all possible album types.
+ */
+public enum AlbumType {
+
+ ALBUM("album"),
+ COMPILATION("compilation"),
+ SINGLE("single");
+
+ private static final Map<String, AlbumType> map = new HashMap<>();
+
+ static {
+ for (AlbumType albumType : AlbumType.values()) {
+ map.put(albumType.type, albumType);
+ }
+ }
+
+ public final String type;
+
+ AlbumType(final String type) {
+ this.type = type;
+ }
+
+ public static AlbumType keyOf(String type) {
+ return map.get(type);
+ }
+
+ /**
+ * Get the album type as a string.
+ *
+ * @return Album type as string.
+ */
+ public String getType() {
+ return type;
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/enums/CopyrightType.java
@@ -0,0 +1,43 @@
+package com.wrapper.spotify.enums;
+
+import com.wrapper.spotify.model_objects.specification.Copyright;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * An enumeration of all possible {@link Copyright} types.
+ */
+public enum CopyrightType {
+
+ C("c"),
+ P("p");
+
+ private static final Map<String, CopyrightType> map = new HashMap<>();
+
+ static {
+ for (CopyrightType copyrightType : CopyrightType.values()) {
+ map.put(copyrightType.type, copyrightType);
+ }
+ }
+
+ public final String type;
+
+ CopyrightType(final String type) {
+ this.type = type;
+ }
+
+ public static CopyrightType keyOf(String type) {
+ return map.get(type);
+ }
+
+ /**
+ * Get the {@link Copyright} type as a string.
+ *
+ * @return {@link Copyright} type as string.
+ */
+ public String getType() {
+ return type;
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/enums/CurrentlyPlayingType.java
@@ -0,0 +1,43 @@
+package com.wrapper.spotify.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * An enumeration of all possible currently playing types.
+ */
+public enum CurrentlyPlayingType {
+
+ TRACK("track"),
+ EPISODE("episode"),
+ AD("ad"),
+ UNKNOWN("unknown");
+
+ private static final Map<String, CurrentlyPlayingType> map = new HashMap<>();
+
+ static {
+ for (CurrentlyPlayingType currentlyPlayingType : CurrentlyPlayingType.values()) {
+ map.put(currentlyPlayingType.type, currentlyPlayingType);
+ }
+ }
+
+
+ private final String type;
+
+ CurrentlyPlayingType(final String type) {
+ this.type = type;
+ }
+
+ public static CurrentlyPlayingType keyOf(String type) {
+ return map.get(type);
+ }
+
+ /**
+ * Get the currently playing type as a string.
+ *
+ * @return The currently playing type as a string.
+ */
+ public String getType() {
+ return type;
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/enums/Modality.java
@@ -0,0 +1,43 @@
+package com.wrapper.spotify.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * An enumeration with the two modality types.
+ *
+ * @see <a href="https://en.wikipedia.org/wiki/Mode_(music)">Wikipedia: Mode (Music)</a>
+ */
+public enum Modality {
+
+ MAJOR(1),
+ MINOR(0);
+
+ private static final Map<Integer, Modality> map = new HashMap<>();
+
+ static {
+ for (Modality modality : Modality.values()) {
+ map.put(modality.mode, modality);
+ }
+ }
+
+ public final int mode;
+
+ Modality(final int mode) {
+ this.mode = mode;
+ }
+
+ public static Modality keyOf(int mode) {
+ return map.get(mode);
+ }
+
+ /**
+ * Get the {@link Modality} type as a string.
+ *
+ * @return {@link Modality} type as a string.
+ */
+ public int getType() {
+ return this.mode;
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/enums/ModelObjectType.java
@@ -0,0 +1,48 @@
+package com.wrapper.spotify.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * An enumeration of all possible model object types.
+ */
+public enum ModelObjectType {
+
+ ALBUM("album"),
+ ARTIST("artist"),
+ AUDIO_FEATURES("audio_features"),
+ EPISODE("episode"),
+ GENRE("genre"),
+ PLAYLIST("playlist"),
+ SHOW("show"),
+ TRACK("track"),
+ USER("user");
+
+ private static final Map<String, ModelObjectType> map = new HashMap<>();
+
+ static {
+ for (ModelObjectType modelObjectType : ModelObjectType.values()) {
+ map.put(modelObjectType.type, modelObjectType);
+ }
+ }
+
+ public final String type;
+
+ ModelObjectType(final String type) {
+ this.type = type;
+ }
+
+ public static ModelObjectType keyOf(String type) {
+ return map.get(type);
+ }
+
+ /**
+ * Get the model object type as a string.
+ *
+ * @return The model object type as a string.
+ */
+ public String getType() {
+ return this.type;
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/enums/ProductType.java
@@ -0,0 +1,44 @@
+package com.wrapper.spotify.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * An enumeration of all possible Spotify product types.
+ */
+public enum ProductType {
+
+ BASIC_DESKTOP("basic-desktop"),
+ DAYPASS("daypass"),
+ FREE("free"),
+ OPEN("open"),
+ PREMIUM("premium");
+
+ private static final Map<String, ProductType> map = new HashMap<>();
+
+ static {
+ for (ProductType productType : ProductType.values()) {
+ map.put(productType.type, productType);
+ }
+ }
+
+ public final String type;
+
+ ProductType(final String type) {
+ this.type = type;
+ }
+
+ public static ProductType keyOf(String type) {
+ return map.get(type);
+ }
+
+ /**
+ * Get the Spotify product type as a string.
+ *
+ * @return The Spotify product type as a string.
+ */
+ public String getType() {
+ return type;
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/enums/ReleaseDatePrecision.java
@@ -0,0 +1,42 @@
+package com.wrapper.spotify.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * An enumeration of all possible release date precisions.
+ */
+public enum ReleaseDatePrecision {
+
+ DAY("day"),
+ MONTH("month"),
+ YEAR("year");
+
+ private static final Map<String, ReleaseDatePrecision> map = new HashMap<>();
+
+ static {
+ for (ReleaseDatePrecision releaseDatePrecision : ReleaseDatePrecision.values()) {
+ map.put(releaseDatePrecision.precision, releaseDatePrecision);
+ }
+ }
+
+ public final String precision;
+
+ ReleaseDatePrecision(final String precision) {
+ this.precision = precision;
+ }
+
+ public static ReleaseDatePrecision keyOf(String precision) {
+ return map.get(precision);
+ }
+
+ /**
+ * Get the release date precision as a string.
+ *
+ * @return The release date precision as a string.
+ */
+ public String getPrecision() {
+ return precision;
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/exceptions/SpotifyWebApiException.java
@@ -0,0 +1,22 @@
+package com.wrapper.spotify.exceptions;
+
+import org.apache.hc.core5.http.HttpException;
+
+/**
+ * An exception happened, eg. a HTTP status code 4** or 5** has been returned in a request.
+ */
+public class SpotifyWebApiException extends HttpException {
+
+ public SpotifyWebApiException() {
+ super();
+ }
+
+ public SpotifyWebApiException(String message) {
+ super(message);
+ }
+
+ public SpotifyWebApiException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/exceptions/detailed/BadGatewayException.java
@@ -0,0 +1,22 @@
+package com.wrapper.spotify.exceptions.detailed;
+
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+
+/**
+ * The server was acting as a gateway or proxy and received an invalid response from the upstream server.
+ */
+public class BadGatewayException extends SpotifyWebApiException {
+
+ public BadGatewayException() {
+ super();
+ }
+
+ public BadGatewayException(String message) {
+ super(message);
+ }
+
+ public BadGatewayException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/exceptions/detailed/BadRequestException.java
@@ -0,0 +1,22 @@
+package com.wrapper.spotify.exceptions.detailed;
+
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+
+/**
+ * The request could not be understood by the server due to malformed syntax.
+ */
+public class BadRequestException extends SpotifyWebApiException {
+
+ public BadRequestException() {
+ super();
+ }
+
+ public BadRequestException(String message) {
+ super(message);
+ }
+
+ public BadRequestException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/exceptions/detailed/ForbiddenException.java
@@ -0,0 +1,22 @@
+package com.wrapper.spotify.exceptions.detailed;
+
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+
+/**
+ * The server understood the request, but is refusing to fulfill it.
+ */
+public class ForbiddenException extends SpotifyWebApiException {
+
+ public ForbiddenException() {
+ super();
+ }
+
+ public ForbiddenException(String message) {
+ super(message);
+ }
+
+ public ForbiddenException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/exceptions/detailed/InternalServerErrorException.java
@@ -0,0 +1,23 @@
+package com.wrapper.spotify.exceptions.detailed;
+
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+
+/**
+ * You should never receive this error because our clever coders catch them all ... but if you are unlucky enough to get
+ * one, please report it to us.
+ */
+public class InternalServerErrorException extends SpotifyWebApiException {
+
+ public InternalServerErrorException() {
+ super();
+ }
+
+ public InternalServerErrorException(String message) {
+ super(message);
+ }
+
+ public InternalServerErrorException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/exceptions/detailed/NotFoundException.java
@@ -0,0 +1,22 @@
+package com.wrapper.spotify.exceptions.detailed;
+
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+
+/**
+ * The requested resource could not be found. This error can be due to a temporary or permanent condition.
+ */
+public class NotFoundException extends SpotifyWebApiException {
+
+ public NotFoundException() {
+ super();
+ }
+
+ public NotFoundException(String message) {
+ super(message);
+ }
+
+ public NotFoundException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/exceptions/detailed/ServiceUnavailableException.java
@@ -0,0 +1,23 @@
+package com.wrapper.spotify.exceptions.detailed;
+
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+
+/**
+ * The server is currently unable to handle the request due to a temporary condition which will be alleviated after some
+ * delay. You can choose to resend the request again.
+ */
+public class ServiceUnavailableException extends SpotifyWebApiException {
+
+ public ServiceUnavailableException() {
+ super();
+ }
+
+ public ServiceUnavailableException(String message) {
+ super(message);
+ }
+
+ public ServiceUnavailableException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/exceptions/detailed/TooManyRequestsException.java
@@ -0,0 +1,37 @@
+package com.wrapper.spotify.exceptions.detailed;
+
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+
+/**
+ * Rate limiting has been applied.
+ */
+public class TooManyRequestsException extends SpotifyWebApiException {
+
+ private int retryAfter;
+
+ public TooManyRequestsException() {
+ super();
+ }
+
+ public TooManyRequestsException(String message, int retryAfter) {
+ super(message);
+ this.setRetryAfter(retryAfter);
+ }
+
+ public TooManyRequestsException(String message) {
+ super(message);
+ }
+
+ public TooManyRequestsException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public int getRetryAfter() {
+ return retryAfter;
+ }
+
+ public void setRetryAfter(int retryAfter) {
+ this.retryAfter = retryAfter;
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/exceptions/detailed/UnauthorizedException.java
@@ -0,0 +1,23 @@
+package com.wrapper.spotify.exceptions.detailed;
+
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+
+/**
+ * The request requires user authorization or, if the request included authorization credentials, authorization has been
+ * refused for those credentials.
+ */
+public class UnauthorizedException extends SpotifyWebApiException {
+
+ public UnauthorizedException() {
+ super();
+ }
+
+ public UnauthorizedException(String message) {
+ super(message);
+ }
+
+ public UnauthorizedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/AbstractModelObject.java
@@ -0,0 +1,211 @@
+package com.wrapper.spotify.model_objects;
+
+import com.google.gson.*;
+import com.wrapper.spotify.model_objects.specification.Cursor;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.model_objects.specification.PagingCursorbased;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.ParameterizedType;
+
+/**
+ * This abstract class (and its wrapping classes) is used as a sort of template for other model object classes and
+ * includes multiple generic methods.
+ */
+public abstract class AbstractModelObject implements IModelObject {
+
+ /**
+ * This constructor initializes the time zone.
+ *
+ * @param builder The builder object of the corresponding model object.
+ */
+ protected AbstractModelObject(final Builder builder) {
+ assert (builder != null);
+ }
+
+ /**
+ * Returns a String representation of this model object in the style:<p>
+ * {@code ModelObject(attr1=value1, attr2=value2, ...)}
+ */
+ @Override
+ public abstract String toString(); // abstract enforces overriding of toString() for subclasses
+
+ /**
+ * Each model object needs to implement its own builder class.
+ */
+ public static abstract class Builder implements IModelObject.Builder {
+ }
+
+ /**
+ * Each model object needs to implement its own JsonUtil class. <br>
+ *
+ * @param <T> The model object type of the corresponding JsonUtil.
+ */
+ public static abstract class JsonUtil<T> implements IModelObject.IJsonUtil<T> {
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean hasAndNotNull(final JsonObject jsonObject, final String memberName) {
+ return jsonObject.has(memberName) && !jsonObject.get(memberName).isJsonNull();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public T createModelObject(final String json) {
+ if (json == null) {
+ return null;
+ } else {
+ return createModelObject(JsonParser.parseString(json).getAsJsonObject());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public T[] createModelObjectArray(final JsonArray jsonArray) {
+ @SuppressWarnings("unchecked")
+ T[] array = (T[]) Array.newInstance((Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0], jsonArray.size());
+
+ for (int i = 0; i < jsonArray.size(); i++) {
+ JsonElement jsonElement = jsonArray.get(i);
+
+ if (jsonElement instanceof JsonNull) {
+ array[i] = null;
+ } else {
+ JsonObject jsonObject = jsonElement.getAsJsonObject();
+ array[i] = createModelObject(jsonObject);
+ }
+ }
+
+ return array;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public T[] createModelObjectArray(final String json) {
+ return createModelObjectArray(JsonParser.parseString(json).getAsJsonArray());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public T[] createModelObjectArray(final String json, final String key) {
+ return createModelObjectArray(JsonParser.parseString(json).getAsJsonObject().get(key).getAsJsonArray());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unchecked")
+ public <X> X[] createModelObjectArray(final JsonArray jsonArray, Class<X> clazz) {
+ X[] array = (X[]) Array.newInstance(clazz, jsonArray.size());
+
+ for (int i = 0; i < jsonArray.size(); i++) {
+ JsonElement jsonElement = jsonArray.get(i);
+ JsonObject jsonObject = jsonElement.getAsJsonObject();
+ array[i] = (X) createModelObject(jsonObject);
+ }
+
+ return array;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Paging<T> createModelObjectPaging(final JsonObject jsonObject) {
+ return new Paging.Builder<T>()
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setItems(
+ hasAndNotNull(jsonObject, "items")
+ ? createModelObjectArray(jsonObject.getAsJsonArray("items"))
+ : null)
+ .setLimit(
+ hasAndNotNull(jsonObject, "limit")
+ ? jsonObject.get("limit").getAsInt()
+ : null)
+ .setNext(
+ hasAndNotNull(jsonObject, "next")
+ ? jsonObject.get("next").getAsString()
+ : null)
+ .setOffset(
+ hasAndNotNull(jsonObject, "offset")
+ ? jsonObject.get("offset").getAsInt()
+ : null)
+ .setPrevious(
+ hasAndNotNull(jsonObject, "previous")
+ ? jsonObject.get("previous").getAsString()
+ : null)
+ .setTotal(
+ hasAndNotNull(jsonObject, "total")
+ ? jsonObject.get("total").getAsInt()
+ : null)
+ .build();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Paging<T> createModelObjectPaging(final String json) {
+ return createModelObjectPaging(JsonParser.parseString(json).getAsJsonObject());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Paging<T> createModelObjectPaging(final String json, final String key) {
+ return createModelObjectPaging(JsonParser.parseString(json).getAsJsonObject().get(key).getAsJsonObject());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PagingCursorbased<T> createModelObjectPagingCursorbased(final JsonObject jsonObject) {
+ return new PagingCursorbased.Builder<T>()
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setItems(
+ hasAndNotNull(jsonObject, "items")
+ ? createModelObjectArray(jsonObject.getAsJsonArray("items"))
+ : null)
+ .setLimit(
+ hasAndNotNull(jsonObject, "limit")
+ ? jsonObject.get("limit").getAsInt()
+ : null)
+ .setNext(
+ hasAndNotNull(jsonObject, "next")
+ ? jsonObject.get("next").getAsString()
+ : null)
+ .setCursors(
+ hasAndNotNull(jsonObject, "cursors")
+ ? new Cursor.JsonUtil().createModelObject(jsonObject.getAsJsonObject("cursors"))
+ : null)
+ .setTotal(
+ hasAndNotNull(jsonObject, "total")
+ ? jsonObject.get("total").getAsInt()
+ : null)
+ .build();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PagingCursorbased<T> createModelObjectPagingCursorbased(final String json) {
+ return createModelObjectPagingCursorbased(JsonParser.parseString(json).getAsJsonObject());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PagingCursorbased<T> createModelObjectPagingCursorbased(final String json, final String key) {
+ return createModelObjectPagingCursorbased(JsonParser.parseString(json).getAsJsonObject().get(key).getAsJsonObject());
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/IModelObject.java
@@ -0,0 +1,158 @@
+package com.wrapper.spotify.model_objects;
+
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.model_objects.specification.PagingCursorbased;
+
+import java.io.Serializable;
+
+/**
+ * Interface with methods used in model objects.
+ */
+public interface IModelObject extends Serializable {
+
+ /**
+ * Create a builder for building an instance of a model object. <br>
+ * The type of the builder and its methods depend on its corresponding implementation.
+ *
+ * @return A builder object.
+ */
+ Builder builder();
+
+ /**
+ * Interface with methods used in builder classes of model objects.
+ */
+ @JsonPOJOBuilder(withPrefix = "set")
+ interface Builder {
+
+ /**
+ * Build a model object with the information set in the builder object. <br>
+ * The type of the model object and its methods depend on its corresponding implementation.
+ *
+ * @return A model object.
+ */
+ IModelObject build();
+ }
+
+ /**
+ * Interface with methods used in JsonUtil classes of model objects.
+ *
+ * @param <T> Type of the corresponding model object.
+ */
+ interface IJsonUtil<T> {
+
+ /**
+ * Check whether the supplied JSON object contains data in the given member, which is not {@code null}.
+ *
+ * @param jsonObject The JSON object.
+ * @param memberName The member name.
+ * @return Whether the supplied JSON object contains data in the given member.
+ */
+ boolean hasAndNotNull(final JsonObject jsonObject, final String memberName);
+
+ /**
+ * Build a model object with the information given in a json object. <br>
+ * The type of the model object and its methods depend on its corresponding implementation.
+ *
+ * @param jsonObject A json object.
+ * @return A model object. The type depends on this methods implementation.
+ */
+ T createModelObject(final JsonObject jsonObject);
+
+ /**
+ * Build a model object with the information given in a json string. <br>
+ * The type of the model object and its methods depend on its corresponding implementation.
+ *
+ * @param json A json object.
+ * @return A model object. The type depends on this methods implementation.
+ */
+ T createModelObject(final String json);
+
+ /**
+ * Create an array of model objects out of a json array object.
+ *
+ * @param jsonArray A {@link JsonArray}.
+ * @return A model object array. The type depends on this methods implementation.
+ */
+ T[] createModelObjectArray(final JsonArray jsonArray);
+
+ /**
+ * Create an array of model objects out of a json string.
+ *
+ * @param json A {@link JsonArray}.
+ * @return A model object array. The type depends on this methods implementation.
+ */
+ T[] createModelObjectArray(final String json);
+
+ /**
+ * Create an array of model objects out of a json array, which is contained in a json object.
+ *
+ * @param json A {@link JsonObject}.
+ * @param key The key of the json array in the json object.
+ * @return A model object array. The type depends on this methods implementation.
+ */
+ T[] createModelObjectArray(final String json, final String key);
+
+ /**
+ * Create an array of model objects out of a json array object and a {@link Class} object.
+ *
+ * @param jsonArray A json array object.
+ * @param clazz The class object.
+ * @param <X> The model object type of the array and class object.
+ * @return A model object array.
+ */
+ <X> X[] createModelObjectArray(final JsonArray jsonArray, final Class<X> clazz);
+
+ /**
+ * Create a paging of model objects out of a json object.
+ *
+ * @param jsonObject A json object.
+ * @return A model object paging.
+ */
+ Paging<T> createModelObjectPaging(final JsonObject jsonObject);
+
+ /**
+ * Create a paging of model objects out of a json string.
+ *
+ * @param json A json string.
+ * @return A model object paging.
+ */
+ Paging<T> createModelObjectPaging(final String json);
+
+ /**
+ * Create a paging of model objects out of a json array, which is contained in a json object.
+ *
+ * @param json A {@link JsonObject}.
+ * @param key The key of the json array in the json object.
+ * @return A model object array. The type depends on this methods implementation.
+ */
+ Paging<T> createModelObjectPaging(final String json, final String key);
+
+ /**
+ * Create a cursor-based paging of model objects out of a json object.
+ *
+ * @param jsonObject A json object.
+ * @return A cursor-based model object paging.
+ */
+ PagingCursorbased<T> createModelObjectPagingCursorbased(final JsonObject jsonObject);
+
+ /**
+ * Create a cursor-based paging of model objects out of a json string.
+ *
+ * @param json A json string.
+ * @return A cursor-based model object paging.
+ */
+ PagingCursorbased<T> createModelObjectPagingCursorbased(final String json);
+
+ /**
+ * Create a cursor-based paging of model objects out of a json array, which is contained in a json object.
+ *
+ * @param json A {@link JsonObject}.
+ * @param key The key of the json array in the json object.
+ * @return A cursor-based model object paging.
+ */
+ PagingCursorbased<T> createModelObjectPagingCursorbased(final String json, final String key);
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/IPlaylistItem.java
@@ -0,0 +1,67 @@
+package com.wrapper.spotify.model_objects;
+
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.model_objects.specification.Episode;
+import com.wrapper.spotify.model_objects.specification.ExternalUrl;
+import com.wrapper.spotify.model_objects.specification.Track;
+
+
+/**
+ * This interface represents objects returned by the API that can be played, saved in a playlist, etc,
+ * currently {@link Episode} and {@link Track}.
+ */
+public interface IPlaylistItem extends IModelObject {
+
+ /**
+ * Get the duration of this playlist item in milliseconds.
+ *
+ * @return The playlist item length in milliseconds.
+ */
+ public Integer getDurationMs();
+
+ /**
+ * Get the external URLs of the playlist item.<br>
+ * Example: Spotify-URL.
+ *
+ * @return Known external URLs for this playlist item.
+ */
+ public ExternalUrl getExternalUrls();
+
+ /**
+ * Get the full Spotify Web API endpoint URL of the playlist item.
+ *
+ * @return A link to the Web API endpoint providing full details of the playlist item.
+ */
+ public String getHref();
+
+ /**
+ * Get the <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify ID</a> of the
+ * playlist item.
+ *
+ * @return The Spotify ID for the playlist item.
+ */
+ public String getId();
+
+ /**
+ * Get the name of the playlist item.
+ *
+ * @return playlist item name.
+ */
+ public String getName();
+
+ /**
+ * Get the type of the IPlaylistItem.
+ * Possible values: {@code ModelObjectType.TRACK} or {@code ModelObjectType.EPISODE}
+ *
+ * @return The type of the IPlaylistItem.
+ */
+ ModelObjectType getType();
+
+ /**
+ * Get the Spotify playlist item URI.
+ *
+ * @return The <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URI</a> for
+ * the playlist item.
+ */
+ public String getUri();
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/credentials/AuthorizationCodeCredentials.java
@@ -0,0 +1,194 @@
+package com.wrapper.spotify.model_objects.credentials;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about
+ * <a href="https://developer.spotify.com/web-api/authorization-guide/#authorization-code-flow">Authorization Code
+ * Credentials</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = AuthorizationCodeCredentials.Builder.class)
+public class AuthorizationCodeCredentials extends AbstractModelObject {
+ private final String accessToken;
+ private final String tokenType;
+ private final String scope;
+ private final Integer expiresIn;
+ private final String refreshToken;
+
+ private AuthorizationCodeCredentials(final Builder builder) {
+ super(builder);
+
+ this.accessToken = builder.accessToken;
+ this.tokenType = builder.tokenType;
+ this.scope = builder.scope;
+ this.expiresIn = builder.expiresIn;
+ this.refreshToken = builder.refreshToken;
+ }
+
+ /**
+ * Get the access token. It becomes invalid after a certain period of time.
+ *
+ * @return An access token that can be provided in subsequent calls, for example to Spotify Web API services.
+ */
+ public String getAccessToken() {
+ return accessToken;
+ }
+
+ /**
+ * Get the type of an access token, which will always be "Bearer".
+ *
+ * @return How the access token may be used: always &quot;Bearer&quot;.
+ */
+ public String getTokenType() {
+ return tokenType;
+ }
+
+
+ /**
+ * Get the <a href="https://developer.spotify.com/web-api/using-scopes/">Scopes</a> specified in the authorization
+ * code credentials request.
+ *
+ * @return The scopes specified in the credentials request.
+ */
+ public String getScope() {
+ return scope;
+ }
+
+ /**
+ * Get the time period (in seconds) for which the access token is valid.
+ *
+ * @return The time period (in seconds) for which the access token is valid.
+ */
+ public Integer getExpiresIn() {
+ return expiresIn;
+ }
+
+ /**
+ * Get the refresh token. This token can be sent to the Spotify Accounts service in place of an authorization code to
+ * retrieve a new access token.
+ *
+ * @return A token that can be sent to the Spotify Accounts service in place of an access token.
+ */
+ public String getRefreshToken() {
+ return refreshToken;
+ }
+
+ @Override
+ public String toString() {
+ return "AuthorizationCodeCredentials(accessToken=" + accessToken + ", tokenType=" + tokenType + ", scope=" + scope
+ + ", expiresIn=" + expiresIn + ", refreshToken=" + refreshToken + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link AuthorizationCodeCredentials} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private String accessToken;
+ private String tokenType;
+ private String scope;
+ private Integer expiresIn;
+ private String refreshToken;
+
+ /**
+ * The access token setter.
+ *
+ * @param accessToken An access token that can be provided in subsequent calls,
+ * for example to Spotify Web API services.
+ * @return An {@link AuthorizationCodeCredentials.Builder}.
+ */
+ public Builder setAccessToken(final String accessToken) {
+ this.accessToken = accessToken;
+ return this;
+ }
+
+ /**
+ * The access token type setter.
+ *
+ * @param tokenType How the access token may be used: always &quot;Bearer&quot;.
+ * @return An {@link AuthorizationCodeCredentials.Builder}.
+ */
+ public Builder setTokenType(final String tokenType) {
+ this.tokenType = tokenType;
+ return this;
+ }
+
+ /**
+ * The scopes setter.
+ *
+ * @param scope The scopes specified in the credentials request.
+ * @return An {@link AuthorizationCodeCredentials.Builder}.
+ */
+ public Builder setScope(final String scope) {
+ this.scope = scope;
+ return this;
+ }
+
+ /**
+ * The expiration time setter.
+ *
+ * @param expiresIn The time period (in seconds) for which the access token is valid.
+ * @return An {@link AuthorizationCodeCredentials.Builder}.
+ */
+ public Builder setExpiresIn(final Integer expiresIn) {
+ this.expiresIn = expiresIn;
+ return this;
+ }
+
+ /**
+ * The refresh token setter.
+ *
+ * @param refreshToken A token that can be sent to the Spotify Accounts service in place of an authorization code.
+ * @return An {@link AuthorizationCodeCredentials.Builder}.
+ */
+ public Builder setRefreshToken(final String refreshToken) {
+ this.refreshToken = refreshToken;
+ return this;
+ }
+
+ @Override
+ public AuthorizationCodeCredentials build() {
+ return new AuthorizationCodeCredentials(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link AuthorizationCodeCredentials} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<AuthorizationCodeCredentials> {
+ public AuthorizationCodeCredentials createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new AuthorizationCodeCredentials.Builder()
+ .setAccessToken(
+ hasAndNotNull(jsonObject, "access_token")
+ ? jsonObject.get("access_token").getAsString()
+ : null)
+ .setTokenType(
+ hasAndNotNull(jsonObject, "token_type")
+ ? jsonObject.get("token_type").getAsString()
+ : null)
+ .setScope(
+ hasAndNotNull(jsonObject, "scope")
+ ? jsonObject.get("scope").getAsString()
+ : null)
+ .setExpiresIn(
+ hasAndNotNull(jsonObject, "expires_in")
+ ? jsonObject.get("expires_in").getAsInt()
+ : null)
+ .setRefreshToken(
+ hasAndNotNull(jsonObject, "refresh_token")
+ ? jsonObject.get("refresh_token").getAsString()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/credentials/ClientCredentials.java
@@ -0,0 +1,136 @@
+package com.wrapper.spotify.model_objects.credentials;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/authorization-guide/#implicit_grant_flow">
+ * Client Credentials</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = ClientCredentials.Builder.class)
+public class ClientCredentials extends AbstractModelObject {
+ private final String accessToken;
+ private final String tokenType;
+ private final Integer expiresIn;
+
+ private ClientCredentials(final Builder builder) {
+ super(builder);
+
+ this.accessToken = builder.accessToken;
+ this.tokenType = builder.tokenType;
+ this.expiresIn = builder.expiresIn;
+ }
+
+ /**
+ * Get the access token. It becomes invalid after a certain period of time.
+ *
+ * @return An access token that can be provided in subsequent calls, for example to Spotify Web API services.
+ */
+ public String getAccessToken() {
+ return accessToken;
+ }
+
+ /**
+ * Get the type of an access token, which will always be "Bearer".
+ *
+ * @return How the access token may be used: always &quot;Bearer&quot;.
+ */
+ public String getTokenType() {
+ return tokenType;
+ }
+
+ /**
+ * Get the time period (in seconds) for which the access token is valid.
+ *
+ * @return The time period (in seconds) for which the access token is valid.
+ */
+ public Integer getExpiresIn() {
+ return expiresIn;
+ }
+
+ @Override
+ public String toString() {
+ return "ClientCredentials(accessToken=" + accessToken + ", tokenType=" + tokenType + ", expiresIn=" + expiresIn
+ + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link ClientCredentials} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private String accessToken;
+ private String tokenType;
+ private Integer expiresIn;
+
+ /**
+ * The access token setter.
+ *
+ * @param accessToken An access token that can be provided in subsequent calls,
+ * for example to Spotify Web API services.
+ * @return A {@link ClientCredentials.Builder}.
+ */
+ public Builder setAccessToken(String accessToken) {
+ this.accessToken = accessToken;
+ return this;
+ }
+
+ /**
+ * The access token type setter.
+ *
+ * @param tokenType How the access token may be used: always &quot;Bearer&quot;.
+ * @return A {@link ClientCredentials.Builder}.
+ */
+ public Builder setTokenType(String tokenType) {
+ this.tokenType = tokenType;
+ return this;
+ }
+
+ /**
+ * The expiration time setter.
+ *
+ * @param expiresIn The time period (in seconds) for which the access token is valid.
+ * @return A {@link ClientCredentials.Builder}.
+ */
+ public Builder setExpiresIn(Integer expiresIn) {
+ this.expiresIn = expiresIn;
+ return this;
+ }
+
+ @Override
+ public ClientCredentials build() {
+ return new ClientCredentials(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link ClientCredentials} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<ClientCredentials> {
+ public ClientCredentials createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new ClientCredentials.Builder()
+ .setAccessToken(
+ hasAndNotNull(jsonObject, "access_token")
+ ? jsonObject.get("access_token").getAsString()
+ : null)
+ .setTokenType(
+ hasAndNotNull(jsonObject, "token_type")
+ ? jsonObject.get("token_type").getAsString()
+ : null)
+ .setExpiresIn(
+ hasAndNotNull(jsonObject, "expires_in")
+ ? jsonObject.get("expires_in").getAsInt()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/credentials/error/AuthenticationError.java
@@ -0,0 +1,110 @@
+package com.wrapper.spotify.model_objects.credentials.error;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about Authorization Error objects by building instances from this class.
+ *
+ * @see <a href="https://developer.spotify.com/web-api/authorization-guide/">Spotify: Authorization Guide</a>
+ */
+@JsonDeserialize(builder = AuthenticationError.Builder.class)
+public class AuthenticationError extends AbstractModelObject {
+ private final String error;
+ private final String error_description;
+
+ private AuthenticationError(final Builder builder) {
+ super(builder);
+
+ this.error = builder.error;
+ this.error_description = builder.error_description;
+ }
+
+ /**
+ * Get the high level description of the error as specified in
+ * <a href="https://tools.ietf.org/html/rfc6749#section-5.2">RFC 6749 Section 5.2</a>.
+ *
+ * @return A high level description of the error as specified in RFC 6749 Section 5.2.
+ */
+ public String getError() {
+ return error;
+ }
+
+ /**
+ * Get the more detailed description of the error as specified in
+ * <a href="https://tools.ietf.org/html/rfc6749#section-4.1.2.1">RFC 6749 Section 4.1.2.1</a>.
+ *
+ * @return string A more detailed description of the error as specified in RFC 6749 Section 4.1.2.1.
+ */
+ public String getError_description() {
+ return error_description;
+ }
+
+ @Override
+ public String toString() {
+ return "AuthenticationError(error=" + error + ", error_description=" + error_description + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link AuthenticationError} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private String error;
+ private String error_description;
+
+ /**
+ * The error setter.
+ *
+ * @param error A high level description of the error as specified in RFC 6749 Section 5.2.
+ * @return An {@link AuthenticationError.Builder}.
+ */
+ public Builder setError(String error) {
+ this.error = error;
+ return this;
+ }
+
+ /**
+ * The error description setter.
+ *
+ * @param error_description A more detailed description of the error as specified in RFC 6749 Section 4.1.2.1.
+ * @return An {@link AuthenticationError.Builder}.
+ */
+ public Builder setError_description(String error_description) {
+ this.error_description = error_description;
+ return this;
+ }
+
+ @Override
+ public AuthenticationError build() {
+ return new AuthenticationError(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link AuthenticationError} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<AuthenticationError> {
+ public AuthenticationError createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new AuthenticationError.Builder()
+ .setError(
+ hasAndNotNull(jsonObject, "error")
+ ? jsonObject.get("error").getAsString()
+ : null)
+ .setError_description(
+ hasAndNotNull(jsonObject, "error_description")
+ ? jsonObject.get("error_description").getAsString()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/miscellaneous/AudioAnalysis.java
@@ -0,0 +1,268 @@
+package com.wrapper.spotify.model_objects.miscellaneous;
+
+import java.util.Arrays;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/get-audio-analysis/">
+ * Audio Analysis objects</a> by building instances from this class. <br>
+ * These objects contain a great amount of additional information to
+ * {@link com.wrapper.spotify.model_objects.specification.AudioFeatures} objects. <br><br>
+ * <p>
+ * <b>Note:</b> Audio Analysis data is created by the Software "Analyzer" by "The Echo Nest", which has been bought by
+ * Spotify in the past. Since then, new Analyzer versions were created by Spotify but there is a lack of documentation
+ * on the side of Spotify, so it is possible that this Javadoc page (and other Audio Analysis related Javadoc pages)
+ * contains speculative information about a few of its corresponding methods.
+ */
+@JsonDeserialize(builder = AudioAnalysis.Builder.class)
+public class AudioAnalysis extends AbstractModelObject {
+ private final AudioAnalysisMeasure[] bars;
+ private final AudioAnalysisMeasure[] beats;
+ private final AudioAnalysisMeta meta;
+ private final AudioAnalysisSection[] sections;
+ private final AudioAnalysisSegment[] segments;
+ private final AudioAnalysisMeasure[] tatums;
+ private final AudioAnalysisTrack track;
+
+ private AudioAnalysis(final Builder builder) {
+ super(builder);
+
+ this.bars = builder.bars;
+ this.beats = builder.beats;
+ this.meta = builder.meta;
+ this.sections = builder.sections;
+ this.segments = builder.segments;
+ this.tatums = builder.tatums;
+ this.track = builder.track;
+ }
+
+ /**
+ * Get the list of bar markers, in seconds. A bar (or measure) is a segment of time defined as a given number of
+ * beats. Bar offsets also indicate downbeats, the first beat of the measure.
+ *
+ * @return The list of bar markers, in seconds.
+ */
+ public AudioAnalysisMeasure[] getBars() {
+ return bars;
+ }
+
+ /**
+ * Get the list of beat markers, in seconds. A beat is the basic time unit of a piece of music; for example, each tick
+ * of a metronome. Beats are typically multiples of tatums.
+ *
+ * @return The list of beat markers, in seconds.
+ */
+ public AudioAnalysisMeasure[] getBeats() {
+ return beats;
+ }
+
+ /**
+ * Get the metadata of the analyzer software for the track.
+ *
+ * @return Analyze, compute, and track information.
+ */
+ public AudioAnalysisMeta getMeta() {
+ return meta;
+ }
+
+ /**
+ * Get the set of section markers, in seconds. Sections are defined by large variations in rhythm or timbre, e.g.
+ * chorus, verse, bridge, guitar solo, etc. Each section contains its own descriptions of tempo, key, mode,
+ * time_signature, and loudness
+ *
+ * @return The set of section markers, in seconds.
+ */
+ public AudioAnalysisSection[] getSections() {
+ return sections;
+ }
+
+ /**
+ * Get the set of sound entities (typically under a second) each relatively uniform in timbre and harmony.
+ * Segments are characterized by their perceptual onsets and duration in seconds, loudness (dB), pitch and timbral
+ * content.
+ *
+ * @return The set of sound entities (typically under a second) each relatively uniform in timbre and harmony.
+ */
+ public AudioAnalysisSegment[] getSegments() {
+ return segments;
+ }
+
+ /**
+ * Get the list of tatum markers, in seconds. Tatums represent the lowest regular pulse train that a listener
+ * intuitively infers from the timing of perceived musical events (segments).
+ *
+ * @return Get the list of tatum markers, in seconds.
+ */
+ public AudioAnalysisMeasure[] getTatums() {
+ return tatums;
+ }
+
+ /**
+ * Get the track data of the audio analysis object.
+ *
+ * @return Track data of audio analysis object.
+ */
+ public AudioAnalysisTrack getTrack() {
+ return track;
+ }
+
+ @Override
+ public String toString() {
+ return "AudioAnalysis(bars=" + Arrays.toString(bars) + ", beats=" + Arrays.toString(beats) + ", meta=" + meta
+ + ", sections=" + Arrays.toString(sections) + ", segments=" + Arrays.toString(segments) + ", tatums="
+ + Arrays.toString(tatums) + ", track=" + track + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link AudioAnalysis} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private AudioAnalysisMeasure[] bars;
+ private AudioAnalysisMeasure[] beats;
+ private AudioAnalysisMeta meta;
+ private AudioAnalysisSection[] sections;
+ private AudioAnalysisSegment[] segments;
+ private AudioAnalysisMeasure[] tatums;
+ private AudioAnalysisTrack track;
+
+ /**
+ * Track bars setter.
+ *
+ * @param bars The list of bar markers, in seconds.
+ * @return An {@link AudioAnalysis.Builder}.
+ */
+ public Builder setBars(AudioAnalysisMeasure[] bars) {
+ this.bars = bars;
+ return this;
+ }
+
+ /**
+ * The track beats setter.
+ *
+ * @param beats The list of beat markers, in seconds.
+ * @return An {@link AudioAnalysis.Builder}.
+ */
+ public Builder setBeats(AudioAnalysisMeasure[] beats) {
+ this.beats = beats;
+ return this;
+ }
+
+ /**
+ * The anaylzer metadata setter.
+ *
+ * @param meta Analyze, compute, and track information.
+ * @return An {@link AudioAnalysis.Builder}.
+ */
+ public Builder setMeta(AudioAnalysisMeta meta) {
+ this.meta = meta;
+ return this;
+ }
+
+ /**
+ * The track sections setter.
+ *
+ * @param sections The set of section markers, in seconds.
+ * @return An {@link AudioAnalysis.Builder}.
+ */
+ public Builder setSections(AudioAnalysisSection[] sections) {
+ this.sections = sections;
+ return this;
+ }
+
+ /**
+ * The track segments setter.
+ *
+ * @param segments The set of sound entities (typically under a second) each relatively uniform in timbre and
+ * harmony.
+ * @return An {@link AudioAnalysis.Builder}.
+ */
+ public Builder setSegments(AudioAnalysisSegment[] segments) {
+ this.segments = segments;
+ return this;
+ }
+
+ /**
+ * The track tatums setter.
+ *
+ * @param tatums Get the list of tatum markers, in seconds.
+ * @return An {@link AudioAnalysis.Builder}.
+ */
+ public Builder setTatums(AudioAnalysisMeasure[] tatums) {
+ this.tatums = tatums;
+ return this;
+ }
+
+ /**
+ * The track data setter.
+ *
+ * @param track Track data of audio analysis object.
+ * @return An {@link AudioAnalysis.Builder}.
+ */
+ public Builder setTrack(AudioAnalysisTrack track) {
+ this.track = track;
+ return this;
+ }
+
+ @Override
+ public AudioAnalysis build() {
+ return new AudioAnalysis(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link AudioAnalysis} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<AudioAnalysis> {
+ public AudioAnalysis createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new AudioAnalysis.Builder()
+ .setBars(
+ hasAndNotNull(jsonObject, "bars")
+ ? new AudioAnalysisMeasure.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("bars"))
+ : null)
+ .setBeats(
+ hasAndNotNull(jsonObject, "beats")
+ ? new AudioAnalysisMeasure.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("beats"))
+ : null)
+ .setMeta(
+ hasAndNotNull(jsonObject, "meta")
+ ? new AudioAnalysisMeta.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("meta"))
+ : null)
+ .setSections(
+ hasAndNotNull(jsonObject, "sections")
+ ? new AudioAnalysisSection.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("sections"))
+ : null)
+ .setSegments(
+ hasAndNotNull(jsonObject, "segments")
+ ? new AudioAnalysisSegment.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("segments"))
+ : null)
+ .setTatums(
+ hasAndNotNull(jsonObject, "tatums")
+ ? new AudioAnalysisMeasure.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("tatums"))
+ : null)
+ .setTrack(
+ hasAndNotNull(jsonObject, "track")
+ ? new AudioAnalysisTrack.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("track"))
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/miscellaneous/AudioAnalysisMeasure.java
@@ -0,0 +1,138 @@
+package com.wrapper.spotify.model_objects.miscellaneous;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about Audio Analysis Measure objects by building instances from this class. <br>
+ * These objects can store the data of various {@link AudioAnalysis} measurements, like {@code tatums}, {@code bars} and
+ * {@code beats}.
+ */
+@JsonDeserialize(builder = AudioAnalysisMeasure.Builder.class)
+public class AudioAnalysisMeasure extends AbstractModelObject {
+ private final Float confidence;
+ private final Float duration;
+ private final Float start;
+
+ private AudioAnalysisMeasure(final Builder builder) {
+ super(builder);
+
+ this.confidence = builder.confidence;
+ this.duration = builder.duration;
+ this.start = builder.start;
+ }
+
+ /**
+ * Get the confidence value of the measurement. <br>
+ * The confidence indicates the reliability of its corresponding attribute. Elements carrying a small confidence value
+ * should be considered speculative.
+ *
+ * @return The confidence value of the measurement between 0.0 and 1.0.
+ */
+ public Float getConfidence() {
+ return confidence;
+ }
+
+ /**
+ * Get the duration of the measurement in seconds. <br>
+ * Example: Get the duration of a bar.
+ *
+ * @return The duration of the measurement in seconds.
+ */
+ public Float getDuration() {
+ return duration;
+ }
+
+ /**
+ * Get the start point of the measurement, eg. when the measured part of the track begins.
+ *
+ * @return The start point of the measurement.
+ */
+ public Float getStart() {
+ return start;
+ }
+
+ @Override
+ public String toString() {
+ return "AudioAnalysisMeasure(confidence=" + confidence + ", duration=" + duration + ", start=" + start + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link AudioAnalysisMeasure} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private Float confidence;
+ private Float duration;
+ private Float start;
+
+ /**
+ * The confidence setter.
+ *
+ * @param confidence The confidence value of the measurement between 0.0 and 1.0.
+ * @return An {@link AudioAnalysisMeasure.Builder}.
+ */
+ public Builder setConfidence(Float confidence) {
+ this.confidence = confidence;
+ return this;
+ }
+
+ /**
+ * The duration setter.
+ *
+ * @param duration The duration of the measurement in seconds.
+ * @return An {@link AudioAnalysisMeasure.Builder}.
+ */
+ public Builder setDuration(Float duration) {
+ this.duration = duration;
+ return this;
+ }
+
+ /**
+ * The start point setter.
+ *
+ * @param start The start point of the measurement.
+ * @return An {@link AudioAnalysisMeasure.Builder}.
+ */
+ public Builder setStart(Float start) {
+ this.start = start;
+ return this;
+ }
+
+ @Override
+ public AudioAnalysisMeasure build() {
+ return new AudioAnalysisMeasure(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link AudioAnalysisMeasure} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<AudioAnalysisMeasure> {
+ public AudioAnalysisMeasure createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new AudioAnalysisMeasure.Builder()
+ .setConfidence(
+ hasAndNotNull(jsonObject, "confidence")
+ ? jsonObject.get("confidence").getAsFloat()
+ : null)
+ .setDuration(
+ hasAndNotNull(jsonObject, "duration")
+ ? jsonObject.get("duration").getAsFloat()
+ : null)
+ .setStart(
+ hasAndNotNull(jsonObject, "start")
+ ? jsonObject.get("start").getAsFloat()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/miscellaneous/AudioAnalysisMeta.java
@@ -0,0 +1,247 @@
+package com.wrapper.spotify.model_objects.miscellaneous;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about Audio Analysis Metadata objects by creating instances from this class. <br>
+ * These objects only contain metadata created by the Analyzer software.
+ */
+@JsonDeserialize(builder = AudioAnalysisMeta.Builder.class)
+public class AudioAnalysisMeta extends AbstractModelObject {
+ private final String analyzerVersion;
+ private final String platform;
+ private final String detailedStatus;
+ private final Integer statusCode;
+ private final Long timestamp;
+ private final Float analysisTime;
+ private final String inputProcess;
+
+ private AudioAnalysisMeta(final Builder builder) {
+ super(builder);
+
+ this.analyzerVersion = builder.analyzerVersion;
+ this.platform = builder.platform;
+ this.detailedStatus = builder.detailedStatus;
+ this.statusCode = builder.statusCode;
+ this.timestamp = builder.timestamp;
+ this.analysisTime = builder.analysisTime;
+ this.inputProcess = builder.inputProcess;
+ }
+
+ /**
+ * Get the version of the Analyzer software, which is used to create audio analysis data.
+ *
+ * @return Analyzer software version.
+ */
+ public String getAnalyzerVersion() {
+ return analyzerVersion;
+ }
+
+ /**
+ * Get the platform, on which the audio analysis was created.
+ *
+ * @return The platform name.
+ */
+ public String getPlatform() {
+ return platform;
+ }
+
+ /**
+ * Get the detailed status of the Analyzer software after creating the audio analysis.
+ *
+ * @return The detailed status of the Analyzer software.
+ */
+ public String getDetailedStatus() {
+ return detailedStatus;
+ }
+
+ /**
+ * Get the exit status code of the Analyzer software.
+ *
+ * @return The exit status code. (Should be 0)
+ */
+ public Integer getStatusCode() {
+ return statusCode;
+ }
+
+ /**
+ * Get the timestamp when the audio analysis object has been created by the Analyzer software.
+ *
+ * @return Timestamp of audio analysis.
+ */
+ public Long getTimestamp() {
+ return timestamp;
+ }
+
+ /**
+ * Get the duration of the audio analysis, eg. in how many seconds the audio analysis has been created by the
+ * software.
+ *
+ * @return Duration of the audio analysis.
+ */
+ public Float getAnalysisTime() {
+ return analysisTime;
+ }
+
+ /**
+ * Get the input process of the audio analysis. The input process is most times the command
+ * {@code libvorbisfile L+R 44100->22050}, which lowers the sample rate of the track. (probably to reduce the duration
+ * of the audio analysis)
+ *
+ * @return The input process of the audio analysis.
+ */
+ public String getInputProcess() {
+ return inputProcess;
+ }
+
+ @Override
+ public String toString() {
+ return "AudioAnalysisMeta(analyzerVersion=" + analyzerVersion + ", platform=" + platform + ", detailedStatus="
+ + detailedStatus + ", statusCode=" + statusCode + ", timestamp=" + timestamp + ", analysisTime=" + analysisTime
+ + ", inputProcess=" + inputProcess + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link AudioAnalysisMeta} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private String analyzerVersion;
+ private String platform;
+ private String detailedStatus;
+ private Integer statusCode;
+ private Long timestamp;
+ private Float analysisTime;
+ private String inputProcess;
+
+ /**
+ * The Analyzer software version setter.
+ *
+ * @param analyzerVersion Analyzer software version.
+ * @return An {@link AudioAnalysisMeta.Builder}.
+ */
+ public Builder setAnalyzerVersion(String analyzerVersion) {
+ this.analyzerVersion = analyzerVersion;
+ return this;
+ }
+
+ /**
+ * The platform setter.
+ *
+ * @param platform The platform name.
+ * @return An {@link AudioAnalysisMeta.Builder}.
+ */
+ public Builder setPlatform(String platform) {
+ this.platform = platform;
+ return this;
+ }
+
+ /**
+ * The detailed status setter.
+ *
+ * @param detailedStatus The detailed status of the Analyzer software.
+ * @return An {@link AudioAnalysisMeta.Builder}.
+ */
+ public Builder setDetailedStatus(String detailedStatus) {
+ this.detailedStatus = detailedStatus;
+ return this;
+ }
+
+ /**
+ * The status code setter.
+ *
+ * @param statusCode The exit status code. (Should be 0)
+ * @return An {@link AudioAnalysisMeta.Builder}.
+ */
+ public Builder setStatusCode(Integer statusCode) {
+ this.statusCode = statusCode;
+ return this;
+ }
+
+ /**
+ * The timestamp setter.
+ *
+ * @param timestamp Timestamp of audio analysis.
+ * @return An {@link AudioAnalysisMeta.Builder}.
+ */
+ public Builder setTimestamp(Long timestamp) {
+ this.timestamp = timestamp;
+ return this;
+ }
+
+ /**
+ * The analysis time setter.
+ *
+ * @param analysisTime Duration of the audio analysis.
+ * @return An {@link AudioAnalysisMeta.Builder}.
+ */
+ public Builder setAnalysisTime(Float analysisTime) {
+ this.analysisTime = analysisTime;
+ return this;
+ }
+
+ /**
+ * The input process setter.
+ *
+ * @param inputProcess The input process of the audio analysis.
+ * @return An {@link AudioAnalysisMeta.Builder}.
+ */
+ public Builder setInputProcess(String inputProcess) {
+ this.inputProcess = inputProcess;
+ return this;
+ }
+
+ @Override
+ public AudioAnalysisMeta build() {
+ return new AudioAnalysisMeta(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link AudioAnalysisMeta} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<AudioAnalysisMeta> {
+ public AudioAnalysisMeta createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new AudioAnalysisMeta.Builder()
+ .setAnalysisTime(
+ hasAndNotNull(jsonObject, "analysis_time")
+ ? jsonObject.get("analysis_time").getAsFloat()
+ : null)
+ .setAnalyzerVersion(
+ hasAndNotNull(jsonObject, "analyzer_version")
+ ? jsonObject.get("analyzer_version").getAsString()
+ : null)
+ .setDetailedStatus(
+ hasAndNotNull(jsonObject, "detailed_status")
+ ? jsonObject.get("detailed_status").getAsString()
+ : null)
+ .setInputProcess(
+ hasAndNotNull(jsonObject, "input_process")
+ ? jsonObject.get("input_process").getAsString()
+ : null)
+ .setPlatform(
+ hasAndNotNull(jsonObject, "platform")
+ ? jsonObject.get("platform").getAsString()
+ : null)
+ .setStatusCode(
+ hasAndNotNull(jsonObject, "status_code")
+ ? jsonObject.get("status_code").getAsInt()
+ : null)
+ .setTimestamp(
+ hasAndNotNull(jsonObject, "timestamp")
+ ? jsonObject.get("timestamp").getAsLong()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/miscellaneous/AudioAnalysisSection.java
@@ -0,0 +1,332 @@
+package com.wrapper.spotify.model_objects.miscellaneous;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.enums.Modality;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about Audio Analysis Section objects by creating instances from this class. <br>
+ * Sections are defined by large variations in rhythm or timbre, e.g. chorus, verse, bridge, guitar solo, etc. Each
+ * section contains its own descriptions of tempo, key, mode, time_signature, and loudness.
+ */
+@JsonDeserialize(builder = AudioAnalysisSection.Builder.class)
+public class AudioAnalysisSection extends AbstractModelObject {
+ private final AudioAnalysisMeasure measure;
+ private final Float loudness;
+ private final Float tempo;
+ private final Float tempoConfidence;
+ private final Integer key;
+ private final Float keyConfidence;
+ private final Modality mode;
+ private final Float modeConfidence;
+ private final Integer timeSignature;
+ private final Float timeSignatureConfidence;
+
+ private AudioAnalysisSection(final Builder builder) {
+ super(builder);
+
+ this.measure = builder.measure;
+ this.loudness = builder.loudness;
+ this.tempo = builder.tempo;
+ this.tempoConfidence = builder.tempoConfidence;
+ this.key = builder.key;
+ this.keyConfidence = builder.keyConfidence;
+ this.mode = builder.mode;
+ this.modeConfidence = builder.modeConfidence;
+ this.timeSignature = builder.timeSignature;
+ this.timeSignatureConfidence = builder.timeSignatureConfidence;
+ }
+
+ /**
+ * Get the measure of the audio analysis section object. This measure contains the start point, duration and
+ * confidence of the section.
+ *
+ * @return The measure of the audio analysis section object.
+ */
+ public AudioAnalysisMeasure getMeasure() {
+ return measure;
+ }
+
+ /**
+ * Get the (average) loudness of the section in decibels.
+ *
+ * @return The loudness of the section.
+ */
+ public Float getLoudness() {
+ return loudness;
+ }
+
+ /**
+ * Get the (estimated) tempo of the section in beats per minute.
+ *
+ * @return The tempo of the section.
+ */
+ public Float getTempo() {
+ return tempo;
+ }
+
+ /**
+ * Get the tempo confidence of the section.
+ *
+ * @return The tempo confidence of the section.
+ */
+ public Float getTempoConfidence() {
+ return tempoConfidence;
+ }
+
+ /**
+ * Get the main key of the section.
+ *
+ * @return Main key of the section.
+ * @see <a href="https://en.wikipedia.org/wiki/Pitch_class">Wikipedia: Pitch class notation</a>
+ */
+ public Integer getKey() {
+ return key;
+ }
+
+ /**
+ * Get the key confidence of the section.
+ *
+ * @return The key confidence of the section.
+ */
+ public Float getKeyConfidence() {
+ return keyConfidence;
+ }
+
+ /**
+ * Get the modality of the section. (either "major" or "minor")
+ *
+ * @return The modality type of the section.
+ * @see <a href="https://en.wikipedia.org/wiki/Mode_(music)">Wikipedia: Mode (music)</a>
+ */
+ public Modality getMode() {
+ return mode;
+ }
+
+ /**
+ * Get the modality confidence of the section.
+ *
+ * @return The modality confidence of the section.
+ */
+ public Float getModeConfidence() {
+ return modeConfidence;
+ }
+
+ /**
+ * Get the estimated overall time signature of the section. The time signature (or meter) is the number of beats in a
+ * bar. <br>
+ * Example: A Viennese waltz has a three-quarters beat, so this method would return the value 3 in this case.
+ *
+ * @return Time signature value of the section.
+ */
+ public Integer getTimeSignature() {
+ return timeSignature;
+ }
+
+ /**
+ * Get the time signature confidence of the section.
+ *
+ * @return The time signature confidence of the section.
+ */
+ public Float getTimeSignatureConfidence() {
+ return timeSignatureConfidence;
+ }
+
+ @Override
+ public String toString() {
+ return "AudioAnalysisSection(measure=" + measure + ", loudness=" + loudness + ", tempo=" + tempo
+ + ", tempoConfidence=" + tempoConfidence + ", key=" + key + ", keyConfidence=" + keyConfidence + ", mode="
+ + mode + ", modeConfidence=" + modeConfidence + ", timeSignature=" + timeSignature
+ + ", timeSignatureConfidence=" + timeSignatureConfidence + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link AudioAnalysisSection} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private AudioAnalysisMeasure measure;
+ private Float loudness;
+ private Float tempo;
+ private Float tempoConfidence;
+ private Integer key;
+ private Float keyConfidence;
+ private Modality mode;
+ private Float modeConfidence;
+ private Integer timeSignature;
+ private Float timeSignatureConfidence;
+
+ /**
+ * The measure setter.
+ *
+ * @param measure The measure of the audio analysis section object.
+ * @return An {@link AudioAnalysisSection.Builder}.
+ */
+ public Builder setMeasure(AudioAnalysisMeasure measure) {
+ this.measure = measure;
+ return this;
+ }
+
+ /**
+ * The loudness setter.
+ *
+ * @param loudness The loudness of the section.
+ * @return An {@link AudioAnalysisSection.Builder}.
+ */
+ public Builder setLoudness(Float loudness) {
+ this.loudness = loudness;
+ return this;
+ }
+
+ /**
+ * The tempo setter.
+ *
+ * @param tempo The tempo of the section.
+ * @return An {@link AudioAnalysisSection.Builder}.
+ */
+ public Builder setTempo(Float tempo) {
+ this.tempo = tempo;
+ return this;
+ }
+
+ /**
+ * The tempo confidence setter.
+ *
+ * @param tempoConfidence The tempo confidence of the section.
+ * @return An {@link AudioAnalysisSection.Builder}.
+ */
+ public Builder setTempoConfidence(Float tempoConfidence) {
+ this.tempoConfidence = tempoConfidence;
+ return this;
+ }
+
+ /**
+ * The key setter.
+ *
+ * @param key Main key of the section.
+ * @return An {@link AudioAnalysisSection.Builder}.
+ */
+ public Builder setKey(Integer key) {
+ this.key = key;
+ return this;
+ }
+
+ /**
+ * The key confidence setter.
+ *
+ * @param keyConfidence The key confidence of the section.
+ * @return An {@link AudioAnalysisSection.Builder}.
+ */
+ public Builder setKeyConfidence(Float keyConfidence) {
+ this.keyConfidence = keyConfidence;
+ return this;
+ }
+
+ /**
+ * The mode setter.
+ *
+ * @param mode The modality type of the section.
+ * @return An {@link AudioAnalysisSection.Builder}.
+ */
+ public Builder setMode(Modality mode) {
+ this.mode = mode;
+ return this;
+ }
+
+ /**
+ * The mode confidence setter.
+ *
+ * @param modeConfidence The modality confidence of the section.
+ * @return An {@link AudioAnalysisSection.Builder}.
+ */
+ public Builder setModeConfidence(Float modeConfidence) {
+ this.modeConfidence = modeConfidence;
+ return this;
+ }
+
+ /**
+ * The time signature setter.
+ *
+ * @param timeSignature Time signature value of the section.
+ * @return An {@link AudioAnalysisSection.Builder}.
+ */
+ public Builder setTimeSignature(Integer timeSignature) {
+ this.timeSignature = timeSignature;
+ return this;
+ }
+
+ /**
+ * The time signature confidence setter.
+ *
+ * @param timeSignatureConfidence The time signature confidence of the section.
+ * @return An {@link AudioAnalysisSection.Builder}.
+ */
+ public Builder setTimeSignatureConfidence(Float timeSignatureConfidence) {
+ this.timeSignatureConfidence = timeSignatureConfidence;
+ return this;
+ }
+
+ @Override
+ public AudioAnalysisSection build() {
+ return new AudioAnalysisSection(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link AudioAnalysisSection} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<AudioAnalysisSection> {
+ public AudioAnalysisSection createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new AudioAnalysisSection.Builder()
+ .setKey(
+ hasAndNotNull(jsonObject, "key")
+ ? jsonObject.get("key").getAsInt()
+ : null)
+ .setKeyConfidence(
+ hasAndNotNull(jsonObject, "key_confidence")
+ ? jsonObject.get("key_confidence").getAsFloat()
+ : null)
+ .setLoudness(
+ hasAndNotNull(jsonObject, "loudness")
+ ? jsonObject.get("loudness").getAsFloat()
+ : null)
+ .setMeasure(
+ new AudioAnalysisMeasure.JsonUtil().createModelObject(jsonObject))
+ .setMode(
+ hasAndNotNull(jsonObject, "type")
+ ? Modality.keyOf(
+ jsonObject.get("mode").getAsInt())
+ : null)
+ .setModeConfidence(
+ hasAndNotNull(jsonObject, "mode_confidence")
+ ? jsonObject.get("mode_confidence").getAsFloat()
+ : null)
+ .setTempo(
+ hasAndNotNull(jsonObject, "tempo")
+ ? jsonObject.get("tempo").getAsFloat()
+ : null)
+ .setTempoConfidence(
+ hasAndNotNull(jsonObject, "tempo_confidence")
+ ? jsonObject.get("tempo_confidence").getAsFloat()
+ : null)
+ .setTimeSignature(
+ hasAndNotNull(jsonObject, "time_signature")
+ ? jsonObject.get("time_signature").getAsInt()
+ : null)
+ .setTimeSignatureConfidence(
+ hasAndNotNull(jsonObject, "time_signature_confidence")
+ ? jsonObject.get("time_signature_confidence").getAsFloat()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/miscellaneous/AudioAnalysisSegment.java
@@ -0,0 +1,257 @@
+package com.wrapper.spotify.model_objects.miscellaneous;
+
+import java.util.Arrays;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about Audio Analysis Segments by creating instances from this class. <br>
+ * Segments are sound entities (typically under a second) each relatively uniform in timbre and harmony. They are
+ * characterized by their perceptual onsets and duration in seconds, loudness (dB), pitch and timbral content.
+ */
+@JsonDeserialize(builder = AudioAnalysisSegment.Builder.class)
+public class AudioAnalysisSegment extends AbstractModelObject {
+ private final AudioAnalysisMeasure measure;
+ private final Float loudnessStart;
+ private final Float loudnessMaxTime;
+ private final Float loudnessMax;
+ private final Float loudnessEnd;
+ private final float[] pitches;
+ private final float[] timbre;
+
+ private AudioAnalysisSegment(final Builder builder) {
+ super(builder);
+
+ this.measure = builder.measure;
+ this.loudnessStart = builder.loudnessStart;
+ this.loudnessMaxTime = builder.loudnessMaxTime;
+ this.loudnessMax = builder.loudnessMax;
+ this.loudnessEnd = builder.loudnessEnd;
+ this.pitches = builder.pitches;
+ this.timbre = builder.timbre;
+ }
+
+ /**
+ * Get the measure of the audio analysis segment object. This measure contains the start point, duration and
+ * confidence of the segment.
+ *
+ * @return The measure of the audio analysis segment object.
+ */
+ public AudioAnalysisMeasure getMeasure() {
+ return measure;
+ }
+
+ /**
+ * Get the loudness level at the start of the segment.
+ *
+ * @return The loudness level at the start of the segment.
+ */
+ public Float getLoudnessStart() {
+ return loudnessStart;
+ }
+
+ /**
+ * Get the offset within the segment of the point of maximum loudness.
+ *
+ * @return The offset within the segment of the point of maximum loudness.
+ */
+ public Float getLoudnessMaxTime() {
+ return loudnessMaxTime;
+ }
+
+ /**
+ * Get the peak loudness value within the segment.
+ *
+ * @return The peak loudness value within the segment.
+ */
+ public Float getLoudnessMax() {
+ return loudnessMax;
+ }
+
+ /**
+ * Get the loudness level at the end of the segment. This is only specified in the last segment of the audio analysis.
+ *
+ * @return The loudness level at the end of the segment.
+ */
+ public Float getLoudnessEnd() {
+ return loudnessEnd;
+ }
+
+ /**
+ * Get the pitches of the segment. <br><br>
+ * <p>
+ * Pitch content is given by a "chroma" vector, corresponding to the 12 pitch classes C, C#, D to B, with values
+ * ranging from 0 to 1 that describe the relative dominance of every pitch in the chromatic scale. For example a C
+ * Major chord would likely be represented by large values of C, E and G (i.e. classes 0, 4, and 7). Vectors are
+ * normalized to 1 by their strongest dimension, therefore noisy sounds are likely represented by values that are all
+ * close to 1, while pure tones are described by one value at 1 (the pitch) and others near
+ *
+ * @return The pitches of the segment.
+ */
+ public float[] getPitches() {
+ return pitches;
+ }
+
+ /**
+ * Get the timbre of the segment. <br><br>
+ * <p>
+ * The timbre is the quality of a musical note or sound that distinguishes different types of musical instruments, or
+ * voices. It is a complex notion also referred to as sound color, texture, or tone quality, and is derived from the
+ * shape of a segments spectro-temporal surface, independently of pitch and loudness.
+ *
+ * @return The timbre of the track.
+ */
+ public float[] getTimbre() {
+ return timbre;
+ }
+
+ @Override
+ public String toString() {
+ return "AudioAnalysisSegment(measure=" + measure + ", loudnessStart=" + loudnessStart + ", loudnessMaxTime="
+ + loudnessMaxTime + ", loudnessMax=" + loudnessMax + ", loudnessEnd=" + loudnessEnd + ", pitches="
+ + Arrays.toString(pitches) + ", timbre=" + Arrays.toString(timbre) + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link AudioAnalysisSegment} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private AudioAnalysisMeasure measure;
+ private Float loudnessStart;
+ private Float loudnessMaxTime;
+ private Float loudnessMax;
+ private Float loudnessEnd;
+ private float[] pitches;
+ private float[] timbre;
+
+ /**
+ * The measure setter.
+ *
+ * @param measure The measure of the audio analysis segment object.
+ * @return An {@link AudioAnalysisSegment.Builder}.
+ */
+ public Builder setMeasure(AudioAnalysisMeasure measure) {
+ this.measure = measure;
+ return this;
+ }
+
+ /**
+ * The start loudness setter.
+ *
+ * @param loudnessStart The loudness level at the start of the segment.
+ * @return An {@link AudioAnalysisSegment.Builder}.
+ */
+ public Builder setLoudnessStart(Float loudnessStart) {
+ this.loudnessStart = loudnessStart;
+ return this;
+ }
+
+ /**
+ * The max loudness time setter.
+ *
+ * @param loudnessMaxTime The offset within the segment of the point of maximum loudness.
+ * @return An {@link AudioAnalysisSegment.Builder}.
+ */
+ public Builder setLoudnessMaxTime(Float loudnessMaxTime) {
+ this.loudnessMaxTime = loudnessMaxTime;
+ return this;
+ }
+
+ /**
+ * The max loudness setter.
+ *
+ * @param loudnessMax The peak loudness value within the segment.
+ * @return An {@link AudioAnalysisSegment.Builder}.
+ */
+ public Builder setLoudnessMax(Float loudnessMax) {
+ this.loudnessMax = loudnessMax;
+ return this;
+ }
+
+ /**
+ * The end loudness setter.
+ *
+ * @param loudnessEnd The loudness level at the end of the segment.
+ * @return An {@link AudioAnalysisSegment.Builder}.
+ */
+ public Builder setLoudnessEnd(Float loudnessEnd) {
+ this.loudnessEnd = loudnessEnd;
+ return this;
+ }
+
+ /**
+ * The pitches setter.
+ *
+ * @param pitches The pitches of the segment.
+ * @return An {@link AudioAnalysisSegment.Builder}.
+ */
+ public Builder setPitches(float[] pitches) {
+ this.pitches = pitches;
+ return this;
+ }
+
+ /**
+ * The timbre setter.
+ *
+ * @param timbre The timbre of the track.
+ * @return An {@link AudioAnalysisSegment.Builder}.
+ */
+ public Builder setTimbre(float[] timbre) {
+ this.timbre = timbre;
+ return this;
+ }
+
+ @Override
+ public AudioAnalysisSegment build() {
+ return new AudioAnalysisSegment(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link AudioAnalysisSegment} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<AudioAnalysisSegment> {
+ public AudioAnalysisSegment createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new AudioAnalysisSegment.Builder()
+ .setLoudnessEnd(
+ hasAndNotNull(jsonObject, "loudness_end")
+ ? jsonObject.get("loudness_end").getAsFloat()
+ : null)
+ .setLoudnessMax(
+ hasAndNotNull(jsonObject, "loudness_max")
+ ? jsonObject.get("loudness_max").getAsFloat()
+ : null)
+ .setLoudnessMaxTime(
+ hasAndNotNull(jsonObject, "loudness_max_time")
+ ? jsonObject.get("loudness_max_time").getAsFloat()
+ : null)
+ .setLoudnessStart(
+ hasAndNotNull(jsonObject, "loudness_start")
+ ? jsonObject.get("loudness_start").getAsFloat()
+ : null)
+ .setMeasure(
+ new AudioAnalysisMeasure.JsonUtil().createModelObject(jsonObject))
+ .setPitches(
+ hasAndNotNull(jsonObject, "pitches")
+ ? new Gson().fromJson(jsonObject.getAsJsonArray("pitches"), float[].class)
+ : null)
+ .setTimbre(
+ hasAndNotNull(jsonObject, "timbre")
+ ? new Gson().fromJson(jsonObject.getAsJsonArray("timbre"), float[].class)
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/miscellaneous/AudioAnalysisTrack.java
@@ -0,0 +1,787 @@
+package com.wrapper.spotify.model_objects.miscellaneous;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.enums.Modality;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about Audio Analysis Track objects by creating instances from this class.
+ */
+@JsonDeserialize(builder = AudioAnalysisTrack.Builder.class)
+public class AudioAnalysisTrack extends AbstractModelObject {
+ private final Long numSamples;
+ private final Float duration;
+ private final String sampleMd5;
+ private final Integer offsetSeconds;
+ private final Integer windowSeconds;
+ private final Long analysisSampleRate;
+ private final Integer analysisChannels;
+ private final Float endOfFadeIn;
+ private final Float startOfFadeOut;
+ private final Float loudness;
+ private final Float tempo;
+ private final Float tempoConfidence;
+ private final Integer timeSignature;
+ private final Float timeSignatureConfidence;
+ private final Integer key;
+ private final Float keyConfidence;
+ private final Modality mode;
+ private final Float modeConfidence;
+ private final String codeString;
+ private final Float codeVersion;
+ private final String echoprintString;
+ private final Float echoprintVersion;
+ private final String synchString;
+ private final Float synchVersion;
+ private final String rhythmString;
+ private final Float rhythmVersion;
+
+ private AudioAnalysisTrack(final Builder builder) {
+ super(builder);
+ this.numSamples = builder.numSamples;
+ this.duration = builder.duration;
+ this.sampleMd5 = builder.sampleMd5;
+ this.offsetSeconds = builder.offsetSeconds;
+ this.windowSeconds = builder.windowSeconds;
+ this.analysisSampleRate = builder.analysisSampleRate;
+ this.analysisChannels = builder.analysisChannels;
+ this.endOfFadeIn = builder.endOfFadeIn;
+ this.startOfFadeOut = builder.startOfFadeOut;
+ this.loudness = builder.loudness;
+ this.tempo = builder.tempo;
+ this.tempoConfidence = builder.tempoConfidence;
+ this.timeSignature = builder.timeSignature;
+ this.timeSignatureConfidence = builder.timeSignatureConfidence;
+ this.key = builder.key;
+ this.keyConfidence = builder.keyConfidence;
+ this.mode = builder.mode;
+ this.modeConfidence = builder.modeConfidence;
+ this.codeString = builder.codeString;
+ this.codeVersion = builder.codeVersion;
+ this.echoprintString = builder.echoprintString;
+ this.echoprintVersion = builder.echoprintVersion;
+ this.synchString = builder.synchString;
+ this.synchVersion = builder.synchVersion;
+ this.rhythmString = builder.rhythmString;
+ this.rhythmVersion = builder.rhythmVersion;
+ }
+
+ /**
+ * Get the number of samples in the track. <br>
+ * The total number of samples is calculated by multiplying the duration of the track with the sample rate. <br>
+ *
+ * @return The total number of samples in the track.
+ */
+ public Long getNumSamples() {
+ return numSamples;
+ }
+
+ /**
+ * Get the duration of the track in seconds.
+ *
+ * @return The duration of the track in seconds.
+ */
+ public Float getDuration() {
+ return duration;
+ }
+
+ /**
+ * Get the sample MD5. <br><br>
+ * <p>
+ * <b>Note:</b> The sample MD5 is <b>probably</b> the MD5 of the track file. In the documentation of the Analyzer
+ * software, this field is mentioned in an example and contains a value, but it seems that audio analysis objects
+ * returned by the Spotify Web API doesn't include a value in the field anymore. <br>
+ *
+ * @return The sample MD5.
+ */
+ public String getSampleMd5() {
+ return sampleMd5;
+ }
+
+ /**
+ * Get the offset seconds. <br>
+ * <b>Note:</b> There is no public documentation available for this field. <br>
+ *
+ * @return The offset seconds.
+ */
+ public Integer getOffsetSeconds() {
+ return offsetSeconds;
+ }
+
+ /**
+ * Get the window seconds. <br>
+ * <b>Note:</b> There is no public documentation available for this field. <br>
+ *
+ * @return The window seconds.
+ */
+ public Integer getWindowSeconds() {
+ return windowSeconds;
+ }
+
+ /**
+ * Get the sample rate in which the audio analysis was performed.
+ *
+ * @return The analysis sample rate.
+ */
+ public Long getAnalysisSampleRate() {
+ return analysisSampleRate;
+ }
+
+ /**
+ * Get the analysis channels.
+ *
+ * @return The analysis channels.
+ */
+ public Integer getAnalysisChannels() {
+ return analysisChannels;
+ }
+
+ /**
+ * Get the end of fade in introduction of the track.
+ *
+ * @return The end of fade in introduction in seconds.
+ */
+ public Float getEndOfFadeIn() {
+ return endOfFadeIn;
+ }
+
+ /**
+ * Get the start of the fade out in seconds.
+ *
+ * @return The start of the fade out in seconds.
+ */
+ public Float getStartOfFadeOut() {
+ return startOfFadeOut;
+ }
+
+ /**
+ * Get the average loudness of the track in decibels. These values are mostly in a range between -60 and 0 decibels.
+ *
+ * @return Average loudness of the track.
+ */
+ public Float getLoudness() {
+ return loudness;
+ }
+
+ /**
+ * Get the estimated tempo of the track in beats per minute.
+ *
+ * @return The estimated tempo of the track.
+ */
+ public Float getTempo() {
+ return tempo;
+ }
+
+ /**
+ * Get the tempo confidence of the track.
+ *
+ * @return The tempo confidence of the track.
+ */
+ public Float getTempoConfidence() {
+ return tempoConfidence;
+ }
+
+ /**
+ * Get the estimated overall time signature of the track. The time signature (or meter) is the number of beats in a
+ * bar. <br>
+ * Example: A Viennese waltz has a three-quarters beat, so this method would return the value 3 in this case.
+ *
+ * @return Time signature value.
+ */
+ public Integer getTimeSignature() {
+ return timeSignature;
+ }
+
+ /**
+ * Get the time signature confidence of the track.
+ *
+ * @return The time signature confidence.
+ */
+ public Float getTimeSignatureConfidence() {
+ return timeSignatureConfidence;
+ }
+
+ /**
+ * Get the estimated main key of the track.
+ *
+ * @return Main key of the track.
+ * @see <a href="https://en.wikipedia.org/wiki/Pitch_class">Wikipedia: Pitch class notation</a>
+ */
+ public Integer getKey() {
+ return key;
+ }
+
+ /**
+ * Get the key confidence of the track.
+ *
+ * @return The key confidence of the track.
+ */
+ public Float getKeyConfidence() {
+ return keyConfidence;
+ }
+
+ /**
+ * Get the modality of the track. (either "major" or "minor")
+ *
+ * @return The modality type of the track.
+ * @see <a href="https://en.wikipedia.org/wiki/Mode_(music)">Wikipedia: Mode (music)</a>
+ */
+ public Modality getMode() {
+ return mode;
+ }
+
+ /**
+ * Get the modality confidence of the track.
+ *
+ * @return The modality confidence of the track.
+ */
+ public Float getModeConfidence() {
+ return modeConfidence;
+ }
+
+ /**
+ * Get the code string of the track. <br><br>
+ * <p>
+ * <b>Note:</b> The code string is a fingerprint computed on the audio and were used by the Echo Nest services for
+ * song identification, which are no longer available.
+ *
+ * @return The code string of the track.
+ */
+ public String getCodeString() {
+ return codeString;
+ }
+
+ /**
+ * Get the version of the code string.
+ *
+ * @return The version of the code string.
+ */
+ public Float getCodeVersion() {
+ return codeVersion;
+ }
+
+ /**
+ * Get the echoprint string of the track. <br><br>
+ * <p>
+ * <b>Note:</b> The echoprint string is a fingerprint computed on the audio and were used by the Echo Nest services
+ * for song identification, which are no longer available.
+ *
+ * @return The echoprint string of the track.
+ */
+ public String getEchoprintString() {
+ return echoprintString;
+ }
+
+ /**
+ * Get the version of the echoprint string.
+ *
+ * @return The version of the echoprint string.
+ */
+ public Float getEchoprintVersion() {
+ return echoprintVersion;
+ }
+
+ /**
+ * Get the synch string of the track. <br>
+ * <p>
+ * It works with a simple synchronization algorithm to be implemented on the client side, which generates offset
+ * values in numbers of samples for 3 locations in the decoded waveform, the beginning, the middle, and the end. These
+ * offsets allow the client application to detect decoding errors (when offsets mismatch). They provide for synching
+ * with sample accuracy, the JSON timing data with the waveform, regardless of which mp3 decoder was used on the
+ * client side (quicktime, ffmpeg, mpg123, etc).
+ *
+ * @return The synch string.
+ */
+ public String getSynchString() {
+ return synchString;
+ }
+
+ /**
+ * Get the version of the synch string.
+ *
+ * @return The synch string version.
+ */
+ public Float getSynchVersion() {
+ return synchVersion;
+ }
+
+ /**
+ * Get the rhythm string of the track.
+ * <p>
+ *
+ * @return The rhythm string of the track.
+ */
+ public String getRhythmString() {
+ return rhythmString;
+ }
+
+ /**
+ * Get the version of the rhythm string.
+ *
+ * @return The rhythm string version.
+ */
+ public Float getRhythmVersion() {
+ return rhythmVersion;
+ }
+
+ @Override
+ public String toString() {
+ return "AudioAnalysisTrack(numSamples=" + numSamples + ", duration=" + duration + ", sampleMd5=" + sampleMd5
+ + ", offsetSeconds=" + offsetSeconds + ", windowSeconds=" + windowSeconds + ", analysisSampleRate="
+ + analysisSampleRate + ", analysisChannels=" + analysisChannels + ", endOfFadeIn=" + endOfFadeIn
+ + ", startOfFadeOut=" + startOfFadeOut + ", loudness=" + loudness + ", tempo=" + tempo + ", tempoConfidence="
+ + tempoConfidence + ", timeSignature=" + timeSignature + ", timeSignatureConfidence=" + timeSignatureConfidence
+ + ", key=" + key + ", keyConfidence=" + keyConfidence + ", mode=" + mode + ", modeConfidence=" + modeConfidence
+ + ", codeString=" + codeString + ", codeVersion=" + codeVersion + ", echoprintString=" + echoprintString
+ + ", echoprintVersion=" + echoprintVersion + ", synchString=" + synchString + ", synchVersion=" + synchVersion
+ + ", rhythmString=" + rhythmString + ", rhythmVersion=" + rhythmVersion + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link AudioAnalysisTrack} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private Long numSamples;
+ private Float duration;
+ private String sampleMd5;
+ private Integer offsetSeconds;
+ private Integer windowSeconds;
+ private Long analysisSampleRate;
+ private Integer analysisChannels;
+ private Float endOfFadeIn;
+ private Float startOfFadeOut;
+ private Float loudness;
+ private Float tempo;
+ private Float tempoConfidence;
+ private Integer timeSignature;
+ private Float timeSignatureConfidence;
+ private Integer key;
+ private Float keyConfidence;
+ private Modality mode;
+ private Float modeConfidence;
+ private String codeString;
+ private Float codeVersion;
+ private String echoprintString;
+ private Float echoprintVersion;
+ private String synchString;
+ private Float synchVersion;
+ private String rhythmString;
+ private Float rhythmVersion;
+
+ /**
+ * The sample number setter.
+ *
+ * @param numSamples The total number of samples in the track.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setNumSamples(Long numSamples) {
+ this.numSamples = numSamples;
+ return this;
+ }
+
+ /**
+ * The track duration setter.
+ *
+ * @param duration The duration of the track in seconds.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setDuration(Float duration) {
+ this.duration = duration;
+ return this;
+ }
+
+ /**
+ * The sample MD5 setter.
+ *
+ * @param sampleMd5 The sample MD5.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setSampleMd5(String sampleMd5) {
+ this.sampleMd5 = sampleMd5;
+ return this;
+ }
+
+ /**
+ * The offset seconds setter.
+ *
+ * @param offsetSeconds The offset seconds.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setOffsetSeconds(Integer offsetSeconds) {
+ this.offsetSeconds = offsetSeconds;
+ return this;
+ }
+
+ /**
+ * The window seconds setter.
+ *
+ * @param windowSeconds The window seconds.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setWindowSeconds(Integer windowSeconds) {
+ this.windowSeconds = windowSeconds;
+ return this;
+ }
+
+ /**
+ * The analysis sample rate setter.
+ *
+ * @param analysisSampleRate The analysis sample rate.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setAnalysisSampleRate(Long analysisSampleRate) {
+ this.analysisSampleRate = analysisSampleRate;
+ return this;
+ }
+
+ /**
+ * The analysis channels setter.
+ *
+ * @param analysisChannels The analysis channels.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setAnalysisChannels(Integer analysisChannels) {
+ this.analysisChannels = analysisChannels;
+ return this;
+ }
+
+ /**
+ * The end of fade in introduction setter.
+ *
+ * @param endOfFadeIn The end of fade in introduction in seconds.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setEndOfFadeIn(Float endOfFadeIn) {
+ this.endOfFadeIn = endOfFadeIn;
+ return this;
+ }
+
+ /**
+ * The start of fade out setter.
+ *
+ * @param startOfFadeOut The start of the fade out in seconds.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setStartOfFadeOut(Float startOfFadeOut) {
+ this.startOfFadeOut = startOfFadeOut;
+ return this;
+ }
+
+ /**
+ * The average loudness setter.
+ *
+ * @param loudness Average loudness of the track.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setLoudness(Float loudness) {
+ this.loudness = loudness;
+ return this;
+ }
+
+ /**
+ * The estimated tempo setter.
+ *
+ * @param tempo The estimated tempo of the track.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setTempo(Float tempo) {
+ this.tempo = tempo;
+ return this;
+ }
+
+ /**
+ * The tempo confidence setter.
+ *
+ * @param tempoConfidence The tempo confidence of the track.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setTempoConfidence(Float tempoConfidence) {
+ this.tempoConfidence = tempoConfidence;
+ return this;
+ }
+
+ /**
+ * The time signature setter.
+ *
+ * @param timeSignature Time signature value.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setTimeSignature(Integer timeSignature) {
+ this.timeSignature = timeSignature;
+ return this;
+ }
+
+ /**
+ * The time signature confidence setter.
+ *
+ * @param timeSignatureConfidence The time signature confidence.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setTimeSignatureConfidence(Float timeSignatureConfidence) {
+ this.timeSignatureConfidence = timeSignatureConfidence;
+ return this;
+ }
+
+ /**
+ * The track key setter.
+ *
+ * @param key Main key of the track.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setKey(Integer key) {
+ this.key = key;
+ return this;
+ }
+
+ /**
+ * The key confidence setter.
+ *
+ * @param keyConfidence The key confidence of the track.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setKeyConfidence(Float keyConfidence) {
+ this.keyConfidence = keyConfidence;
+ return this;
+ }
+
+ /**
+ * The track modality setter.
+ *
+ * @param mode The modality type of the track.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setMode(Modality mode) {
+ this.mode = mode;
+ return this;
+ }
+
+ /**
+ * The modality confidence setter.
+ *
+ * @param modeConfidence The modality confidence of the track.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setModeConfidence(Float modeConfidence) {
+ this.modeConfidence = modeConfidence;
+ return this;
+ }
+
+ /**
+ * The code string setter.
+ *
+ * @param codeString The code string of the track.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setCodeString(String codeString) {
+ this.codeString = codeString;
+ return this;
+ }
+
+ /**
+ * The code string version setter.
+ *
+ * @param codeVersion The version of the code string.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setCodeVersion(Float codeVersion) {
+ this.codeVersion = codeVersion;
+ return this;
+ }
+
+ /**
+ * The echoprint string setter.
+ *
+ * @param echoprintString The echoprint string of the track.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setEchoprintString(String echoprintString) {
+ this.echoprintString = echoprintString;
+ return this;
+ }
+
+ /**
+ * The echoprint string version setter.
+ *
+ * @param echoprintVersion The version of the echoprint string.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setEchoprintVersion(Float echoprintVersion) {
+ this.echoprintVersion = echoprintVersion;
+ return this;
+ }
+
+ /**
+ * The synch string setter.
+ *
+ * @param synchString The synch string.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setSynchString(String synchString) {
+ this.synchString = synchString;
+ return this;
+ }
+
+ /**
+ * The synch string version setter.
+ *
+ * @param synchVersion The synch string version.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setSynchVersion(Float synchVersion) {
+ this.synchVersion = synchVersion;
+ return this;
+ }
+
+ /**
+ * The rhythm string setter.
+ *
+ * @param rhythmString The rhythm string of the track.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setRhythmString(String rhythmString) {
+ this.rhythmString = rhythmString;
+ return this;
+ }
+
+ /**
+ * The rhythm string version setter.
+ *
+ * @param rhythmVersion The rhythm string version.
+ * @return An {@link AudioAnalysisTrack.Builder}.
+ */
+ public Builder setRhythmVersion(Float rhythmVersion) {
+ this.rhythmVersion = rhythmVersion;
+ return this;
+ }
+
+ @Override
+ public AudioAnalysisTrack build() {
+ return new AudioAnalysisTrack(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link AudioAnalysisTrack} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<AudioAnalysisTrack> {
+ public AudioAnalysisTrack createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new AudioAnalysisTrack.Builder()
+ .setAnalysisChannels(
+ hasAndNotNull(jsonObject, "analysis_channels")
+ ? jsonObject.get("analysis_channels").getAsInt()
+ : null)
+ .setAnalysisSampleRate(
+ hasAndNotNull(jsonObject, "analysis_sample_rate")
+ ? jsonObject.get("analysis_sample_rate").getAsLong()
+ : null)
+ .setCodeString(
+ hasAndNotNull(jsonObject, "code_string")
+ ? jsonObject.get("code_string").getAsString()
+ : null)
+ .setCodeVersion(
+ hasAndNotNull(jsonObject, "code_version")
+ ? jsonObject.get("code_version").getAsFloat()
+ : null)
+ .setDuration(
+ hasAndNotNull(jsonObject, "duration")
+ ? jsonObject.get("duration").getAsFloat()
+ : null)
+ .setEchoprintString(
+ hasAndNotNull(jsonObject, "echoprintstring")
+ ? jsonObject.get("echoprintstring").getAsString()
+ : null)
+ .setEchoprintVersion(
+ hasAndNotNull(jsonObject, "echoprint_version")
+ ? jsonObject.get("echoprint_version").getAsFloat()
+ : null)
+ .setEndOfFadeIn(
+ hasAndNotNull(jsonObject, "end_of_face_in")
+ ? jsonObject.get("end_of_face_in").getAsFloat()
+ : null)
+ .setKey(
+ hasAndNotNull(jsonObject, "key")
+ ? jsonObject.get("key").getAsInt()
+ : null)
+ .setKeyConfidence(
+ hasAndNotNull(jsonObject, "key_confidence")
+ ? jsonObject.get("key_confidence").getAsFloat()
+ : null)
+ .setLoudness(
+ hasAndNotNull(jsonObject, "loudness")
+ ? jsonObject.get("loudness").getAsFloat()
+ : null)
+ .setMode(
+ hasAndNotNull(jsonObject, "type")
+ ? Modality.keyOf(
+ jsonObject.get("mode").getAsInt())
+ : null)
+ .setModeConfidence(
+ hasAndNotNull(jsonObject, "mode_confidence")
+ ? jsonObject.get("mode_confidence").getAsFloat()
+ : null)
+ .setNumSamples(
+ hasAndNotNull(jsonObject, "num_samples")
+ ? jsonObject.get("num_samples").getAsLong()
+ : null)
+ .setOffsetSeconds(
+ hasAndNotNull(jsonObject, "offset_seconds")
+ ? jsonObject.get("offset_seconds").getAsInt()
+ : null)
+ .setRhythmString(
+ hasAndNotNull(jsonObject, "rhythmstring")
+ ? jsonObject.get("rhythmstring").getAsString()
+ : null)
+ .setRhythmVersion(
+ hasAndNotNull(jsonObject, "rhythm_version")
+ ? jsonObject.get("rhythm_version").getAsFloat()
+ : null)
+ .setSampleMd5(
+ hasAndNotNull(jsonObject, "sample_md5")
+ ? jsonObject.get("sample_md5").getAsString()
+ : null)
+ .setStartOfFadeOut(
+ hasAndNotNull(jsonObject, "start_of_fade_out")
+ ? jsonObject.get("start_of_fade_out").getAsFloat()
+ : null)
+ .setSynchString(
+ hasAndNotNull(jsonObject, "synchstring")
+ ? jsonObject.get("synchstring").getAsString()
+ : null)
+ .setSynchVersion(
+ hasAndNotNull(jsonObject, "synch_version")
+ ? jsonObject.get("synch_version").getAsFloat()
+ : null)
+ .setTempo(
+ hasAndNotNull(jsonObject, "tempo")
+ ? jsonObject.get("tempo").getAsFloat()
+ : null)
+ .setTempoConfidence(
+ hasAndNotNull(jsonObject, "tempo_confidence")
+ ? jsonObject.get("tempo_confidence").getAsFloat()
+ : null)
+ .setTimeSignature(
+ hasAndNotNull(jsonObject, "time_signature")
+ ? jsonObject.get("time_signature").getAsInt()
+ : null)
+ .setTimeSignatureConfidence(
+ hasAndNotNull(jsonObject, "time_signature_confidence")
+ ? jsonObject.get("time_signature_confidence").getAsFloat()
+ : null)
+ .setWindowSeconds(
+ hasAndNotNull(jsonObject, "windows_seconds")
+ ? jsonObject.get("windows_seconds").getAsInt()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/miscellaneous/CurrentlyPlaying.java
@@ -0,0 +1,258 @@
+package com.wrapper.spotify.model_objects.miscellaneous;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.enums.CurrentlyPlayingType;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+import com.wrapper.spotify.model_objects.IPlaylistItem;
+import com.wrapper.spotify.model_objects.special.Actions;
+import com.wrapper.spotify.model_objects.specification.Context;
+import com.wrapper.spotify.model_objects.specification.Disallows;
+import com.wrapper.spotify.model_objects.specification.Episode;
+import com.wrapper.spotify.model_objects.specification.Track;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/get-the-users-currently-playing-track/">
+ * Currently Playing objects</a> by creating instances from this class.
+ */
+@JsonDeserialize(builder = CurrentlyPlaying.Builder.class)
+public class CurrentlyPlaying extends AbstractModelObject {
+ private final Context context;
+ private final Long timestamp;
+ private final Integer progress_ms;
+ private final Boolean is_playing;
+ private final IPlaylistItem item;
+ private final CurrentlyPlayingType currentlyPlayingType;
+ private final Actions actions;
+
+ private CurrentlyPlaying(final Builder builder) {
+ super(builder);
+
+ this.context = builder.context;
+ this.timestamp = builder.timestamp;
+ this.progress_ms = builder.progress_ms;
+ this.is_playing = builder.is_playing;
+ this.item = builder.item;
+ this.currentlyPlayingType = builder.currentlyPlayingType;
+ this.actions = builder.actions;
+ }
+
+ /**
+ * Get the context the item was played from.
+ *
+ * @return The context the item was played from. Can be {@code null}.
+ */
+ public Context getContext() {
+ return context;
+ }
+
+ /**
+ * Get the timestamp when the received data was fetched.
+ *
+ * @return Unix Millisecond Timestamp when data was fetched.
+ */
+ public Long getTimestamp() {
+ return timestamp;
+ }
+
+ /**
+ * Get the progress of the current played item.
+ *
+ * @return Progress into the currently playing item. Can be {@code null}.
+ */
+ public Integer getProgress_ms() {
+ return progress_ms;
+ }
+
+ /**
+ * Check if something is played at the moment.
+ *
+ * @return If something is currently playing.
+ */
+ public Boolean getIs_playing() {
+ return is_playing;
+ }
+
+ /**
+ * Get the currently played track or episode.
+ *
+ * @return The currently playing track or episode. Can be {@code null}.
+ */
+ public IPlaylistItem getItem() {
+ return item;
+ }
+
+ /**
+ * Get the type of the currently playing item.
+ *
+ * @return The type of the currently playing item.
+ */
+ public CurrentlyPlayingType getCurrentlyPlayingType() {
+ return currentlyPlayingType;
+ }
+
+ /**
+ * Get which playback actions are available within the current context.
+ *
+ * @return A {@link Actions} object which contains a {@link Disallows} object.
+ */
+ public Actions getActions() {
+ return actions;
+ }
+
+ @Override
+ public String toString() {
+ return "CurrentlyPlaying(context=" + context + ", timestamp=" + timestamp + ", progress_ms=" + progress_ms
+ + ", is_playing=" + is_playing + ", item=" + item + ", currentlyPlayingType=" + currentlyPlayingType
+ + ", actions=" + actions + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link CurrentlyPlaying} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private Context context;
+ private Long timestamp;
+ private Integer progress_ms;
+ private Boolean is_playing;
+ private IPlaylistItem item;
+ private CurrentlyPlayingType currentlyPlayingType;
+ private Actions actions;
+
+ /**
+ * The playing context setter.
+ *
+ * @param context The context the track was played from. Can be {@code null}.
+ * @return A {@link CurrentlyPlaying.Builder}.
+ */
+ public Builder setContext(Context context) {
+ this.context = context;
+ return this;
+ }
+
+ /**
+ * The timestamp setter.
+ *
+ * @param timestamp Unix Millisecond Timestamp when data was fetched.
+ * @return A {@link CurrentlyPlaying.Builder}.
+ */
+ public Builder setTimestamp(Long timestamp) {
+ this.timestamp = timestamp;
+ return this;
+ }
+
+ /**
+ * The current track progress setter.
+ *
+ * @param progress_ms Progress into the currently playing track. Can be {@code null}.
+ * @return A {@link CurrentlyPlaying.Builder}.
+ */
+ public Builder setProgress_ms(Integer progress_ms) {
+ this.progress_ms = progress_ms;
+ return this;
+ }
+
+ /**
+ * The playing state setter.
+ *
+ * @param is_playing If something is currently playing.
+ * @return A {@link CurrentlyPlaying.Builder}.
+ */
+ public Builder setIs_playing(Boolean is_playing) {
+ this.is_playing = is_playing;
+ return this;
+ }
+
+ /**
+ * The currently playing item setter.
+ *
+ * @param item The currently playing item. Can be {@code null}.
+ * @return A {@link CurrentlyPlaying.Builder}.
+ */
+ public Builder setItem(IPlaylistItem item) {
+ this.item = item;
+ return this;
+ }
+
+ /**
+ * The currently playing type setter.
+ *
+ * @param currentlyPlayingType The type of the currently playing item.
+ * @return A {@link CurrentlyPlaying.Builder}.
+ */
+ public Builder setCurrentlyPlayingType(CurrentlyPlayingType currentlyPlayingType) {
+ this.currentlyPlayingType = currentlyPlayingType;
+ return this;
+ }
+
+ /**
+ * The actions setter.
+ *
+ * @param actions A {@link Actions} object which contains a {@link Disallows} object.
+ * @return A {@link CurrentlyPlaying.Builder}.
+ */
+ public Builder setActions(Actions actions) {
+ this.actions = actions;
+ return this;
+ }
+
+ @Override
+ public CurrentlyPlaying build() {
+ return new CurrentlyPlaying(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link CurrentlyPlaying} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<CurrentlyPlaying> {
+ public CurrentlyPlaying createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new CurrentlyPlaying.Builder()
+ .setContext(
+ hasAndNotNull(jsonObject, "context")
+ ? new Context.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("context"))
+ : null)
+ .setTimestamp(
+ hasAndNotNull(jsonObject, "timestamp")
+ ? jsonObject.get("timestamp").getAsLong()
+ : null)
+ .setProgress_ms(
+ hasAndNotNull(jsonObject, "progress_ms")
+ ? jsonObject.get("progress_ms").getAsInt()
+ : null)
+ .setIs_playing(
+ hasAndNotNull(jsonObject, "is_playing")
+ ? jsonObject.get("is_playing").getAsBoolean()
+ : null)
+ .setItem(
+ hasAndNotNull(jsonObject, "item") && hasAndNotNull(jsonObject, "currently_playing_type")
+ ? (jsonObject.get("currently_playing_type").getAsString().equals("track")
+ ? new Track.JsonUtil().createModelObject(jsonObject.getAsJsonObject("item"))
+ : jsonObject.get("currently_playing_type").getAsString().equals("episode")
+ ? new Episode.JsonUtil().createModelObject(jsonObject.getAsJsonObject("item"))
+ : null)
+ : null)
+ .setCurrentlyPlayingType(
+ hasAndNotNull(jsonObject, "currently_playing_type")
+ ? CurrentlyPlayingType.keyOf(
+ jsonObject.get("currently_playing_type").getAsString().toLowerCase())
+ : null)
+ .setActions(
+ hasAndNotNull(jsonObject, "actions")
+ ? new Actions.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("actions"))
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/miscellaneous/CurrentlyPlayingContext.java
@@ -0,0 +1,342 @@
+package com.wrapper.spotify.model_objects.miscellaneous;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.enums.CurrentlyPlayingType;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+import com.wrapper.spotify.model_objects.IPlaylistItem;
+import com.wrapper.spotify.model_objects.special.Actions;
+import com.wrapper.spotify.model_objects.specification.Context;
+import com.wrapper.spotify.model_objects.specification.Disallows;
+import com.wrapper.spotify.model_objects.specification.Episode;
+import com.wrapper.spotify.model_objects.specification.Track;
+
+/**
+ * Retrieve information about
+ * <a href="https://developer.spotify.com/web-api/get-information-about-the-users-current-playback/">Currently Playing
+ * Context objects</a> by creating instances from this class.
+ */
+@JsonDeserialize(builder = CurrentlyPlayingContext.Builder.class)
+public class CurrentlyPlayingContext extends AbstractModelObject {
+ private final Device device;
+ private final String repeat_state;
+ private final Boolean shuffle_state;
+ private final Context context;
+ private final Long timestamp;
+ private final Integer progress_ms;
+ private final Boolean is_playing;
+ private final IPlaylistItem item;
+ private final CurrentlyPlayingType currentlyPlayingType;
+ private final Actions actions;
+
+ private CurrentlyPlayingContext(final Builder builder) {
+ super(builder);
+
+ this.device = builder.device;
+ this.repeat_state = builder.repeat_state;
+ this.shuffle_state = builder.shuffle_state;
+ this.context = builder.context;
+ this.timestamp = builder.timestamp;
+ this.progress_ms = builder.progress_ms;
+ this.is_playing = builder.is_playing;
+ this.item = builder.item;
+ this.currentlyPlayingType = builder.currentlyPlayingType;
+ this.actions = builder.actions;
+ }
+
+ /**
+ * Get the currently active device.
+ *
+ * @return The device that is currently active.
+ */
+ public Device getDevice() {
+ return device;
+ }
+
+ /**
+ * Get the repeat state of the device. (No repeat, track repeat, context repeat)
+ *
+ * @return The repeat state.
+ */
+ public String getRepeat_state() {
+ return repeat_state;
+ }
+
+ /**
+ * Get the shuffle state of the device.
+ *
+ * @return If shuffle is on or off.
+ */
+ public Boolean getShuffle_state() {
+ return shuffle_state;
+ }
+
+ /**
+ * Get the context from where the currently playing item is played from.
+ *
+ * @return A Context Object. Can be {@code null}.
+ */
+ public Context getContext() {
+ return context;
+ }
+
+ /**
+ * Get the Unix timestamp in milliseconds when the time was fetched.
+ *
+ * @return Unix Millisecond Timestamp when data was fetched.
+ */
+ public Long getTimestamp() {
+ return timestamp;
+ }
+
+ /**
+ * Get the progress of the currently playing item.
+ *
+ * @return Progress into the currently playing item. Can be {@code null}.
+ */
+ public Integer getProgress_ms() {
+ return progress_ms;
+ }
+
+ /**
+ * Check whether a track or episode is playing on the device or not.
+ *
+ * @return If something is currently playing.
+ */
+ public Boolean getIs_playing() {
+ return is_playing;
+ }
+
+ /**
+ * Get the currently playing track or episode, if the device is playing something.
+ *
+ * @return The currently playing item. Can be {@code null}.
+ */
+ public IPlaylistItem getItem() {
+ return item;
+ }
+
+ /**
+ * Get the type of the currently playing item.
+ *
+ * @return The type of the currently playing item.
+ */
+ public CurrentlyPlayingType getCurrentlyPlayingType() {
+ return currentlyPlayingType;
+ }
+
+ /**
+ * Get which playback actions are available within the current context.
+ *
+ * @return A {@link Actions} object which contains a {@link Disallows} object.
+ */
+ public Actions getActions() {
+ return actions;
+ }
+
+ @Override
+ public String toString() {
+ return "CurrentlyPlayingContext(device=" + device + ", repeat_state=" + repeat_state + ", shuffle_state="
+ + shuffle_state + ", context=" + context + ", timestamp=" + timestamp + ", progress_ms=" + progress_ms
+ + ", is_playing=" + is_playing + ", item=" + item + ", currentlyPlayingType=" + currentlyPlayingType
+ + ", actions=" + actions + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link CurrentlyPlayingContext} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private Device device;
+ private String repeat_state;
+ private Boolean shuffle_state;
+ private Context context;
+ private Long timestamp;
+ private Integer progress_ms;
+ private Boolean is_playing;
+ private IPlaylistItem item;
+ private CurrentlyPlayingType currentlyPlayingType;
+ private Actions actions;
+
+ /**
+ * The active device setter.
+ *
+ * @param device The device that is currently active.
+ * @return A {@link CurrentlyPlayingContext.Builder}.
+ */
+ public Builder setDevice(Device device) {
+ this.device = device;
+ return this;
+ }
+
+ /**
+ * The repeat state setter.
+ *
+ * @param repeat_state The repeat state.
+ * @return A {@link CurrentlyPlayingContext.Builder}.
+ */
+ public Builder setRepeat_state(String repeat_state) {
+ this.repeat_state = repeat_state;
+ return this;
+ }
+
+ /**
+ * The shuffle state setter.
+ *
+ * @param shuffle_state If shuffle is on or off.
+ * @return A {@link CurrentlyPlayingContext.Builder}.
+ */
+ public Builder setShuffle_state(Boolean shuffle_state) {
+ this.shuffle_state = shuffle_state;
+ return this;
+ }
+
+ /**
+ * The playing context setter.
+ *
+ * @param context A Context Object. Can be {@code null}.
+ * @return A {@link CurrentlyPlayingContext.Builder}.
+ */
+ public Builder setContext(Context context) {
+ this.context = context;
+ return this;
+ }
+
+ /**
+ * The timestamp setter.
+ *
+ * @param timestamp Unix Millisecond Timestamp when data was fetched.
+ * @return A {@link CurrentlyPlayingContext.Builder}.
+ */
+ public Builder setTimestamp(Long timestamp) {
+ this.timestamp = timestamp;
+ return this;
+ }
+
+ /**
+ * The item progress setter.
+ *
+ * @param progress_ms Progress into the currently playing item. Can be {@code null}.
+ * @return A {@link CurrentlyPlayingContext.Builder}.
+ */
+ public Builder setProgress_ms(Integer progress_ms) {
+ this.progress_ms = progress_ms;
+ return this;
+ }
+
+ /**
+ * The playing state setter.
+ *
+ * @param is_playing If something is currently playing.
+ * @return A {@link CurrentlyPlayingContext.Builder}.
+ */
+ public Builder setIs_playing(Boolean is_playing) {
+ this.is_playing = is_playing;
+ return this;
+ }
+
+ /**
+ * The currently playing item setter.
+ *
+ * @param item If something is currently playing.
+ * @return A {@link CurrentlyPlayingContext.Builder}.
+ */
+ public Builder setItem(IPlaylistItem item) {
+ this.item = item;
+ return this;
+ }
+
+ /**
+ * The currently playing type setter.
+ *
+ * @param currentlyPlayingType The type of the currently playing item.
+ * @return A {@link CurrentlyPlayingContext.Builder}.
+ */
+ public Builder setCurrentlyPlayingType(CurrentlyPlayingType currentlyPlayingType) {
+ this.currentlyPlayingType = currentlyPlayingType;
+ return this;
+ }
+
+ /**
+ * The actions setter.
+ *
+ * @param actions A {@link Actions} object which contains a {@link Disallows} object.
+ * @return A {@link CurrentlyPlayingContext.Builder}.
+ */
+ public Builder setActions(Actions actions) {
+ this.actions = actions;
+ return this;
+ }
+
+ @Override
+ public CurrentlyPlayingContext build() {
+ return new CurrentlyPlayingContext(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link CurrentlyPlayingContext} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<CurrentlyPlayingContext> {
+ public CurrentlyPlayingContext createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Builder()
+ .setDevice(
+ hasAndNotNull(jsonObject, "device")
+ ? new Device.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("device"))
+ : null)
+ .setRepeat_state(
+ hasAndNotNull(jsonObject, "repeat_state")
+ ? jsonObject.get("repeat_state").getAsString()
+ : null)
+ .setShuffle_state(
+ hasAndNotNull(jsonObject, "shuffle_state")
+ ? jsonObject.get("shuffle_state").getAsBoolean()
+ : null)
+ .setContext(
+ hasAndNotNull(jsonObject, "context")
+ ? new Context.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("context"))
+ : null)
+ .setTimestamp(
+ hasAndNotNull(jsonObject, "timestamp")
+ ? jsonObject.get("timestamp").getAsLong()
+ : null)
+ .setProgress_ms(
+ hasAndNotNull(jsonObject, "progress_ms")
+ ? jsonObject.get("progress_ms").getAsInt()
+ : null)
+ .setIs_playing(
+ hasAndNotNull(jsonObject, "is_playing")
+ ? jsonObject.get("is_playing").getAsBoolean()
+ : null)
+ .setItem(
+ hasAndNotNull(jsonObject, "item") && hasAndNotNull(jsonObject, "currently_playing_type")
+ ? (jsonObject.get("currently_playing_type").getAsString().equals("track")
+ ? new Track.JsonUtil().createModelObject(jsonObject.getAsJsonObject("item"))
+ : jsonObject.get("currently_playing_type").getAsString().equals("episode")
+ ? new Episode.JsonUtil().createModelObject(jsonObject.getAsJsonObject("item"))
+ : null)
+ : null)
+ .setCurrentlyPlayingType(
+ hasAndNotNull(jsonObject, "currently_playing_type")
+ ? CurrentlyPlayingType.keyOf(
+ jsonObject.get("currently_playing_type").getAsString().toLowerCase())
+ : null)
+ .setActions(
+ hasAndNotNull(jsonObject, "actions")
+ ? new Actions.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("actions"))
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/miscellaneous/Device.java
@@ -0,0 +1,216 @@
+package com.wrapper.spotify.model_objects.miscellaneous;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/get-a-users-available-devices/">Device
+ * objects</a> by creating instances from this class.
+ */
+@JsonDeserialize(builder = Device.Builder.class)
+public class Device extends AbstractModelObject {
+ private final String id;
+ private final Boolean is_active;
+ private final Boolean is_restricted;
+ private final String name;
+ private final String type;
+ private final Integer volume_percent;
+
+ private Device(final Builder builder) {
+ super(builder);
+
+ this.id = builder.id;
+ this.is_active = builder.is_active;
+ this.is_restricted = builder.is_restricted;
+ this.name = builder.name;
+ this.type = builder.type;
+ this.volume_percent = builder.volume_percent;
+ }
+
+ /**
+ * Get the ID of the device.
+ *
+ * @return The device ID. This may be {@code null}.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Check whether the device is the currently active device.
+ *
+ * @return If this device is the currently active device.
+ */
+ public Boolean getIs_active() {
+ return is_active;
+ }
+
+ /**
+ * Check whether the device is restricted or not. Restricted devices don't accept Spotify Web API calls.
+ *
+ * @return Whether controlling this device is restricted.
+ */
+ public Boolean getIs_restricted() {
+ return is_restricted;
+ }
+
+ /**
+ * Get the name of the device.
+ *
+ * @return The name of the device.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the type of the device.
+ *
+ * @return Device type, such as "Computer", "Smartphone" or "Speaker".
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * Get the current volume of the device in percent.
+ *
+ * @return The current volume in percent. This may be {@code null}.
+ */
+ public Integer getVolume_percent() {
+ return volume_percent;
+ }
+
+ @Override
+ public String toString() {
+ return "Device(id=" + id + ", is_active=" + is_active + ", is_restricted=" + is_restricted + ", name=" + name
+ + ", type=" + type + ", volume_percent=" + volume_percent + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link Device} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private String id;
+ private Boolean is_active;
+ private Boolean is_restricted;
+ private String name;
+ private String type;
+ private Integer volume_percent;
+
+ /**
+ * The device ID setter.
+ *
+ * @param id The device ID. This may be {@code null}.
+ * @return A {@link Device.Builder}.
+ */
+ public Builder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * The active device state setter.
+ *
+ * @param is_active If this device is the currently active device.
+ * @return A {@link Device.Builder}.
+ */
+ public Builder setIs_active(Boolean is_active) {
+ this.is_active = is_active;
+ return this;
+ }
+
+ /**
+ * The device restriction state setter.
+ *
+ * @param is_restricted Whether controlling this device is restricted.
+ * @return A {@link Device.Builder}.
+ */
+ public Builder setIs_restricted(Boolean is_restricted) {
+ this.is_restricted = is_restricted;
+ return this;
+ }
+
+ /**
+ * The device name setter.
+ *
+ * @param name The name of the device.
+ * @return A {@link Device.Builder}.
+ */
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * The device type setter.
+ *
+ * @param type Device type, such as "Computer", "Smartphone" or "Speaker".
+ * @return A {@link Device.Builder}.
+ */
+ public Builder setType(String type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * The device volume setter.
+ *
+ * @param volume_percent The current volume in percent. This may be {@code null}.
+ * @return A {@link Device.Builder}.
+ */
+ public Builder setVolume_percent(Integer volume_percent) {
+ this.volume_percent = volume_percent;
+ return this;
+ }
+
+ @Override
+ public Device build() {
+ return new Device(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link Device} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<Device> {
+ public Device createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Device.Builder()
+ .setId(
+ hasAndNotNull(jsonObject, "id")
+ ? jsonObject.get("id").getAsString()
+ : null)
+ .setIs_active(
+ hasAndNotNull(jsonObject, "is_active")
+ ? jsonObject.get("is_active").getAsBoolean()
+ : null)
+ .setIs_restricted(
+ hasAndNotNull(jsonObject, "is_restricted")
+ ? jsonObject.get("is_restricted").getAsBoolean()
+ : null)
+ .setName(
+ hasAndNotNull(jsonObject, "name")
+ ? jsonObject.get("name").getAsString()
+ : null)
+ .setType(
+ hasAndNotNull(jsonObject, "type")
+ ? jsonObject.get("type").getAsString()
+ : null)
+ .setVolume_percent(
+ hasAndNotNull(jsonObject, "volume_percent")
+ ? jsonObject.get("volume_percent").getAsInt()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/miscellaneous/PlaylistTracksInformation.java
@@ -0,0 +1,106 @@
+package com.wrapper.spotify.model_objects.miscellaneous;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about Playlist Track Information objects by building instances from this class.
+ */
+@JsonDeserialize(builder = PlaylistTracksInformation.Builder.class)
+public class PlaylistTracksInformation extends AbstractModelObject {
+ private final String href;
+ private final Integer total;
+
+ private PlaylistTracksInformation(final Builder builder) {
+ super(builder);
+
+ this.href = builder.href;
+ this.total = builder.total;
+ }
+
+ /**
+ * Get the Spotify Web API endpoint URL of the playlist tracks object.
+ *
+ * @return A Spotify API endpoint URL.
+ */
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Get the total amount of tracks in the playlist.
+ *
+ * @return The total amount of tracks in the playlist.
+ */
+ public Integer getTotal() {
+ return total;
+ }
+
+ @Override
+ public String toString() {
+ return "PlaylistTracksInformation(href=" + href + ", total=" + total + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link PlaylistTracksInformation} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private String href;
+ private Integer total;
+
+ /**
+ * Set href of Spotify Web API endpoint of the playlist tracks information object to be built.
+ *
+ * @param href Spotify Web API endpoint URL.
+ * @return A {@link PlaylistTracksInformation.Builder}.
+ */
+ public Builder setHref(String href) {
+ this.href = href;
+ return this;
+ }
+
+ /**
+ * Set the total amount of tracks in the playlist.
+ *
+ * @param total Total amount of tracks.
+ * @return A {@link PlaylistTracksInformation.Builder}.
+ */
+ public Builder setTotal(Integer total) {
+ this.total = total;
+ return this;
+ }
+
+ @Override
+ public PlaylistTracksInformation build() {
+ return new PlaylistTracksInformation(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link PlaylistTracksInformation} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<PlaylistTracksInformation> {
+ public PlaylistTracksInformation createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new PlaylistTracksInformation.Builder()
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setTotal(
+ hasAndNotNull(jsonObject, "total")
+ ? jsonObject.get("total").getAsInt()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/miscellaneous/README.md
@@ -0,0 +1,3 @@
+# Miscellaneous Objects
+
+Objects, that are defined on an API endpoint reference page, but not within the [main object model](https://developer.spotify.com/documentation/web-api/reference/object-model/).
\ No newline at end of file
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/miscellaneous/Restrictions.java
@@ -0,0 +1,84 @@
+package com.wrapper.spotify.model_objects.miscellaneous;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about Restriction objects by building instances from this class. <br><br>
+ * <p>
+ * Part of the response when <a href="https://developer.spotify.com/web-api/track-relinking-guide/">Track Relinking</a>
+ * is applied, the original track is not available in the given market, and Spotify did not have any tracks to relink it
+ * with. The track response will still contain metadata for the original track, and a restrictions object containing the
+ * reason why the track is not available
+ */
+@JsonDeserialize(builder = Restrictions.Builder.class)
+public class Restrictions extends AbstractModelObject {
+ private final String reason;
+
+ private Restrictions(final Builder builder) {
+ super(builder);
+
+ this.reason = builder.reason;
+ }
+
+ /**
+ * Get the reason why the track is not available.
+ *
+ * @return The track restriction reason.
+ */
+ public String getReason() {
+ return reason;
+ }
+
+ @Override
+ public String toString() {
+ return "Restrictions(reason=" + reason + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link Restrictions} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private String reason;
+
+ /**
+ * The restriction reason setter.
+ *
+ * @param reason The track restriction reason.
+ * @return A {@link Restrictions.Builder}.
+ */
+ public Builder setReason(String reason) {
+ this.reason = reason;
+ return this;
+ }
+
+ @Override
+ public Restrictions build() {
+ return new Restrictions(this);
+ }
+ }
+
+ /**
+ * JSonUtil class for building {@link Restrictions} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<Restrictions> {
+ public Restrictions createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Restrictions.Builder()
+ .setReason(
+ hasAndNotNull(jsonObject, "reason")
+ ? jsonObject.get("reason").getAsString()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/special/Actions.java
@@ -0,0 +1,84 @@
+package com.wrapper.spotify.model_objects.special;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+import com.wrapper.spotify.model_objects.specification.Disallows;
+
+/**
+ * Retrieve information about Actions objects by building instances from this class. These objects
+ * contain information to allow to update the user interface based on which playback actions are
+ * available within the current context. These objects contain a {@link Disallows} object.
+ */
+@JsonDeserialize(builder = Actions.Builder.class)
+public class Actions extends AbstractModelObject {
+ private final Disallows disallows;
+
+ public Actions(Builder builder) {
+ super(builder);
+ this.disallows = builder.disallows;
+ }
+
+ /**
+ * Get the Disallows object.
+ *
+ * @return Disallows object.
+ */
+ public Disallows getDisallows() {
+ return disallows;
+ }
+
+ @Override
+ public String toString() {
+ return "Actions(disallows=" + disallows + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link Actions} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private Disallows disallows;
+
+ /**
+ * Set the Disallows object of the Actions object to be built.
+ *
+ * @param disallows The {@link Disallows} object.
+ * @return A {@link Actions.Builder}.
+ */
+ public Builder setDisallows(Disallows disallows) {
+ this.disallows = disallows;
+ return this;
+ }
+
+ @Override
+ public Actions build() {
+ return new Actions(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link Actions} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<Actions> {
+ @Override
+ public Actions createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Builder()
+ .setDisallows(
+ hasAndNotNull(jsonObject, "disallows")
+ ? new Disallows.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("disallows"))
+ : null)
+ .build();
+ }
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/special/AlbumSimplifiedSpecial.java
@@ -0,0 +1,436 @@
+package com.wrapper.spotify.model_objects.special;
+
+import java.util.Arrays;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.enums.AlbumType;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.enums.ReleaseDatePrecision;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+import com.wrapper.spotify.model_objects.specification.Album;
+import com.wrapper.spotify.model_objects.specification.ArtistSimplified;
+import com.wrapper.spotify.model_objects.specification.ExternalUrl;
+import com.wrapper.spotify.model_objects.specification.Image;
+import com.wrapper.spotify.requests.data.search.interfaces.ISearchModelObject;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/object-model/#album-object-simplified">
+ * simplified Album objects</a> by building instances from this class.
+ * <p>
+ * This class exists because it includes the property {@code totalTracks}, which is not documented in the official
+ * specification, although the albums object as returned by the searches API includes it.
+ */
+@JsonDeserialize(builder = AlbumSimplifiedSpecial.Builder.class)
+public class AlbumSimplifiedSpecial extends AbstractModelObject implements ISearchModelObject {
+ private final AlbumType albumType;
+ private final ArtistSimplified[] artists;
+ private final CountryCode[] availableMarkets;
+ private final ExternalUrl externalUrls;
+ private final String href;
+ private final String id;
+ private final Image[] images;
+ private final String name;
+ private final String releaseDate;
+ private final ReleaseDatePrecision releaseDatePrecision;
+ private final Integer totalTracks;
+ private final ModelObjectType type;
+ private final String uri;
+
+ private AlbumSimplifiedSpecial(final Builder builder) {
+ super(builder);
+
+ this.albumType = builder.albumType;
+ this.artists = builder.artists;
+ this.availableMarkets = builder.availableMarkets;
+ this.externalUrls = builder.externalUrls;
+ this.href = builder.href;
+ this.id = builder.id;
+ this.images = builder.images;
+ this.name = builder.name;
+ this.releaseDate = builder.releaseDate;
+ this.releaseDatePrecision = builder.releaseDatePrecision;
+ this.totalTracks = builder.totalTracks;
+ this.type = builder.type;
+ this.uri = builder.uri;
+ }
+
+ /**
+ * Get the type of the album.
+ *
+ * @return The {@link AlbumType}.
+ */
+ public AlbumType getAlbumType() {
+ return albumType;
+ }
+
+ /**
+ * Get the artists of the album.
+ *
+ * @return An array of {@link ArtistSimplified} objects.
+ */
+ public ArtistSimplified[] getArtists() {
+ return artists;
+ }
+
+ /**
+ * Get the country codes of all countries, in which the album is available.
+ *
+ * @return An array of <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha-2 country
+ * codes</a>.
+ */
+ public CountryCode[] getAvailableMarkets() {
+ return availableMarkets;
+ }
+
+ /**
+ * Get the external URLs of the album. <br>
+ * Example: <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify-URL</a>
+ *
+ * @return An {@link ExternalUrl} object.
+ */
+ public ExternalUrl getExternalUrls() {
+ return externalUrls;
+ }
+
+ /**
+ * Get the full Spotify Web API endpoint URL of the album.
+ *
+ * @return A Spotify Web API endpoint URL.
+ */
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Get the Spotify ID of the album.
+ *
+ * @return A Spotify album ID.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Get the album cover art of the album in different sizes.
+ *
+ * @return An array of {@link Image} objects.
+ */
+ public Image[] getImages() {
+ return images;
+ }
+
+ /**
+ * Get the name of the album.
+ *
+ * @return Album name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the release date of the album with the highest precision available.
+ *
+ * @return The release date of the album.
+ */
+ public String getReleaseDate() {
+ return releaseDate;
+ }
+
+ /**
+ * Get the precision of the albums release date. This is needed when the exact release day of an album is not known.
+ *
+ * @return The precision of the albums release date.
+ */
+ public ReleaseDatePrecision getReleaseDatePrecision() {
+ return releaseDatePrecision;
+ }
+
+ /**
+ * Get the total tracks of the album.
+ *
+ * @return The total tracks of the album.
+ */
+ public Integer getTotalTracks() {
+ return totalTracks;
+ }
+
+ /**
+ * Get the model object type. In this case "album".
+ *
+ * @return A {@link ModelObjectType}.
+ */
+ public ModelObjectType getType() {
+ return type;
+ }
+
+ /**
+ * Get the Spotify URI of the album.
+ *
+ * @return <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify album URI</a>.
+ */
+ public String getUri() {
+ return uri;
+ }
+
+ @Override
+ public String toString() {
+ return "AlbumSimplifiedSpecial(albumType=" + albumType + ", artists=" + Arrays.toString(artists)
+ + ", availableMarkets=" + Arrays.toString(availableMarkets) + ", externalUrls=" + externalUrls + ", href="
+ + href + ", id=" + id + ", images=" + Arrays.toString(images) + ", name=" + name + ", releaseDate="
+ + releaseDate + ", releaseDatePrecision=" + releaseDatePrecision + ", totalTracks=" + totalTracks + ", type="
+ + type + ", uri=" + uri + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link AlbumSimplifiedSpecial} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+
+ private AlbumType albumType;
+ private ArtistSimplified[] artists;
+ private CountryCode[] availableMarkets;
+ private ExternalUrl externalUrls;
+ private String href;
+ private String id;
+ private Image[] images;
+ private String name;
+ private String releaseDate;
+ private ReleaseDatePrecision releaseDatePrecision;
+ private Integer totalTracks;
+ private ModelObjectType type;
+ private String uri;
+
+ /**
+ * Set the type of the album to be built.
+ *
+ * @param albumType The {@link AlbumType}.
+ * @return A {@link AlbumSimplifiedSpecial.Builder}.
+ */
+ public Builder setAlbumType(AlbumType albumType) {
+ this.albumType = albumType;
+ return this;
+
+ }
+
+ /**
+ * Set the artists of the album to be built.
+ *
+ * @param artists {@link ArtistSimplified} objects.
+ * @return A {@link AlbumSimplifiedSpecial.Builder}.
+ */
+ public Builder setArtists(ArtistSimplified... artists) {
+ this.artists = artists;
+ return this;
+ }
+
+ /**
+ * Set the available markets of the album to be built.
+ *
+ * @param availableMarkets <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">
+ * ISO 3166-1 alpha-2 country codes</a>.
+ * @return A {@link AlbumSimplifiedSpecial.Builder}.
+ */
+ public Builder setAvailableMarkets(CountryCode... availableMarkets) {
+ this.availableMarkets = availableMarkets;
+ return this;
+ }
+
+ /**
+ * Set external URLs of the album to be built.
+ *
+ * @param externalUrls {@link ExternalUrl} object.
+ * @return A {@link AlbumSimplifiedSpecial.Builder}.
+ */
+ public Builder setExternalUrls(ExternalUrl externalUrls) {
+ this.externalUrls = externalUrls;
+ return this;
+ }
+
+ /**
+ * Set href of Spotify Web API endpoint of the album to be built.
+ *
+ * @param href Spotify Web API endpoint URL.
+ * @return A {@link AlbumSimplifiedSpecial.Builder}.
+ */
+ public Builder setHref(String href) {
+ this.href = href;
+ return this;
+ }
+
+ /**
+ * Set album ID of the album to be built.
+ *
+ * @param id <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify album ID</a>.
+ * @return A {@link Album.Builder}.
+ */
+ public Builder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * Set the cover art in different sizes of the album to be built.
+ *
+ * @param images {@link Image} objects.
+ * @return A {@link AlbumSimplifiedSpecial.Builder}.
+ */
+ public Builder setImages(Image... images) {
+ this.images = images;
+ return this;
+ }
+
+ /**
+ * Set the name of the album to be built.
+ *
+ * @param name The album name.
+ * @return A {@link AlbumSimplifiedSpecial.Builder}.
+ */
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Set the release date of the album to be built.
+ *
+ * @param releaseDate The release date of the album.
+ * @return A {@link AlbumSimplifiedSpecial.Builder}.
+ */
+ public Builder setReleaseDate(String releaseDate) {
+ this.releaseDate = releaseDate;
+ return this;
+ }
+
+ /**
+ * Set the release date precision of the album to be built.
+ *
+ * @param releaseDatePrecision The {@link ReleaseDatePrecision} of the album.
+ * @return A {@link AlbumSimplifiedSpecial.Builder}.
+ */
+ public Builder setReleaseDatePrecision(ReleaseDatePrecision releaseDatePrecision) {
+ this.releaseDatePrecision = releaseDatePrecision;
+ return this;
+ }
+
+ /**
+ * Set the number of total tracks of the album to be built.
+ *
+ * @param totalTracks The number of total tracks of the album.
+ * @return A {@link AlbumSimplifiedSpecial.Builder}.
+ */
+ public Builder setTotalTracks(Integer totalTracks) {
+ this.totalTracks = totalTracks;
+ return this;
+ }
+
+ /**
+ * Set the type of the model object. In this case "album".
+ *
+ * @param type The {@link ModelObjectType}.
+ * @return A {@link AlbumSimplifiedSpecial.Builder}.
+ */
+ public Builder setType(ModelObjectType type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Set the Spotify album URI of the album to be built.
+ *
+ * @param uri <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">
+ * Spotify album URI</a>.
+ * @return A {@link AlbumSimplifiedSpecial.Builder}.
+ */
+ public Builder setUri(String uri) {
+ this.uri = uri;
+ return this;
+ }
+
+ @Override
+ public AlbumSimplifiedSpecial build() {
+ return new AlbumSimplifiedSpecial(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link AlbumSimplifiedSpecial} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<AlbumSimplifiedSpecial> {
+ public AlbumSimplifiedSpecial createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new AlbumSimplifiedSpecial.Builder()
+ .setAlbumType(
+ hasAndNotNull(jsonObject, "album_type")
+ ? AlbumType.keyOf(
+ jsonObject.get("album_type").getAsString().toLowerCase())
+ : null)
+ .setArtists(
+ hasAndNotNull(jsonObject, "artists")
+ ? new ArtistSimplified.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("artists"))
+ : null)
+ .setAvailableMarkets(
+ hasAndNotNull(jsonObject, "available_markets")
+ ? new Gson().fromJson(
+ jsonObject.get("available_markets"), CountryCode[].class)
+ : null)
+ .setExternalUrls(
+ hasAndNotNull(jsonObject, "external_urls")
+ ? new ExternalUrl.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("external_urls"))
+ : null)
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setId(
+ hasAndNotNull(jsonObject, "id")
+ ? jsonObject.get("id").getAsString()
+ : null)
+ .setImages(
+ hasAndNotNull(jsonObject, "images")
+ ? new Image.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("images"))
+ : null)
+ .setName(
+ hasAndNotNull(jsonObject, "name")
+ ? jsonObject.get("name").getAsString()
+ : null)
+ .setReleaseDate(
+ hasAndNotNull(jsonObject, "release_date")
+ ? jsonObject.get("release_date").getAsString()
+ : null)
+ .setReleaseDatePrecision(
+ hasAndNotNull(jsonObject, "release_date_precision")
+ ? ReleaseDatePrecision.keyOf(
+ jsonObject.get("release_date_precision").getAsString().toLowerCase())
+ : null)
+ .setTotalTracks(
+ hasAndNotNull(jsonObject, "total_tracks")
+ ? jsonObject.get("total_tracks").getAsInt()
+ : null)
+ .setType(
+ hasAndNotNull(jsonObject, "type")
+ ? ModelObjectType.keyOf(
+ jsonObject.get("type").getAsString().toLowerCase())
+ : null)
+ .setUri(
+ hasAndNotNull(jsonObject, "uri")
+ ? jsonObject.get("uri").getAsString()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/special/FeaturedPlaylists.java
@@ -0,0 +1,111 @@
+package com.wrapper.spotify.model_objects.special;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.model_objects.specification.PlaylistSimplified;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/console/get-featured-playlists">
+ * Featured Playlist objects</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = FeaturedPlaylists.Builder.class)
+public class FeaturedPlaylists extends AbstractModelObject {
+ private final String message;
+ private final Paging<PlaylistSimplified> playlists;
+
+ private FeaturedPlaylists(final Builder builder) {
+ super(builder);
+
+ this.message = builder.message;
+ this.playlists = builder.playlists;
+ }
+
+ /**
+ * Get the message which is displayed on the front page of the "browse" tab in the Spotify client. <br>
+ * The message usually refers to the featured playlists.
+ *
+ * @return A "welcoming" message.
+ */
+ public String getMessage() {
+ return message;
+ }
+
+ /**
+ * Get the page of featured playlists.
+ *
+ * @return Featured playlists page.
+ */
+ public Paging<PlaylistSimplified> getPlaylists() {
+ return playlists;
+ }
+
+ @Override
+ public String toString() {
+ return "FeaturedPlaylists(message=" + message + ", playlists=" + playlists + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link FeaturedPlaylists} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private String message;
+ private Paging<PlaylistSimplified> playlists;
+
+ /**
+ * Set the message, which normally would be displayed on the front page of the "browse" tab.
+ *
+ * @param message Message to be set.
+ * @return A {@link FeaturedPlaylists.Builder}.
+ */
+ public Builder setMessage(String message) {
+ this.message = message;
+ return this;
+ }
+
+ /**
+ * Set a page of playlists contained in the featured playlists object to be built.
+ *
+ * @param playlists A page of simplified playlists.
+ * @return A {@link FeaturedPlaylists.Builder}.
+ */
+ public Builder setPlaylists(Paging<PlaylistSimplified> playlists) {
+ this.playlists = playlists;
+ return this;
+ }
+
+ @Override
+ public FeaturedPlaylists build() {
+ return new FeaturedPlaylists(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link FeaturedPlaylists} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<FeaturedPlaylists> {
+ public FeaturedPlaylists createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new FeaturedPlaylists.Builder()
+ .setMessage(
+ hasAndNotNull(jsonObject, "message")
+ ? jsonObject.get("message").getAsString()
+ : null)
+ .setPlaylists(
+ hasAndNotNull(jsonObject, "playlists")
+ ? new PlaylistSimplified.JsonUtil().createModelObjectPaging(
+ jsonObject.getAsJsonObject("playlists"))
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/special/PlaylistTrackPosition.java
@@ -0,0 +1,100 @@
+package com.wrapper.spotify.model_objects.special;
+
+import java.util.Arrays;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about Playlist Track Position objects by building instances from this class. These objects
+ * contain the position in a playlist, where tracks should be added in a request.
+ */
+@JsonDeserialize(builder = PlaylistTrackPosition.Builder.class)
+public class PlaylistTrackPosition extends AbstractModelObject {
+ private final String uri;
+ private final int[] positions;
+
+ public PlaylistTrackPosition(final Builder builder) {
+ super(builder);
+
+ this.uri = builder.uri;
+ this.positions = builder.positions;
+ }
+
+ /**
+ * Get the <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URI</a> of the
+ * track.
+ *
+ * @return Spotify track URI.
+ */
+ public String getUri() {
+ return uri;
+ }
+
+ /**
+ * Get the position, where the track should be added in the playlist.
+ *
+ * @return Track position.
+ */
+ public int[] getPositions() {
+ return positions;
+ }
+
+ @Override
+ public String toString() {
+ return "PlaylistTrackPosition(uri=" + uri + ", positions=" + Arrays.toString(positions) + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link PlaylistTrackPosition} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private String uri;
+ private int[] positions;
+
+ public Builder setUri(String uri) {
+ this.uri = uri;
+ return this;
+ }
+
+ public Builder setPositions(int... positions) {
+ this.positions = positions;
+ return this;
+ }
+
+ @Override
+ public PlaylistTrackPosition build() {
+ return new PlaylistTrackPosition(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link PlaylistTrackPosition} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<PlaylistTrackPosition> {
+ public PlaylistTrackPosition createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new PlaylistTrackPosition.Builder()
+ .setPositions(
+ hasAndNotNull(jsonObject, "positions")
+ ? new Gson().fromJson(
+ jsonObject.getAsJsonArray("positions"), int[].class)
+ : null)
+ .setUri(
+ hasAndNotNull(jsonObject, "uri")
+ ? jsonObject.get("uri").getAsString()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/special/README.md
@@ -0,0 +1,3 @@
+# Special Objects
+
+Objects, that are nowhere defined, but returned by requests.
\ No newline at end of file
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/special/SearchResult.java
@@ -0,0 +1,240 @@
+package com.wrapper.spotify.model_objects.special;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+import com.wrapper.spotify.model_objects.specification.*;
+import com.wrapper.spotify.requests.data.personalization.interfaces.IArtistTrackModelObject;
+import com.wrapper.spotify.requests.data.search.SearchItemRequest;
+import com.wrapper.spotify.requests.data.search.interfaces.ISearchModelObject;
+
+/**
+ * Retrieve the searched-for items by building instances from this class. This objects contains
+ * for every type specified by the {@code type} parameter in the {@link SearchItemRequest}
+ * the searched-for items wrapped in a {@link Paging} object.
+ */
+@JsonDeserialize(builder = SearchResult.Builder.class)
+public class SearchResult extends AbstractModelObject implements IArtistTrackModelObject, ISearchModelObject {
+ private final Paging<AlbumSimplified> albums;
+ private final Paging<Artist> artists;
+ private final Paging<EpisodeSimplified> episodes;
+ private final Paging<PlaylistSimplified> playlists;
+ private final Paging<ShowSimplified> shows;
+ private final Paging<Track> tracks;
+
+ private SearchResult(final Builder builder) {
+ super(builder);
+
+ this.albums = builder.albums;
+ this.artists = builder.artists;
+ this.episodes = builder.episodes;
+ this.playlists = builder.playlists;
+ this.shows = builder.shows;
+ this.tracks = builder.tracks;
+ }
+
+ /**
+ * Get the album objects contained in the search result object. <br>
+ * <b>Note:</b> The search result only contains album objects when the {@code album} parameter has been specified
+ * in the request.
+ *
+ * @return Albums from the search result.
+ */
+ public Paging<AlbumSimplified> getAlbums() {
+ return albums;
+ }
+
+ /**
+ * Get the artist objects contained in the search result object. <br>
+ * <b>Note:</b> The search result only contains artist objects when the {@code artist} parameter has been specified
+ * in the request.
+ *
+ * @return Artists from the search result.
+ */
+ public Paging<Artist> getArtists() {
+ return artists;
+ }
+
+ /**
+ * Get the episode objects contained in the search result object. <br>
+ * <b>Note:</b> The search result only contains episode objects when the {@code episode} parameter has been specified
+ * in the request.
+ *
+ * @return Episodes from the search result.
+ */
+ public Paging<EpisodeSimplified> getEpisodes() {
+ return episodes;
+ }
+
+ /**
+ * Get the playlist objects contained in the search result object. <br>
+ * <b>Note:</b> The search result only contains playlist objects when the {@code playlist} parameter has been specified
+ * in the request.
+ *
+ * @return Playlists from the search result.
+ */
+ public Paging<PlaylistSimplified> getPlaylists() {
+ return playlists;
+ }
+
+ /**
+ * Get the show objects contained in the search result object. <br>
+ * <b>Note:</b> The search result only contains show objects when the {@code show} parameter has been specified
+ * in the request.
+ *
+ * @return Shows from the search result.
+ */
+ public Paging<ShowSimplified> getShows() {
+ return shows;
+ }
+
+ /**
+ * Get the track objects contained in the search result object. <br>
+ * <b>Note:</b> The search result only contains track objects when the {@code track} parameter has been specified
+ * in the request.
+ *
+ * @return Tracks from the search result.
+ */
+ public Paging<Track> getTracks() {
+ return tracks;
+ }
+
+ @Override
+ public String toString() {
+ return "SearchResult(albums=" + albums + ", artists=" + artists + ", episodes=" + episodes + ", playlists="
+ + playlists + ", shows=" + shows + ", tracks=" + tracks + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link SearchResult} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private Paging<AlbumSimplified> albums;
+ private Paging<Artist> artists;
+ private Paging<EpisodeSimplified> episodes;
+ private Paging<PlaylistSimplified> playlists;
+ private Paging<ShowSimplified> shows;
+ private Paging<Track> tracks;
+
+ /**
+ * The albums setter.
+ *
+ * @param albums Albums from the search result.
+ * @return A {@link SearchResult.Builder}.
+ */
+ public Builder setAlbums(Paging<AlbumSimplified> albums) {
+ this.albums = albums;
+ return this;
+ }
+
+ /**
+ * The artists setter.
+ *
+ * @param artists Artists from the search result.
+ * @return A {@link SearchResult.Builder}.
+ */
+ public Builder setArtists(Paging<Artist> artists) {
+ this.artists = artists;
+ return this;
+ }
+
+ /**
+ * The episodes setter.
+ *
+ * @param episodes Episodes from the search result.
+ * @return A {@link SearchResult.Builder}.
+ */
+ public Builder setEpisodes(Paging<EpisodeSimplified> episodes) {
+ this.episodes = episodes;
+ return this;
+ }
+
+ /**
+ * The playlists setter.
+ *
+ * @param playlists Playlists from the search result.
+ * @return A {@link SearchResult.Builder}.
+ */
+ public Builder setPlaylists(Paging<PlaylistSimplified> playlists) {
+ this.playlists = playlists;
+ return this;
+ }
+
+ /**
+ * The shows setter.
+ *
+ * @param shows Shows from the search result.
+ * @return A {@link SearchResult.Builder}.
+ */
+ public Builder setShows(Paging<ShowSimplified> shows) {
+ this.shows = shows;
+ return this;
+ }
+
+ /**
+ * The tracks setter.
+ *
+ * @param tracks Tracks from the search result.
+ * @return A {@link SearchResult.Builder}.
+ */
+ public Builder setTracks(Paging<Track> tracks) {
+ this.tracks = tracks;
+ return this;
+ }
+
+ @Override
+ public SearchResult build() {
+ return new SearchResult(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link SearchResult} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<SearchResult> {
+ public SearchResult createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new SearchResult.Builder()
+ .setAlbums(
+ hasAndNotNull(jsonObject, "albums")
+ ? new AlbumSimplified.JsonUtil().createModelObjectPaging(
+ jsonObject.getAsJsonObject("albums"))
+ : null)
+ .setArtists(
+ hasAndNotNull(jsonObject, "artists")
+ ? new Artist.JsonUtil().createModelObjectPaging(
+ jsonObject.getAsJsonObject("artists"))
+ : null)
+ .setEpisodes(
+ hasAndNotNull(jsonObject, "episodes")
+ ? new EpisodeSimplified.JsonUtil().createModelObjectPaging(
+ jsonObject.getAsJsonObject("episodes"))
+ : null)
+ .setPlaylists(
+ hasAndNotNull(jsonObject, "playlists")
+ ? new PlaylistSimplified.JsonUtil().createModelObjectPaging(
+ jsonObject.getAsJsonObject("playlists"))
+ : null)
+ .setShows(
+ hasAndNotNull(jsonObject, "shows")
+ ? new ShowSimplified.JsonUtil().createModelObjectPaging(
+ jsonObject.getAsJsonObject("shows"))
+ : null)
+ .setTracks(
+ hasAndNotNull(jsonObject, "tracks")
+ ? new Track.JsonUtil().createModelObjectPaging(
+ jsonObject.getAsJsonObject("tracks"))
+ : null)
+ .build();
+ }
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/special/SnapshotResult.java
@@ -0,0 +1,77 @@
+package com.wrapper.spotify.model_objects.special;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about Snapshot Result objects by building instances from this class. These objects contain a
+ * playlist snapshot ID, which is created after adding or removing tracks from a playlist.
+ * <p>
+ * <a href="https://developer.spotify.com/web-api/working-with-playlists/#version-control-and-snapshots">
+ * Spotify: Working With Playlists</a>
+ */
+@JsonDeserialize(builder = SnapshotResult.Builder.class)
+public class SnapshotResult extends AbstractModelObject {
+ private final String snapshotId;
+
+ private SnapshotResult(final Builder builder) {
+ super(builder);
+
+ this.snapshotId = builder.snapshotId;
+ }
+
+ /**
+ * Get the snapshot ID.
+ *
+ * @return The snapshot ID.
+ */
+ public String getSnapshotId() {
+ return snapshotId;
+ }
+
+ @Override
+ public String toString() {
+ return "SnapshotResult(snapshotId=" + snapshotId + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link SnapshotResult} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ public String snapshotId;
+
+ public Builder setSnapshotId(String snapshotId) {
+ this.snapshotId = snapshotId;
+ return this;
+ }
+
+ @Override
+ public SnapshotResult build() {
+ return new SnapshotResult(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link SnapshotResult} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<SnapshotResult> {
+ public SnapshotResult createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new SnapshotResult.Builder()
+ .setSnapshotId(
+ hasAndNotNull(jsonObject, "snapshot_id")
+ ? jsonObject.get("snapshot_id").getAsString()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/Album.java
@@ -0,0 +1,570 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import java.util.Arrays;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.enums.AlbumType;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.enums.ReleaseDatePrecision;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/object-model/#album-object-full">
+ * Album objects</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = Album.Builder.class)
+public class Album extends AbstractModelObject {
+ private final AlbumType albumType;
+ private final ArtistSimplified[] artists;
+ private final CountryCode[] availableMarkets;
+ private final Copyright[] copyrights;
+ private final ExternalId externalIds;
+ private final ExternalUrl externalUrls;
+ private final String[] genres;
+ private final String href;
+ private final String id;
+ private final Image[] images;
+ private final String label;
+ private final String name;
+ private final Integer popularity;
+ private final String releaseDate;
+ private final ReleaseDatePrecision releaseDatePrecision;
+ private final Paging<TrackSimplified> tracks;
+ private final ModelObjectType type;
+ private final String uri;
+
+ private Album(final Builder builder) {
+ super(builder);
+
+ this.albumType = builder.albumType;
+ this.artists = builder.artists;
+ this.availableMarkets = builder.availableMarkets;
+ this.copyrights = builder.copyrights;
+ this.externalIds = builder.externalIds;
+ this.externalUrls = builder.externalUrls;
+ this.genres = builder.genres;
+ this.href = builder.href;
+ this.id = builder.id;
+ this.images = builder.images;
+ this.label = builder.label;
+ this.name = builder.name;
+ this.popularity = builder.popularity;
+ this.releaseDate = builder.releaseDate;
+ this.releaseDatePrecision = builder.releaseDatePrecision;
+ this.tracks = builder.tracks;
+ this.type = builder.type;
+ this.uri = builder.uri;
+ }
+
+ /**
+ * Get the type of the album.
+ *
+ * @return The {@link AlbumType}.
+ */
+ public AlbumType getAlbumType() {
+ return albumType;
+ }
+
+ /**
+ * Get the artists of the album.
+ *
+ * @return An array of {@link ArtistSimplified} objects.
+ */
+ public ArtistSimplified[] getArtists() {
+ return artists;
+ }
+
+ /**
+ * Get the country codes of all countries, in which the album is available.
+ *
+ * @return An array of <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha-2 country
+ * codes</a>.
+ */
+ public CountryCode[] getAvailableMarkets() {
+ return availableMarkets;
+ }
+
+ /**
+ * Get all copyright texts of the album.
+ *
+ * @return An array of {@link Copyright} objects.
+ */
+ public Copyright[] getCopyrights() {
+ return copyrights;
+ }
+
+ /**
+ * Get the external IDs of the album. <br>
+ * Example: upc -&gt; "Universal Product Code".
+ *
+ * @return An array of {@link ExternalId} objects.
+ */
+ public ExternalId getExternalIds() {
+ return externalIds;
+ }
+
+ /**
+ * Get the external URLs of the album. <br>
+ * Example: <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify-URL</a>
+ *
+ * @return An {@link ExternalUrl} object.
+ */
+ public ExternalUrl getExternalUrls() {
+ return externalUrls;
+ }
+
+ /**
+ * Get a list of all genres of the album. <br>
+ * A great amount of albums may contain no information about their genres.
+ *
+ * @return An array of all genres of an album.
+ */
+ public String[] getGenres() {
+ return genres;
+ }
+
+ /**
+ * Get the full Spotify Web API endpoint URL of the album.
+ *
+ * @return A Spotify Web API endpoint URL.
+ */
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Get the Spotify ID of the album.
+ *
+ * @return A <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify album ID</a>.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Get the album cover art of the album in different sizes.
+ *
+ * @return An array of {@link Image} objects.
+ */
+ public Image[] getImages() {
+ return images;
+ }
+
+ /**
+ * Get the label for the album.
+ *
+ * @return The label for the album.
+ */
+ public String getLabel() {
+ return label;
+ }
+
+ /**
+ * Get the name of the album.
+ *
+ * @return Album name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the popularity of the album in a range between 0 and 100. (higher = more popular) <br>
+ * The popularity of the album is based on the popularity of its individual tracks.
+ *
+ * @return The popularity of the album.
+ */
+ public Integer getPopularity() {
+ return popularity;
+ }
+
+ /**
+ * Get the release date of the album with the highest precision available.
+ *
+ * @return The release date of the album.
+ */
+ public String getReleaseDate() {
+ return releaseDate;
+ }
+
+ /**
+ * Get the precision of the albums release date. This is needed when the exact release day of an album is not known.
+ *
+ * @return The precision of the albums release date.
+ */
+ public ReleaseDatePrecision getReleaseDatePrecision() {
+ return releaseDatePrecision;
+ }
+
+ /**
+ * Get a page of tracks of the album.
+ *
+ * @return A {@link Paging} object containing {@link TrackSimplified} objects.
+ */
+ public Paging<TrackSimplified> getTracks() {
+ return tracks;
+ }
+
+ /**
+ * Get the model object type. In this case "album".
+ *
+ * @return A {@link ModelObjectType}.
+ */
+ public ModelObjectType getType() {
+ return type;
+ }
+
+ /**
+ * Get the Spotify URI of the album.
+ *
+ * @return <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify album URI</a>.
+ */
+ public String getUri() {
+ return uri;
+ }
+
+ @Override
+ public String toString() {
+ return "Album(artists=" + Arrays.toString(artists) + ", name=" + name + ", albumType=" + albumType
+ + ", availableMarkets=" + Arrays.toString(availableMarkets) + ", copyrights=" + Arrays.toString(copyrights)
+ + ", externalIds=" + externalIds + ", externalUrls=" + externalUrls + ", genres=" + Arrays.toString(genres)
+ + ", href=" + href + ", id=" + id + ", images=" + Arrays.toString(images) + ", label=" + label + ", popularity="
+ + popularity + ", releaseDate=" + releaseDate + ", releaseDatePrecision=" + releaseDatePrecision + ", tracks="
+ + tracks + ", type=" + type + ", uri=" + uri + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link Album} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+
+ private AlbumType albumType;
+ private ArtistSimplified[] artists;
+ private CountryCode[] availableMarkets;
+ private Copyright[] copyrights;
+ private ExternalId externalIds;
+ private ExternalUrl externalUrls;
+ private String[] genres;
+ private String href;
+ private String id;
+ private Image[] images;
+ private String label;
+ private String name;
+ private Integer popularity;
+ private String releaseDate;
+ private ReleaseDatePrecision releaseDatePrecision;
+ private Paging<TrackSimplified> tracks;
+ private ModelObjectType type;
+ private String uri;
+
+ /**
+ * Set the type of the album to be built.
+ *
+ * @param albumType The {@link AlbumType}.
+ * @return A {@link Album.Builder}.
+ */
+ public Builder setAlbumType(AlbumType albumType) {
+ this.albumType = albumType;
+ return this;
+ }
+
+ /**
+ * Set the artists of the album to be built.
+ *
+ * @param artists {@link ArtistSimplified} objects.
+ * @return A {@link Album.Builder}.
+ */
+ public Builder setArtists(ArtistSimplified... artists) {
+ this.artists = artists;
+ return this;
+ }
+
+ /**
+ * Set the available markets of the album to be built.
+ *
+ * @param availableMarkets <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">
+ * ISO 3166-1 alpha-2 country codes</a>.
+ * @return A {@link Album.Builder}.
+ */
+ public Builder setAvailableMarkets(CountryCode... availableMarkets) {
+ this.availableMarkets = availableMarkets;
+ return this;
+ }
+
+ /**
+ * Set the copyrights of the album to be built.
+ *
+ * @param copyrights {@link Copyright} objects.
+ * @return A {@link Album.Builder}.
+ */
+ public Builder setCopyrights(Copyright... copyrights) {
+ this.copyrights = copyrights;
+ return this;
+ }
+
+ /**
+ * Set the external IDs of the album to be built.
+ *
+ * @param externalIds {@link ExternalId} object.
+ * @return A {@link Album.Builder}.
+ */
+ public Builder setExternalIds(ExternalId externalIds) {
+ this.externalIds = externalIds;
+ return this;
+ }
+
+ /**
+ * Set external URLs of the album to be built.
+ *
+ * @param externalUrls {@link ExternalUrl} object.
+ * @return A {@link Album.Builder}.
+ */
+ public Builder setExternalUrls(ExternalUrl externalUrls) {
+ this.externalUrls = externalUrls;
+ return this;
+ }
+
+ /**
+ * Set the genres of the album to be built.
+ *
+ * @param genres Genre names.
+ * @return A {@link Album.Builder}.
+ */
+ public Builder setGenres(String... genres) {
+ this.genres = genres;
+ return this;
+ }
+
+ /**
+ * Set href of Spotify Web API endpoint of the album to be built.
+ *
+ * @param href Spotify Web API endpoint URL.
+ * @return A {@link Album.Builder}.
+ */
+ public Builder setHref(String href) {
+ this.href = href;
+ return this;
+ }
+
+ /**
+ * Set album ID of the album to be built.
+ *
+ * @param id <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify album ID</a>.
+ * @return A {@link Album.Builder}.
+ */
+ public Builder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * Set the cover art in different sizes of the album to be built.
+ *
+ * @param images {@link Image} objects.
+ * @return A {@link Album.Builder}.
+ */
+ public Builder setImages(Image... images) {
+ this.images = images;
+ return this;
+ }
+
+ /**
+ * Set the label of the album to be built.
+ *
+ * @param label The album label.
+ * @return A {@link Album.Builder}.
+ */
+ public Builder setLabel(String label) {
+ this.label = label;
+ return this;
+ }
+
+ /**
+ * Set the name of the album to be built.
+ *
+ * @param name The album name.
+ * @return A {@link Album.Builder}.
+ */
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Set the popularity of the album to be built.
+ *
+ * @param popularity The popularity of the album between 0 and 100.
+ * @return A {@link Album.Builder}.
+ */
+ public Builder setPopularity(Integer popularity) {
+ this.popularity = popularity;
+ return this;
+ }
+
+ /**
+ * Set the release date of the album to be built.
+ *
+ * @param releaseDate The release date of the album.
+ * @return A {@link Album.Builder}.
+ */
+ public Builder setReleaseDate(String releaseDate) {
+ this.releaseDate = releaseDate;
+ return this;
+ }
+
+ /**
+ * Set the release date precision of the album to be built.
+ *
+ * @param releaseDatePrecision The {@link ReleaseDatePrecision} of the album.
+ * @return A {@link Album.Builder}.
+ */
+ public Builder setReleaseDatePrecision(ReleaseDatePrecision releaseDatePrecision) {
+ this.releaseDatePrecision = releaseDatePrecision;
+ return this;
+ }
+
+ /**
+ * Set the tracks of the album to be built.
+ *
+ * @param tracks A {@link Paging} object containing {@link TrackSimplified} objects.
+ * @return A {@link Album.Builder}.
+ */
+ public Builder setTracks(Paging<TrackSimplified> tracks) {
+ this.tracks = tracks;
+ return this;
+ }
+
+ /**
+ * Set the type of the model object. In this case "album".
+ *
+ * @param type The {@link ModelObjectType}.
+ * @return A {@link Album.Builder}.
+ */
+ public Builder setType(ModelObjectType type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Set the Spotify album URI of the album to be built.
+ *
+ * @param uri <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">
+ * Spotify album URI</a>.
+ * @return A {@link Album.Builder}.
+ */
+ public Builder setUri(String uri) {
+ this.uri = uri;
+ return this;
+ }
+
+ @Override
+ public Album build() {
+ return new Album(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link Album} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<Album> {
+ public Album createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Album.Builder()
+ .setAlbumType(
+ hasAndNotNull(jsonObject, "album_type")
+ ? AlbumType.keyOf(
+ jsonObject.get("album_type").getAsString().toLowerCase())
+ : null)
+ .setArtists(
+ hasAndNotNull(jsonObject, "artists")
+ ? new ArtistSimplified.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("artists"))
+ : null)
+ .setAvailableMarkets(
+ hasAndNotNull(jsonObject, "available_markets")
+ ? new Gson().fromJson(
+ jsonObject.getAsJsonArray("available_markets"), CountryCode[].class)
+ : null)
+ .setCopyrights(
+ hasAndNotNull(jsonObject, "copyrights")
+ ? new Copyright.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("copyrights"))
+ : null)
+ .setExternalIds(
+ hasAndNotNull(jsonObject, "external_ids")
+ ? new ExternalId.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("external_ids"))
+ : null)
+ .setExternalUrls(
+ hasAndNotNull(jsonObject, "external_urls")
+ ? new ExternalUrl.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("external_urls"))
+ : null)
+ .setGenres(
+ hasAndNotNull(jsonObject, "genres")
+ ? new Gson().fromJson(
+ jsonObject.getAsJsonArray("genres"), String[].class)
+ : null)
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setId(
+ hasAndNotNull(jsonObject, "id")
+ ? jsonObject.get("id").getAsString()
+ : null)
+ .setImages(
+ hasAndNotNull(jsonObject, "images")
+ ? new Image.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("images"))
+ : null)
+ .setLabel(
+ hasAndNotNull(jsonObject, "label")
+ ? jsonObject.get("label").getAsString()
+ : null)
+ .setName(
+ hasAndNotNull(jsonObject, "name")
+ ? jsonObject.get("name").getAsString()
+ : null)
+ .setPopularity(
+ hasAndNotNull(jsonObject, "popularity")
+ ? jsonObject.get("popularity").getAsInt()
+ : null)
+ .setReleaseDate(
+ hasAndNotNull(jsonObject, "release_date")
+ ? jsonObject.get("release_date").getAsString()
+ : null)
+ .setReleaseDatePrecision(
+ hasAndNotNull(jsonObject, "release_date_precision")
+ ? ReleaseDatePrecision.keyOf(
+ jsonObject.get("release_date_precision").getAsString().toLowerCase())
+ : null)
+ .setTracks(
+ hasAndNotNull(jsonObject, "tracks")
+ ? new TrackSimplified.JsonUtil().createModelObjectPaging(
+ jsonObject.getAsJsonObject("tracks"))
+ : null)
+ .setType(
+ hasAndNotNull(jsonObject, "type")
+ ? ModelObjectType.keyOf(
+ jsonObject.get("type").getAsString().toLowerCase())
+ : null)
+ .setUri(
+ hasAndNotNull(jsonObject, "uri")
+ ? jsonObject.get("uri").getAsString()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/AlbumSimplified.java
@@ -0,0 +1,459 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import java.util.Arrays;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.enums.AlbumGroup;
+import com.wrapper.spotify.enums.AlbumType;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.enums.ReleaseDatePrecision;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+import com.wrapper.spotify.model_objects.miscellaneous.Restrictions;
+import com.wrapper.spotify.requests.data.search.interfaces.ISearchModelObject;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/object-model/#album-object-simplified">
+ * simplified Album objects</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = AlbumSimplified.Builder.class)
+public class AlbumSimplified extends AbstractModelObject implements ISearchModelObject {
+ private final AlbumGroup albumGroup;
+ private final AlbumType albumType;
+ private final ArtistSimplified[] artists;
+ private final CountryCode[] availableMarkets;
+ private final ExternalUrl externalUrls;
+ private final String href;
+ private final String id;
+ private final Image[] images;
+ private final String name;
+ private final String releaseDate;
+ private final ReleaseDatePrecision releaseDatePrecision;
+ private final Restrictions restrictions;
+ private final ModelObjectType type;
+ private final String uri;
+
+ private AlbumSimplified(final Builder builder) {
+ super(builder);
+
+ this.albumGroup = builder.albumGroup;
+ this.albumType = builder.albumType;
+ this.artists = builder.artists;
+ this.availableMarkets = builder.availableMarkets;
+ this.externalUrls = builder.externalUrls;
+ this.href = builder.href;
+ this.id = builder.id;
+ this.images = builder.images;
+ this.name = builder.name;
+ this.releaseDate = builder.releaseDate;
+ this.releaseDatePrecision = builder.releaseDatePrecision;
+ this.restrictions = builder.restrictions;
+ this.type = builder.type;
+ this.uri = builder.uri;
+ }
+
+ /**
+ * Get the Spotify Album Group of the album.
+ *
+ * @return The album group date of the album.
+ */
+ public AlbumGroup getAlbumGroup() {
+ return albumGroup;
+ }
+
+ /**
+ * Get the type of the album.
+ *
+ * @return The {@link AlbumType}.
+ */
+ public AlbumType getAlbumType() {
+ return albumType;
+ }
+
+ /**
+ * Get the artists of the album.
+ *
+ * @return An array of {@link ArtistSimplified} objects.
+ */
+ public ArtistSimplified[] getArtists() {
+ return artists;
+ }
+
+ /**
+ * Get the country codes of all countries, in which the album is available.
+ *
+ * @return An array of <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha-2 country
+ * codes</a>.
+ */
+ public CountryCode[] getAvailableMarkets() {
+ return availableMarkets;
+ }
+
+ /**
+ * Get the external URLs of the album. <br>
+ * Example: <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify-URL</a>
+ *
+ * @return An {@link ExternalUrl} object.
+ */
+ public ExternalUrl getExternalUrls() {
+ return externalUrls;
+ }
+
+ /**
+ * Get the full Spotify Web API endpoint URL of the album.
+ *
+ * @return A Spotify Web API endpoint URL.
+ */
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Get the Spotify ID of the album.
+ *
+ * @return A Spotify album ID.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Get the album cover art of the album in different sizes.
+ *
+ * @return An array of {@link Image} objects.
+ */
+ public Image[] getImages() {
+ return images;
+ }
+
+ /**
+ * Get the name of the album.
+ *
+ * @return Album name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the release date of the album with the highest precision available.
+ *
+ * @return The release date of the album.
+ */
+ public String getReleaseDate() {
+ return releaseDate;
+ }
+
+ /**
+ * Get the precision of the albums release date. This is needed when the exact release day of an album is not known.
+ *
+ * @return The precision of the albums release date.
+ */
+ public ReleaseDatePrecision getReleaseDatePrecision() {
+ return releaseDatePrecision;
+ }
+
+ /**
+ * Get the Restrictions of the album.
+ *
+ * @return An object of {@link Restrictions} .
+ */
+ public Restrictions getRestrictions() {
+ return restrictions;
+ }
+
+ /**
+ * Get the model object type. In this case "album".
+ *
+ * @return A {@link ModelObjectType}.
+ */
+ public ModelObjectType getType() {
+ return type;
+ }
+
+ /**
+ * Get the Spotify URI of the album.
+ *
+ * @return <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify album URI</a>.
+ */
+ public String getUri() {
+ return uri;
+ }
+
+ @Override
+ public String toString() {
+ return "AlbumSimplified(artists=" + Arrays.toString(artists) + ", name=" + name + ", albumGroup=" + albumGroup
+ + ", albumType=" + albumType + ", availableMarkets=" + Arrays.toString(availableMarkets) + ", externalUrls="
+ + externalUrls + ", href=" + href + ", id=" + id + ", images=" + Arrays.toString(images) + ", releaseDate="
+ + releaseDate + ", releaseDatePrecision=" + releaseDatePrecision + ", restrictions=" + restrictions + ", type="
+ + type + ", uri=" + uri + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link AlbumSimplified} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+
+ private AlbumGroup albumGroup;
+ private AlbumType albumType;
+ private ArtistSimplified[] artists;
+ private CountryCode[] availableMarkets;
+ private ExternalUrl externalUrls;
+ private String href;
+ private String id;
+ private Image[] images;
+ private String name;
+ private String releaseDate;
+ private ReleaseDatePrecision releaseDatePrecision;
+ private Restrictions restrictions;
+ private ModelObjectType type;
+ private String uri;
+
+ /**
+ * Set the album group of the album to be built.
+ *
+ * @param albumGroup The album group of the album.
+ * @return A {@link AlbumSimplified.Builder}.
+ */
+ public Builder setAlbumGroup(AlbumGroup albumGroup) {
+ this.albumGroup = albumGroup;
+ return this;
+ }
+
+ /**
+ * Set the type of the album to be built.
+ *
+ * @param albumType The {@link AlbumType}.
+ * @return A {@link AlbumSimplified.Builder}.
+ */
+ public Builder setAlbumType(AlbumType albumType) {
+ this.albumType = albumType;
+ return this;
+ }
+
+ /**
+ * Set the artists of the album to be built.
+ *
+ * @param artists {@link ArtistSimplified} objects.
+ * @return A {@link AlbumSimplified.Builder}.
+ */
+ public Builder setArtists(ArtistSimplified... artists) {
+ this.artists = artists;
+ return this;
+ }
+
+ /**
+ * Set the available markets of the album to be built.
+ *
+ * @param availableMarkets <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">
+ * ISO 3166-1 alpha-2 country codes</a>.
+ * @return A {@link AlbumSimplified.Builder}.
+ */
+ public Builder setAvailableMarkets(CountryCode... availableMarkets) {
+ this.availableMarkets = availableMarkets;
+ return this;
+ }
+
+ /**
+ * Set external URLs of the album to be built.
+ *
+ * @param externalUrls {@link ExternalUrl} object.
+ * @return A {@link AlbumSimplified.Builder}.
+ */
+ public Builder setExternalUrls(ExternalUrl externalUrls) {
+ this.externalUrls = externalUrls;
+ return this;
+ }
+
+ /**
+ * Set href of Spotify Web API endpoint of the album to be built.
+ *
+ * @param href Spotify Web API endpoint URL.
+ * @return A {@link AlbumSimplified.Builder}.
+ */
+ public Builder setHref(String href) {
+ this.href = href;
+ return this;
+ }
+
+ /**
+ * Set album ID of the album to be built.
+ *
+ * @param id <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify album ID</a>.
+ * @return A {@link AlbumSimplified.Builder}.
+ */
+ public Builder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * Set the cover art in different sizes of the album to be built.
+ *
+ * @param images {@link Image} objects.
+ * @return A {@link AlbumSimplified.Builder}.
+ */
+ public Builder setImages(Image... images) {
+ this.images = images;
+ return this;
+ }
+
+ /**
+ * Set the name of the album to be built.
+ *
+ * @param name The album name.
+ * @return A {@link AlbumSimplified.Builder}.
+ */
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Set the release date of the album to be built.
+ *
+ * @param releaseDate The release date of the album.
+ * @return A {@link AlbumSimplified.Builder}.
+ */
+ public Builder setReleaseDate(String releaseDate) {
+ this.releaseDate = releaseDate;
+ return this;
+ }
+
+ /**
+ * Set the release date precision of the album to be built.
+ *
+ * @param releaseDatePrecision The {@link ReleaseDatePrecision} of the album.
+ * @return A {@link AlbumSimplified.Builder}.
+ */
+ public Builder setReleaseDatePrecision(ReleaseDatePrecision releaseDatePrecision) {
+ this.releaseDatePrecision = releaseDatePrecision;
+ return this;
+ }
+
+ /**
+ * Set the restrictions of the album to be built.
+ *
+ * @param restrictions The restrictions of the album.
+ * @return A {@link AlbumSimplified.Builder}.
+ */
+ public Builder setRestrictions(Restrictions restrictions) {
+ this.restrictions = restrictions;
+ return this;
+ }
+
+ /**
+ * Set the type of the model object. In this case "album".
+ *
+ * @param type The {@link ModelObjectType}.
+ * @return A {@link AlbumSimplified.Builder}.
+ */
+ public Builder setType(ModelObjectType type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Set the Spotify album URI of the album to be built.
+ *
+ * @param uri <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">
+ * Spotify album URI</a>.
+ * @return A {@link AlbumSimplified.Builder}.
+ */
+ public Builder setUri(String uri) {
+ this.uri = uri;
+ return this;
+ }
+
+ @Override
+ public AlbumSimplified build() {
+ return new AlbumSimplified(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link AlbumSimplified} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<AlbumSimplified> {
+ public AlbumSimplified createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new AlbumSimplified.Builder()
+ .setAlbumGroup(
+ hasAndNotNull(jsonObject, "album_group")
+ ? AlbumGroup.keyOf(
+ jsonObject.get("album_group").getAsString().toLowerCase())
+ : null)
+ .setAlbumType(
+ hasAndNotNull(jsonObject, "album_type")
+ ? AlbumType.keyOf(
+ jsonObject.get("album_type").getAsString().toLowerCase())
+ : null)
+ .setArtists(
+ hasAndNotNull(jsonObject, "artists")
+ ? new ArtistSimplified.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("artists"))
+ : null)
+ .setAvailableMarkets(
+ hasAndNotNull(jsonObject, "available_markets")
+ ? new Gson().fromJson(
+ jsonObject.get("available_markets"), CountryCode[].class)
+ : null)
+ .setExternalUrls(
+ hasAndNotNull(jsonObject, "external_urls")
+ ? new ExternalUrl.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("external_urls"))
+ : null)
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setId(
+ hasAndNotNull(jsonObject, "id")
+ ? jsonObject.get("id").getAsString()
+ : null)
+ .setImages(
+ hasAndNotNull(jsonObject, "images")
+ ? new Image.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("images"))
+ : null)
+ .setName(
+ hasAndNotNull(jsonObject, "name")
+ ? jsonObject.get("name").getAsString()
+ : null)
+ .setReleaseDate(
+ hasAndNotNull(jsonObject, "release_date")
+ ? jsonObject.get("release_date").getAsString()
+ : null)
+ .setReleaseDatePrecision(
+ hasAndNotNull(jsonObject, "release_date_precision")
+ ? ReleaseDatePrecision.keyOf(
+ jsonObject.get("release_date_precision").getAsString().toLowerCase())
+ : null)
+ .setRestrictions(
+ hasAndNotNull(jsonObject, "restrictions")
+ ? new Restrictions.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("restrictions"))
+ : null)
+ .setType(
+ hasAndNotNull(jsonObject, "type")
+ ? ModelObjectType.keyOf(
+ jsonObject.get("type").getAsString().toLowerCase())
+ : null)
+ .setUri(
+ hasAndNotNull(jsonObject, "uri")
+ ? jsonObject.get("uri").getAsString()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/Artist.java
@@ -0,0 +1,342 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import java.util.Arrays;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+import com.wrapper.spotify.requests.data.personalization.interfaces.IArtistTrackModelObject;
+import com.wrapper.spotify.requests.data.search.interfaces.ISearchModelObject;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/object-model/#artist-object-full">
+ * Artist objects</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = Artist.Builder.class)
+public class Artist extends AbstractModelObject implements IArtistTrackModelObject, ISearchModelObject {
+ private final ExternalUrl externalUrls;
+ private final Followers followers;
+ private final String[] genres;
+ private final String href;
+ private final String id;
+ private final Image[] images;
+ private final String name;
+ private final Integer popularity;
+ private final ModelObjectType type;
+ private final String uri;
+
+ private Artist(final Builder builder) {
+ super(builder);
+
+ this.externalUrls = builder.externalUrls;
+ this.followers = builder.followers;
+ this.genres = builder.genres;
+ this.href = builder.href;
+ this.id = builder.id;
+ this.images = builder.images;
+ this.name = builder.name;
+ this.popularity = builder.popularity;
+ this.type = builder.type;
+ this.uri = builder.uri;
+ }
+
+ /**
+ * Get the external URLs of the artist. <br>
+ * Example: <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify-URL</a>
+ *
+ * @return An {@link ExternalUrl} object.
+ */
+ public ExternalUrl getExternalUrls() {
+ return externalUrls;
+ }
+
+ /**
+ * Get information about the followers of the artist. <br>
+ * Example: Follower count.
+ *
+ * @return A {@link Followers} object.
+ */
+ public Followers getFollowers() {
+ return followers;
+ }
+
+ /**
+ * Get a list of all genres of the artist. <br>
+ * A great amount of artists may contain no information about their genres.
+ *
+ * @return An array of genre names.
+ */
+ public String[] getGenres() {
+ return genres;
+ }
+
+ /**
+ * Get the full Spotify Web API endpoint URL of the artist.
+ *
+ * @return A Spotify Web API endpoint URL.
+ */
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Get the Spotify ID of the artist.
+ *
+ * @return A <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify artist ID</a>.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Get all images of the artist (like header image) in different sizes.
+ *
+ * @return An array of {@link Image} objects.
+ */
+ public Image[] getImages() {
+ return images;
+ }
+
+ /**
+ * Get the name of the artist.
+ *
+ * @return Artist name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the popularity of the artist in a range between 0 and 100. (higher = more popular)<br>
+ * The popularity of the artist is based on the popularity of its tracks.
+ *
+ * @return The popularity of the artist.
+ */
+ public Integer getPopularity() {
+ return popularity;
+ }
+
+ /**
+ * Get the model object type. In this case "artist".
+ *
+ * @return A {@link ModelObjectType}.
+ */
+ public ModelObjectType getType() {
+ return type;
+ }
+
+ /**
+ * Get the Spotify URI of the artist.
+ *
+ * @return <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify artist URI</a>.
+ */
+ public String getUri() {
+ return uri;
+ }
+
+ @Override
+ public String toString() {
+ return "Artist(name=" + name + ", externalUrls=" + externalUrls + ", followers=" + followers + ", genres="
+ + Arrays.toString(genres) + ", href=" + href + ", id=" + id + ", images=" + Arrays.toString(images)
+ + ", popularity=" + popularity + ", type=" + type + ", uri=" + uri + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link Artist} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private ExternalUrl externalUrls;
+ private Followers followers;
+ private String[] genres;
+ private String href;
+ private String id;
+ private Image[] images;
+ private String name;
+ private Integer popularity;
+ private ModelObjectType type;
+ private String uri;
+
+ /**
+ * Set external URLs of the artist to be built.
+ *
+ * @param externalUrls {@link ExternalUrl} object.
+ * @return A {@link Artist.Builder}.
+ */
+ public Builder setExternalUrls(ExternalUrl externalUrls) {
+ this.externalUrls = externalUrls;
+ return this;
+ }
+
+ /**
+ * Set the followers object of the artist to be built.
+ *
+ * @param followers A {@link Followers} object.
+ * @return A {@link Artist.Builder}.
+ */
+ public Builder setFollowers(Followers followers) {
+ this.followers = followers;
+ return this;
+ }
+
+ /**
+ * Set the genres of the artist to be built.
+ *
+ * @param genres Genre names.
+ * @return A {@link Artist.Builder}.
+ */
+ public Builder setGenres(String... genres) {
+ this.genres = genres;
+ return this;
+ }
+
+ /**
+ * Set href of Spotify Web API endpoint of the artist to be built.
+ *
+ * @param href Spotify Web API endpoint URL.
+ * @return A {@link Artist.Builder}.
+ */
+ public Builder setHref(String href) {
+ this.href = href;
+ return this;
+ }
+
+ /**
+ * Set artist ID of the artist to be built.
+ *
+ * @param id <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify artist ID</a>.
+ * @return A {@link Artist.Builder}.
+ */
+ public Builder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * Set the images of the artist to be built, like the header image.
+ *
+ * @param images {@link Image} objects.
+ * @return A {@link Artist.Builder}.
+ */
+ public Builder setImages(Image... images) {
+ this.images = images;
+ return this;
+ }
+
+ /**
+ * Set the name of the artist to be built.
+ *
+ * @param name The artist name.
+ * @return A {@link Artist.Builder}.
+ */
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Set the popularity of the artist to be built.
+ *
+ * @param popularity The popularity of the artist between 0 and 100.
+ * @return A {@link Artist.Builder}.
+ */
+ public Builder setPopularity(Integer popularity) {
+ this.popularity = popularity;
+ return this;
+ }
+
+ /**
+ * Set the type of the model object. In this case "artist".
+ *
+ * @param type The {@link ModelObjectType}.
+ * @return A {@link Artist.Builder}.
+ */
+ public Builder setType(ModelObjectType type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Set the Spotify artist URI of the artist to be built.
+ *
+ * @param uri <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">
+ * Spotify artist URI</a>.
+ * @return A {@link Artist.Builder}.
+ */
+ public Builder setUri(String uri) {
+ this.uri = uri;
+ return this;
+ }
+
+ @Override
+ public Artist build() {
+ return new Artist(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link Artist} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<Artist> {
+ public Artist createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Artist.Builder()
+ .setExternalUrls(
+ hasAndNotNull(jsonObject, "external_urls")
+ ? new ExternalUrl.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("external_urls"))
+ : null)
+ .setFollowers(
+ hasAndNotNull(jsonObject, "followers")
+ ? new Followers.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("followers"))
+ : null)
+ .setGenres(
+ hasAndNotNull(jsonObject, "genres")
+ ? new Gson().fromJson(
+ jsonObject.getAsJsonArray("genres"), String[].class)
+ : null)
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setId(
+ hasAndNotNull(jsonObject, "id")
+ ? jsonObject.get("id").getAsString()
+ : null)
+ .setImages(
+ hasAndNotNull(jsonObject, "images")
+ ? new Image.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("images"))
+ : null)
+ .setName(
+ hasAndNotNull(jsonObject, "name")
+ ? jsonObject.get("name").getAsString()
+ : null)
+ .setPopularity(
+ hasAndNotNull(jsonObject, "popularity")
+ ? jsonObject.get("popularity").getAsInt()
+ : null)
+ .setType(
+ hasAndNotNull(jsonObject, "type")
+ ? ModelObjectType.keyOf(
+ jsonObject.get("type").getAsString().toLowerCase())
+ : null)
+ .setUri(
+ hasAndNotNull(jsonObject, "uri")
+ ? jsonObject.get("uri").getAsString()
+ : null)
+ .build();
+ }
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/ArtistSimplified.java
@@ -0,0 +1,221 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/object-model/#artist-object-simplified">
+ * simplified Artist objects</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = ArtistSimplified.Builder.class)
+public class ArtistSimplified extends AbstractModelObject {
+ private final ExternalUrl externalUrls;
+ private final String href;
+ private final String id;
+ private final String name;
+ private final ModelObjectType type;
+ private final String uri;
+
+ private ArtistSimplified(final Builder builder) {
+ super(builder);
+
+ this.externalUrls = builder.externalUrls;
+ this.href = builder.href;
+ this.id = builder.id;
+ this.name = builder.name;
+ this.type = builder.type;
+ this.uri = builder.uri;
+ }
+
+ /**
+ * Get the external URLs of the artist. <br>
+ * Example: <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify-URL</a>
+ *
+ * @return An {@link ExternalUrl} object.
+ */
+ public ExternalUrl getExternalUrls() {
+ return externalUrls;
+ }
+
+ /**
+ * Get the full Spotify Web API endpoint URL of the artist.
+ *
+ * @return A Spotify Web API endpoint URL.
+ */
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Get the Spotify ID of the artist.
+ *
+ * @return A <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify artist ID</a>.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Get the name of the artist.
+ *
+ * @return Artist name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the model object type. In this case "artist".
+ *
+ * @return A {@link ModelObjectType}.
+ */
+ public ModelObjectType getType() {
+ return type;
+ }
+
+ /**
+ * Get the Spotify URI of the artist.
+ *
+ * @return <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify artist URI</a>.
+ */
+ public String getUri() {
+ return uri;
+ }
+
+ @Override
+ public String toString() {
+ return "ArtistSimplified(name=" + name + ", externalUrls=" + externalUrls + ", href=" + href + ", id=" + id
+ + ", type=" + type + ", uri=" + uri + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link ArtistSimplified} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private ExternalUrl externalUrls;
+ private String href;
+ private String id;
+ private String name;
+ private ModelObjectType type;
+ private String uri;
+
+ /**
+ * Set external URLs of the artist to be built.
+ *
+ * @param externalUrls {@link ExternalUrl} object.
+ * @return A {@link ArtistSimplified.Builder}.
+ */
+ public Builder setExternalUrls(ExternalUrl externalUrls) {
+ this.externalUrls = externalUrls;
+ return this;
+ }
+
+ /**
+ * Set href of Spotify Web API endpoint of the artist to be built.
+ *
+ * @param href Spotify Web API endpoint URL.
+ * @return A {@link ArtistSimplified.Builder}.
+ */
+ public Builder setHref(String href) {
+ this.href = href;
+ return this;
+ }
+
+ /**
+ * Set artist ID of the artist to be built.
+ *
+ * @param id <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify artist ID</a>.
+ * @return A {@link ArtistSimplified.Builder}.
+ */
+ public Builder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * Set the name of the artist to be built.
+ *
+ * @param name The artist name.
+ * @return A {@link ArtistSimplified.Builder}.
+ */
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Set the type of the model object. In this case "artist".
+ *
+ * @param type The {@link ModelObjectType}.
+ * @return A {@link ArtistSimplified.Builder}.
+ */
+ public Builder setType(ModelObjectType type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Set the Spotify artist URI of the artist to be built.
+ *
+ * @param uri <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">
+ * Spotify artist URI</a>.
+ * @return A {@link ArtistSimplified.Builder}.
+ */
+ public Builder setUri(String uri) {
+ this.uri = uri;
+ return this;
+ }
+
+ @Override
+ public ArtistSimplified build() {
+ return new ArtistSimplified(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link ArtistSimplified} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<ArtistSimplified> {
+ public ArtistSimplified createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new ArtistSimplified.Builder()
+ .setExternalUrls(
+ hasAndNotNull(jsonObject, "external_urls")
+ ? new ExternalUrl.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("external_urls"))
+ : null)
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setId(
+ hasAndNotNull(jsonObject, "id")
+ ? jsonObject.get("id").getAsString()
+ : null)
+ .setName(
+ hasAndNotNull(jsonObject, "name")
+ ? jsonObject.get("name").getAsString()
+ : null)
+ .setType(
+ hasAndNotNull(jsonObject, "type")
+ ? ModelObjectType.keyOf(
+ jsonObject.get("type").getAsString().toLowerCase())
+ : null)
+ .setUri(
+ hasAndNotNull(jsonObject, "uri")
+ ? jsonObject.get("uri").getAsString()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/AudioFeatures.java
@@ -0,0 +1,568 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.enums.Modality;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/object-model/#audio-features-object">
+ * Audio Feature objects</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = AudioFeatures.Builder.class)
+public class AudioFeatures extends AbstractModelObject {
+ private final Float acousticness;
+ private final String analysisUrl;
+ private final Float danceability;
+ private final Integer durationMs;
+ private final Float energy;
+ private final String id;
+ private final Float instrumentalness;
+ private final Integer key;
+ private final Float liveness;
+ private final Float loudness;
+ private final Modality mode;
+ private final Float speechiness;
+ private final Float tempo;
+ private final Integer timeSignature;
+ private final String trackHref;
+ private final ModelObjectType type;
+ private final String uri;
+ private final Float valence;
+
+ private AudioFeatures(final Builder builder) {
+ super(builder);
+
+ this.acousticness = builder.acousticness;
+ this.analysisUrl = builder.analysisUrl;
+ this.danceability = builder.danceability;
+ this.durationMs = builder.durationMs;
+ this.energy = builder.energy;
+ this.id = builder.id;
+ this.instrumentalness = builder.instrumentalness;
+ this.key = builder.key;
+ this.liveness = builder.liveness;
+ this.loudness = builder.loudness;
+ this.mode = builder.mode;
+ this.speechiness = builder.speechiness;
+ this.tempo = builder.tempo;
+ this.timeSignature = builder.timeSignature;
+ this.trackHref = builder.trackHref;
+ this.type = builder.type;
+ this.uri = builder.uri;
+ this.valence = builder.valence;
+ }
+
+ /**
+ * Get the acousticness of the track as a value between 0.0 and 1.0. <br>
+ * The higher the value, the higher the chance the track is acoustic.
+ *
+ * @return Acousticness value.
+ */
+ public Float getAcousticness() {
+ return acousticness;
+ }
+
+ /**
+ * Get the Spotify Web API endpoint URL for a full audio analysis. An audio analysis contains additional information
+ * to audio feature objects.
+ *
+ * @return Spotify Web API endpoint URL.
+ * @see com.wrapper.spotify.model_objects.miscellaneous.AudioAnalysis
+ */
+ public String getAnalysisUrl() {
+ return analysisUrl;
+ }
+
+ /**
+ * Get the danceability of the track as a value between 0.0 and 1.0. <br>
+ * The danceability depends on factors like tempo and rhythm stability. Higher is better.
+ *
+ * @return Danceability value.
+ */
+ public Float getDanceability() {
+ return danceability;
+ }
+
+ /**
+ * Get the duration of the track in milliseconds.
+ *
+ * @return Track duration.
+ */
+ public Integer getDurationMs() {
+ return durationMs;
+ }
+
+ /**
+ * Get the energy of the track as a value between 0.0 and 1.0.<br>
+ * The energetic value of the track depends on factors like speed and loudness. Fast and loud tracks feel more
+ * energetic than slow and quiet tracks.
+ *
+ * @return Energetic value.
+ */
+ public Float getEnergy() {
+ return energy;
+ }
+
+ /**
+ * Get the <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify ID</a>
+ * of the track.
+ *
+ * @return Spotify track ID.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Get the instrumentalness of the track as a value between 0.0 and 1.0. <br>
+ * The higher the value, the higher the chance the track contains no vocals.
+ *
+ * @return Instrumentalness value.
+ * @see #getSpeechiness()
+ */
+ public Float getInstrumentalness() {
+ return instrumentalness;
+ }
+
+ /**
+ * Get the main key of the track.
+ *
+ * @return Main key.
+ * @see <a href="https://en.wikipedia.org/wiki/Pitch_class">Wikipedia: Pitch class notation</a>
+ */
+ public Integer getKey() {
+ return key;
+ }
+
+ /**
+ * Get the liveness of the track as a value between 0.0 and 1.0.<br>
+ * The liveness depends on ambient sounds like the presence of an audience. The higher the value, the higher the
+ * chance the track was performed live.
+ *
+ * @return Liveness value.
+ */
+ public Float getLiveness() {
+ return liveness;
+ }
+
+ /**
+ * Get the average loudness of the track. These values have mostly a range between -60 and 0 decibels.
+ *
+ * @return Loudness value.
+ */
+ public Float getLoudness() {
+ return loudness;
+ }
+
+ /**
+ * Get the modality of the track. (either "major" or "minor")
+ *
+ * @return {@link Modality} type.
+ * @see <a href="https://en.wikipedia.org/wiki/Mode_(music)">Wikipedia: Mode (music)</a>
+ */
+ public Modality getMode() {
+ return mode;
+ }
+
+ /**
+ * Get the speechiness of the track as a value between 0.0 and 1.0. <br>
+ * The higher the value, the higher the chance the track only consists of spoken words.
+ *
+ * @return Speechiness value.
+ * @see #getInstrumentalness()
+ */
+ public Float getSpeechiness() {
+ return speechiness;
+ }
+
+ /**
+ * Get the estimated tempo of the track in beats per minute.
+ *
+ * @return Tempo value.
+ */
+ public Float getTempo() {
+ return tempo;
+ }
+
+ /**
+ * Get the estimated overall time signature of the track. The time signature (or meter) is the number of beats in a
+ * bar. <br>
+ * Example: A Viennese waltz has a three-quarters beat, so this method would return the value 3 in this case.
+ *
+ * @return Time signature value.
+ */
+ public Integer getTimeSignature() {
+ return timeSignature;
+ }
+
+ /**
+ * Get the Spotify Web API endpoint URL of the track.
+ *
+ * @return A Spotify Web API endpoint URL.
+ */
+ public String getTrackHref() {
+ return trackHref;
+ }
+
+ /**
+ * Get the model object type. In this case "audio_features".
+ *
+ * @return A {@link ModelObjectType}.
+ */
+ public ModelObjectType getType() {
+ return type;
+ }
+
+ /**
+ * Get the <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URI</a>
+ * of the track.
+ *
+ * @return Spotify track URI.
+ */
+ public String getUri() {
+ return uri;
+ }
+
+ /**
+ * Get the valence of the track as a value between 0.0 and 1.0.<br>
+ * A track with a high valence sounds more positive (happy, cheerful, euphoric) like the track with a low valence.
+ * (sad, depressed, angry)
+ *
+ * @return Valence value.
+ */
+ public Float getValence() {
+ return valence;
+ }
+
+ @Override
+ public String toString() {
+ return "AudioFeatures(acousticness=" + acousticness + ", analysisUrl=" + analysisUrl + ", danceability="
+ + danceability + ", durationMs=" + durationMs + ", energy=" + energy + ", id=" + id + ", instrumentalness="
+ + instrumentalness + ", key=" + key + ", liveness=" + liveness + ", loudness=" + loudness + ", mode=" + mode
+ + ", speechiness=" + speechiness + ", tempo=" + tempo + ", timeSignature=" + timeSignature + ", trackHref="
+ + trackHref + ", type=" + type + ", uri=" + uri + ", valence=" + valence + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link AudioFeatures} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private Float acousticness;
+ private String analysisUrl;
+ private Float danceability;
+ private Integer durationMs;
+ private Float energy;
+ private String id;
+ private Float instrumentalness;
+ private Integer key;
+ private Float liveness;
+ private Float loudness;
+ private Modality mode;
+ private Float speechiness;
+ private Float tempo;
+ private Integer timeSignature;
+ private String trackHref;
+ private ModelObjectType type;
+ private String uri;
+ private Float valence;
+
+ /**
+ * Set the acousticness of the audio features object to be built.
+ *
+ * @param acousticness Acousticness value between 0.0 and 1.0.
+ * @return An {@link AudioFeatures.Builder}.
+ */
+ public Builder setAcousticness(Float acousticness) {
+ this.acousticness = acousticness;
+ return this;
+ }
+
+ /**
+ * Set the Spotify Web API audio analysis endpoint URL of the audio features object to be built.
+ *
+ * @param analysisUrl Spotify Web API endpoint URL.
+ * @return An {@link AudioFeatures.Builder}.
+ */
+ public Builder setAnalysisUrl(String analysisUrl) {
+ this.analysisUrl = analysisUrl;
+ return this;
+ }
+
+ /**
+ * Set the danceability of the audio features object to be built.
+ *
+ * @param danceability Danceability value between 0.0 and 1.0.
+ * @return An {@link AudioFeatures.Builder}.
+ */
+ public Builder setDanceability(Float danceability) {
+ this.danceability = danceability;
+ return this;
+ }
+
+ /**
+ * Set the duration in milliseconds of the audio features object to be built.
+ *
+ * @param durationMs Duration in milliseconds.
+ * @return An {@link AudioFeatures.Builder}.
+ */
+ public Builder setDurationMs(Integer durationMs) {
+ this.durationMs = durationMs;
+ return this;
+ }
+
+ /**
+ * Set the energy of the audio features object to be built.
+ *
+ * @param energy Energy value between 0.0 and 1.0.
+ * @return An {@link AudioFeatures.Builder}.
+ */
+ public Builder setEnergy(Float energy) {
+ this.energy = energy;
+ return this;
+ }
+
+ /**
+ * Set the Spotify track ID of the audio features object to be built.
+ *
+ * @param id Spotify track ID.
+ * @return An {@link AudioFeatures.Builder}.
+ */
+ public Builder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * Set the instrumentalness of the audio features object to be built.
+ *
+ * @param instrumentalness Instrumentalness value between 0.0 and 1.0.
+ * @return An {@link AudioFeatures.Builder}.
+ */
+ public Builder setInstrumentalness(Float instrumentalness) {
+ this.instrumentalness = instrumentalness;
+ return this;
+ }
+
+ /**
+ * Set the key of the audio features object to be built.
+ *
+ * @param key Track key.
+ * @return An {@link AudioFeatures.Builder}.
+ */
+ public Builder setKey(Integer key) {
+ this.key = key;
+ return this;
+ }
+
+ /**
+ * Set the liveness of the audio features object to be built.
+ *
+ * @param liveness Liveness value between 0.0 and 1.0.
+ * @return An {@link AudioFeatures.Builder}.
+ */
+ public Builder setLiveness(Float liveness) {
+ this.liveness = liveness;
+ return this;
+ }
+
+ /**
+ * Set the loudness of the audio features object to be built.
+ *
+ * @param loudness Loudness value between 0.0 and 1.0.
+ * @return An {@link AudioFeatures.Builder}.
+ */
+ public Builder setLoudness(Float loudness) {
+ this.loudness = loudness;
+ return this;
+ }
+
+ /**
+ * Set the mode of the audio features object to be built.
+ *
+ * @param mode Track mode.
+ * @return An {@link AudioFeatures.Builder}.
+ */
+ public Builder setMode(Modality mode) {
+ this.mode = mode;
+ return this;
+ }
+
+ /**
+ * Set the speechiness of the audio features object to be built.
+ *
+ * @param speechiness Speechiness value between 0.0 and 1.0.
+ * @return An {@link AudioFeatures.Builder}.
+ */
+ public Builder setSpeechiness(Float speechiness) {
+ this.speechiness = speechiness;
+ return this;
+ }
+
+ /**
+ * Set the tempo of the audio features object to be built.
+ *
+ * @param tempo Tempo value in beats per minute.
+ * @return An {@link AudioFeatures.Builder}.
+ */
+ public Builder setTempo(Float tempo) {
+ this.tempo = tempo;
+ return this;
+ }
+
+ /**
+ * Set the time signature of the audio features object to be built.
+ *
+ * @param timeSignature Time signature of the track.
+ * @return An {@link AudioFeatures.Builder}.
+ */
+ public Builder setTimeSignature(Integer timeSignature) {
+ this.timeSignature = timeSignature;
+ return this;
+ }
+
+ /**
+ * Set the track href to the Spotify Web API endpoint of the audio features object to be built.
+ *
+ * @param trackHref Spotify Web API endpoint URL.
+ * @return An {@link AudioFeatures.Builder}.
+ */
+ public Builder setTrackHref(String trackHref) {
+ this.trackHref = trackHref;
+ return this;
+ }
+
+ /**
+ * Set the type of the model object. In this case "audio_features".
+ *
+ * @param type The {@link ModelObjectType}.
+ * @return An {@link AudioFeatures.Builder}.
+ */
+ public Builder setType(ModelObjectType type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Set the <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URI</a>
+ * of the audio feature objects track to be built.
+ *
+ * @param uri The Spotify track URI.
+ * @return An {@link AudioFeatures.Builder}.
+ */
+ public Builder setUri(String uri) {
+ this.uri = uri;
+ return this;
+ }
+
+ /**
+ * Set the valence of the audio features object to be built.
+ *
+ * @param valence Valence value between 0.0 and 1.0.
+ * @return An {@link AudioFeatures.Builder}.
+ */
+ public Builder setValence(Float valence) {
+ this.valence = valence;
+ return this;
+ }
+
+ @Override
+ public AudioFeatures build() {
+ return new AudioFeatures(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link AudioFeatures} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<AudioFeatures> {
+ public AudioFeatures createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new AudioFeatures.Builder()
+ .setAcousticness(
+ hasAndNotNull(jsonObject, "acousticness")
+ ? jsonObject.get("acousticness").getAsFloat()
+ : null)
+ .setAnalysisUrl(
+ hasAndNotNull(jsonObject, "analysis_url")
+ ? jsonObject.get("analysis_url").getAsString()
+ : null)
+ .setDanceability(
+ hasAndNotNull(jsonObject, "danceability")
+ ? jsonObject.get("danceability").getAsFloat()
+ : null)
+ .setDurationMs(
+ hasAndNotNull(jsonObject, "duration_ms")
+ ? jsonObject.get("duration_ms").getAsInt()
+ : null)
+ .setEnergy(
+ hasAndNotNull(jsonObject, "energy")
+ ? jsonObject.get("energy").getAsFloat()
+ : null)
+ .setId(
+ hasAndNotNull(jsonObject, "id")
+ ? jsonObject.get("id").getAsString()
+ : null)
+ .setInstrumentalness(
+ hasAndNotNull(jsonObject, "instrumentalness")
+ ? jsonObject.get("instrumentalness").getAsFloat()
+ : null)
+ .setKey(
+ hasAndNotNull(jsonObject, "key")
+ ? jsonObject.get("key").getAsInt()
+ : null)
+ .setLiveness(
+ hasAndNotNull(jsonObject, "liveness")
+ ? jsonObject.get("liveness").getAsFloat()
+ : null)
+ .setLoudness(
+ hasAndNotNull(jsonObject, "loudness")
+ ? jsonObject.get("loudness").getAsFloat()
+ : null)
+ .setMode(
+ hasAndNotNull(jsonObject, "mode")
+ ? Modality.keyOf(
+ jsonObject.get("mode").getAsInt())
+ : null)
+ .setSpeechiness(
+ hasAndNotNull(jsonObject, "speechiness")
+ ? jsonObject.get("speechiness").getAsFloat()
+ : null)
+ .setTempo(
+ hasAndNotNull(jsonObject, "tempo")
+ ? jsonObject.get("tempo").getAsFloat()
+ : null)
+ .setTimeSignature(
+ hasAndNotNull(jsonObject, "time_signature")
+ ? jsonObject.get("time_signature").getAsInt()
+ : null)
+ .setTrackHref(
+ hasAndNotNull(jsonObject, "track_href")
+ ? jsonObject.get("track_href").getAsString()
+ : null)
+ .setType(
+ hasAndNotNull(jsonObject, "type")
+ ? ModelObjectType.keyOf(
+ jsonObject.get("type").getAsString().toLowerCase())
+ : null)
+ .setUri(
+ hasAndNotNull(jsonObject, "uri")
+ ? jsonObject.get("uri").getAsString()
+ : null)
+ .setValence(
+ hasAndNotNull(jsonObject, "valence")
+ ? jsonObject.get("valence").getAsFloat()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/Category.java
@@ -0,0 +1,165 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import java.util.Arrays;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/object-model/#category-object">
+ * Category objects</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = Category.Builder.class)
+public class Category extends AbstractModelObject {
+ private final String href;
+ private final Image[] icons;
+ private final String id;
+ private final String name;
+
+ private Category(final Builder builder) {
+ super(builder);
+
+ this.href = builder.href;
+ this.icons = builder.icons;
+ this.id = builder.id;
+ this.name = builder.name;
+ }
+
+ /**
+ * Get the Spotify Web API endpoint URL of the category.
+ *
+ * @return A link to the Spotify Web API endpoint returning full details of the category.
+ */
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Get the category icon in various sizes.
+ *
+ * @return The category icon, in various sizes.
+ */
+ public Image[] getIcons() {
+ return icons;
+ }
+
+ /**
+ * Get the <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify ID</a>
+ * of the category.
+ *
+ * @return The Spotify category ID of the category.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Get the name of the category.
+ *
+ * @return The name of the category.
+ */
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String toString() {
+ return "Category(href=" + href + ", icons=" + Arrays.toString(icons) + ", id=" + id + ", name=" + name + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link Category} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private String href;
+ private Image[] icons;
+ private String id;
+ private String name;
+
+ /**
+ * The category href setter.
+ *
+ * @param href A link to the Spotify Web API endpoint returning full details of the category.
+ * @return A Category builder.
+ */
+ public Builder setHref(String href) {
+ this.href = href;
+ return this;
+ }
+
+ /**
+ * The category icon setter.
+ *
+ * @param icons The category icon, in various sizes.
+ * @return A Category builder.
+ */
+ public Builder setIcons(Image... icons) {
+ this.icons = icons;
+ return this;
+ }
+
+ /**
+ * The category ID setter.
+ *
+ * @param id The Spotify category ID of the category.
+ * @return A Category builder.
+ */
+ public Builder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * The category name setter.
+ *
+ * @param name The name of the category.
+ * @return A Category builder.
+ */
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ @Override
+ public Category build() {
+ return new Category(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link Category} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<Category> {
+ public Category createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Category.Builder()
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setIcons(
+ hasAndNotNull(jsonObject, "icons")
+ ? new Image.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("icons"))
+ : null)
+ .setId(
+ hasAndNotNull(jsonObject, "id")
+ ? jsonObject.get("id").getAsString()
+ : null)
+ .setName(
+ hasAndNotNull(jsonObject, "name")
+ ? jsonObject.get("name").getAsString()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/Context.java
@@ -0,0 +1,168 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/object-model/#context-object">
+ * Context objects</a> by building instances from this class. Context objects contain information from where a
+ * specific track has been played by a user.
+ *
+ * @see PlayHistory
+ */
+@JsonDeserialize(builder = Context.Builder.class)
+public class Context extends AbstractModelObject {
+ private final ModelObjectType type;
+ private final String href;
+ private final ExternalUrl externalUrls;
+ private final String uri;
+
+ private Context(final Builder builder) {
+ super(builder);
+
+ this.type = builder.type;
+ this.href = builder.href;
+ this.externalUrls = builder.externalUrls;
+ this.uri = builder.uri;
+ }
+
+ /**
+ * Get the model object type of the context.
+ *
+ * @return The {@link ModelObjectType}.
+ */
+ public ModelObjectType getType() {
+ return type;
+ }
+
+ /**
+ * Get a link to the Spotify Web API endpoint providing full details of the track.
+ *
+ * @return A link to the Spotify Web API endpoint providing full details of the track.
+ */
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Get the external URLs of the context.
+ *
+ * @return An {@link ExternalUrl} object.
+ */
+ public ExternalUrl getExternalUrls() {
+ return externalUrls;
+ }
+
+ /**
+ * Get the <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URI</a>
+ * for the context.
+ *
+ * @return The Spotify URI for the context.
+ */
+ public String getUri() {
+ return uri;
+ }
+
+ @Override
+ public String toString() {
+ return "Context(type=" + type + ", href=" + href + ", externalUrls=" + externalUrls + ", uri=" + uri + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link Context} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private ModelObjectType type;
+ private String href;
+ private ExternalUrl externalUrls;
+ private String uri;
+
+ /**
+ * The model object type setter.
+ *
+ * @param type The {@link ModelObjectType}.
+ * @return A {@link Context.Builder}.
+ */
+ public Builder setType(ModelObjectType type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * The context href setter.
+ *
+ * @param href A link to the Spotify Web API endpoint providing full details of the track.
+ * @return A {@link Context.Builder}.
+ */
+ public Builder setHref(String href) {
+ this.href = href;
+ return this;
+ }
+
+ /**
+ * The external URLs setter.
+ *
+ * @param externalUrls External URLs for this context.
+ * @return A {@link Context.Builder}.
+ */
+ public Builder setExternalUrls(ExternalUrl externalUrls) {
+ this.externalUrls = externalUrls;
+ return this;
+ }
+
+ /**
+ * The Spotify URI setter.
+ *
+ * @param uri Spotify URI for this context.
+ * @return A {@link Context.Builder}.
+ */
+ public Builder setUri(String uri) {
+ this.uri = uri;
+ return this;
+ }
+
+ @Override
+ public Context build() {
+ return new Context(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link Context} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<Context> {
+ public Context createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Context.Builder()
+ .setType(
+ hasAndNotNull(jsonObject, "type")
+ ? ModelObjectType.keyOf(
+ jsonObject.get("type").getAsString().toLowerCase())
+ : null)
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setExternalUrls(
+ hasAndNotNull(jsonObject, "external_urls")
+ ? new ExternalUrl.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("external_urls"))
+ : null)
+ .setUri(
+ hasAndNotNull(jsonObject, "uri")
+ ? jsonObject.get("uri").getAsString()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/Copyright.java
@@ -0,0 +1,110 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.enums.CopyrightType;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about
+ * <a href="https://developer.spotify.com/web-api/object-model/#copyright-object">Copyright objects</a>
+ * by building instances from this class.
+ */
+@JsonDeserialize(builder = Copyright.Builder.class)
+public class Copyright extends AbstractModelObject {
+ private final String text;
+ private final CopyrightType type;
+
+ private Copyright(final Builder builder) {
+ super(builder);
+
+ this.text = builder.text;
+ this.type = builder.type;
+ }
+
+ /**
+ * Get the copyright text of the {@link Album}.
+ *
+ * @return The copyright text for this {@link Album}.
+ */
+ public String getText() {
+ return text;
+ }
+
+ /**
+ * Get the {@link CopyrightType} of this {@link Copyright} object.
+ *
+ * @return The type of copyright: C = the copyright, P = the sound recording (performance) copyright.
+ */
+ public CopyrightType getType() {
+ return type;
+ }
+
+ @Override
+ public String toString() {
+ return "Copyright(text=" + text + ", type=" + type + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link Copyright} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private String text;
+ private CopyrightType type;
+
+ /**
+ * The copyright text setter.
+ *
+ * @param text The copyright text for this album.
+ * @return A {@link Copyright.Builder}
+ */
+ public Builder setText(String text) {
+ this.text = text;
+ return this;
+ }
+
+ /**
+ * The copyright type setter.
+ *
+ * @param type The type of copyright: C = the copyright, P = the sound recording (performance) copyright.
+ * @return A {@link Copyright.Builder}
+ */
+ public Builder setType(CopyrightType type) {
+ this.type = type;
+ return this;
+ }
+
+ @Override
+ public Copyright build() {
+ return new Copyright(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link Copyright} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<Copyright> {
+ public Copyright createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Copyright.Builder()
+ .setText(
+ hasAndNotNull(jsonObject, "text")
+ ? jsonObject.get("text").getAsString()
+ : null)
+ .setType(
+ hasAndNotNull(jsonObject, "type")
+ ? CopyrightType.keyOf(
+ jsonObject.get("type").getAsString().toLowerCase())
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/Cursor.java
@@ -0,0 +1,84 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about
+ * <a href="https://developer.spotify.com/web-api/object-model/#cursor-object">Cursor objects</a>
+ * by building instances from this class.
+ */
+@JsonDeserialize(builder = Cursor.Builder.class)
+public class Cursor extends AbstractModelObject {
+ private final String after;
+
+ private Cursor(final Builder builder) {
+ super(builder);
+
+ this.after = builder.after;
+ }
+
+ /**
+ * Get the key of this {@link Cursor} to find the next set of items in a
+ * <a href="https://developer.spotify.com/web-api/object-model/#cursor-based-paging-object">
+ * cursor-based paging object</a>.
+ *
+ * @return The cursor to use as key to find the next page of items.
+ * @see PagingCursorbased
+ */
+ public String getAfter() {
+ return after;
+ }
+
+ @Override
+ public String toString() {
+ return "Cursor(after=" + after + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link Cursor} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private String after;
+
+ /**
+ * The after key setter.
+ *
+ * @param after The cursor to use as key to find the next page of items.
+ * @return A {@link Cursor.Builder}.
+ */
+ public Builder setAfter(String after) {
+ this.after = after;
+ return this;
+ }
+
+ @Override
+ public Cursor build() {
+ return new Cursor(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link Cursor} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<Cursor> {
+ public Cursor createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Cursor.Builder()
+ .setAfter(
+ hasAndNotNull(jsonObject, "after")
+ ? jsonObject.get("after").getAsString()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/Disallows.java
@@ -0,0 +1,94 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.enums.Action;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+import com.wrapper.spotify.model_objects.special.Actions;
+
+import java.util.EnumSet;
+import java.util.Map;
+
+/**
+ * Retrieve information about
+ * <a href="https://developer.spotify.com/web-api/object-model/#disallows-object">Disallows objects</a>
+ * by building instances from this class.
+ */
+@JsonDeserialize(builder = Actions.Builder.class)
+public class Disallows extends AbstractModelObject {
+ private final EnumSet<Action> disallowedActions;
+
+ public Disallows(Builder builder) {
+ super(builder);
+ this.disallowedActions = builder.disallowedActions;
+ }
+
+ /**
+ * Get a set of disallowed actions.
+ *
+ * @return The set of disallowed actions.
+ */
+ public EnumSet<Action> getDisallowedActions() {
+ return disallowedActions;
+ }
+
+ @Override
+ public String toString() {
+ return "Disallows(disallowedActions=" + disallowedActions + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link Disallows} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private EnumSet<Action> disallowedActions;
+
+ /**
+ * Set the set of disallowed actions.
+ *
+ * @param disallowedActions The set of disallowed actions.
+ * @return A {@link Disallows.Builder}.
+ */
+ public Builder setDisallowedActions(EnumSet<Action> disallowedActions) {
+ this.disallowedActions = disallowedActions;
+ return this;
+ }
+
+ @Override
+ public Disallows build() {
+ return new Disallows(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link Disallows} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<Disallows> {
+ @Override
+ public Disallows createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ EnumSet<Action> disallowedActions = EnumSet.noneOf(Action.class);
+ for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
+ if (entry.getValue().getAsJsonPrimitive().getAsBoolean()) {
+ disallowedActions.add(
+ Action.keyOf(entry.getKey().toLowerCase()));
+ }
+ }
+
+ return new Builder()
+ .setDisallowedActions(
+ disallowedActions)
+ .build();
+ }
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/Episode.java
@@ -0,0 +1,566 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import java.util.Arrays;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.enums.ReleaseDatePrecision;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+import com.wrapper.spotify.model_objects.IPlaylistItem;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/documentation/web-api/reference/object-model/#episode-object-full">
+ * episode objects</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = Episode.Builder.class)
+public class Episode extends AbstractModelObject implements IPlaylistItem {
+ private final String audioPreviewUrl;
+ private final String description;
+ private final Integer durationMs;
+ private final Boolean explicit;
+ private final ExternalUrl externalUrls;
+ private final String href;
+ private final String id;
+ private final Image[] images;
+ private final Boolean isExternallyHosted;
+ private final Boolean isPlayable;
+ private final String[] languages;
+ private final String name;
+ private final String releaseDate;
+ private final ReleaseDatePrecision releaseDatePrecision;
+ private final ResumePoint resumePoint;
+ private final ShowSimplified show;
+ private final ModelObjectType type;
+ private final String uri;
+
+ private Episode(final Builder builder) {
+ super(builder);
+ this.audioPreviewUrl = builder.audioPreviewUrl;
+ this.description = builder.description;
+ this.durationMs = builder.durationMs;
+ this.explicit = builder.explicit;
+ this.externalUrls = builder.externalUrls;
+ this.href = builder.href;
+ this.id = builder.id;
+ this.images = builder.images;
+ this.isExternallyHosted = builder.isExternallyHosted;
+ this.isPlayable = builder.isPlayable;
+ this.languages = builder.languages;
+ this.name = builder.name;
+ this.releaseDate = builder.releaseDate;
+ this.releaseDatePrecision = builder.releaseDatePrecision;
+ this.resumePoint = builder.resumePoint;
+ this.show = builder.show;
+ this.type = builder.type;
+ this.uri = builder.uri;
+ }
+
+ /**
+ * Get a URL to a 30 second preview (MP3 format) of the episode. {@code null} if not available.
+ *
+ * @return A URL to an audio preview.
+ */
+ public String getAudioPreviewUrl() {
+ return audioPreviewUrl;
+ }
+
+ /**
+ * Get a description of the episode.
+ *
+ * @return The description of the episode.
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Get the duration of the episode in milliseconds.
+ *
+ * @return The length of the episode in milliseconds.
+ */
+ @Override
+ public Integer getDurationMs() {
+ return durationMs;
+ }
+
+ /**
+ * Check whether the episode is explicit or not.
+ *
+ * @return Whether or not the episode has explicit content ({@code true} = yes it does; {@code false} = no it does not
+ * <b>OR</b> unknown).
+ */
+ public Boolean getExplicit() {
+ return explicit;
+ }
+
+ /**
+ * Get the external URLs of the episode. <br>
+ * Example: <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify-URL</a>
+ *
+ * @return An {@link ExternalUrl} object.
+ */
+ @Override
+ public ExternalUrl getExternalUrls() {
+ return externalUrls;
+ }
+
+ /**
+ * Get the full Spotify Web API endpoint URL of the episode.
+ *
+ * @return A link to the Web API endpoint providing full details of the episode.
+ */
+ @Override
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Get the Spotify ID of the episode.
+ *
+ * @return A <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify episode ID</a>.
+ */
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Get the cover art for the episode in various sizes, widest first.
+ *
+ * @return An array of {@link Image} objects.
+ */
+ public Image[] getImages() {
+ return images;
+ }
+
+ /**
+ * Check whether the episode is hosted outside of Spotify's CDN.
+ *
+ * @return True if the episode is hosted outside of Spotify’s CDN.
+ */
+ public Boolean getExternallyHosted() {
+ return isExternallyHosted;
+ }
+
+ /**
+ * Check whether the episode is playable in the given market.
+ *
+ * @return True if the episode is playable in the given market. Otherwise false.
+ */
+ public Boolean getPlayable() {
+ return isPlayable;
+ }
+
+ /**
+ * Get a list of the languages used in the episode, identified by their ISO 639 code.
+ *
+ * @return An array of <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha-2 country codes</a>.
+ */
+ public String[] getLanguages() {
+ return languages;
+ }
+
+ /**
+ * Get the name of the episode.
+ *
+ * @return The name of the episode.
+ */
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the date the episode was first released, for example "1981-12-15". Depending on the precision, it might be shown as "1981" or "1981-12".
+ *
+ * @return The release date of the episode.
+ */
+ public String getReleaseDate() {
+ return releaseDate;
+ }
+
+ /**
+ * Get the precision with which the release date is known.
+ *
+ * @return A {@link ReleaseDatePrecision} object.
+ */
+ public ReleaseDatePrecision getReleaseDatePrecision() {
+ return releaseDatePrecision;
+ }
+
+ /**
+ * Get the user’s most recent position in the episode. Set if the supplied access token is a user token and has the scope {@code user-read-playback-position}.
+ *
+ * @return A {@link ResumePoint} object.
+ */
+ public ResumePoint getResumePoint() {
+ return resumePoint;
+ }
+
+ /**
+ * Get the show on which the episode belongs.
+ *
+ * @return A {@link Show} object on which the episode belongs.
+ */
+ public ShowSimplified getShow() {
+ return show;
+ }
+
+ /**
+ * Get the model object type. In this case "episode".
+ *
+ * @return A {@link ModelObjectType}.
+ */
+ @Override
+ public ModelObjectType getType() {
+ return type;
+ }
+
+ /**
+ * Get the Spotify URI of the episode.
+ *
+ * @return <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify episode URI</a>.
+ */
+ @Override
+ public String getUri() {
+ return uri;
+ }
+
+ @Override
+ public String toString() {
+ return "Episode(name=" + name + ", description=" + description + ", show=" + show + ", audioPreviewUrl="
+ + audioPreviewUrl + ", durationMs=" + durationMs + ", explicit=" + explicit + ", externalUrls=" + externalUrls
+ + ", href=" + href + ", id=" + id + ", images=" + Arrays.toString(images) + ", isExternallyHosted="
+ + isExternallyHosted + ", isPlayable=" + isPlayable + ", languages=" + Arrays.toString(languages)
+ + ", releaseDate=" + releaseDate + ", releaseDatePrecision=" + releaseDatePrecision + ", resumePoint="
+ + resumePoint + ", type=" + type + ", uri=" + uri + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link Episode} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private String audioPreviewUrl;
+ private String description;
+ private Integer durationMs;
+ private Boolean explicit;
+ private ExternalUrl externalUrls;
+ private String href;
+ private String id;
+ private Image[] images;
+ private Boolean isExternallyHosted;
+ private Boolean isPlayable;
+ private String[] languages;
+ private String name;
+ private String releaseDate;
+ private ReleaseDatePrecision releaseDatePrecision;
+ private ResumePoint resumePoint;
+ private ShowSimplified show;
+ private ModelObjectType type;
+ private String uri;
+
+ /**
+ * Set the URL to a audio preview for the episode to be built.
+ *
+ * @param audioPreviewUrl The URL to an audio preview.
+ * @return A {@link Episode.Builder}.
+ */
+ public Builder setAudioPreviewUrl(String audioPreviewUrl) {
+ this.audioPreviewUrl = audioPreviewUrl;
+ return this;
+ }
+
+ /**
+ * Set the description for the episode to be built.
+ *
+ * @param description The description of the episode.
+ * @return A {@link Episode.Builder}.
+ */
+ public Builder setDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ /**
+ * Set the duration for the episode to be built.
+ *
+ * @param durationMs The duration of the episode in milliseconds.
+ * @return A {@link Episode.Builder}.
+ */
+ public Builder setDurationMs(Integer durationMs) {
+ this.durationMs = durationMs;
+ return this;
+ }
+
+ /**
+ * Set whether the episode to be built is explicit or not.
+ *
+ * @param explicit Whether or not the episode has explicit content (true = yes it does; false = no it does not OR unknown).
+ * @return A {@link Episode.Builder}.
+ */
+ public Builder setExplicit(Boolean explicit) {
+ this.explicit = explicit;
+ return this;
+ }
+
+ /**
+ * Set the external URLs for the episode to be built.
+ *
+ * @param externalUrls The {@link ExternalUrl} for the episode object.
+ * @return A {@link Episode.Builder}.
+ */
+ public Builder setExternalUrls(ExternalUrl externalUrls) {
+ this.externalUrls = externalUrls;
+ return this;
+ }
+
+ /**
+ * Set the link to the Web API endpoint providing full details of the episode to be built.
+ *
+ * @param href The link to the Web API endpoint providing full details of the episode.
+ * @return A {@link Episode.Builder}.
+ */
+ public Builder setHref(String href) {
+ this.href = href;
+ return this;
+ }
+
+ /**
+ * Set the Spotify ID for the episode to be built.
+ *
+ * @param id <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify episode ID</a>.
+ * @return A {@link Episode.Builder}.
+ */
+ public Builder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * Set the cover art for the episode to be built.
+ *
+ * @param images {@link Image} objects.
+ * @return A {@link Episode.Builder}.
+ */
+ public Builder setImages(Image... images) {
+ this.images = images;
+ return this;
+ }
+
+ /**
+ * Set whether the episode to be built is hosted outside of Spotify's CDN.
+ *
+ * @param externallyHosted True if the episode is hosted outside of Spotify’s CDN.
+ * @return A {@link Episode.Builder}.
+ */
+ public Builder setExternallyHosted(Boolean externallyHosted) {
+ isExternallyHosted = externallyHosted;
+ return this;
+ }
+
+ /**
+ * Set whether the episode to be built is playable in the given market.
+ *
+ * @param playable True if the episode is playable in the given market. Otherwise false.
+ * @return A {@link Episode.Builder}.
+ */
+ public Builder setPlayable(Boolean playable) {
+ isPlayable = playable;
+ return this;
+ }
+
+ /**
+ * Set a list of the languages used in the episode to be built.
+ *
+ * @param languages An array of <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha-2 country codes</a>.
+ * @return A {@link Episode.Builder}.
+ */
+ public Builder setLanguages(String... languages) {
+ this.languages = languages;
+ return this;
+ }
+
+ /**
+ * Set the name for the episode to be built.
+ *
+ * @param name The name of the episode.
+ * @return A {@link Episode.Builder}.
+ */
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Set the release date for the episode to be built.
+ *
+ * @param releaseDate The release date of the episode.
+ * @return A {@link Episode.Builder}.
+ */
+ public Builder setReleaseDate(String releaseDate) {
+ this.releaseDate = releaseDate;
+ return this;
+ }
+
+ /**
+ * Set the release date precision for the episode to be built.
+ *
+ * @param releaseDatePrecision The {@link ReleaseDatePrecision} of the episode.
+ * @return A {@link Episode.Builder}.
+ */
+ public Builder setReleaseDatePrecision(ReleaseDatePrecision releaseDatePrecision) {
+ this.releaseDatePrecision = releaseDatePrecision;
+ return this;
+ }
+
+ /**
+ * Set the user's most recent resume point for the episode to be built.
+ *
+ * @param resumePoint The {@link ResumePoint} of the episode.
+ * @return A {@link Episode.Builder}.
+ */
+ public Builder setResumePoint(ResumePoint resumePoint) {
+ this.resumePoint = resumePoint;
+ return this;
+ }
+
+ /**
+ * Set the show the episode to be built belongs on.
+ *
+ * @param show The {@link ShowSimplified} the episode belongs on.
+ * @return A {@link Episode.Builder}.
+ */
+ public Builder setShow(ShowSimplified show) {
+ this.show = show;
+ return this;
+ }
+
+ /**
+ * Set the type of model object. In this case "episode".
+ *
+ * @param type The {@link ModelObjectType}.
+ * @return A {@link Episode.Builder}.
+ */
+ public Builder setType(ModelObjectType type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Set the Spotify URI for the episode to be built.
+ *
+ * @param uri The <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URI</a> for the episode.
+ * @return A {@link Episode.Builder}.
+ */
+ public Builder setUri(String uri) {
+ this.uri = uri;
+ return this;
+ }
+
+ @Override
+ public Episode build() {
+ return new Episode(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link Episode} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<Episode> {
+ @Override
+ public Episode createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Builder()
+ .setAudioPreviewUrl(
+ hasAndNotNull(jsonObject, "audio_preview_url")
+ ? jsonObject.get("audio_preview_url").getAsString()
+ : null)
+ .setDescription(
+ hasAndNotNull(jsonObject, "description")
+ ? jsonObject.get("description").getAsString()
+ : null)
+ .setDurationMs(
+ hasAndNotNull(jsonObject, "duration_ms")
+ ? jsonObject.get("duration_ms").getAsInt()
+ : null)
+ .setExplicit(
+ hasAndNotNull(jsonObject, "explicit")
+ ? jsonObject.get("explicit").getAsBoolean()
+ : null)
+ .setExternalUrls(
+ hasAndNotNull(jsonObject, "external_urls")
+ ? new ExternalUrl.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("external_urls"))
+ : null)
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setId(
+ hasAndNotNull(jsonObject, "id")
+ ? jsonObject.get("id").getAsString()
+ : null)
+ .setImages(
+ hasAndNotNull(jsonObject, "images")
+ ? new Image.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("images"))
+ : null)
+ .setExternallyHosted(
+ hasAndNotNull(jsonObject, "is_externally_hosted")
+ ? jsonObject.get("is_externally_hosted").getAsBoolean()
+ : null)
+ .setPlayable(
+ hasAndNotNull(jsonObject, "is_playable")
+ ? jsonObject.get("is_playable").getAsBoolean()
+ : null)
+ .setLanguages(
+ hasAndNotNull(jsonObject, "languages")
+ ? new Gson().fromJson(
+ jsonObject.getAsJsonArray("languages"), String[].class)
+ : null)
+ .setName(
+ hasAndNotNull(jsonObject, "name")
+ ? jsonObject.get("name").getAsString()
+ : null)
+ .setReleaseDate(
+ hasAndNotNull(jsonObject, "release_date")
+ ? jsonObject.get("release_date").getAsString()
+ : null)
+ .setReleaseDatePrecision(
+ hasAndNotNull(jsonObject, "release_date_precision")
+ ? ReleaseDatePrecision.keyOf(
+ jsonObject.get("release_date_precision").getAsString().toLowerCase())
+ : null)
+ .setResumePoint(
+ hasAndNotNull(jsonObject, "resume_point")
+ ? new ResumePoint.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("resume_point"))
+ : null)
+ .setShow(
+ hasAndNotNull(jsonObject, "show")
+ ? new ShowSimplified.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("show"))
+ : null)
+ .setType(
+ hasAndNotNull(jsonObject, "type")
+ ? ModelObjectType.keyOf(
+ jsonObject.get("type").getAsString().toLowerCase())
+ : null)
+ .setUri(
+ hasAndNotNull(jsonObject, "uri")
+ ? jsonObject.get("uri").getAsString()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/EpisodeSimplified.java
@@ -0,0 +1,531 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import java.util.Arrays;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.enums.ReleaseDatePrecision;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+import com.wrapper.spotify.requests.data.search.interfaces.ISearchModelObject;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/documentation/web-api/reference/object-model/#episode-object-simplified">
+ * simplified Episode objects</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = EpisodeSimplified.Builder.class)
+public class EpisodeSimplified extends AbstractModelObject implements ISearchModelObject {
+ private final String audioPreviewUrl;
+ private final String description;
+ private final Integer durationMs;
+ private final Boolean explicit;
+ private final ExternalUrl externalUrls;
+ private final String href;
+ private final String id;
+ private final Image[] images;
+ private final Boolean isExternallyHosted;
+ private final Boolean isPlayable;
+ private final String[] languages;
+ private final String name;
+ private final String releaseDate;
+ private final ReleaseDatePrecision releaseDatePrecision;
+ private final ResumePoint resumePoint;
+ private final ModelObjectType type;
+ private final String uri;
+
+ private EpisodeSimplified(final Builder builder) {
+ super(builder);
+ this.audioPreviewUrl = builder.audioPreviewUrl;
+ this.description = builder.description;
+ this.durationMs = builder.durationMs;
+ this.explicit = builder.explicit;
+ this.externalUrls = builder.externalUrls;
+ this.href = builder.href;
+ this.id = builder.id;
+ this.images = builder.images;
+ this.isExternallyHosted = builder.isExternallyHosted;
+ this.isPlayable = builder.isPlayable;
+ this.languages = builder.languages;
+ this.name = builder.name;
+ this.releaseDate = builder.releaseDate;
+ this.releaseDatePrecision = builder.releaseDatePrecision;
+ this.resumePoint = builder.resumePoint;
+ this.type = builder.type;
+ this.uri = builder.uri;
+ }
+
+ /**
+ * Get a URL to a 30 second preview (MP3 format) of the episode. {@code null} if not available.
+ *
+ * @return A URL to an audio preview.
+ */
+ public String getAudioPreviewUrl() {
+ return audioPreviewUrl;
+ }
+
+ /**
+ * Get a description of the episode.
+ *
+ * @return The description of the episode.
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Get the duration of the episode in milliseconds.
+ *
+ * @return The length of the episode in milliseconds.
+ */
+ public Integer getDurationMs() {
+ return durationMs;
+ }
+
+ /**
+ * Check whether the episode is explicit or not.
+ *
+ * @return Whether or not the episode has explicit content ({@code true} = yes it does; {@code false} = no it does not
+ * <b>OR</b> unknown).
+ */
+ public Boolean getExplicit() {
+ return explicit;
+ }
+
+ /**
+ * Get the external URLs of the episode. <br>
+ * Example: <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify-URL</a>
+ *
+ * @return An {@link ExternalUrl} object.
+ */
+ public ExternalUrl getExternalUrls() {
+ return externalUrls;
+ }
+
+ /**
+ * Get the full Spotify Web API endpoint URL of the episode.
+ *
+ * @return A link to the Web API endpoint providing full details of the episode.
+ */
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Get the Spotify ID of the episode.
+ *
+ * @return A <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify episode ID</a>.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Get the cover art for the episode in various sizes, widest first.
+ *
+ * @return An array of {@link Image} objects.
+ */
+ public Image[] getImages() {
+ return images;
+ }
+
+ /**
+ * Check whether the episode is hosted outside of Spotify's CDN.
+ *
+ * @return True if the episode is hosted outside of Spotify’s CDN.
+ */
+ public Boolean getExternallyHosted() {
+ return isExternallyHosted;
+ }
+
+ /**
+ * Check whether the episode is playable in the given market.
+ *
+ * @return True if the episode is playable in the given market. Otherwise false.
+ */
+ public Boolean getPlayable() {
+ return isPlayable;
+ }
+
+ /**
+ * Get a list of the languages used in the episode, identified by their ISO 639 code.
+ *
+ * @return An array of <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha-2 country codes</a>.
+ */
+ public String[] getLanguages() {
+ return languages;
+ }
+
+ /**
+ * Get the name of the episode.
+ *
+ * @return The name of the episode.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the date the episode was first released, for example "1981-12-15". Depending on the precision, it might be shown as "1981" or "1981-12".
+ *
+ * @return The release date of the episode.
+ */
+ public String getReleaseDate() {
+ return releaseDate;
+ }
+
+ /**
+ * Get the precision with which the release date is known.
+ *
+ * @return A {@link ReleaseDatePrecision} object.
+ */
+ public ReleaseDatePrecision getReleaseDatePrecision() {
+ return releaseDatePrecision;
+ }
+
+ /**
+ * Get the user’s most recent position in the episode. Set if the supplied access token is a user token and has the scope {@code user-read-playback-position}.
+ *
+ * @return A {@link ResumePoint} object.
+ */
+ public ResumePoint getResumePoint() {
+ return resumePoint;
+ }
+
+ /**
+ * Get the model object type. In this case "episode".
+ *
+ * @return A {@link ModelObjectType}.
+ */
+ public ModelObjectType getType() {
+ return type;
+ }
+
+ /**
+ * Get the Spotify URI of the episode.
+ *
+ * @return <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify episode URI</a>.
+ */
+ public String getUri() {
+ return uri;
+ }
+
+ @Override
+ public String toString() {
+ return "EpisodeSimplified(name=" + name + ", audioPreviewUrl=" + audioPreviewUrl + ", description=" + description
+ + ", durationMs=" + durationMs + ", explicit=" + explicit + ", externalUrls=" + externalUrls + ", href=" + href
+ + ", id=" + id + ", images=" + Arrays.toString(images) + ", isExternallyHosted=" + isExternallyHosted
+ + ", isPlayable=" + isPlayable + ", languages=" + Arrays.toString(languages) + ", releaseDate=" + releaseDate
+ + ", releaseDatePrecision=" + releaseDatePrecision + ", resumePoint=" + resumePoint + ", type=" + type
+ + ", uri=" + uri + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link EpisodeSimplified} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private String audioPreviewUrl;
+ private String description;
+ private Integer durationMs;
+ private Boolean explicit;
+ private ExternalUrl externalUrls;
+ private String href;
+ private String id;
+ private Image[] images;
+ private Boolean isExternallyHosted;
+ private Boolean isPlayable;
+ private String[] languages;
+ private String name;
+ private String releaseDate;
+ private ReleaseDatePrecision releaseDatePrecision;
+ private ResumePoint resumePoint;
+ private ModelObjectType type;
+ private String uri;
+
+ /**
+ * Set the URL to a audio preview for the episode to be built.
+ *
+ * @param audioPreviewUrl The URL to an audio preview.
+ * @return A {@link EpisodeSimplified.Builder}.
+ */
+ public Builder setAudioPreviewUrl(String audioPreviewUrl) {
+ this.audioPreviewUrl = audioPreviewUrl;
+ return this;
+ }
+
+ /**
+ * Set the description for the episode to be built.
+ *
+ * @param description The description of the episode.
+ * @return A {@link EpisodeSimplified.Builder}.
+ */
+ public Builder setDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ /**
+ * Set the duration for the episode to be built.
+ *
+ * @param durationMs The duration of the episode in milliseconds.
+ * @return A {@link EpisodeSimplified.Builder}.
+ */
+ public Builder setDurationMs(Integer durationMs) {
+ this.durationMs = durationMs;
+ return this;
+ }
+
+ /**
+ * Set whether the episode to be built is explicit or not.
+ *
+ * @param explicit Whether or not the episode has explicit content (true = yes it does; false = no it does not OR unknown).
+ * @return A {@link EpisodeSimplified.Builder}.
+ */
+ public Builder setExplicit(Boolean explicit) {
+ this.explicit = explicit;
+ return this;
+ }
+
+ /**
+ * Set the external URLs for the episode to be built.
+ *
+ * @param externalUrls The {@link ExternalUrl} for the episode object.
+ * @return A {@link EpisodeSimplified.Builder}.
+ */
+ public Builder setExternalUrls(ExternalUrl externalUrls) {
+ this.externalUrls = externalUrls;
+ return this;
+ }
+
+ /**
+ * Set the link to the Web API endpoint providing full details of the episode to be built.
+ *
+ * @param href The link to the Web API endpoint providing full details of the episode.
+ * @return A {@link EpisodeSimplified.Builder}.
+ */
+ public Builder setHref(String href) {
+ this.href = href;
+ return this;
+ }
+
+ /**
+ * Set the Spotify ID for the episode to be built.
+ *
+ * @param id <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify episode ID</a>.
+ * @return A {@link EpisodeSimplified.Builder}.
+ */
+ public Builder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * Set the cover art for the episode to be built.
+ *
+ * @param images {@link Image} objects.
+ * @return A {@link EpisodeSimplified.Builder}.
+ */
+ public Builder setImages(Image... images) {
+ this.images = images;
+ return this;
+ }
+
+ /**
+ * Set whether the episode to be built is hosted outside of Spotify's CDN.
+ *
+ * @param externallyHosted True if the episode is hosted outside of Spotify’s CDN.
+ * @return A {@link EpisodeSimplified.Builder}.
+ */
+ public Builder setExternallyHosted(Boolean externallyHosted) {
+ isExternallyHosted = externallyHosted;
+ return this;
+ }
+
+ /**
+ * Set whether the episode to be built is playable in the given market.
+ *
+ * @param playable True if the episode is playable in the given market. Otherwise false.
+ * @return A {@link EpisodeSimplified.Builder}.
+ */
+ public Builder setPlayable(Boolean playable) {
+ isPlayable = playable;
+ return this;
+ }
+
+ /**
+ * Set a list of the languages used in the episode to be built.
+ *
+ * @param languages An array of <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha-2 country codes</a>.
+ * @return A {@link EpisodeSimplified.Builder}.
+ */
+ public Builder setLanguages(String... languages) {
+ this.languages = languages;
+ return this;
+ }
+
+ /**
+ * Set the name for the episode to be built.
+ *
+ * @param name The name of the episode.
+ * @return A {@link EpisodeSimplified.Builder}.
+ */
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Set the release date for the episode to be built.
+ *
+ * @param releaseDate The release date of the episode.
+ * @return A {@link EpisodeSimplified.Builder}.
+ */
+ public Builder setReleaseDate(String releaseDate) {
+ this.releaseDate = releaseDate;
+ return this;
+ }
+
+ /**
+ * Set the release date precision for the episode to be built.
+ *
+ * @param releaseDatePrecision The {@link ReleaseDatePrecision} of the episode.
+ * @return A {@link EpisodeSimplified.Builder}.
+ */
+ public Builder setReleaseDatePrecision(ReleaseDatePrecision releaseDatePrecision) {
+ this.releaseDatePrecision = releaseDatePrecision;
+ return this;
+ }
+
+ /**
+ * Set the user's most recent resume point for the episode to be built.
+ *
+ * @param resumePoint The {@link ResumePoint} of the episode.
+ * @return A {@link EpisodeSimplified.Builder}.
+ */
+ public Builder setResumePoint(ResumePoint resumePoint) {
+ this.resumePoint = resumePoint;
+ return this;
+ }
+
+ /**
+ * Set the type of model object. In this case "episode".
+ *
+ * @param type The {@link ModelObjectType}.
+ * @return A {@link EpisodeSimplified.Builder}.
+ */
+ public Builder setType(ModelObjectType type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Set the Spotify URI for the episode to be built.
+ *
+ * @param uri The <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URI</a> for the episode.
+ * @return A {@link EpisodeSimplified.Builder}.
+ */
+ public Builder setUri(String uri) {
+ this.uri = uri;
+ return this;
+ }
+
+ @Override
+ public EpisodeSimplified build() {
+ return new EpisodeSimplified(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link EpisodeSimplified} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<EpisodeSimplified> {
+ @Override
+ public EpisodeSimplified createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Builder()
+ .setAudioPreviewUrl(
+ hasAndNotNull(jsonObject, "audio_preview_url")
+ ? jsonObject.get("audio_preview_url").getAsString()
+ : null)
+ .setDescription(
+ hasAndNotNull(jsonObject, "description")
+ ? jsonObject.get("description").getAsString()
+ : null)
+ .setDurationMs(
+ hasAndNotNull(jsonObject, "duration_ms")
+ ? jsonObject.get("duration_ms").getAsInt()
+ : null)
+ .setExplicit(
+ hasAndNotNull(jsonObject, "explicit")
+ ? jsonObject.get("explicit").getAsBoolean()
+ : null)
+ .setExternalUrls(
+ hasAndNotNull(jsonObject, "external_urls")
+ ? new ExternalUrl.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("external_urls"))
+ : null)
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setId(
+ hasAndNotNull(jsonObject, "id")
+ ? jsonObject.get("id").getAsString()
+ : null)
+ .setImages(
+ hasAndNotNull(jsonObject, "images")
+ ? new Image.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("images"))
+ : null)
+ .setExternallyHosted(
+ hasAndNotNull(jsonObject, "is_externally_hosted")
+ ? jsonObject.get("is_externally_hosted").getAsBoolean()
+ : null)
+ .setPlayable(
+ hasAndNotNull(jsonObject, "is_playable")
+ ? jsonObject.get("is_playable").getAsBoolean()
+ : null)
+ .setLanguages(
+ hasAndNotNull(jsonObject, "languages")
+ ? new Gson().fromJson(
+ jsonObject.getAsJsonArray("languages"), String[].class)
+ : null)
+ .setName(
+ hasAndNotNull(jsonObject, "name")
+ ? jsonObject.get("name").getAsString()
+ : null)
+ .setReleaseDate(
+ hasAndNotNull(jsonObject, "release_date")
+ ? jsonObject.get("release_date").getAsString()
+ : null)
+ .setReleaseDatePrecision(
+ hasAndNotNull(jsonObject, "release_date_precision")
+ ? ReleaseDatePrecision.keyOf(
+ jsonObject.get("release_date_precision").getAsString().toLowerCase())
+ : null)
+ .setResumePoint(
+ hasAndNotNull(jsonObject, "resume_point")
+ ? new ResumePoint.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("resume_point"))
+ : null)
+ .setType(
+ hasAndNotNull(jsonObject, "type")
+ ? ModelObjectType.keyOf(
+ jsonObject.get("type").getAsString().toLowerCase())
+ : null)
+ .setUri(
+ hasAndNotNull(jsonObject, "uri")
+ ? jsonObject.get("uri").getAsString()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/Error.java
@@ -0,0 +1,110 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/object-model/#error-object">
+ * Error objects</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = Error.Builder.class)
+public class Error extends AbstractModelObject {
+ private final Integer status;
+ private final String message;
+
+ private Error(final Builder builder) {
+ super(builder);
+
+ this.status = builder.status;
+ this.message = builder.message;
+ }
+
+ /**
+ * Get the <a href="https://developer.spotify.com/web-api/user-guide/#response-status-codes">HTTP status code</a>
+ * of the {@link Error} object.
+ *
+ * @return The <a href="https://developer.spotify.com/web-api/user-guide/#response-status-codes">HTTP status code</a>.
+ */
+ public Integer getStatus() {
+ return status;
+ }
+
+ /**
+ * Get the error message (description of the cause) of the {@link Error} object.
+ *
+ * @return A short description of the cause of the error.
+ */
+ public String getMessage() {
+ return message;
+ }
+
+ @Override
+ public String toString() {
+ return "Error(status=" + status + ", message=" + message + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link Error} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private Integer status;
+ private String message;
+
+ /**
+ * The error <a href="https://developer.spotify.com/web-api/user-guide/#response-status-codes">HTTP status
+ * code</a> setter.
+ *
+ * @param status The <a href="https://developer.spotify.com/web-api/user-guide/#response-status-codes">
+ * HTTP status code</a>.
+ * @return A {@link Error.Builder}.
+ */
+ public Builder setStatus(Integer status) {
+ this.status = status;
+ return this;
+ }
+
+ /**
+ * The error message setter.
+ *
+ * @param message A short description of the cause of the error.
+ * @return A {@link Error.Builder}.
+ */
+ public Builder setMessage(String message) {
+ this.message = message;
+ return this;
+ }
+
+ @Override
+ public Error build() {
+ return new Error(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link Error} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<Error> {
+ public Error createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Error.Builder()
+ .setStatus(
+ hasAndNotNull(jsonObject, "status")
+ ? jsonObject.get("status").getAsInt()
+ : null)
+ .setMessage(
+ hasAndNotNull(jsonObject, "message")
+ ? jsonObject.get("message").getAsString()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/ExternalId.java
@@ -0,0 +1,92 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+import java.util.Map;
+
+/**
+ * Retrieve information about
+ * <a href="https://developer.spotify.com/web-api/object-model/#external-id-object">External ID objects</a>
+ * by building instances from this class.
+ */
+@JsonDeserialize(builder = ExternalId.Builder.class)
+public class ExternalId extends AbstractModelObject {
+ private final Map<String, String> externalIds;
+
+ private ExternalId(final Builder builder) {
+ super(builder);
+
+ this.externalIds = builder.externalIds;
+ }
+
+ /**
+ * Get the external IDs from this <a href="https://developer.spotify.com/web-api/object-model/#external-id-object">
+ * External ID object</a>. <br><br>
+ * <p>
+ * External ID examples:<br>
+ * "isrc" - <a href="http://en.wikipedia.org/wiki/International_Standard_Recording_Code">
+ * International Standard Recording Code</a><br>
+ * "ean" - <a href="http://en.wikipedia.org/wiki/International_Article_Number_%28EAN%29">International Article Number
+ * </a><br>
+ * "upc" - <a href="http://en.wikipedia.org/wiki/Universal_Product_Code">Universal Product Code</a>
+ *
+ * @return A {@link Map} of external IDs, containing external identifiers for the object.
+ */
+ public Map<String, String> getExternalIds() {
+ return externalIds;
+ }
+
+ @Override
+ public String toString() {
+ return "ExternalId(externalIds=" + externalIds + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link ExternalId} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private Map<String, String> externalIds;
+
+ /**
+ * External IDs setter.
+ *
+ * @param externalIds A {@link Map} of external IDs, containing external identifiers for the object.
+ * @return A {@link ExternalId.Builder}.
+ */
+ public Builder setExternalIds(Map<String, String> externalIds) {
+ this.externalIds = externalIds;
+ return this;
+ }
+
+ @Override
+ public ExternalId build() {
+ return new ExternalId(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link ExternalId} instances.
+ */
+ @SuppressWarnings("unchecked")
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<ExternalId> {
+ public ExternalId createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ Map<String, String> map = new Gson().fromJson(jsonObject, Map.class);
+
+ return new ExternalId.Builder()
+ .setExternalIds(map)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/ExternalUrl.java
@@ -0,0 +1,101 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+import java.util.Map;
+
+/**
+ * Retrieve information about
+ * <a href="https://developer.spotify.com/web-api/object-model/#external-url-object">External URL objects</a>
+ * by building instances from this class.
+ */
+@JsonDeserialize(builder = ExternalUrl.Builder.class)
+public class ExternalUrl extends AbstractModelObject {
+ private final Map<String, String> externalUrls;
+
+ private ExternalUrl(final Builder builder) {
+ super(builder);
+
+ this.externalUrls = builder.externalUrls;
+ }
+
+ /**
+ * Get an specific external URL from this external URLs object with the key string given below.
+ *
+ * @param key The type of the URL.
+ * @return An external, public URL to the object.
+ */
+ public String get(String key) {
+ return externalUrls.get(key);
+ }
+
+ /**
+ * Get the external URLs from this
+ * <a href="https://developer.spotify.com/web-api/object-model/#external-url-object">External URL object</a>.
+ * <br><br>
+ * <p>
+ * External URL example: <br>
+ * "spotify" - The <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URL</a>
+ * for the object.
+ *
+ * @return A {@link Map} of external public URLs to its objects.
+ */
+ public Map<String, String> getExternalUrls() {
+ return externalUrls;
+ }
+
+ @Override
+ public String toString() {
+ return "ExternalUrl(externalUrls=" + externalUrls + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link ExternalUrl} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private Map<String, String> externalUrls;
+
+ /**
+ * The external URLs setter.
+ *
+ * @param externalUrls A {@link Map} of external public URLs to its objects.
+ * @return A {@link ExternalUrl.Builder}.
+ */
+ public Builder setExternalUrls(Map<String, String> externalUrls) {
+ this.externalUrls = externalUrls;
+ return this;
+ }
+
+ @Override
+ public ExternalUrl build() {
+ return new ExternalUrl(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link ExternalUrl} instances.
+ */
+ @SuppressWarnings("unchecked")
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<ExternalUrl> {
+ public ExternalUrl createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ Map<String, String> map = new Gson().fromJson(jsonObject, Map.class);
+
+ return new ExternalUrl.Builder()
+ .setExternalUrls(map)
+ .build();
+ }
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/Followers.java
@@ -0,0 +1,110 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about
+ * <a href="https://developer.spotify.com/web-api/object-model/#followers-object">Follower objects</a>
+ * by building instances from this class.
+ */
+@JsonDeserialize(builder = Followers.Builder.class)
+public class Followers extends AbstractModelObject {
+ private final String href;
+ private final Integer total;
+
+ private Followers(final Builder builder) {
+ super(builder);
+
+ this.href = builder.href;
+ this.total = builder.total;
+ }
+
+ /**
+ * Get a link to the Web API endpoint providing full details of the followers object. <br>
+ * <b>Please note:</b> This will always be set to {@code null}, as the Web API does not support it at the moment.
+ *
+ * @return A link to the Web API endpoint providing full details of the followers; {@code null} if not available.
+ */
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Get the total number of followers.
+ *
+ * @return The total number of followers.
+ */
+ public Integer getTotal() {
+ return total;
+ }
+
+ @Override
+ public String toString() {
+ return "Followers(href=" + href + ", total=" + total + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link Followers} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private String href;
+ private Integer total;
+
+ /**
+ * The href setter.
+ *
+ * @param href A link to the Web API endpoint providing full details of the followers; {@code null} if not
+ * available.
+ * @return A {@link Followers.Builder}.
+ */
+ public Builder setHref(String href) {
+ this.href = href;
+ return this;
+ }
+
+ /**
+ * The follower count setter.
+ *
+ * @param total The total number of followers.
+ * @return A {@link Followers.Builder}.
+ */
+ public Builder setTotal(Integer total) {
+ this.total = total;
+ return this;
+ }
+
+ @Override
+ public Followers build() {
+ return new Followers(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link Followers} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<Followers> {
+ public Followers createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Followers.Builder()
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setTotal(
+ hasAndNotNull(jsonObject, "total")
+ ? jsonObject.get("total").getAsInt()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/Image.java
@@ -0,0 +1,135 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about
+ * <a href="https://developer.spotify.com/web-api/object-model/#image-object">Image objects</a>
+ * by building instances from this class.
+ */
+@JsonDeserialize(builder = Image.Builder.class)
+public class Image extends AbstractModelObject {
+ private final Integer height;
+ private final String url;
+ private final Integer width;
+
+ private Image(final Builder builder) {
+ super(builder);
+
+ this.height = builder.height;
+ this.url = builder.url;
+ this.width = builder.width;
+ }
+
+ /**
+ * Get the height of the image in pixels.
+ *
+ * @return The image height in pixels. If unknown: {@code null}.
+ */
+ public Integer getHeight() {
+ return height;
+ }
+
+ /**
+ * Get the source URL of the image.
+ *
+ * @return The source URL of the image.
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * Get the width of the image in pixels.
+ *
+ * @return The image width in pixels. If unknown: {@code null}.
+ */
+ public Integer getWidth() {
+ return width;
+ }
+
+ @Override
+ public String toString() {
+ return "Image(height=" + height + ", url=" + url + ", width=" + width + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link Image} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private Integer height;
+ private String url;
+ private Integer width;
+
+ /**
+ * The height setter.
+ *
+ * @param height The image height in pixels. If unknown: {@code null}.
+ * @return A {@link Image.Builder}.
+ */
+ public Builder setHeight(Integer height) {
+ this.height = height;
+ return this;
+ }
+
+ /**
+ * The source URL setter.
+ *
+ * @param url The source URL of the image.
+ * @return A {@link Image.Builder}.
+ */
+ public Builder setUrl(String url) {
+ this.url = url;
+ return this;
+ }
+
+ /**
+ * The width setter.
+ *
+ * @param width The image width in pixels. If unknown: {@code null}.
+ * @return A {@link Image.Builder}.
+ */
+ public Builder setWidth(Integer width) {
+ this.width = width;
+ return this;
+ }
+
+ @Override
+ public Image build() {
+ return new Image(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link Image} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<Image> {
+ public Image createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Image.Builder()
+ .setHeight(
+ hasAndNotNull(jsonObject, "height")
+ ? jsonObject.get("height").getAsInt()
+ : null)
+ .setUrl(
+ hasAndNotNull(jsonObject, "url")
+ ? jsonObject.get("url").getAsString()
+ : null)
+ .setWidth(
+ hasAndNotNull(jsonObject, "width")
+ ? jsonObject.get("width").getAsInt()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/Paging.java
@@ -0,0 +1,255 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+import java.lang.reflect.ParameterizedType;
+import java.util.Arrays;
+
+/**
+ * Retrieve information about
+ * <a href="https://developer.spotify.com/web-api/object-model/#paging-object">Paging objects</a>
+ * by building instances from this class. <br>
+ * This offset-based paging object is a container for a set of objects.
+ *
+ * @param <T> The type of the objects contained in a paging object.
+ */
+@JsonDeserialize(builder = Paging.Builder.class)
+public class Paging<T> extends AbstractModelObject {
+ private final String href;
+ private final T[] items;
+ private final Integer limit;
+ private final String next;
+ private final Integer offset;
+ private final String previous;
+ private final Integer total;
+
+ private Paging(final Paging.Builder<T> builder) {
+ super(builder);
+
+ this.href = builder.href;
+ this.items = builder.items;
+ this.limit = builder.limit;
+ this.next = builder.next;
+ this.offset = builder.offset;
+ this.previous = builder.previous;
+ this.total = builder.total;
+ }
+
+ /**
+ * Get a link to the Web API endpoint returning the full result of the request.
+ *
+ * @return A link to the Web API endpoint returning the full result of the request.
+ */
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Get the items contained in the paging object.
+ *
+ * @return The items contained in the paging object.
+ */
+ public T[] getItems() {
+ return items;
+ }
+
+ /**
+ * Get the maximum number of items in the response (as set in the query or by default).
+ *
+ * @return The maximum number of items in the response (as set in the query or by default).
+ */
+ public Integer getLimit() {
+ return limit;
+ }
+
+ /**
+ * Get the URL to the next page of items. ({@code null} if none)
+ *
+ * @return URL to the next page of items. ({@code null} if none)
+ */
+ public String getNext() {
+ return next;
+ }
+
+ /**
+ * Get the offset of the items returned (as set in the query or by default).
+ *
+ * @return The offset of the items returned (as set in the query or by default).
+ */
+ public Integer getOffset() {
+ return offset;
+ }
+
+ /**
+ * Get the URL to the previous page of items. ({@code null} if none)
+ *
+ * @return URL to the previous page of items. ({@code null} if none)
+ */
+ public String getPrevious() {
+ return previous;
+ }
+
+ /**
+ * Get the total number of items available to return.
+ *
+ * @return The total number of items available to return.
+ */
+ public Integer getTotal() {
+ return total;
+ }
+
+ @Override
+ public String toString() {
+ return "Paging(href=" + href + ", items=" + Arrays.toString(items) + ", limit=" + limit + ", next=" + next
+ + ", offset=" + offset + ", previous=" + previous + ", total=" + total + ")";
+ }
+
+ @Override
+ public Builder<T> builder() {
+ return new Builder<>();
+ }
+
+ /**
+ * Builder class for building {@link Paging} instances.
+ *
+ * @param <T> The type of the objects contained in a paging object.
+ */
+ public static final class Builder<T> extends AbstractModelObject.Builder {
+ private String href;
+ private T[] items;
+ private Integer limit;
+ private String next;
+ private Integer offset;
+ private String previous;
+ private Integer total;
+
+ /**
+ * The href setter.
+ *
+ * @param href A link to the Web API endpoint returning the full result of the request.
+ * @return A {@link Paging.Builder}.
+ */
+ public Builder<T> setHref(String href) {
+ this.href = href;
+ return this;
+ }
+
+ /**
+ * The items setter.
+ *
+ * @param items A page of items.
+ * @return A {@link Paging.Builder}.
+ */
+ public Builder<T> setItems(T[] items) {
+ this.items = items;
+ return this;
+ }
+
+ /**
+ * The request limit setter.
+ *
+ * @param limit The maximum number of items in the response (as set in the query or by default).
+ * @return A {@link Paging.Builder}.
+ */
+ public Builder<T> setLimit(Integer limit) {
+ this.limit = limit;
+ return this;
+ }
+
+ /**
+ * The next URL setter.
+ *
+ * @param next URL to the next page of items. ({@code null} if none)
+ * @return A {@link Paging.Builder}.
+ */
+ public Builder<T> setNext(String next) {
+ this.next = next;
+ return this;
+ }
+
+ /**
+ * The offset setter.
+ *
+ * @param offset The offset of the items returned (as set in the query or by default).
+ * @return A {@link Paging.Builder}.
+ */
+ public Builder<T> setOffset(Integer offset) {
+ this.offset = offset;
+ return this;
+ }
+
+ /**
+ * The previous URL setter.
+ *
+ * @param previous URL to the previous page of items. ({@code null} if none)
+ * @return A {@link Paging.Builder}.
+ */
+ public Builder<T> setPrevious(String previous) {
+ this.previous = previous;
+ return this;
+ }
+
+ /**
+ * The total amount setter.
+ *
+ * @param total The total number of items available to return.
+ * @return A {@link Paging.Builder}.
+ */
+ public Builder<T> setTotal(Integer total) {
+ this.total = total;
+ return this;
+ }
+
+ @Override
+ public Paging<T> build() {
+ return new Paging<>(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link Paging} instances.
+ *
+ * @param <X> The type of the objects contained in a paging object.
+ */
+ @SuppressWarnings("unchecked")
+ public static final class JsonUtil<X> extends AbstractModelObject.JsonUtil<Paging<X>> {
+ public Paging<X> createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Paging.Builder<X>()
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setItems(
+ createModelObjectArray(
+ jsonObject.getAsJsonArray("items"), (Class<X>) ((ParameterizedType) getClass()
+ .getGenericSuperclass()).getActualTypeArguments()[0]))
+ .setLimit(
+ hasAndNotNull(jsonObject, "limit")
+ ? jsonObject.get("limit").getAsInt()
+ : null)
+ .setNext(
+ hasAndNotNull(jsonObject, "next")
+ ? jsonObject.get("next").getAsString()
+ : null)
+ .setOffset(
+ hasAndNotNull(jsonObject, "offset")
+ ? jsonObject.get("offset").getAsInt()
+ : null)
+ .setPrevious(
+ hasAndNotNull(jsonObject, "previous")
+ ? jsonObject.get("previous").getAsString()
+ : null)
+ .setTotal(
+ hasAndNotNull(jsonObject, "total")
+ ? jsonObject.get("total").getAsInt()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/PagingCursorbased.java
@@ -0,0 +1,231 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+import java.lang.reflect.ParameterizedType;
+import java.util.Arrays;
+
+/**
+ * Retrieve information about
+ * <a href="https://developer.spotify.com/web-api/object-model/#cursor-based-paging-object">
+ * cursor-based Paging objects</a> by building instances from this class. <br>
+ * This cursor-based paging object is a container for a set of objects.
+ *
+ * @param <T> The type of the objects contained in a paging object.
+ */
+@JsonDeserialize(builder = PagingCursorbased.Builder.class)
+public class PagingCursorbased<T> extends AbstractModelObject {
+ private final String href;
+ private final T[] items;
+ private final Integer limit;
+ private final String next;
+ private final Cursor[] cursors;
+ private final Integer total;
+
+ private PagingCursorbased(final PagingCursorbased.Builder<T> builder) {
+ super(builder);
+
+ this.href = builder.href;
+ this.items = builder.items;
+ this.limit = builder.limit;
+ this.next = builder.next;
+ this.cursors = builder.cursors;
+ this.total = builder.total;
+ }
+
+ /**
+ * Get a link to the Web API endpoint returning the full result of the request.
+ *
+ * @return A link to the Web API endpoint returning the full result of the request.
+ */
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Get the items contained in the paging object.
+ *
+ * @return The items contained in the paging object.
+ */
+ public T[] getItems() {
+ return items;
+ }
+
+ /**
+ * Get the maximum number of items in the response (as set in the query or by default).
+ *
+ * @return The maximum number of items in the response (as set in the query or by default).
+ */
+ public Integer getLimit() {
+ return limit;
+ }
+
+ /**
+ * Get the URL to the next page of items. ({@code null} if none)
+ *
+ * @return URL to the next page of items. ({@code null} if none)
+ */
+ public String getNext() {
+ return next;
+ }
+
+ /**
+ * Get the cursors used to find the next set of items.
+ *
+ * @return The cursors used to find the next set of items.
+ */
+ public Cursor[] getCursors() {
+ return cursors;
+ }
+
+ /**
+ * Get the total number of items available to return.
+ *
+ * @return The total number of items available to return.
+ */
+ public Integer getTotal() {
+ return total;
+ }
+
+ @Override
+ public String toString() {
+ return "PagingCursorbased(href=" + href + ", items=" + Arrays.toString(items) + ", limit=" + limit + ", next="
+ + next + ", cursors=" + Arrays.toString(cursors) + ", total=" + total + ")";
+ }
+
+ @Override
+ public Builder<T> builder() {
+ return new Builder<>();
+ }
+
+ /**
+ * Builder class for building {@link PagingCursorbased} instances.
+ *
+ * @param <T> The type of the objects contained in a paging object.
+ */
+ public static final class Builder<T> extends AbstractModelObject.Builder {
+ private String href;
+ private T[] items;
+ private Integer limit;
+ private String next;
+ private Cursor[] cursors;
+ private Integer total;
+
+ /**
+ * The href setter.
+ *
+ * @param href A link to the Web API endpoint returning the full result of the request.
+ * @return A {@link PagingCursorbased.Builder}.
+ */
+ public Builder<T> setHref(String href) {
+ this.href = href;
+ return this;
+ }
+
+ /**
+ * The items setter.
+ *
+ * @param items A page of items.
+ * @return A {@link PagingCursorbased.Builder}.
+ */
+ public Builder<T> setItems(T[] items) {
+ this.items = items;
+ return this;
+ }
+
+ /**
+ * The request limit setter.
+ *
+ * @param limit The maximum number of items in the response (as set in the query or by default).
+ * @return A {@link PagingCursorbased.Builder}.
+ */
+ public Builder<T> setLimit(Integer limit) {
+ this.limit = limit;
+ return this;
+ }
+
+ /**
+ * The next URL setter.
+ *
+ * @param next URL to the next page of items. ({@code null} if none)
+ * @return A {@link PagingCursorbased.Builder}.
+ */
+ public Builder<T> setNext(String next) {
+ this.next = next;
+ return this;
+ }
+
+ /**
+ * The cursor setter.
+ *
+ * @param cursors The cursors used to find the next set of items.
+ * @return A {@link PagingCursorbased.Builder}.
+ */
+ public Builder<T> setCursors(Cursor... cursors) {
+ this.cursors = cursors;
+ return this;
+ }
+
+ /**
+ * The total amount setter.
+ *
+ * @param total The total number of items available to return.
+ * @return A {@link PagingCursorbased.Builder}.
+ */
+ public Builder<T> setTotal(Integer total) {
+ this.total = total;
+ return this;
+ }
+
+ @Override
+ public PagingCursorbased<T> build() {
+ return new PagingCursorbased<>(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link PagingCursorbased} instances.
+ *
+ * @param <X> The type of the objects contained in a paging object.
+ */
+ @SuppressWarnings("unchecked")
+ public static final class JsonUtil<X> extends AbstractModelObject.JsonUtil<PagingCursorbased<X>> {
+ public PagingCursorbased<X> createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Builder<X>()
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setItems(
+ hasAndNotNull(jsonObject, "items")
+ ? createModelObjectArray(
+ jsonObject.getAsJsonArray("items"), (Class<X>) ((ParameterizedType) getClass()
+ .getGenericSuperclass()).getActualTypeArguments()[0])
+ : null)
+ .setLimit(
+ hasAndNotNull(jsonObject, "limit")
+ ? jsonObject.get("limit").getAsInt()
+ : null)
+ .setNext(
+ hasAndNotNull(jsonObject, "next")
+ ? jsonObject.get("next").getAsString()
+ : null)
+ .setCursors(
+ hasAndNotNull(jsonObject, "cursors")
+ ? new Cursor.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("cursors"))
+ : null)
+ .setTotal(
+ hasAndNotNull(jsonObject, "total")
+ ? jsonObject.get("total").getAsInt()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/PlayHistory.java
@@ -0,0 +1,146 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.SpotifyApi;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+import java.text.ParseException;
+import java.util.Date;
+import java.util.logging.Level;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/object-model/#play-history-object">
+ * Play History objects</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = PlayHistory.Builder.class)
+public class PlayHistory extends AbstractModelObject {
+ private final TrackSimplified track;
+ private final Date playedAt;
+ private final Context context;
+
+ private PlayHistory(final Builder builder) {
+ super(builder);
+
+ this.track = builder.track;
+ this.playedAt = builder.playedAt;
+ this.context = builder.context;
+ }
+
+ /**
+ * Get the track the user listened to.
+ *
+ * @return The (simplified) track the user listened to.
+ */
+ public TrackSimplified getTrack() {
+ return track;
+ }
+
+ /**
+ * Get the date and time the track was played.
+ *
+ * @return The date and time the track was played.
+ */
+ public Date getPlayedAt() {
+ return playedAt;
+ }
+
+ /**
+ * Get the context the track was played from.
+ *
+ * @return The context the track was played from.
+ */
+ public Context getContext() {
+ return context;
+ }
+
+ @Override
+ public String toString() {
+ return "PlayHistory(track=" + track + ", playedAt=" + playedAt + ", context=" + context + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link PlayHistory} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private TrackSimplified track;
+ private Date playedAt;
+ private Context context;
+
+ /**
+ * The track setter.
+ *
+ * @param track The track the user listened to.
+ * @return A {@link PlayHistory.Builder}.
+ */
+ public Builder setTrack(TrackSimplified track) {
+ this.track = track;
+ return this;
+ }
+
+ /**
+ * The played at date setter.
+ *
+ * @param playedAt The date and time the track was played.
+ * @return A {@link PlayHistory.Builder}.
+ */
+ public Builder setPlayedAt(Date playedAt) {
+ this.playedAt = playedAt;
+ return this;
+ }
+
+ /**
+ * The context setter.
+ *
+ * @param context The context the track was played from.
+ * @return A {@link PlayHistory.Builder}.
+ */
+ public Builder setContext(Context context) {
+ this.context = context;
+ return this;
+ }
+
+ @Override
+ public PlayHistory build() {
+ return new PlayHistory(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link PlayHistory} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<PlayHistory> {
+ public PlayHistory createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ try {
+ return new Builder()
+ .setTrack(
+ hasAndNotNull(jsonObject, "track")
+ ? new TrackSimplified.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("track"))
+ : null)
+ .setPlayedAt(
+ hasAndNotNull(jsonObject, "played_at")
+ ? SpotifyApi.parseDefaultDate(jsonObject.get("played_at").getAsString())
+ : null)
+ .setContext(
+ hasAndNotNull(jsonObject, "context")
+ ? new Context.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("context"))
+ : null)
+ .build();
+ } catch (ParseException e) {
+ SpotifyApi.LOGGER.log(Level.SEVERE, e.getMessage());
+ return null;
+ }
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/Playlist.java
@@ -0,0 +1,463 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import java.util.Arrays;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+import com.wrapper.spotify.requests.data.playlists.RemoveItemsFromPlaylistRequest;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/object-model/#playlist-object-full">
+ * Playlist objects</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = Playlist.Builder.class)
+public class Playlist extends AbstractModelObject {
+ private final Boolean collaborative;
+ private final String description;
+ private final ExternalUrl externalUrls;
+ private final Followers followers;
+ private final String href;
+ private final String id;
+ private final Image[] images;
+ private final String name;
+ private final User owner;
+ private final Boolean publicAccess;
+ private final String snapshotId;
+ private final Paging<PlaylistTrack> tracks;
+ private final ModelObjectType type;
+ private final String uri;
+
+ private Playlist(final Builder builder) {
+ super(builder);
+
+ this.collaborative = builder.collaborative;
+ this.description = builder.description;
+ this.externalUrls = builder.externalUrls;
+ this.followers = builder.followers;
+ this.href = builder.href;
+ this.id = builder.id;
+ this.images = builder.images;
+ this.name = builder.name;
+ this.owner = builder.owner;
+ this.publicAccess = builder.publicAccess;
+ this.snapshotId = builder.snapshotId;
+ this.tracks = builder.tracks;
+ this.type = builder.type;
+ this.uri = builder.uri;
+ }
+
+ /**
+ * Check whether the playlist is collaborative or not.
+ *
+ * @return {@code true} if the owner allows other users to modify the playlist, {@code false} if not.
+ * @see <a
+ * href="https://developer.spotify.com/web-api/working-with-playlists/#public-private-and-collaborative-status">
+ * Spotify: Working With Playlists</a>
+ */
+ public Boolean getIsCollaborative() {
+ return collaborative;
+ }
+
+ /**
+ * Get the description of the playlist.
+ *
+ * @return The playlist description. Only returned for modified, verified playlists, otherwise {@code null}.
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Get the external URLs of the playlist. <br>
+ * Example: Spotify-URL.
+ *
+ * @return Known external URLs for this playlist.
+ */
+ public ExternalUrl getExternalUrls() {
+ return externalUrls;
+ }
+
+ /**
+ * Get information about the followers of the playlist. <br>
+ * Example: Follower count.
+ *
+ * @return Information about the followers of the playlist.
+ */
+ public Followers getFollowers() {
+ return followers;
+ }
+
+ /**
+ * Get the full Spotify API endpoint url of the playlist.
+ *
+ * @return A link to the Web API endpoint providing full details of the playlist.
+ */
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Get the <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify ID</a>
+ * of a playlist.
+ *
+ * @return The Spotify ID for the playlist.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Images for the playlist. The array may be empty or contain up to three images. The images are returned by size in
+ * descending order. <br>
+ * <b>Note:</b> If returned, the source URL for the image is temporary and will expire in less than a day.
+ *
+ * @return An array of images in different sizes.
+ * @see <a href="https://developer.spotify.com/web-api/working-with-playlists/#using-playlist-images">
+ * Spotify: Working With Playlists</a>
+ */
+ public Image[] getImages() {
+ return images;
+ }
+
+ /**
+ * Get the name of a playlist.
+ *
+ * @return Playlist name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the owners user object of a playlist.
+ *
+ * @return A user object.
+ */
+ public User getOwner() {
+ return owner;
+ }
+
+ /**
+ * Check whether a playlist is available in public or is private.
+ *
+ * @return {@code true} the playlist is public, {@code false} the playlist is private, {@code null}
+ * the playlist status is not relevant.
+ * @see <a
+ * href="https://developer.spotify.com/web-api/working-with-playlists/#public-private-and-collaborative-status">
+ * Spotify: Working With Playlists</a>
+ */
+ public Boolean getIsPublicAccess() {
+ return publicAccess;
+ }
+
+ /**
+ * Get the snapshot ID, the version identifier for the current playlist. Can be supplied in other requests to target
+ * a specific playlist version.
+ *
+ * @return The version identifier for the current playlist.
+ * @see RemoveItemsFromPlaylistRequest
+ */
+ public String getSnapshotId() {
+ return snapshotId;
+ }
+
+ /**
+ * Get information about the tracks of the playlist.
+ *
+ * @return Information about the tracks of the playlist.
+ */
+ public Paging<PlaylistTrack> getTracks() {
+ return tracks;
+ }
+
+ /**
+ * Get the model object type. In this case "playlist".
+ *
+ * @return The object type: "playlist"
+ */
+ public ModelObjectType getType() {
+ return type;
+ }
+
+ /**
+ * Get the <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URI</a>
+ * of a playlist.
+ *
+ * @return Spotify playlist URI.
+ */
+ public String getUri() {
+ return uri;
+ }
+
+ @Override
+ public String toString() {
+ return "Playlist(name=" + name + ", description=" + description + ", tracks=" + tracks + ", collaborative="
+ + collaborative + ", externalUrls=" + externalUrls + ", followers=" + followers + ", href=" + href + ", id="
+ + id + ", images=" + Arrays.toString(images) + ", owner=" + owner + ", publicAccess=" + publicAccess
+ + ", snapshotId=" + snapshotId + ", type=" + type + ", uri=" + uri + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link Playlist} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private Boolean collaborative;
+ private String description;
+ private ExternalUrl externalUrls;
+ private Followers followers;
+ private String href;
+ private String id;
+ private Image[] images;
+ private String name;
+ private User owner;
+ private Boolean publicAccess;
+ private String snapshotId;
+ private Paging<PlaylistTrack> tracks;
+ private ModelObjectType type;
+ private String uri;
+
+ /**
+ * Set whether the playlist to be built is collaborative or not.
+ *
+ * @param collaborative {@code true} if the owner allows other users to modify the playlist, {@code false} if not.
+ * @return A {@link Playlist.Builder}.
+ */
+ public Builder setCollaborative(Boolean collaborative) {
+ this.collaborative = collaborative;
+ return this;
+ }
+
+ /**
+ * Set the description of the playlist to be built.
+ *
+ * @param description Playlist description.
+ * @return A {@link Playlist.Builder}.
+ */
+ public Builder setDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ /**
+ * Set the external URLs of the playlist to be built.
+ *
+ * @param externalUrls Known external URLs for this playlist.
+ * @return A {@link Playlist.Builder}.
+ */
+ public Builder setExternalUrls(ExternalUrl externalUrls) {
+ this.externalUrls = externalUrls;
+ return this;
+ }
+
+ /**
+ * Set information about the followers of the playlist to be built.
+ *
+ * @param followers Information about the followers of the playlist.
+ * @return A {@link Playlist.Builder}.
+ */
+ public Builder setFollowers(Followers followers) {
+ this.followers = followers;
+ return this;
+ }
+
+ /**
+ * Set the link to the Spotify Web API endpoint providing full details of the playlist.
+ *
+ * @param href A link to the Spotify Web API endpoint providing full details of the playlist.
+ * @return A {@link Playlist.Builder}.
+ */
+ public Builder setHref(String href) {
+ this.href = href;
+ return this;
+ }
+
+ /**
+ * Set the Spotify ID for the playlist to be built.
+ *
+ * @param id The Spotify ID for the playlist.
+ * @return A {@link Playlist.Builder}.
+ */
+ public Builder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * Set the cover image of the playlist to be built.
+ *
+ * @param images An array of images in different sizes.
+ * @return A {@link Playlist.Builder}.
+ */
+ public Builder setImages(Image... images) {
+ this.images = images;
+ return this;
+ }
+
+ /**
+ * Set the name of the playlist to be built.
+ *
+ * @param name The playlist name.
+ * @return A {@link Playlist.Builder}.
+ */
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Set the owner of the playlist to be built.
+ *
+ * @param owner A user object.
+ * @return A {@link Playlist.Builder}.
+ */
+ public Builder setOwner(User owner) {
+ this.owner = owner;
+ return this;
+ }
+
+ /**
+ * Set whether the playlist to be built is available in public or not.
+ *
+ * @param publicAccess {@code true} the playlist is public, {@code false} the playlist is private, {@code null}
+ * the playlist status is not relevant.
+ * @return A {@link Playlist.Builder}.
+ */
+ public Builder setPublicAccess(Boolean publicAccess) {
+ this.publicAccess = publicAccess;
+ return this;
+ }
+
+ /**
+ * Set the version identifier for the playlist to be built.
+ *
+ * @param snapshotId The version identifier for the playlist.
+ * @return A {@link Playlist.Builder}.
+ */
+ public Builder setSnapshotId(String snapshotId) {
+ this.snapshotId = snapshotId;
+ return this;
+ }
+
+ /**
+ * Set the tracks of the playlist to be built.
+ *
+ * @param tracks Information about the tracks of the playlist.
+ * @return A {@link Playlist.Builder}.
+ */
+ public Builder setTracks(Paging<PlaylistTrack> tracks) {
+ this.tracks = tracks;
+ return this;
+ }
+
+ /**
+ * Set the type of the model object. In this case "playlist".
+ *
+ * @param type The model object type.
+ * @return A {@link Playlist.Builder}.
+ */
+ public Builder setType(ModelObjectType type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Set the <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URI</a>
+ * of the playlist to be built.
+ *
+ * @param uri The Spotify playlist URI.
+ * @return A {@link Playlist.Builder}.
+ */
+ public Builder setUri(String uri) {
+ this.uri = uri;
+ return this;
+ }
+
+ @Override
+ public Playlist build() {
+ return new Playlist(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link Playlist} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<Playlist> {
+ public Playlist createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Playlist.Builder()
+ .setCollaborative(
+ hasAndNotNull(jsonObject, "collaborative")
+ ? jsonObject.get("collaborative").getAsBoolean()
+ : null)
+ .setDescription(
+ hasAndNotNull(jsonObject, "description")
+ ? jsonObject.get("description").getAsString()
+ : null)
+ .setExternalUrls(
+ hasAndNotNull(jsonObject, "external_urls")
+ ? new ExternalUrl.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("external_urls"))
+ : null)
+ .setFollowers(
+ hasAndNotNull(jsonObject, "followers")
+ ? new Followers.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("followers"))
+ : null)
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setId(
+ hasAndNotNull(jsonObject, "id")
+ ? jsonObject.get("id").getAsString()
+ : null)
+ .setImages(
+ hasAndNotNull(jsonObject, "images")
+ ? new Image.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("images"))
+ : null)
+ .setName(
+ hasAndNotNull(jsonObject, "name")
+ ? jsonObject.get("name").getAsString()
+ : null)
+ .setOwner(
+ hasAndNotNull(jsonObject, "owner")
+ ? new User.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("owner"))
+ : null)
+ .setPublicAccess(
+ hasAndNotNull(jsonObject, "public")
+ ? jsonObject.get("public").getAsBoolean()
+ : null)
+ .setSnapshotId(
+ hasAndNotNull(jsonObject, "snapshot_id")
+ ? jsonObject.get("snapshot_id").getAsString()
+ : null)
+ .setTracks(
+ hasAndNotNull(jsonObject, "tracks")
+ ? new PlaylistTrack.JsonUtil().createModelObjectPaging(
+ jsonObject.getAsJsonObject("tracks"))
+ : null)
+ .setType(
+ hasAndNotNull(jsonObject, "type")
+ ? ModelObjectType.keyOf(
+ jsonObject.get("type").getAsString().toLowerCase())
+ : null)
+ .setUri(
+ hasAndNotNull(jsonObject, "uri")
+ ? jsonObject.get("uri").getAsString()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/PlaylistSimplified.java
@@ -0,0 +1,409 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import java.util.Arrays;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+import com.wrapper.spotify.model_objects.miscellaneous.PlaylistTracksInformation;
+import com.wrapper.spotify.requests.data.playlists.RemoveItemsFromPlaylistRequest;
+import com.wrapper.spotify.requests.data.search.interfaces.ISearchModelObject;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/object-model/#playlist-object-simplified">
+ * simplified Playlist objects</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = PlaylistSimplified.Builder.class)
+public class PlaylistSimplified extends AbstractModelObject implements ISearchModelObject {
+ private final Boolean collaborative;
+ private final ExternalUrl externalUrls;
+ private final String href;
+ private final String id;
+ private final Image[] images;
+ private final String name;
+ private final User owner;
+ private final Boolean publicAccess;
+ private final String snapshotId;
+ private final PlaylistTracksInformation tracks;
+ private final ModelObjectType type;
+ private final String uri;
+
+ private PlaylistSimplified(final Builder builder) {
+ super(builder);
+
+ this.collaborative = builder.collaborative;
+ this.externalUrls = builder.externalUrls;
+ this.href = builder.href;
+ this.id = builder.id;
+ this.images = builder.images;
+ this.name = builder.name;
+ this.owner = builder.owner;
+ this.publicAccess = builder.publicAccess;
+ this.snapshotId = builder.snapshotId;
+ this.tracks = builder.tracks;
+ this.type = builder.type;
+ this.uri = builder.uri;
+ }
+
+ /**
+ * Check whether the playlist is collaborative or not.
+ *
+ * @return {@code true} if the owner allows other users to modify the playlist, {@code false} if not.
+ * @see <a
+ * href="https://developer.spotify.com/web-api/working-with-playlists/#public-private-and-collaborative-status">
+ * Spotify: Working With Playlists</a>
+ */
+ public Boolean getIsCollaborative() {
+ return collaborative;
+ }
+
+ /**
+ * Get the external URLs of the playlist. <br>
+ * Example: Spotify-URL.
+ *
+ * @return Known external URLs for this playlist.
+ */
+ public ExternalUrl getExternalUrls() {
+ return externalUrls;
+ }
+
+ /**
+ * Get the full Spotify API endpoint url of the playlist.
+ *
+ * @return A link to the Web API endpoint providing full details of the playlist.
+ */
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Get the <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify ID</a>
+ * of a playlist.
+ *
+ * @return The Spotify ID for the playlist.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Images for the playlist. The array may be empty or contain up to three images. The images are returned by size in
+ * descending order. <br>
+ * <b>Note:</b> If returned, the source URL for the image is temporary and will expire in less than a day.
+ *
+ * @return An array of images in different sizes.
+ * @see <a href="https://developer.spotify.com/web-api/working-with-playlists/#using-playlist-images">
+ * Spotify: Working With Playlists</a>
+ */
+ public Image[] getImages() {
+ return images;
+ }
+
+ /**
+ * Get the name of a playlist.
+ *
+ * @return Playlist name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the owners user object of a playlist.
+ *
+ * @return A user object.
+ */
+ public User getOwner() {
+ return owner;
+ }
+
+ /**
+ * Check whether a playlist is available in public or is private.
+ *
+ * @return {@code true} the playlist is public, {@code false} the playlist is private, {@code null}
+ * the playlist status is not relevant.
+ * @see <a
+ * href="https://developer.spotify.com/web-api/working-with-playlists/#public-private-and-collaborative-status">
+ * Spotify: Working With Playlists</a>
+ */
+ public Boolean getIsPublicAccess() {
+ return publicAccess;
+ }
+
+ /**
+ * Get the snapshot ID, the version identifier for the current playlist. Can be supplied in other requests to target
+ * a specific playlist version.
+ *
+ * @return The version identifier for the current playlist.
+ * @see RemoveItemsFromPlaylistRequest
+ */
+ public String getSnapshotId() {
+ return snapshotId;
+ }
+
+ /**
+ * Get information about the tracks of the playlist.
+ *
+ * @return Information about the tracks of the playlist.
+ */
+ public PlaylistTracksInformation getTracks() {
+ return tracks;
+ }
+
+ /**
+ * Get the model object type. In this case "playlist".
+ *
+ * @return The object type: "playlist"
+ */
+ public ModelObjectType getType() {
+ return type;
+ }
+
+ /**
+ * Get the <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URI</a>
+ * of a playlist.
+ *
+ * @return Spotify playlist URI.
+ */
+ public String getUri() {
+ return uri;
+ }
+
+ @Override
+ public String toString() {
+ return "PlaylistSimplified(name=" + name + ", tracks=" + tracks + ", collaborative=" + collaborative
+ + ", externalUrls=" + externalUrls + ", href=" + href + ", id=" + id + ", images=" + Arrays.toString(images)
+ + ", owner=" + owner + ", publicAccess=" + publicAccess + ", snapshotId=" + snapshotId + ", type=" + type
+ + ", uri=" + uri + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link PlaylistSimplified} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private Boolean collaborative;
+ private ExternalUrl externalUrls;
+ private String href;
+ private String id;
+ private Image[] images;
+ private String name;
+ private User owner;
+ private Boolean publicAccess;
+ private String snapshotId;
+ private PlaylistTracksInformation tracks;
+ private ModelObjectType type;
+ private String uri;
+
+ /**
+ * Set whether the playlist to be built is collaborative or not.
+ *
+ * @param collaborative {@code true} if the owner allows other users to modify the playlist, {@code false} if not.
+ * @return A {@link PlaylistSimplified.Builder}.
+ */
+ public Builder setCollaborative(Boolean collaborative) {
+ this.collaborative = collaborative;
+ return this;
+ }
+
+ /**
+ * Set the external URLs of the playlist to be built.
+ *
+ * @param externalUrls Known external URLs for this playlist.
+ * @return A {@link PlaylistSimplified.Builder}.
+ */
+ public Builder setExternalUrls(ExternalUrl externalUrls) {
+ this.externalUrls = externalUrls;
+ return this;
+ }
+
+ /**
+ * Set the link to the Spotify Web API endpoint providing full details of the playlist.
+ *
+ * @param href A link to the Spotify Web API endpoint providing full details of the playlist.
+ * @return A {@link PlaylistSimplified.Builder}.
+ */
+ public Builder setHref(String href) {
+ this.href = href;
+ return this;
+ }
+
+ /**
+ * Set the Spotify ID for the playlist to be built.
+ *
+ * @param id The Spotify ID for the playlist.
+ * @return A {@link PlaylistSimplified.Builder}.
+ */
+ public Builder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * Set the cover image of the playlist to be built.
+ *
+ * @param images An array of images in different sizes.
+ * @return A {@link PlaylistSimplified.Builder}.
+ */
+ public Builder setImages(Image... images) {
+ this.images = images;
+ return this;
+ }
+
+ /**
+ * Set the name of the playlist to be built.
+ *
+ * @param name The playlist name.
+ * @return A {@link PlaylistSimplified.Builder}.
+ */
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Set the owner of the playlist to be built.
+ *
+ * @param owner A user object.
+ * @return A {@link PlaylistSimplified.Builder}.
+ */
+ public Builder setOwner(User owner) {
+ this.owner = owner;
+ return this;
+ }
+
+ /**
+ * Set whether the playlist to be built is available in public or not.
+ *
+ * @param publicAccess {@code true} the playlist is public, {@code false} the playlist is private, {@code null}
+ * the playlist status is not relevant.
+ * @return A {@link PlaylistSimplified.Builder}.
+ */
+ public Builder setPublicAccess(Boolean publicAccess) {
+ this.publicAccess = publicAccess;
+ return this;
+ }
+
+ /**
+ * Set the version identifier for the playlist to be built.
+ *
+ * @param snapshotId The version identifier for the playlist.
+ * @return A {@link PlaylistSimplified.Builder}.
+ */
+ public Builder setSnapshotId(String snapshotId) {
+ this.snapshotId = snapshotId;
+ return this;
+ }
+
+ /**
+ * Set some track information of the playlist to be built.
+ *
+ * @param tracks A playlist tracks information object.
+ * @return A {@link PlaylistSimplified.Builder}.
+ */
+ public Builder setTracks(PlaylistTracksInformation tracks) {
+ this.tracks = tracks;
+ return this;
+ }
+
+ /**
+ * Set the type of the model object. In this case "playlist".
+ *
+ * @param type The model object type.
+ * @return A {@link PlaylistSimplified.Builder}.
+ */
+ public Builder setType(ModelObjectType type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Set the <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URI</a>
+ * of the playlist to be built.
+ *
+ * @param uri The Spotify playlist URI.
+ * @return A {@link PlaylistSimplified.Builder}.
+ */
+ public Builder setUri(String uri) {
+ this.uri = uri;
+ return this;
+ }
+
+ @Override
+ public PlaylistSimplified build() {
+ return new PlaylistSimplified(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link PlaylistSimplified} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<PlaylistSimplified> {
+ public PlaylistSimplified createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new PlaylistSimplified.Builder()
+ .setCollaborative(
+ hasAndNotNull(jsonObject, "collaborative")
+ ? jsonObject.get("collaborative").getAsBoolean()
+ : null)
+ .setExternalUrls(
+ hasAndNotNull(jsonObject, "external_urls")
+ ? new ExternalUrl.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("external_urls"))
+ : null)
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setId(
+ hasAndNotNull(jsonObject, "id")
+ ? jsonObject.get("id").getAsString()
+ : null)
+ .setImages(
+ hasAndNotNull(jsonObject, "images")
+ ? new Image.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("images"))
+ : null)
+ .setName(
+ hasAndNotNull(jsonObject, "name")
+ ? jsonObject.get("name").getAsString()
+ : null)
+ .setOwner(
+ hasAndNotNull(jsonObject, "owner")
+ ? new User.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("owner"))
+ : null)
+ .setPublicAccess(
+ hasAndNotNull(jsonObject, "public")
+ ? jsonObject.get("public").getAsBoolean()
+ : null)
+ .setSnapshotId(
+ hasAndNotNull(jsonObject, "snapshot_id")
+ ? jsonObject.get("snapshot_id").getAsString()
+ : null)
+ .setTracks(
+ hasAndNotNull(jsonObject, "tracks")
+ ? new PlaylistTracksInformation.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("tracks"))
+ : null)
+ .setType(
+ hasAndNotNull(jsonObject, "type")
+ ? ModelObjectType.keyOf(
+ jsonObject.get("type").getAsString().toLowerCase())
+ : null)
+ .setUri(
+ hasAndNotNull(jsonObject, "uri")
+ ? jsonObject.get("uri").getAsString()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/PlaylistTrack.java
@@ -0,0 +1,190 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.SpotifyApi;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+import com.wrapper.spotify.model_objects.IPlaylistItem;
+
+import java.text.ParseException;
+import java.util.Date;
+import java.util.logging.Level;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/object-model/#playlist-track-object">
+ * Playlist Track objects</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = PlaylistTrack.Builder.class)
+public class PlaylistTrack extends AbstractModelObject {
+ private final Date addedAt;
+ private final User addedBy;
+ private final Boolean isLocal;
+ private final IPlaylistItem track;
+
+ private PlaylistTrack(final Builder builder) {
+ super(builder);
+
+ this.addedAt = builder.addedAt;
+ this.addedBy = builder.addedBy;
+ this.isLocal = builder.isLocal;
+ this.track = builder.track;
+ }
+
+ /**
+ * Get the date, when the track or episode has been added to its playlist.
+ * <b>Note:</b> Some very old playlists may return {@code null} in this field.
+ *
+ * @return The date and time the track or episode was added.
+ */
+ public Date getAddedAt() {
+ return addedAt;
+ }
+
+ /**
+ * Get the user, who added the track or episode to its playlist.
+ * <b>Note:</b> Some very old playlists may return null in this field.
+ *
+ * @return The Spotify user who added the track or episode.
+ */
+ public User getAddedBy() {
+ return addedBy;
+ }
+
+ /**
+ * Check whether a playlist track is a local track or episode or not.<br>
+ * Local tracks can only be played on devices, where the track files are present.
+ *
+ * @return Whether this track is a local file or not.
+ */
+ public Boolean getIsLocal() {
+ return isLocal;
+ }
+
+ /**
+ * Get a full track or episode object from this playlist track object.
+ *
+ * @return Information about the track.
+ */
+ public IPlaylistItem getTrack() {
+ return track;
+ }
+
+ @Override
+ public String toString() {
+ return "PlaylistTrack(track=" + track + ", addedAt=" + addedAt + ", addedBy=" + addedBy + ", isLocal=" + isLocal
+ + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link PlaylistTrack} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private Date addedAt;
+ private User addedBy;
+ private Boolean isLocal;
+ private IPlaylistItem track;
+
+ /**
+ * Set the "added at" date of the playlist track to be built.
+ *
+ * @param addedAt The date and time the track or episode was added.
+ * @return A {@link PlaylistTrack.Builder}.
+ */
+ public Builder setAddedAt(Date addedAt) {
+ this.addedAt = addedAt;
+ return this;
+ }
+
+ /**
+ * Set the user who added the track or episode to the playlist.
+ *
+ * @param addedBy The Spotify user who added the track or episode.
+ * @return A {@link PlaylistTrack.Builder}.
+ */
+ public Builder setAddedBy(User addedBy) {
+ this.addedBy = addedBy;
+ return this;
+ }
+
+ /**
+ * Set whether the track to be built is local or not.
+ *
+ * @param isLocal Whether this track or episode is a local file or not.
+ * @return A {@link PlaylistTrack.Builder}.
+ */
+ public Builder setIsLocal(Boolean isLocal) {
+ this.isLocal = isLocal;
+ return this;
+ }
+
+ /**
+ * Set the full track or episode object of the playlist track to be built.
+ *
+ * @param track Information about the track.
+ * @return A {@link PlaylistTrack.Builder}.
+ */
+ public Builder setTrack(IPlaylistItem track) {
+ this.track = track;
+ return this;
+ }
+
+ @Override
+ public PlaylistTrack build() {
+ return new PlaylistTrack(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link PlaylistTrack} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<PlaylistTrack> {
+ public PlaylistTrack createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ try {
+ IPlaylistItem track = null;
+
+ if (hasAndNotNull(jsonObject, "track")) {
+ final JsonObject trackObj = jsonObject.getAsJsonObject("track");
+
+ if (hasAndNotNull(trackObj, "type")) {
+ String type = trackObj.get("type").getAsString().toLowerCase();
+
+ if (type.equals("track")) {
+ track = new Track.JsonUtil().createModelObject(trackObj);
+ } else if (type.equals("episode")) {
+ track = new Episode.JsonUtil().createModelObject(trackObj);
+ }
+ }
+ }
+
+ return new Builder()
+ .setAddedAt(
+ hasAndNotNull(jsonObject, "added_at")
+ ? SpotifyApi.parseDefaultDate(jsonObject.get("added_at").getAsString())
+ : null)
+ .setAddedBy(
+ hasAndNotNull(jsonObject, "added_by")
+ ? new User.JsonUtil().createModelObject(
+ jsonObject.get("added_by").getAsJsonObject())
+ : null)
+ .setIsLocal(
+ hasAndNotNull(jsonObject, "is_local")
+ ? jsonObject.get("is_local").getAsBoolean()
+ : null)
+ .setTrack(track)
+ .build();
+ } catch (ParseException e) {
+ SpotifyApi.LOGGER.log(Level.SEVERE, e.getMessage());
+ return null;
+ }
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/Recommendations.java
@@ -0,0 +1,111 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import java.util.Arrays;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/object-model/#recommendations-object">
+ * Recommendation objects</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = Recommendations.Builder.class)
+public class Recommendations extends AbstractModelObject {
+ private final RecommendationsSeed[] seeds;
+ private final TrackSimplified[] tracks;
+
+ private Recommendations(final Builder builder) {
+ super(builder);
+
+ this.seeds = builder.seeds;
+ this.tracks = builder.tracks;
+ }
+
+ /**
+ * Get the recommendation seeds from the recommendations object.
+ *
+ * @return An array of recommendation seed objects.
+ */
+ public RecommendationsSeed[] getSeeds() {
+ return seeds;
+ }
+
+ /**
+ * Get the (simplified) tracks from the recommendations object.
+ *
+ * @return An array of track object (simplified) ordered according to the parameters supplied.
+ */
+ public TrackSimplified[] getTracks() {
+ return tracks;
+ }
+
+ @Override
+ public String toString() {
+ return "Recommendations(seeds=" + Arrays.toString(seeds) + ", tracks=" + Arrays.toString(tracks) + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link Recommendations} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private RecommendationsSeed[] seeds;
+ private TrackSimplified[] tracks;
+
+ /**
+ * The recommendation seeds setter.
+ *
+ * @param seeds An array of recommendation seed objects.
+ * @return A {@link Recommendations.Builder}.
+ */
+ public Builder setSeeds(RecommendationsSeed... seeds) {
+ this.seeds = seeds;
+ return this;
+ }
+
+ /**
+ * The recommended tracks setter.
+ *
+ * @param tracks An array of track objects (simplified).
+ * @return A {@link Recommendations.Builder}.
+ */
+ public Builder setTracks(TrackSimplified... tracks) {
+ this.tracks = tracks;
+ return this;
+ }
+
+ @Override
+ public Recommendations build() {
+ return new Recommendations(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link Recommendations} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<Recommendations> {
+ public Recommendations createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Recommendations.Builder()
+ .setSeeds(
+ hasAndNotNull(jsonObject, "seeds")
+ ? new RecommendationsSeed.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("seeds"))
+ : null)
+ .setTracks(
+ hasAndNotNull(jsonObject, "tracks")
+ ? new TrackSimplified.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("tracks"))
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/RecommendationsSeed.java
@@ -0,0 +1,224 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/object-model/#recommendations-seed-object">
+ * Recommendation Seed objects</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = RecommendationsSeed.Builder.class)
+public class RecommendationsSeed extends AbstractModelObject {
+ private final Integer afterFilteringSize;
+ private final Integer afterRelinkingSize;
+ private final String href;
+ private final String id;
+ private final Integer initialPoolSize;
+ private final ModelObjectType type;
+
+ private RecommendationsSeed(final Builder builder) {
+ super(builder);
+
+ this.afterFilteringSize = builder.afterFilteringSize;
+ this.afterRelinkingSize = builder.afterRelinkingSize;
+ this.href = builder.href;
+ this.id = builder.id;
+ this.initialPoolSize = builder.initialPoolSize;
+ this.type = builder.type;
+ }
+
+ /**
+ * Get the number of tracks available after {@code min_*} and {@code max_*} filters have been applied.
+ *
+ * @return The number of tracks available after {@code min_*} and {@code max_*} filters have been applied.
+ */
+ public Integer getAfterFilteringSize() {
+ return afterFilteringSize;
+ }
+
+ /**
+ * Get the number of tracks available after relinking for regional availability.
+ *
+ * @return The number of tracks available after relinking for regional availability.
+ */
+ public Integer getAfterRelinkingSize() {
+ return afterRelinkingSize;
+ }
+
+ /**
+ * Get the link to the full track or artist data for this seed.
+ *
+ * @return A link to the full track or artist data for this seed. For tracks this will be a link to a {@link Track}
+ * object. For artists a link to an {@link Artist} Object. For genre seeds, this value will be {@code null}.
+ */
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Get the ID used to select this seed.
+ *
+ * @return The ID used to select this seed. This will be the same as the string used in the {@code seed_artists},
+ * {@code seed_tracks} or {@code seed_genres} request parameter.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Get the number of recommended tracks available for this seed.
+ *
+ * @return The number of recommended tracks available for this seed.
+ */
+ public Integer getInitialPoolSize() {
+ return initialPoolSize;
+ }
+
+ /**
+ * Get the entity type of this seed.
+ *
+ * @return The model object type of this seed. One of {@code artist}, {@code track} or {@code genre}.
+ */
+ public ModelObjectType getType() {
+ return type;
+ }
+
+ @Override
+ public String toString() {
+ return "RecommendationsSeed(afterFilteringSize=" + afterFilteringSize + ", afterRelinkingSize=" + afterRelinkingSize
+ + ", href=" + href + ", id=" + id + ", initialPoolSize=" + initialPoolSize + ", type=" + type + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link RecommendationsSeed} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private Integer afterFilteringSize;
+ private Integer afterRelinkingSize;
+ private String href;
+ private String id;
+ private Integer initialPoolSize;
+ private ModelObjectType type;
+
+ /**
+ * The after filtering size setter.
+ *
+ * @param afterFilteringSize The number of tracks available after {@code min_*} and {@code max_*} filters have been
+ * applied.
+ * @return A {@link RecommendationsSeed.Builder}.
+ */
+ public Builder setAfterFilteringSize(Integer afterFilteringSize) {
+ this.afterFilteringSize = afterFilteringSize;
+ return this;
+ }
+
+ /**
+ * The after relinking size setter.
+ *
+ * @param afterRelinkingSize The number of tracks available after relinking for regional availability.
+ * @return A {@link RecommendationsSeed.Builder}.
+ */
+ public Builder setAfterRelinkingSize(Integer afterRelinkingSize) {
+ this.afterRelinkingSize = afterRelinkingSize;
+ return this;
+ }
+
+ /**
+ * The href setter.
+ *
+ * @param href A link to the full track or artist data for this seed. For tracks this will be a link to a
+ * {@link Track} object. For artists a link to an {@link Artist} Object. For genre seeds, this value
+ * will be {@code null}.
+ * @return A {@link RecommendationsSeed.Builder}.
+ */
+ public Builder setHref(String href) {
+ this.href = href;
+ return this;
+ }
+
+ /**
+ * The ID setter.
+ *
+ * @param id The ID used to select this seed. This will be the same as the string used in the {@code seed_artists},
+ * {@code seed_tracks} or {@code seed_genres} request parameter.
+ * @return A {@link RecommendationsSeed.Builder}.
+ */
+ public Builder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * The initial pool size setter.
+ *
+ * @param initialPoolSize The number of recommended tracks available for this seed.
+ * @return A {@link RecommendationsSeed.Builder}.
+ */
+ public Builder setInitialPoolSize(Integer initialPoolSize) {
+ this.initialPoolSize = initialPoolSize;
+ return this;
+ }
+
+ /**
+ * The model object type setter.
+ *
+ * @param type The model object type of this seed. One of {@code artist}, {@code track} or {@code genre}.
+ * @return A {@link RecommendationsSeed.Builder}.
+ */
+ public Builder setType(ModelObjectType type) {
+ this.type = type;
+ return this;
+ }
+
+ @Override
+ public RecommendationsSeed build() {
+ return new RecommendationsSeed(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link RecommendationsSeed} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<RecommendationsSeed> {
+ public RecommendationsSeed createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new RecommendationsSeed.Builder()
+ .setAfterFilteringSize(
+ hasAndNotNull(jsonObject, "afterFilteringSize")
+ ? jsonObject.get("afterFilteringSize").getAsInt()
+ : null)
+ .setAfterRelinkingSize(
+ hasAndNotNull(jsonObject, "afterRelinkingSize")
+ ? jsonObject.get("afterRelinkingSize").getAsInt()
+ : null)
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setId(
+ hasAndNotNull(jsonObject, "id")
+ ? jsonObject.get("id").getAsString()
+ : null)
+ .setInitialPoolSize(
+ hasAndNotNull(jsonObject, "initialPoolSize")
+ ? jsonObject.get("initialPoolSize").getAsInt()
+ : null)
+ .setType(
+ hasAndNotNull(jsonObject, "type")
+ ? ModelObjectType.keyOf(
+ jsonObject.get("type").getAsString().toLowerCase())
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/ResumePoint.java
@@ -0,0 +1,109 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about
+ * <a href="https://developer.spotify.com/web-api/object-model/#resume-point-object">Resume Point objects</a>
+ * by building instances from this class.
+ */
+@JsonDeserialize(builder = ResumePoint.Builder.class)
+public class ResumePoint extends AbstractModelObject {
+ private final Boolean fullyPlayed;
+ private final Integer resumePositionMs;
+
+ private ResumePoint(final Builder builder) {
+ super(builder);
+ this.fullyPlayed = builder.fullyPlayed;
+ this.resumePositionMs = builder.resumePositionMs;
+ }
+
+ /**
+ * Check whether the episode has been fully played by the user.
+ *
+ * @return If {@code true}, the episode has been fully played by the user.
+ */
+ public Boolean getFullyPlayed() {
+ return fullyPlayed;
+ }
+
+ /**
+ * Get the user’s most recent position in the episode in milliseconds.
+ *
+ * @return The user’s most recent position in the episode in milliseconds.
+ */
+ public Integer getResumePositionMs() {
+ return resumePositionMs;
+ }
+
+ @Override
+ public String toString() {
+ return "ResumePoint(fullyPlayed=" + fullyPlayed + ", resumePositionMs=" + resumePositionMs + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link ResumePoint} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private Boolean fullyPlayed;
+ private Integer resumePositionMs;
+
+ /**
+ * Set whether the episode has been fully played by the user.
+ *
+ * @param fullyPlayed {@code true} if episode has been fully played by the user.
+ * @return A {@link ResumePoint.Builder}.
+ */
+ public Builder setFullyPlayed(Boolean fullyPlayed) {
+ this.fullyPlayed = fullyPlayed;
+ return this;
+ }
+
+ /**
+ * Set the user’s most recent position in the episode in milliseconds.
+ *
+ * @param resumePositionMs The user’s most recent position in the episode in milliseconds.
+ * @return A {@link ResumePoint.Builder}.
+ */
+ public Builder setResumePositionMs(Integer resumePositionMs) {
+ this.resumePositionMs = resumePositionMs;
+ return this;
+ }
+
+ @Override
+ public ResumePoint build() {
+ return new ResumePoint(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link ResumePoint} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<ResumePoint> {
+ @Override
+ public ResumePoint createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Builder()
+ .setFullyPlayed(
+ hasAndNotNull(jsonObject, "fully_played")
+ ? jsonObject.get("fully_played").getAsBoolean()
+ : null)
+ .setResumePositionMs(
+ hasAndNotNull(jsonObject, "resume_position_ms")
+ ? jsonObject.get("resume_position_ms").getAsInt()
+ : null)
+ .build();
+ }
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/SavedAlbum.java
@@ -0,0 +1,118 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.SpotifyApi;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+import java.text.ParseException;
+import java.util.Date;
+import java.util.logging.Level;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/object-model/#saved-album-object">
+ * Saved Album objects</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = SavedAlbum.Builder.class)
+public class SavedAlbum extends AbstractModelObject {
+ private final Date addedAt;
+ private final Album album;
+
+ private SavedAlbum(final Builder builder) {
+ super(builder);
+
+ this.addedAt = builder.addedAt;
+ this.album = builder.album;
+ }
+
+ /**
+ * Get the date, when the album has been saved.
+ *
+ * @return The date and time the album was saved.
+ */
+ public Date getAddedAt() {
+ return addedAt;
+ }
+
+ /**
+ * Get information about the album from a saved album object.
+ *
+ * @return Information about the album.
+ */
+ public Album getAlbum() {
+ return album;
+ }
+
+ @Override
+ public String toString() {
+ return "SavedAlbum(addedAt=" + addedAt + ", album=" + album + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link SavedAlbum} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private Date addedAt;
+ private Album album;
+
+ /**
+ * Set the "added at" date of the saved album to be built.
+ *
+ * @param addedAt The date and time the album was saved.
+ * @return A {@link SavedAlbum.Builder}.
+ */
+ public Builder setAddedAt(Date addedAt) {
+ this.addedAt = addedAt;
+ return this;
+ }
+
+ /**
+ * Set the full album object of the saved album to be built.
+ *
+ * @param album Information about the album.
+ * @return A {@link SavedAlbum.Builder}.
+ */
+ public Builder setAlbum(Album album) {
+ this.album = album;
+ return this;
+ }
+
+ @Override
+ public SavedAlbum build() {
+ return new SavedAlbum(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link SavedAlbum} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<SavedAlbum> {
+ public SavedAlbum createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ try {
+ return new Builder()
+ .setAddedAt(
+ hasAndNotNull(jsonObject, "added_at")
+ ? SpotifyApi.parseDefaultDate(jsonObject.get("added_at").getAsString())
+ : null)
+ .setAlbum(
+ hasAndNotNull(jsonObject, "album")
+ ? new Album.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("album"))
+ : null)
+ .build();
+ } catch (ParseException e) {
+ SpotifyApi.LOGGER.log(Level.SEVERE, e.getMessage());
+ return null;
+ }
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/SavedShow.java
@@ -0,0 +1,119 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.SpotifyApi;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+import java.text.ParseException;
+import java.util.Date;
+import java.util.logging.Level;
+
+/**
+ * Retrieve information about
+ * <a href="https://developer.spotify.com/web-api/object-model/#saved-show-object">Saved Show objects</a>
+ * by building instances from this class.
+ */
+@JsonDeserialize(builder = SavedShow.Builder.class)
+public class SavedShow extends AbstractModelObject {
+ private final Date addedAt;
+ private final ShowSimplified show;
+
+ private SavedShow(final Builder builder) {
+ super(builder);
+ this.addedAt = builder.addedAt;
+ this.show = builder.show;
+ }
+
+ /**
+ * Get the date, when the show has been saved.
+ *
+ * @return The date and time the show was saved.
+ */
+ public Date getAddedAt() {
+ return addedAt;
+ }
+
+ /**
+ * Get information about the show from a saved show object.
+ *
+ * @return Information about the show.
+ */
+ public ShowSimplified getShow() {
+ return show;
+ }
+
+ @Override
+ public String toString() {
+ return "SavedShow(addedAt=" + addedAt + ", show=" + show + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link SavedShow} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private Date addedAt;
+ private ShowSimplified show;
+
+ /**
+ * Set the "added at" date of the saved show to be built.
+ *
+ * @param addedAt The date and time the show was saved.
+ * @return A {@link SavedShow.Builder}.
+ */
+ public Builder setAddedAt(Date addedAt) {
+ this.addedAt = addedAt;
+ return this;
+ }
+
+ /**
+ * Set the full show object of the saved show to be built.
+ *
+ * @param show Information about the show.
+ * @return A {@link SavedShow.Builder}.
+ */
+ public Builder setShow(ShowSimplified show) {
+ this.show = show;
+ return this;
+ }
+
+ @Override
+ public SavedShow build() {
+ return new SavedShow(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link SavedShow} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<SavedShow> {
+ @Override
+ public SavedShow createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ try {
+ return new Builder()
+ .setAddedAt(
+ hasAndNotNull(jsonObject, "added_at")
+ ? SpotifyApi.parseDefaultDate(jsonObject.get("added_at").getAsString())
+ : null)
+ .setShow(
+ hasAndNotNull(jsonObject, "show")
+ ? new ShowSimplified.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("show"))
+ : null)
+ .build();
+ } catch (ParseException e) {
+ SpotifyApi.LOGGER.log(Level.SEVERE, e.getMessage());
+ return null;
+ }
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/SavedTrack.java
@@ -0,0 +1,118 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.SpotifyApi;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+import java.text.ParseException;
+import java.util.Date;
+import java.util.logging.Level;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/object-model/#saved-track-object">
+ * Saved Track objects</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = SavedTrack.Builder.class)
+public class SavedTrack extends AbstractModelObject {
+ private final Date addedAt;
+ private final Track track;
+
+ private SavedTrack(final Builder builder) {
+ super(builder);
+
+ this.addedAt = builder.addedAt;
+ this.track = builder.track;
+ }
+
+ /**
+ * Get the date, when the track has been saved.
+ *
+ * @return The date and time the track was saved.
+ */
+ public Date getAddedAt() {
+ return addedAt;
+ }
+
+ /**
+ * Get information about the track from a saved track object.
+ *
+ * @return Information about the track.
+ */
+ public Track getTrack() {
+ return track;
+ }
+
+ @Override
+ public String toString() {
+ return "SavedTrack(addedAt=" + addedAt + ", track=" + track + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link SavedTrack} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private Date addedAt;
+ private Track track;
+
+ /**
+ * Set the "added at" date of the saved track to be built.
+ *
+ * @param addedAt The date and time the track was saved.
+ * @return A {@link SavedTrack.Builder}.
+ */
+ public Builder setAddedAt(Date addedAt) {
+ this.addedAt = addedAt;
+ return this;
+ }
+
+ /**
+ * Set the full track object of the saved track to be built.
+ *
+ * @param track Information about the track.
+ * @return A {@link SavedTrack.Builder}.
+ */
+ public Builder setTrack(Track track) {
+ this.track = track;
+ return this;
+ }
+
+ @Override
+ public SavedTrack build() {
+ return new SavedTrack(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link SavedTrack} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<SavedTrack> {
+ public SavedTrack createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ try {
+ return new Builder()
+ .setAddedAt(
+ hasAndNotNull(jsonObject, "added_at")
+ ? SpotifyApi.parseDefaultDate(jsonObject.get("added_at").getAsString())
+ : null)
+ .setTrack(
+ hasAndNotNull(jsonObject, "track")
+ ? new Track.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("track"))
+ : null)
+ .build();
+ } catch (ParseException e) {
+ SpotifyApi.LOGGER.log(Level.SEVERE, e.getMessage());
+ return null;
+ }
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/Show.java
@@ -0,0 +1,505 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import java.util.Arrays;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/documentation/web-api/reference/object-model/#show-object-full">
+ * Show objects</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = Show.Builder.class)
+public class Show extends AbstractModelObject {
+ private final CountryCode[] availableMarkets;
+ private final Copyright[] copyrights;
+ private final String description;
+ private final Boolean explicit;
+ private final Paging<EpisodeSimplified> episodes;
+ private final ExternalUrl externalUrls;
+ private final String href;
+ private final String id;
+ private final Image[] images;
+ private final Boolean isExternallyHosted;
+ private final String[] languages;
+ private final String mediaType;
+ private final String name;
+ private final String publisher;
+ private final ModelObjectType type;
+ private final String uri;
+
+ public Show(Builder builder) {
+ super(builder);
+ this.availableMarkets = builder.availableMarkets;
+ this.copyrights = builder.copyrights;
+ this.description = builder.description;
+ this.explicit = builder.explicit;
+ this.episodes = builder.episodes;
+ this.externalUrls = builder.externalUrls;
+ this.href = builder.href;
+ this.id = builder.id;
+ this.images = builder.images;
+ this.isExternallyHosted = builder.isExternallyHosted;
+ this.languages = builder.languages;
+ this.mediaType = builder.mediaType;
+ this.name = builder.name;
+ this.publisher = builder.publisher;
+ this.type = builder.type;
+ this.uri = builder.uri;
+ }
+
+ /**
+ * Get a list of the countries in which the show can be played.
+ *
+ * @return An array of <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha-2 country
+ * * codes</a>.
+ */
+ public CountryCode[] getAvailableMarkets() {
+ return availableMarkets;
+ }
+
+ /**
+ * Get the copyright statements of the show.
+ *
+ * @return An array of {@link Copyright} objects.
+ */
+ public Copyright[] getCopyrights() {
+ return copyrights;
+ }
+
+ /**
+ * Get a description of the show.
+ *
+ * @return The description of the show.
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Check whether the show is explicit or not.
+ *
+ * @return Whether or not the show has explicit content ({@code true} = yes it does; {@code false} = no it does not
+ * <b>OR</b> unknown).
+ */
+ public Boolean getExplicit() {
+ return explicit;
+ }
+
+ /**
+ * Get a list of the show’s episodes.
+ *
+ * @return A {@link Paging} object containing {@link EpisodeSimplified} objects.
+ */
+ public Paging<EpisodeSimplified> getEpisodes() {
+ return episodes;
+ }
+
+ /**
+ * Get the external URLs of the show. <br>
+ * Example: <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify-URL</a>
+ *
+ * @return An {@link ExternalUrl} object.
+ */
+ public ExternalUrl getExternalUrls() {
+ return externalUrls;
+ }
+
+ /**
+ * Get the full Spotify Web API endpoint URL of the show.
+ *
+ * @return A link to the Web API endpoint providing full details of the show.
+ */
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Get the Spotify ID of the show.
+ *
+ * @return A <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify show ID</a>.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Get the cover art for the show in various sizes, widest first.
+ *
+ * @return An array of {@link Image} objects.
+ */
+ public Image[] getImages() {
+ return images;
+ }
+
+ /**
+ * Check whether the show is hosted outside of Spotify's CDN.
+ *
+ * @return True if the show is hosted outside of Spotify’s CDN. Might be {@code null} in some cases.
+ */
+ public Boolean getExternallyHosted() {
+ return isExternallyHosted;
+ }
+
+ /**
+ * Get a list of the languages used in the show, identified by their ISO 639 code.
+ *
+ * @return An array of <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha-2 country codes</a>.
+ */
+ public String[] getLanguages() {
+ return languages;
+ }
+
+ /**
+ * Get the media type of the show.
+ *
+ * @return The media type of the show.
+ */
+ public String getMediaType() {
+ return mediaType;
+ }
+
+ /**
+ * Get the name of the show.
+ *
+ * @return The name of the show.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the publisher of the show.
+ *
+ * @return The publisher of the show.
+ */
+ public String getPublisher() {
+ return publisher;
+ }
+
+ /**
+ * Get the model object type. In this case "show".
+ *
+ * @return A {@link ModelObjectType}.
+ */
+ public ModelObjectType getType() {
+ return type;
+ }
+
+ /**
+ * Get the Spotify URI of the show.
+ *
+ * @return <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify show URI</a>.
+ */
+ public String getUri() {
+ return uri;
+ }
+
+ @Override
+ public String toString() {
+ return "Show(availableMarkets=" + Arrays.toString(availableMarkets) + ", copyrights=" + Arrays.toString(copyrights)
+ + ", description=" + description + ", explicit=" + explicit + ", episodes=" + episodes + ", externalUrls="
+ + externalUrls + ", href=" + href + ", id=" + id + ", images=" + Arrays.toString(images)
+ + ", isExternallyHosted=" + isExternallyHosted + ", languages=" + Arrays.toString(languages) + ", mediaType="
+ + mediaType + ", name=" + name + ", publisher=" + publisher + ", type=" + type + ", uri=" + uri + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link Show} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private CountryCode[] availableMarkets;
+ private Copyright[] copyrights;
+ private String description;
+ private Boolean explicit;
+ private Paging<EpisodeSimplified> episodes;
+ private ExternalUrl externalUrls;
+ private String href;
+ private String id;
+ private Image[] images;
+ private Boolean isExternallyHosted;
+ private String[] languages;
+ private String mediaType;
+ private String name;
+ private String publisher;
+ private ModelObjectType type;
+ private String uri;
+
+ /**
+ * Set the available markets of the show to be built.
+ *
+ * @param availableMarkets A list of the countries in which the show can be played, identified by their
+ * <a href="http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha-2</a> code.
+ * @return A {@link Show.Builder}.
+ */
+ public Builder setAvailableMarkets(CountryCode... availableMarkets) {
+ this.availableMarkets = availableMarkets;
+ return this;
+ }
+
+ /**
+ * Set the copyrights of the show to be built.
+ *
+ * @param copyrights {@link Copyright} objects.
+ * @return A {@link Show.Builder}.
+ */
+ public Builder setCopyrights(Copyright... copyrights) {
+ this.copyrights = copyrights;
+ return this;
+ }
+
+ /**
+ * Set the description for the show to be built.
+ *
+ * @param description The description of the show.
+ * @return A {@link Show.Builder}.
+ */
+ public Builder setDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ /**
+ * Set whether the show to be built is explicit or not.
+ *
+ * @param explicit Whether or not the show has explicit content (true = yes it does; false = no it does not OR unknown).
+ * @return A {@link Show.Builder}.
+ */
+ public Builder setExplicit(Boolean explicit) {
+ this.explicit = explicit;
+ return this;
+ }
+
+ /**
+ * Set a list of the show's episodes.
+ *
+ * @param episodes A {@link Paging} object containing {@link EpisodeSimplified} objects.
+ * @return A {@link Show.Builder}.
+ */
+ public Builder setEpisodes(Paging<EpisodeSimplified> episodes) {
+ this.episodes = episodes;
+ return this;
+ }
+
+ /**
+ * Set the external URLs for the show to be built.
+ *
+ * @param externalUrls The {@link ExternalUrl} for the show object.
+ * @return A {@link Show.Builder}.
+ */
+ public Builder setExternalUrls(ExternalUrl externalUrls) {
+ this.externalUrls = externalUrls;
+ return this;
+ }
+
+ /**
+ * Set the link to the Web API endpoint providing full details of the show to be built.
+ *
+ * @param href The link to the Web API endpoint providing full details of the show.
+ * @return A {@link Show.Builder}.
+ */
+ public Builder setHref(String href) {
+ this.href = href;
+ return this;
+ }
+
+ /**
+ * Set the Spotify ID for the show to be built.
+ *
+ * @param id <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify show ID</a>.
+ * @return A {@link Show.Builder}.
+ */
+ public Builder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * Set the cover art for the show to be built.
+ *
+ * @param images {@link Image} objects.
+ * @return A {@link Show.Builder}.
+ */
+ public Builder setImages(Image... images) {
+ this.images = images;
+ return this;
+ }
+
+ /**
+ * Set whether the show to be built is hosted outside of Spotify's CDN.
+ *
+ * @param externallyHosted True if the show is hosted outside of Spotify’s CDN.
+ * @return A {@link Show.Builder}.
+ */
+ public Builder setExternallyHosted(Boolean externallyHosted) {
+ isExternallyHosted = externallyHosted;
+ return this;
+ }
+
+ /**
+ * Set a list of the languages used in the show to be built.
+ *
+ * @param languages An array of <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha-2 country codes</a>.
+ * @return A {@link Show.Builder}.
+ */
+ public Builder setLanguages(String[] languages) {
+ this.languages = languages;
+ return this;
+ }
+
+ /**
+ * Set the media type of the show.
+ *
+ * @param mediaType The media type of the show.
+ * @return A {@link Show.Builder}.
+ */
+ public Builder setMediaType(String mediaType) {
+ this.mediaType = mediaType;
+ return this;
+ }
+
+ /**
+ * Set the name for the show to be built.
+ *
+ * @param name The name of the show.
+ * @return A {@link Show.Builder}.
+ */
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Set the publisher for the show to be built.
+ *
+ * @param publisher The publisher of the show.
+ * @return A {@link Show.Builder}.
+ */
+ public Builder setPublisher(String publisher) {
+ this.publisher = publisher;
+ return this;
+ }
+
+ /**
+ * Set the type of the model object. In this case "show".
+ *
+ * @param type The {@link ModelObjectType}.
+ * @return A {@link Show.Builder}.
+ */
+ public Builder setType(ModelObjectType type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Set the Spotify URI for the show to be built.
+ *
+ * @param uri The <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URI</a> for the show.
+ * @return A {@link Show.Builder}.
+ */
+ public Builder setUri(String uri) {
+ this.uri = uri;
+ return this;
+ }
+
+ @Override
+ public Show build() {
+ return new Show(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link Show} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<Show> {
+ @Override
+ public Show createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Builder()
+ .setAvailableMarkets(
+ hasAndNotNull(jsonObject, "available_markets")
+ ? new Gson().fromJson(
+ jsonObject.getAsJsonArray("available_markets"), CountryCode[].class)
+ : null)
+ .setCopyrights(
+ hasAndNotNull(jsonObject, "copyrights")
+ ? new Gson().fromJson(
+ jsonObject.getAsJsonArray("copyrights"), Copyright[].class)
+ : null)
+ .setDescription(
+ hasAndNotNull(jsonObject, "description")
+ ? jsonObject.get("description").getAsString()
+ : null)
+ .setExplicit(
+ hasAndNotNull(jsonObject, "explicit")
+ ? jsonObject.get("explicit").getAsBoolean()
+ : null)
+ .setEpisodes(
+ hasAndNotNull(jsonObject, "episodes")
+ ? new EpisodeSimplified.JsonUtil().createModelObjectPaging(
+ jsonObject.getAsJsonObject("episodes"))
+ : null)
+ .setExternalUrls(
+ hasAndNotNull(jsonObject, "external_urls")
+ ? new ExternalUrl.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("external_urls"))
+ : null)
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setId(
+ hasAndNotNull(jsonObject, "id")
+ ? jsonObject.get("id").getAsString()
+ : null)
+ .setImages(
+ hasAndNotNull(jsonObject, "images")
+ ? new Image.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("images"))
+ : null)
+ .setExternallyHosted(
+ hasAndNotNull(jsonObject, "is_externally_hosted")
+ ? jsonObject.get("is_externally_hosted").getAsBoolean()
+ : null)
+ .setLanguages(
+ hasAndNotNull(jsonObject, "languages")
+ ? new Gson().fromJson(
+ jsonObject.getAsJsonArray("languages"), String[].class)
+ : null)
+ .setMediaType(
+ hasAndNotNull(jsonObject, "media_type")
+ ? jsonObject.get("media_type").getAsString()
+ : null)
+ .setName(
+ hasAndNotNull(jsonObject, "name")
+ ? jsonObject.get("name").getAsString()
+ : null)
+ .setPublisher(
+ hasAndNotNull(jsonObject, "publisher")
+ ? jsonObject.get("publisher").getAsString()
+ : null)
+ .setType(
+ hasAndNotNull(jsonObject, "type")
+ ? ModelObjectType.keyOf(
+ jsonObject.get("type").getAsString().toLowerCase())
+ : null)
+ .setUri(
+ hasAndNotNull(jsonObject, "uri")
+ ? jsonObject.get("uri").getAsString()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/ShowSimplified.java
@@ -0,0 +1,478 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import java.util.Arrays;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+import com.wrapper.spotify.requests.data.search.interfaces.ISearchModelObject;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/documentation/web-api/reference/object-model/#show-object-simplified">
+ * simplified Show objects</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = ShowSimplified.Builder.class)
+public class ShowSimplified extends AbstractModelObject implements ISearchModelObject {
+ private final CountryCode[] availableMarkets;
+ private final Copyright[] copyrights;
+ private final String description;
+ private final Boolean explicit;
+ private final ExternalUrl externalUrls;
+ private final String href;
+ private final String id;
+ private final Image[] images;
+ private final Boolean isExternallyHosted;
+ private final String[] languages;
+ private final String mediaType;
+ private final String name;
+ private final String publisher;
+ private final ModelObjectType type;
+ private final String uri;
+
+ public ShowSimplified(Builder builder) {
+ super(builder);
+ this.availableMarkets = builder.availableMarkets;
+ this.copyrights = builder.copyrights;
+ this.description = builder.description;
+ this.explicit = builder.explicit;
+ this.externalUrls = builder.externalUrls;
+ this.href = builder.href;
+ this.id = builder.id;
+ this.images = builder.images;
+ this.isExternallyHosted = builder.isExternallyHosted;
+ this.languages = builder.languages;
+ this.mediaType = builder.mediaType;
+ this.name = builder.name;
+ this.publisher = builder.publisher;
+ this.type = builder.type;
+ this.uri = builder.uri;
+ }
+
+ /**
+ * Get a list of the countries in which the show can be played.
+ *
+ * @return An array of <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha-2 country
+ * * codes</a>.
+ */
+ public CountryCode[] getAvailableMarkets() {
+ return availableMarkets;
+ }
+
+ /**
+ * Get the copyright statements of the show.
+ *
+ * @return An array of {@link Copyright} objects.
+ */
+ public Copyright[] getCopyrights() {
+ return copyrights;
+ }
+
+ /**
+ * Get a description of the show.
+ *
+ * @return The description of the show.
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Check whether the show is explicit or not.
+ *
+ * @return Whether or not the show has explicit content ({@code true} = yes it does; {@code false} = no it does not
+ * <b>OR</b> unknown).
+ */
+ public Boolean getExplicit() {
+ return explicit;
+ }
+
+ /**
+ * Get the external URLs of the show. <br>
+ * Example: <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify-URL</a>
+ *
+ * @return An {@link ExternalUrl} object.
+ */
+ public ExternalUrl getExternalUrls() {
+ return externalUrls;
+ }
+
+ /**
+ * Get the full Spotify Web API endpoint URL of the show.
+ *
+ * @return A link to the Web API endpoint providing full details of the show.
+ */
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Get the Spotify ID of the show.
+ *
+ * @return A <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify show ID</a>.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Get the cover art for the show in various sizes, widest first.
+ *
+ * @return An array of {@link Image} objects.
+ */
+ public Image[] getImages() {
+ return images;
+ }
+
+ /**
+ * Check whether the show is hosted outside of Spotify's CDN.
+ *
+ * @return True if the show is hosted outside of Spotify’s CDN. Might be {@code null} in some cases.
+ */
+ public Boolean getExternallyHosted() {
+ return isExternallyHosted;
+ }
+
+ /**
+ * Get a list of the languages used in the show, identified by their ISO 639 code.
+ *
+ * @return An array of <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha-2 country codes</a>.
+ */
+ public String[] getLanguages() {
+ return languages;
+ }
+
+ /**
+ * Get the media type of the show.
+ *
+ * @return The media type of the show.
+ */
+ public String getMediaType() {
+ return mediaType;
+ }
+
+ /**
+ * Get the name of the show.
+ *
+ * @return The name of the show.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the publisher of the show.
+ *
+ * @return The publisher of the show.
+ */
+ public String getPublisher() {
+ return publisher;
+ }
+
+ /**
+ * Get the model object type. In this case "show".
+ *
+ * @return A {@link ModelObjectType}.
+ */
+ public ModelObjectType getType() {
+ return type;
+ }
+
+ /**
+ * Get the Spotify URI of the show.
+ *
+ * @return <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify show URI</a>.
+ */
+ public String getUri() {
+ return uri;
+ }
+
+ @Override
+ public String toString() {
+ return "ShowSimplified(availableMarkets=" + Arrays.toString(availableMarkets) + ", copyrights="
+ + Arrays.toString(copyrights) + ", description=" + description + ", explicit=" + explicit + ", externalUrls="
+ + externalUrls + ", href=" + href + ", id=" + id + ", images=" + Arrays.toString(images)
+ + ", isExternallyHosted=" + isExternallyHosted + ", languages=" + Arrays.toString(languages) + ", mediaType="
+ + mediaType + ", name=" + name + ", publisher=" + publisher + ", type=" + type + ", uri=" + uri + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link ShowSimplified} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private CountryCode[] availableMarkets;
+ private Copyright[] copyrights;
+ private String description;
+ private Boolean explicit;
+ private ExternalUrl externalUrls;
+ private String href;
+ private String id;
+ private Image[] images;
+ private Boolean isExternallyHosted;
+ private String[] languages;
+ private String mediaType;
+ private String name;
+ private String publisher;
+ private ModelObjectType type;
+ private String uri;
+
+ /**
+ * Set the available markets of the show to be built.
+ *
+ * @param availableMarkets A list of the countries in which the show can be played, identified by their
+ * <a href="http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha-2</a> code.
+ * @return A {@link ShowSimplified.Builder}.
+ */
+ public Builder setAvailableMarkets(CountryCode... availableMarkets) {
+ this.availableMarkets = availableMarkets;
+ return this;
+ }
+
+ /**
+ * Set the copyrights of the show to be built.
+ *
+ * @param copyrights {@link Copyright} objects.
+ * @return A {@link ShowSimplified.Builder}.
+ */
+ public Builder setCopyrights(Copyright... copyrights) {
+ this.copyrights = copyrights;
+ return this;
+ }
+
+ /**
+ * Set the description for the show to be built.
+ *
+ * @param description The description of the show.
+ * @return A {@link ShowSimplified.Builder}.
+ */
+ public Builder setDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ /**
+ * Set whether the show to be built is explicit or not.
+ *
+ * @param explicit Whether or not the show has explicit content (true = yes it does; false = no it does not OR unknown).
+ * @return A {@link ShowSimplified.Builder}.
+ */
+ public Builder setExplicit(Boolean explicit) {
+ this.explicit = explicit;
+ return this;
+ }
+
+ /**
+ * Set the external URLs for the show to be built.
+ *
+ * @param externalUrls The {@link ExternalUrl} for the show object.
+ * @return A {@link ShowSimplified.Builder}.
+ */
+ public Builder setExternalUrls(ExternalUrl externalUrls) {
+ this.externalUrls = externalUrls;
+ return this;
+ }
+
+ /**
+ * Set the link to the Web API endpoint providing full details of the show to be built.
+ *
+ * @param href The link to the Web API endpoint providing full details of the show.
+ * @return A {@link ShowSimplified.Builder}.
+ */
+ public Builder setHref(String href) {
+ this.href = href;
+ return this;
+ }
+
+ /**
+ * Set the Spotify ID for the show to be built.
+ *
+ * @param id <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify show ID</a>.
+ * @return A {@link ShowSimplified.Builder}.
+ */
+ public Builder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * Set the cover art for the show to be built.
+ *
+ * @param images {@link Image} objects.
+ * @return A {@link ShowSimplified.Builder}.
+ */
+ public Builder setImages(Image... images) {
+ this.images = images;
+ return this;
+ }
+
+ /**
+ * Set whether the show to be built is hosted outside of Spotify's CDN.
+ *
+ * @param externallyHosted True if the show is hosted outside of Spotify’s CDN.
+ * @return A {@link ShowSimplified.Builder}.
+ */
+ public Builder setExternallyHosted(Boolean externallyHosted) {
+ isExternallyHosted = externallyHosted;
+ return this;
+ }
+
+ /**
+ * Set a list of the languages used in the show to be built.
+ *
+ * @param languages An array of <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha-2 country codes</a>.
+ * @return A {@link ShowSimplified.Builder}.
+ */
+ public Builder setLanguages(String[] languages) {
+ this.languages = languages;
+ return this;
+ }
+
+ /**
+ * Set the media type of the show.
+ *
+ * @param mediaType The media type of the show.
+ * @return A {@link ShowSimplified.Builder}.
+ */
+ public Builder setMediaType(String mediaType) {
+ this.mediaType = mediaType;
+ return this;
+ }
+
+ /**
+ * Set the name for the show to be built.
+ *
+ * @param name The name of the show.
+ * @return A {@link ShowSimplified.Builder}.
+ */
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Set the publisher for the show to be built.
+ *
+ * @param publisher The publisher of the show.
+ * @return A {@link ShowSimplified.Builder}.
+ */
+ public Builder setPublisher(String publisher) {
+ this.publisher = publisher;
+ return this;
+ }
+
+ /**
+ * Set the type of the model object. In this case "show".
+ *
+ * @param type The {@link ModelObjectType}.
+ * @return A {@link ShowSimplified.Builder}.
+ */
+ public Builder setType(ModelObjectType type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Set the Spotify URI for the show to be built.
+ *
+ * @param uri The <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URI</a> for the show.
+ * @return A {@link ShowSimplified.Builder}.
+ */
+ public Builder setUri(String uri) {
+ this.uri = uri;
+ return this;
+ }
+
+ @Override
+ public ShowSimplified build() {
+ return new ShowSimplified(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link ShowSimplified} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<ShowSimplified> {
+ @Override
+ public ShowSimplified createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Builder()
+ .setAvailableMarkets(
+ hasAndNotNull(jsonObject, "available_markets")
+ ? new Gson().fromJson(
+ jsonObject.getAsJsonArray("available_markets"), CountryCode[].class)
+ : null)
+ .setCopyrights(
+ hasAndNotNull(jsonObject, "copyrights")
+ ? new Gson().fromJson(
+ jsonObject.getAsJsonArray("copyrights"), Copyright[].class)
+ : null)
+ .setDescription(
+ hasAndNotNull(jsonObject, "description")
+ ? jsonObject.get("description").getAsString()
+ : null)
+ .setExplicit(
+ hasAndNotNull(jsonObject, "explicit")
+ ? jsonObject.get("explicit").getAsBoolean()
+ : null)
+ .setExternalUrls(
+ hasAndNotNull(jsonObject, "external_urls")
+ ? new ExternalUrl.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("external_urls"))
+ : null)
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setId(
+ hasAndNotNull(jsonObject, "id")
+ ? jsonObject.get("id").getAsString()
+ : null)
+ .setImages(
+ hasAndNotNull(jsonObject, "images")
+ ? new Image.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("images"))
+ : null)
+ .setExternallyHosted(
+ hasAndNotNull(jsonObject, "is_externally_hosted")
+ ? jsonObject.get("is_externally_hosted").getAsBoolean()
+ : null)
+ .setLanguages(
+ hasAndNotNull(jsonObject, "languages")
+ ? new Gson().fromJson(
+ jsonObject.getAsJsonArray("languages"), String[].class)
+ : null)
+ .setMediaType(
+ hasAndNotNull(jsonObject, "media_type")
+ ? jsonObject.get("media_type").getAsString()
+ : null)
+ .setName(
+ hasAndNotNull(jsonObject, "name")
+ ? jsonObject.get("name").getAsString()
+ : null)
+ .setPublisher(
+ hasAndNotNull(jsonObject, "publisher")
+ ? jsonObject.get("publisher").getAsString()
+ : null)
+ .setType(
+ hasAndNotNull(jsonObject, "type")
+ ? ModelObjectType.keyOf(
+ jsonObject.get("type").getAsString().toLowerCase())
+ : null)
+ .setUri(
+ hasAndNotNull(jsonObject, "uri")
+ ? jsonObject.get("uri").getAsString()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/Track.java
@@ -0,0 +1,629 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import java.util.Arrays;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+import com.wrapper.spotify.model_objects.IPlaylistItem;
+import com.wrapper.spotify.model_objects.miscellaneous.Restrictions;
+import com.wrapper.spotify.requests.data.personalization.interfaces.IArtistTrackModelObject;
+import com.wrapper.spotify.requests.data.search.interfaces.ISearchModelObject;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/object-model/#track-object-full">
+ * Track objects</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = Track.Builder.class)
+public class Track extends AbstractModelObject implements IArtistTrackModelObject, ISearchModelObject, IPlaylistItem {
+ private final AlbumSimplified album;
+ private final ArtistSimplified[] artists;
+ private final CountryCode[] availableMarkets;
+ private final Integer discNumber;
+ private final Integer durationMs;
+ private final Boolean explicit;
+ private final ExternalId externalIds;
+ private final ExternalUrl externalUrls;
+ private final String href;
+ private final String id;
+ private final Boolean isPlayable;
+ private final TrackLink linkedFrom;
+ private final Restrictions restrictions;
+ private final String name;
+ private final Integer popularity;
+ private final String previewUrl;
+ private final Integer trackNumber;
+ private final ModelObjectType type;
+ private final String uri;
+
+ private Track(final Builder builder) {
+ super(builder);
+
+ this.album = builder.album;
+ this.artists = builder.artists;
+ this.availableMarkets = builder.availableMarkets;
+ this.discNumber = builder.discNumber;
+ this.durationMs = builder.durationMs;
+ this.explicit = builder.explicit;
+ this.externalIds = builder.externalIds;
+ this.externalUrls = builder.externalUrls;
+ this.href = builder.href;
+ this.id = builder.id;
+ this.isPlayable = builder.isPlayable;
+ this.linkedFrom = builder.linkedFrom;
+ this.restrictions = builder.restrictions;
+ this.name = builder.name;
+ this.popularity = builder.popularity;
+ this.previewUrl = builder.previewUrl;
+ this.trackNumber = builder.trackNumber;
+ this.type = builder.type;
+ this.uri = builder.uri;
+ }
+
+ /**
+ * Get the album on which the track appears.
+ *
+ * @return The album on which the track appears. The (simplified) album object includes a link in href to full
+ * information about the album.
+ */
+ public AlbumSimplified getAlbum() {
+ return album;
+ }
+
+ /**
+ * Get the artists who performed the track.
+ *
+ * @return The artists who performed the track. Each artist object includes a link in {@code href} to more detailed
+ * information about the artist.
+ */
+ public ArtistSimplified[] getArtists() {
+ return artists;
+ }
+
+ /**
+ * Get the country codes of all countries, in which the track is available.
+ *
+ * @return A list of the countries in which the track can be played, identified by their
+ * <a href="http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha-2</a> code.
+ */
+ public CountryCode[] getAvailableMarkets() {
+ return availableMarkets;
+ }
+
+ /**
+ * Get the disc number of the track in its album.
+ *
+ * @return The disc number (usually 1 unless the album consists of more than one disc).
+ */
+ public Integer getDiscNumber() {
+ return discNumber;
+ }
+
+ /**
+ * Get the duration of the track in milliseconds.
+ *
+ * @return The track length in milliseconds.
+ */
+ @Override
+ public Integer getDurationMs() {
+ return durationMs;
+ }
+
+ /**
+ * Check whether the track is explicit or not.
+ *
+ * @return Whether or not the track has explicit lyrics ({@code true} = yes it does; {@code false} = no it does not
+ * <b>OR</b> unknown).
+ */
+ public Boolean getIsExplicit() {
+ return explicit;
+ }
+
+ /**
+ * Get the external IDs of the track.<br>
+ * Example: isrc -&gt; "International Standard Recording Code".
+ *
+ * @return Known external IDs for the track.
+ */
+ public ExternalId getExternalIds() {
+ return externalIds;
+ }
+
+ /**
+ * Get the external URLs of the track.<br>
+ * Example: Spotify-URL.
+ *
+ * @return Known external URLs for this track.
+ */
+ @Override
+ public ExternalUrl getExternalUrls() {
+ return externalUrls;
+ }
+
+ /**
+ * Get the full Spotify Web API endpoint URL of the track.
+ *
+ * @return A link to the Web API endpoint providing full details of the track.
+ */
+ @Override
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Get the <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify ID</a> of the
+ * track.
+ *
+ * @return The Spotify ID for the track.
+ */
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Check whether the track is playable in the market, which may has been specified somewhere before requesting it.
+ * Part of the response when <a href="https://developer.spotify.com/web-api/track-relinking-guide/">Track Relinking
+ * </a> is applied.
+ *
+ * @return If {@code true}, the track is playable in the given market. Otherwise {@code false}.
+ */
+ public Boolean getIsPlayable() {
+ return isPlayable;
+ }
+
+ /**
+ * Get the track link object of the track if <a href="https://developer.spotify.com/web-api/track-relinking-guide/">
+ * Track Relinking</a> was applied and the requested track has been replaced with a different track. The track in the
+ * {@code linked_from} object contains information about the originally requested track.
+ *
+ * @return The track in the {@code linked_from} object contains information about the originally requested track.
+ */
+ public TrackLink getLinkedFrom() {
+ return linkedFrom;
+ }
+
+ /**
+ * Get the restrictions of the track. Part of the response when
+ * <a href="https://developer.spotify.com/web-api/track-relinking-guide/">Track Relinking</a> is applied, the original
+ * track is not available in the given market, and Spotify did not have any tracks to relink it with. The track
+ * response will still contain metadata for the original track, and a restrictions object containing the reason why
+ * the track is not available. <br>
+ * Example: {@code "restrictions" : {"reason" : "market"}}
+ *
+ * @return The track response will still contain metadata for the original track, and a restrictions object containing
+ * the reason why the track is not available.
+ */
+ public Restrictions getRestrictions() {
+ return restrictions;
+ }
+
+ /**
+ * Get the name of the track.
+ *
+ * @return Track name.
+ */
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the popularity of the track. The value will be between 0 and 100, with 100 being the most popular. <br><br>
+ * <p>
+ * The popularity of a track is a value between 0 and 100, with 100 being the most popular. The popularity is
+ * calculated by algorithm and is based, in the most part, on the total number of plays the track has had and how
+ * recent those plays are.<br><br>
+ * <p>
+ * Generally speaking, songs that are being played a lot now will have a higher popularity than songs that were played
+ * a lot in the past. Duplicate tracks (e.g. the same track from a single and an album) are rated independently.
+ * Artist and album popularity is derived mathematically from track popularity. Note that the popularity value may lag
+ * actual popularity by a few days: the value is not updated in real time.
+ *
+ * @return The popularity of the track. The value will be between 0 and 100, with 100 being the most popular.
+ */
+ public Integer getPopularity() {
+ return popularity;
+ }
+
+ /**
+ * Get a link to a 30 second preview (MP3 format) of the track. {@code null} if not available.
+ *
+ * @return A link to a 30 second preview (MP3 format) of the track. {@code null} if not available.
+ */
+ public String getPreviewUrl() {
+ return previewUrl;
+ }
+
+ /**
+ * Get the track number of the track. If an album has several discs, the track number is the number on the specified
+ * disc.
+ *
+ * @return The number of the track.
+ */
+ public Integer getTrackNumber() {
+ return trackNumber;
+ }
+
+ /**
+ * Get the model object type, which should be a "track" in this case.
+ *
+ * @return The object type: "track".
+ */
+ @Override
+ public ModelObjectType getType() {
+ return type;
+ }
+
+ /**
+ * Get the Spotify track URI.
+ *
+ * @return The <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URI</a> for
+ * the track.
+ */
+ @Override
+ public String getUri() {
+ return uri;
+ }
+
+ @Override
+ public String toString() {
+ return "Track(name=" + name + ", artists=" + Arrays.toString(artists) + ", album=" + album + ", availableMarkets="
+ + Arrays.toString(availableMarkets) + ", discNumber=" + discNumber + ", durationMs=" + durationMs
+ + ", explicit=" + explicit + ", externalIds=" + externalIds + ", externalUrls=" + externalUrls + ", href="
+ + href + ", id=" + id + ", isPlayable=" + isPlayable + ", linkedFrom=" + linkedFrom + ", restrictions="
+ + restrictions + ", popularity=" + popularity + ", previewUrl=" + previewUrl + ", trackNumber=" + trackNumber
+ + ", type=" + type + ", uri=" + uri + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link Track} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private AlbumSimplified album;
+ private ArtistSimplified[] artists;
+ private CountryCode[] availableMarkets;
+ private Integer discNumber;
+ private Integer durationMs;
+ private Boolean explicit;
+ private ExternalId externalIds;
+ private ExternalUrl externalUrls;
+ private String href;
+ private String id;
+ private Boolean isPlayable;
+ private TrackLink linkedFrom;
+ private Restrictions restrictions;
+ private String name;
+ private Integer popularity;
+ private String previewUrl;
+ private Integer trackNumber;
+ private ModelObjectType type;
+ private String uri;
+
+ /**
+ * Set the album of the track to be built.
+ *
+ * @param album The album on which the track appears.
+ * @return A {@link Track.Builder}.
+ */
+ public Builder setAlbum(AlbumSimplified album) {
+ this.album = album;
+ return this;
+ }
+
+ /**
+ * Set the artists of the track to be built.
+ *
+ * @param artists The artists who performed the track.
+ * @return A {@link Track.Builder}.
+ */
+ public Builder setArtists(ArtistSimplified... artists) {
+ this.artists = artists;
+ return this;
+ }
+
+ /**
+ * Set the available markets of the track to be built.
+ *
+ * @param availableMarkets A list of the countries in which the track can be played, identified by their
+ * <a href="http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha-2</a> code.
+ * @return A {@link Track.Builder}.
+ */
+ public Builder setAvailableMarkets(CountryCode... availableMarkets) {
+ this.availableMarkets = availableMarkets;
+ return this;
+ }
+
+ /**
+ * Set the disc number of the track to be built.
+ *
+ * @param discNumber The disc number (usually 1 unless the album consists of more than one disc).
+ * @return A {@link Track.Builder}.
+ */
+ public Builder setDiscNumber(Integer discNumber) {
+ this.discNumber = discNumber;
+ return this;
+ }
+
+ /**
+ * Set the duration in milliseconds of the track to be built.
+ *
+ * @param durationMs The track length in milliseconds.
+ * @return A {@link Track.Builder}.
+ */
+ public Builder setDurationMs(Integer durationMs) {
+ this.durationMs = durationMs;
+ return this;
+ }
+
+ /**
+ * Set whether the track to be built is explicit or not.
+ *
+ * @param explicit Whether or not the track has explicit lyrics ({@code true} = yes it does; {@code false} = no it
+ * does not <b>OR</b> unknown).
+ * @return A {@link Track.Builder}.
+ */
+ public Builder setExplicit(Boolean explicit) {
+ this.explicit = explicit;
+ return this;
+ }
+
+ /**
+ * Set the external IDs of the track to be built.
+ *
+ * @param externalIds Known external IDs for the track.
+ * @return A {@link Track.Builder}.
+ */
+ public Builder setExternalIds(ExternalId externalIds) {
+ this.externalIds = externalIds;
+ return this;
+ }
+
+ /**
+ * Set external URLs of the track to be built.
+ *
+ * @param externalUrls Known external URLs for the track.
+ * @return A {@link Track.Builder}.
+ */
+ public Builder setExternalUrls(ExternalUrl externalUrls) {
+ this.externalUrls = externalUrls;
+ return this;
+ }
+
+ /**
+ * Set href of Spotify Web API endpoint of the track to be built.
+ *
+ * @param href A link to the Web API endpoint providing full details of the track.
+ * @return A {@link Track.Builder}.
+ */
+ public Builder setHref(String href) {
+ this.href = href;
+ return this;
+ }
+
+ /**
+ * Set track ID of the track to be built.
+ *
+ * @param id The Spotify ID for the track.
+ * @return A {@link Track.Builder}.
+ */
+ public Builder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * Set whether the track to be built is playable in your market region or not.
+ *
+ * @param isPlayable If {@code true}, the track is playable in the given market. Otherwise {@code false}.
+ * @return A {@link Track.Builder}.
+ */
+ public Builder setIsPlayable(Boolean isPlayable) {
+ this.isPlayable = isPlayable;
+ return this;
+ }
+
+ /**
+ * Set the track link object of the track to be built.
+ *
+ * @param linkedFrom The track in the {@code linked_from} object contains information about the originally requested
+ * track.
+ * @return A {@link Track.Builder}.
+ */
+ public Builder setLinkedFrom(TrackLink linkedFrom) {
+ this.linkedFrom = linkedFrom;
+ return this;
+ }
+
+ /**
+ * Set the restrictions object of the track to be built.
+ *
+ * @param restrictions The track response will still contain metadata for the original track, and a restrictions
+ * object containing the reason why the track is not available.
+ * @return A {@link Track.Builder}.
+ */
+ public Builder setRestrictions(Restrictions restrictions) {
+ this.restrictions = restrictions;
+ return this;
+ }
+
+ /**
+ * Set the name of the track to be built.
+ *
+ * @param name Track name.
+ * @return A {@link Track.Builder}.
+ */
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Set the popularity of the track to be built.
+ *
+ * @param popularity The popularity of the track. The value will be between 0 and 100, with 100 being the most
+ * popular.
+ * @return A {@link Track.Builder}.
+ */
+ public Builder setPopularity(Integer popularity) {
+ this.popularity = popularity;
+ return this;
+ }
+
+ /**
+ * Set the preview URL of the track to be built.
+ *
+ * @param previewUrl A link to a 30 second preview (MP3 format) of the track. {@code null} if not available.
+ * @return A {@link Track.Builder}.
+ */
+ public Builder setPreviewUrl(String previewUrl) {
+ this.previewUrl = previewUrl;
+ return this;
+ }
+
+ /**
+ * Set the track number of the track to be built.
+ *
+ * @param trackNumber The track number.
+ * @return A {@link Track.Builder}.
+ */
+ public Builder setTrackNumber(Integer trackNumber) {
+ this.trackNumber = trackNumber;
+ return this;
+ }
+
+ /**
+ * Set the type of the model object. In this case "track".
+ *
+ * @param type The object type: "track".
+ * @return A {@link Track.Builder}.
+ */
+ public Builder setType(ModelObjectType type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Set Spotify URI of the track to be built.
+ *
+ * @param uri The <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URI</a>
+ * for the track.
+ * @return A {@link Track.Builder}.
+ */
+ public Builder setUri(String uri) {
+ this.uri = uri;
+ return this;
+ }
+
+ @Override
+ public Track build() {
+ return new Track(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link Track} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<Track> {
+ public Track createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Track.Builder()
+ .setAlbum(
+ hasAndNotNull(jsonObject, "album")
+ ? new AlbumSimplified.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("album"))
+ : null)
+ .setArtists(
+ hasAndNotNull(jsonObject, "artists")
+ ? new ArtistSimplified.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("artists"))
+ : null)
+ .setAvailableMarkets(
+ hasAndNotNull(jsonObject, "available_markets")
+ ? new Gson().fromJson(
+ jsonObject.getAsJsonArray("available_markets"), CountryCode[].class)
+ : null)
+ .setDiscNumber(
+ hasAndNotNull(jsonObject, "disc_number")
+ ? jsonObject.get("disc_number").getAsInt()
+ : null)
+ .setDurationMs(
+ hasAndNotNull(jsonObject, "duration_ms")
+ ? jsonObject.get("duration_ms").getAsInt()
+ : null)
+ .setExplicit(
+ hasAndNotNull(jsonObject, "explicit")
+ ? jsonObject.get("explicit").getAsBoolean()
+ : null)
+ .setExternalIds(
+ hasAndNotNull(jsonObject, "external_ids")
+ ? new ExternalId.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("external_ids"))
+ : null)
+ .setExternalUrls(
+ hasAndNotNull(jsonObject, "external_urls")
+ ? new ExternalUrl.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("external_urls"))
+ : null)
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setId(
+ hasAndNotNull(jsonObject, "id")
+ ? jsonObject.get("id").getAsString()
+ : null)
+ .setIsPlayable(
+ hasAndNotNull(jsonObject, "is_playable")
+ ? jsonObject.get("is_playable").getAsBoolean()
+ : null)
+ .setLinkedFrom(
+ hasAndNotNull(jsonObject, "linked_from")
+ ? new TrackLink.JsonUtil().createModelObject(
+ jsonObject.get("linked_from").getAsJsonObject())
+ : null)
+ .setRestrictions(
+ hasAndNotNull(jsonObject, "restrictions")
+ ? new Restrictions.JsonUtil().createModelObject(
+ jsonObject.get("restrictions").getAsJsonObject())
+ : null)
+ .setName(
+ hasAndNotNull(jsonObject, "name")
+ ? jsonObject.get("name").getAsString()
+ : null)
+ .setPopularity(
+ hasAndNotNull(jsonObject, "popularity")
+ ? jsonObject.get("popularity").getAsInt()
+ : null)
+ .setPreviewUrl(
+ hasAndNotNull(jsonObject, "preview_url")
+ ? jsonObject.get("preview_url").getAsString()
+ : null)
+ .setTrackNumber(
+ hasAndNotNull(jsonObject, "track_number")
+ ? jsonObject.get("track_number").getAsInt()
+ : null)
+ .setType(
+ hasAndNotNull(jsonObject, "type")
+ ? ModelObjectType.keyOf(
+ jsonObject.get("type").getAsString().toLowerCase())
+ : null)
+ .setUri(
+ hasAndNotNull(jsonObject, "uri")
+ ? jsonObject.get("uri").getAsString()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/TrackLink.java
@@ -0,0 +1,199 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/object-model/#track-link-object">
+ * Track Link objects</a> by building instances from this class. <br>
+ * Track Link objects contain information about originally requested tracks, when the given track is not available in
+ * your market region.
+ *
+ * @see <a href="https://developer.spotify.com/web-api/track-relinking-guide/">Spotify: Track Relinking Guide</a>
+ */
+@JsonDeserialize(builder = TrackLink.Builder.class)
+public class TrackLink extends AbstractModelObject {
+ private final ExternalUrl externalUrls;
+ private final String href;
+ private final String id;
+ private final ModelObjectType type;
+ private final String uri;
+
+ private TrackLink(final Builder builder) {
+ super(builder);
+
+ this.externalUrls = builder.externalUrls;
+ this.href = builder.href;
+ this.id = builder.id;
+ this.type = builder.type;
+ this.uri = builder.uri;
+ }
+
+ /**
+ * Get the external URLs of the track.<br>
+ * Example: Spotify-URL.
+ *
+ * @return Known external URLs for this track.
+ */
+ public ExternalUrl getExternalUrls() {
+ return externalUrls;
+ }
+
+ /**
+ * Get the Spotify Web API endpoint URL of the track.
+ *
+ * @return A link to the Web API endpoint providing full details of the track.
+ */
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Get the <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify ID</a> of the
+ * track.
+ *
+ * @return A Spotify track ID.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Get the model object type, which should be a "track" in this case.
+ *
+ * @return The object type: "track".
+ */
+ public ModelObjectType getType() {
+ return type;
+ }
+
+ /**
+ * Get the <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URI</a> of the
+ * track.
+ *
+ * @return The Spotify URI for the track.
+ */
+ public String getUri() {
+ return uri;
+ }
+
+ @Override
+ public String toString() {
+ return "TrackLink(externalUrls=" + externalUrls + ", href=" + href + ", id=" + id + ", type=" + type + ", uri="
+ + uri + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link TrackLink} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private ExternalUrl externalUrls;
+ private String href;
+ private String id;
+ private ModelObjectType type;
+ private String uri;
+
+ /**
+ * Set external URLs of the track to be built.
+ *
+ * @param externalUrls Known external URLs for this track.
+ * @return A {@link TrackLink.Builder}.
+ */
+ public Builder setExternalUrls(ExternalUrl externalUrls) {
+ this.externalUrls = externalUrls;
+ return this;
+ }
+
+ /**
+ * Set href of Spotify Web API endpoint of the track to be built.
+ *
+ * @param href A link to the Web API endpoint providing full details of the track.
+ * @return A {@link TrackLink.Builder}.
+ */
+ public Builder setHref(String href) {
+ this.href = href;
+ return this;
+ }
+
+ /**
+ * Set the Spotify ID of the track to be built.
+ *
+ * @param id A Spotify track ID.
+ * @return A {@link TrackLink.Builder}.
+ */
+ public Builder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * Set the type of the model object. In this case "track".
+ *
+ * @param type The object type: "track".
+ * @return A {@link TrackLink.Builder}.
+ */
+ public Builder setType(ModelObjectType type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Set the Spotify URI of the track to be built.
+ *
+ * @param uri The Spotify URI for the track.
+ * @return A {@link TrackLink.Builder}.
+ */
+ public Builder setUri(String uri) {
+ this.uri = uri;
+ return this;
+ }
+
+ @Override
+ public TrackLink build() {
+ return new TrackLink(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link TrackLink} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<TrackLink> {
+ public TrackLink createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new TrackLink.Builder()
+ .setExternalUrls(
+ hasAndNotNull(jsonObject, "external_urls")
+ ? new ExternalUrl.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("external_urls"))
+ : null)
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setId(
+ hasAndNotNull(jsonObject, "id")
+ ? jsonObject.get("id").getAsString()
+ : null)
+ .setType(
+ hasAndNotNull(jsonObject, "type")
+ ? ModelObjectType.keyOf(
+ jsonObject.get("type").getAsString().toLowerCase())
+ : null)
+ .setUri(
+ hasAndNotNull(jsonObject, "uri")
+ ? jsonObject.get("uri").getAsString()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/TrackSimplified.java
@@ -0,0 +1,487 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import java.util.Arrays;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/object-model/#track-object-simplified">
+ * simplified Track objects</a> by building instances from this class.
+ */
+@JsonDeserialize(builder = TrackSimplified.Builder.class)
+public class TrackSimplified extends AbstractModelObject {
+ private final ArtistSimplified[] artists;
+ private final CountryCode[] availableMarkets;
+ private final Integer discNumber;
+ private final Integer durationMs;
+ private final Boolean explicit;
+ private final ExternalUrl externalUrls;
+ private final String href;
+ private final String id;
+ private final Boolean isPlayable;
+ private final TrackLink linkedFrom;
+ private final String name;
+ private final String previewUrl;
+ private final Integer trackNumber;
+ private final ModelObjectType type;
+ private final String uri;
+
+ private TrackSimplified(final Builder builder) {
+ super(builder);
+
+ this.artists = builder.artists;
+ this.availableMarkets = builder.availableMarkets;
+ this.discNumber = builder.discNumber;
+ this.durationMs = builder.durationMs;
+ this.explicit = builder.explicit;
+ this.externalUrls = builder.externalUrls;
+ this.href = builder.href;
+ this.id = builder.id;
+ this.isPlayable = builder.isPlayable;
+ this.linkedFrom = builder.linkedFrom;
+ this.name = builder.name;
+ this.previewUrl = builder.previewUrl;
+ this.trackNumber = builder.trackNumber;
+ this.type = builder.type;
+ this.uri = builder.uri;
+ }
+
+ /**
+ * Get the artists who performed the track.
+ *
+ * @return The artists who performed the track. Each artist object includes a link in {@code href} to more detailed
+ * information about the artist.
+ */
+ public ArtistSimplified[] getArtists() {
+ return artists;
+ }
+
+ /**
+ * Get the country codes of all countries, in which the track is available.
+ *
+ * @return A list of the countries in which the track can be played, identified by their
+ * <a href="http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha-2</a> code.
+ */
+ public CountryCode[] getAvailableMarkets() {
+ return availableMarkets;
+ }
+
+ /**
+ * Get the disc number of the track in its album.
+ *
+ * @return The disc number (usually 1 unless the album consists of more than one disc).
+ */
+ public Integer getDiscNumber() {
+ return discNumber;
+ }
+
+ /**
+ * Get the duration of the track in milliseconds.
+ *
+ * @return The track length in milliseconds.
+ */
+ public Integer getDurationMs() {
+ return durationMs;
+ }
+
+ /**
+ * Check whether the track is explicit or not.
+ *
+ * @return Whether or not the track has explicit lyrics ({@code true} = yes it does; {@code false} = no it does not
+ * <b>OR</b> unknown).
+ */
+ public Boolean getIsExplicit() {
+ return explicit;
+ }
+
+ /**
+ * Get the external URLs of the track.<br>
+ * Example: Spotify-URL.
+ *
+ * @return Known external URLs for this track.
+ */
+ public ExternalUrl getExternalUrls() {
+ return externalUrls;
+ }
+
+ /**
+ * Get the full Spotify Web API endpoint URL of the track.
+ *
+ * @return A link to the Web API endpoint providing full details of the track.
+ */
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Get the <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify ID</a> of the
+ * track.
+ *
+ * @return The Spotify ID for the track.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Check whether the track is playable in the market, which may has been specified somewhere before requesting it.
+ * Part of the response when <a href="https://developer.spotify.com/web-api/track-relinking-guide/">Track Relinking
+ * </a> is applied.
+ *
+ * @return If {@code true}, the track is playable in the given market. Otherwise {@code false}.
+ */
+ public Boolean getIsPlayable() {
+ return isPlayable;
+ }
+
+ /**
+ * Get the track link object of the track if <a href="https://developer.spotify.com/web-api/track-relinking-guide/">
+ * Track Relinking</a> was applied and the requested track has been replaced with a different track. The track in the
+ * {@code linked_from} object contains information about the originally requested track.
+ *
+ * @return The track in the {@code linked_from} object contains information about the originally requested track.
+ */
+ public TrackLink getLinkedFrom() {
+ return linkedFrom;
+ }
+
+ /**
+ * Get the name of a track.
+ *
+ * @return Track name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get a link to a 30 second preview (MP3 format) of the track. {@code null} if not available.
+ *
+ * @return A link to a 30 second preview (MP3 format) of the track. {@code null} if not available.
+ */
+ public String getPreviewUrl() {
+ return previewUrl;
+ }
+
+ /**
+ * Get the track number of the track. If an album has several discs, the track number is the number on the specified
+ * disc.
+ *
+ * @return The number of the track.
+ */
+ public Integer getTrackNumber() {
+ return trackNumber;
+ }
+
+ /**
+ * Get the model object type, which should be a "track" in this case.
+ *
+ * @return The object type: "track".
+ */
+ public ModelObjectType getType() {
+ return type;
+ }
+
+ /**
+ * Get the Spotify track URI.
+ *
+ * @return The <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URI</a> for
+ * the track.
+ */
+ public String getUri() {
+ return uri;
+ }
+
+ @Override
+ public String toString() {
+ return "TrackSimplified(name=" + name + ", artists=" + Arrays.toString(artists) + ", availableMarkets="
+ + Arrays.toString(availableMarkets) + ", discNumber=" + discNumber + ", durationMs=" + durationMs
+ + ", explicit=" + explicit + ", externalUrls=" + externalUrls + ", href=" + href + ", id=" + id
+ + ", isPlayable=" + isPlayable + ", linkedFrom=" + linkedFrom + ", previewUrl=" + previewUrl + ", trackNumber="
+ + trackNumber + ", type=" + type + ", uri=" + uri + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link TrackSimplified} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private ArtistSimplified[] artists;
+ private CountryCode[] availableMarkets;
+ private Integer discNumber;
+ private Integer durationMs;
+ private Boolean explicit;
+ private ExternalUrl externalUrls;
+ private String href;
+ private String id;
+ private Boolean isPlayable;
+ private TrackLink linkedFrom;
+ private String name;
+ private String previewUrl;
+ private Integer trackNumber;
+ private ModelObjectType type;
+ private String uri;
+
+ /**
+ * Set the artists of the track to be built.
+ *
+ * @param artists The artists who performed the track.
+ * @return A {@link TrackSimplified.Builder}.
+ */
+ public Builder setArtists(ArtistSimplified... artists) {
+ this.artists = artists;
+ return this;
+ }
+
+ /**
+ * Set the available markets of the track to be built.
+ *
+ * @param availableMarkets A list of the countries in which the track can be played, identified by their
+ * <a href="http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha-2</a> code.
+ * @return A {@link TrackSimplified.Builder}.
+ */
+ public Builder setAvailableMarkets(CountryCode... availableMarkets) {
+ this.availableMarkets = availableMarkets;
+ return this;
+ }
+
+ /**
+ * Set the disc number of the track to be built.
+ *
+ * @param discNumber The disc number (usually 1 unless the album consists of more than one disc).
+ * @return A {@link TrackSimplified.Builder}.
+ */
+ public Builder setDiscNumber(Integer discNumber) {
+ this.discNumber = discNumber;
+ return this;
+ }
+
+ /**
+ * Set the duration in milliseconds of the track to be built.
+ *
+ * @param durationMs The track length in milliseconds.
+ * @return A {@link TrackSimplified.Builder}.
+ */
+ public Builder setDurationMs(Integer durationMs) {
+ this.durationMs = durationMs;
+ return this;
+ }
+
+ /**
+ * Set whether the track to be built is explicit or not.
+ *
+ * @param explicit Whether or not the track has explicit lyrics ({@code true} = yes it does; {@code false} = no it
+ * does not <b>OR</b> unknown).
+ * @return A {@link TrackSimplified.Builder}.
+ */
+ public Builder setExplicit(Boolean explicit) {
+ this.explicit = explicit;
+ return this;
+ }
+
+ /**
+ * Set external URLs of the track to be built.
+ *
+ * @param externalUrls Known external URLs for the track.
+ * @return A {@link TrackSimplified.Builder}.
+ */
+ public Builder setExternalUrls(ExternalUrl externalUrls) {
+ this.externalUrls = externalUrls;
+ return this;
+ }
+
+ /**
+ * Set href of Spotify Web API endpoint of the track to be built.
+ *
+ * @param href A link to the Web API endpoint providing full details of the track.
+ * @return A {@link TrackSimplified.Builder}.
+ */
+ public Builder setHref(String href) {
+ this.href = href;
+ return this;
+ }
+
+ /**
+ * Set track ID of the track to be built.
+ *
+ * @param id The Spotify ID for the track.
+ * @return A {@link TrackSimplified.Builder}.
+ */
+ public Builder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * Set whether the track to be built is playable in your market region or not.
+ *
+ * @param isPlayable If {@code true}, the track is playable in the given market. Otherwise {@code false}.
+ * @return A {@link TrackSimplified.Builder}.
+ */
+ public Builder setIsPlayable(Boolean isPlayable) {
+ this.isPlayable = isPlayable;
+ return this;
+ }
+
+ /**
+ * Set the track link object of the track to be built.
+ *
+ * @param linkedFrom The track in the {@code linked_from} object contains information about the originally requested
+ * track.
+ * @return A {@link TrackSimplified.Builder}.
+ */
+ public Builder setLinkedFrom(TrackLink linkedFrom) {
+ this.linkedFrom = linkedFrom;
+ return this;
+ }
+
+ /**
+ * Set the name of the track to be built.
+ *
+ * @param name Track name.
+ * @return A {@link TrackSimplified.Builder}.
+ */
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Set the preview URL of the track to be built.
+ *
+ * @param previewUrl A link to a 30 second preview (MP3 format) of the track. {@code null} if not available.
+ * @return A {@link TrackSimplified.Builder}.
+ */
+ public Builder setPreviewUrl(String previewUrl) {
+ this.previewUrl = previewUrl;
+ return this;
+ }
+
+ /**
+ * Set the track number of the track to be built.
+ *
+ * @param trackNumber The track number.
+ * @return A {@link TrackSimplified.Builder}.
+ */
+ public Builder setTrackNumber(Integer trackNumber) {
+ this.trackNumber = trackNumber;
+ return this;
+ }
+
+ /**
+ * Set the type of the model object. In this case "track".
+ *
+ * @param type The object type: "track".
+ * @return A {@link TrackSimplified.Builder}.
+ */
+ public Builder setType(ModelObjectType type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Set Spotify URI of the track to be built.
+ *
+ * @param uri The <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URI</a>
+ * for the track.
+ * @return A {@link TrackSimplified.Builder}.
+ */
+ public Builder setUri(String uri) {
+ this.uri = uri;
+ return this;
+ }
+
+ @Override
+ public TrackSimplified build() {
+ return new TrackSimplified(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link TrackSimplified} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<TrackSimplified> {
+ public TrackSimplified createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new Builder()
+ .setArtists(
+ hasAndNotNull(jsonObject, "artists")
+ ? new ArtistSimplified.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("artists"))
+ : null)
+ .setAvailableMarkets(
+ hasAndNotNull(jsonObject, "available_markets")
+ ? new Gson().fromJson(jsonObject.getAsJsonArray(
+ "available_markets"), CountryCode[].class)
+ : null)
+ .setDiscNumber(
+ hasAndNotNull(jsonObject, "disc_number")
+ ? jsonObject.get("disc_number").getAsInt()
+ : null)
+ .setDurationMs(
+ hasAndNotNull(jsonObject, "duration_ms")
+ ? jsonObject.get("duration_ms").getAsInt()
+ : null)
+ .setExplicit(
+ hasAndNotNull(jsonObject, "explicit")
+ ? jsonObject.get("explicit").getAsBoolean()
+ : null)
+ .setExternalUrls(
+ hasAndNotNull(jsonObject, "external_urls")
+ ? new ExternalUrl.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("external_urls"))
+ : null)
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setId(
+ hasAndNotNull(jsonObject, "id")
+ ? jsonObject.get("id").getAsString()
+ : null)
+ .setIsPlayable(
+ hasAndNotNull(jsonObject, "is_playable")
+ ? jsonObject.get("is_playable").getAsBoolean()
+ : null)
+ .setLinkedFrom(
+ hasAndNotNull(jsonObject, "linked_from")
+ ? new TrackLink.JsonUtil().createModelObject(
+ jsonObject.get("linked_from").getAsJsonObject())
+ : null)
+ .setName(
+ hasAndNotNull(jsonObject, "name")
+ ? jsonObject.get("name").getAsString()
+ : null)
+ .setPreviewUrl(
+ hasAndNotNull(jsonObject, "preview_url")
+ ? jsonObject.get("preview_url").getAsString()
+ : null)
+ .setTrackNumber(
+ hasAndNotNull(jsonObject, "track_number")
+ ? jsonObject.get("track_number").getAsInt()
+ : null)
+ .setType(
+ hasAndNotNull(jsonObject, "type")
+ ? ModelObjectType.keyOf(
+ jsonObject.get("type").getAsString().toLowerCase())
+ : null)
+ .setUri(
+ hasAndNotNull(jsonObject, "uri")
+ ? jsonObject.get("uri").getAsString()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/model_objects/specification/User.java
@@ -0,0 +1,414 @@
+package com.wrapper.spotify.model_objects.specification;
+
+import java.util.Arrays;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.enums.ProductType;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+
+/**
+ * Retrieve information about <a href="https://developer.spotify.com/web-api/object-model/#user-object-private">
+ * User objects</a> by building instances from this class. <br>
+ * <b>Note:</b> Many methods of this model object may return {@code null}, depending on the scopes specified in the
+ * authentication request.
+ *
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+@JsonDeserialize(builder = User.Builder.class)
+public class User extends AbstractModelObject {
+ private final String birthdate;
+ private final CountryCode country;
+ private final String displayName;
+ private final String email;
+ private final ExternalUrl externalUrls;
+ private final Followers followers;
+ private final String href;
+ private final String id;
+ private final Image[] images;
+ private final ProductType product;
+ private final ModelObjectType type;
+ private final String uri;
+
+ private User(final Builder builder) {
+ super(builder);
+
+ this.birthdate = builder.birthdate;
+ this.country = builder.country;
+ this.displayName = builder.displayName;
+ this.email = builder.email;
+ this.externalUrls = builder.externalUrls;
+ this.followers = builder.followers;
+ this.href = builder.href;
+ this.id = builder.id;
+ this.images = builder.images;
+ this.product = builder.product;
+ this.type = builder.type;
+ this.uri = builder.uri;
+ }
+
+ /**
+ * Get the users birthdate. <br>
+ * <b>Note:</b> This field is only available when the current user has granted access to the
+ * {@code user-read-birthdate} scope.
+ *
+ * @return The user's date-of-birth.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public String getBirthdate() {
+ return birthdate;
+ }
+
+ /**
+ * Get the country code of the users set home country. <br>
+ * <b>Note:</b> This field is only available when the current user has granted access to the {@code user-read-private}
+ * scope.
+ *
+ * @return An <a href="http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha-2</a> country code.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public CountryCode getCountry() {
+ return country;
+ }
+
+ /**
+ * Get the users display name if available. <br>
+ * If the display name is not available, {@code null} will be returned.
+ *
+ * @return The name displayed on the user's profile. {@code null} if not available.
+ */
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ /**
+ * Get the users email address. <br>
+ * <b>Important!</b> This email address is unverified; there is no proof that it actually belongs to the user. <br>
+ * <b>Note:</b> This field is only available when the current user has granted access to the {@code user-read-email}
+ * scope.
+ *
+ * @return The user's email address, as entered by the user when creating their account.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public String getEmail() {
+ return email;
+ }
+
+ /**
+ * Get the external URLs of the user. <br>
+ * Example: Spotify-URL.
+ *
+ * @return Known external URLs for this user.
+ */
+ public ExternalUrl getExternalUrls() {
+ return externalUrls;
+ }
+
+ /**
+ * Get information about the followers of the user. <br>
+ * Example: Follower count.
+ *
+ * @return Information about the followers of the user.
+ */
+ public Followers getFollowers() {
+ return followers;
+ }
+
+ /**
+ * Get the Spotify Web API endpoint URL of the user.
+ *
+ * @return A link to the Spotify Web API endpoint for this user.
+ */
+ public String getHref() {
+ return href;
+ }
+
+ /**
+ * Get the Spotify ID of the user.
+ *
+ * @return The <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify user ID</a>
+ * for the user.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Get the profile image of the user in different sizes.
+ *
+ * @return The user's profile image.
+ */
+ public Image[] getImages() {
+ return images;
+ }
+
+ /**
+ * Get the product type of the users account. <br>
+ * Product type refers to premium account, free account, etc. <br>
+ * <b>Note:</b> This field is only available when the current user has granted access to the {@code user-read-private}
+ * scope.
+ *
+ * @return The user's Spotify subscription level: "premium", "free", etc.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public ProductType getProduct() {
+ return product;
+ }
+
+ /**
+ * Get the model object type. In this case "user".
+ *
+ * @return The object type: "user"
+ */
+ public ModelObjectType getType() {
+ return type;
+ }
+
+ /**
+ * Get the <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URI</a> of the
+ * user.
+ *
+ * @return The Spotify URI for the user.
+ */
+ public String getUri() {
+ return uri;
+ }
+
+ @Override
+ public String toString() {
+ return "User(birthdate=" + birthdate + ", country=" + country + ", displayName=" + displayName + ", email=" + email
+ + ", externalUrls=" + externalUrls + ", followers=" + followers + ", href=" + href + ", id=" + id + ", images="
+ + Arrays.toString(images) + ", product=" + product + ", type=" + type + ", uri=" + uri + ")";
+ }
+
+ @Override
+ public Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for building {@link User} instances.
+ */
+ public static final class Builder extends AbstractModelObject.Builder {
+ private String birthdate;
+ private CountryCode country;
+ private String displayName;
+ private String email;
+ private ExternalUrl externalUrls;
+ private Followers followers;
+ private String href;
+ private String id;
+ private Image[] images;
+ private ProductType product;
+ private ModelObjectType type;
+ private String uri;
+
+ /**
+ * Set the birthday of the user object to be built.
+ *
+ * @param birthdate The user's date-of-birth.
+ * @return A {@link User.Builder}.
+ */
+ public Builder setBirthdate(String birthdate) {
+ this.birthdate = birthdate;
+ return this;
+ }
+
+ /**
+ * Set the home country of the user object to be built.
+ *
+ * @param country An ISO 3166-1 alpha-2 country code.
+ * @return A {@link User.Builder}.
+ */
+ public Builder setCountry(CountryCode country) {
+ this.country = country;
+ return this;
+ }
+
+ /**
+ * Set the display name of the user to be built. If the user hasn't a display name, set {@code null} instead.
+ *
+ * @param displayName The name displayed on the user's profile. {@code null} if not available.
+ * @return A {@link User.Builder}.
+ */
+ public Builder setDisplayName(String displayName) {
+ this.displayName = displayName;
+ return this;
+ }
+
+ /**
+ * Set the email address of the user to be built.
+ *
+ * @param email The user's email address, as entered by the user when creating their account.
+ * @return A {@link User.Builder}.
+ */
+ public Builder setEmail(String email) {
+ this.email = email;
+ return this;
+ }
+
+ /**
+ * Set external urls of the user to be built.
+ *
+ * @param externalUrls Known external URLs for this user.
+ * @return A {@link User.Builder}.
+ */
+ public Builder setExternalUrls(ExternalUrl externalUrls) {
+ this.externalUrls = externalUrls;
+ return this;
+ }
+
+ /**
+ * Set the followers object of the user to be built.
+ *
+ * @param followers Information about the followers of the user.
+ * @return A {@link User.Builder}.
+ */
+ public Builder setFollowers(Followers followers) {
+ this.followers = followers;
+ return this;
+ }
+
+ /**
+ * Set href of Spotify api endpoint of the user to be built.
+ *
+ * @param href A link to the Spotify Web API endpoint for this user.
+ * @return A {@link User.Builder}.
+ */
+ public Builder setHref(String href) {
+ this.href = href;
+ return this;
+ }
+
+ /**
+ * Set user ID of the user to be built.
+ *
+ * @param id The <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify user ID
+ * </a> for the user.
+ * @return A {@link User.Builder}.
+ */
+ public Builder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * Set the profile image of the user to be built.
+ *
+ * @param images The user's profile image.
+ * @return A {@link User.Builder}.
+ */
+ public Builder setImages(Image... images) {
+ this.images = images;
+ return this;
+ }
+
+ /**
+ * Set the product type of the user to be built.
+ *
+ * @param product The user's Spotify subscription level: "premium", "free", etc.
+ * @return A {@link User.Builder}.
+ */
+ public Builder setProduct(ProductType product) {
+ this.product = product;
+ return this;
+ }
+
+ /**
+ * Set the type of the model object. In this case "user".
+ *
+ * @param type The object type: "user"
+ * @return A {@link User.Builder}.
+ */
+ public Builder setType(ModelObjectType type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Set the Spotify URI of the user to be built.
+ *
+ * @param uri The Spotify URI for the user.
+ * @return A {@link User.Builder}.
+ */
+ public Builder setUri(String uri) {
+ this.uri = uri;
+ return this;
+ }
+
+ @Override
+ public User build() {
+ return new User(this);
+ }
+ }
+
+ /**
+ * JsonUtil class for building {@link User} instances.
+ */
+ public static final class JsonUtil extends AbstractModelObject.JsonUtil<User> {
+ public User createModelObject(JsonObject jsonObject) {
+ if (jsonObject == null || jsonObject.isJsonNull()) {
+ return null;
+ }
+
+ return new User.Builder()
+ .setBirthdate(
+ hasAndNotNull(jsonObject, "birthdate")
+ ? jsonObject.get("birthdate").getAsString()
+ : null)
+ .setCountry(
+ hasAndNotNull(jsonObject, "country")
+ ? CountryCode.getByCode(
+ jsonObject.get("country").getAsString())
+ : null)
+ .setDisplayName(
+ hasAndNotNull(jsonObject, "display_name")
+ ? jsonObject.get("display_name").getAsString()
+ : null)
+ .setEmail(
+ hasAndNotNull(jsonObject, "email")
+ ? jsonObject.get("email").getAsString()
+ : null)
+ .setExternalUrls(
+ hasAndNotNull(jsonObject, "external_urls")
+ ? new ExternalUrl.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("external_urls"))
+ : null)
+ .setFollowers(
+ hasAndNotNull(jsonObject, "followers")
+ ? new Followers.JsonUtil().createModelObject(
+ jsonObject.getAsJsonObject("followers"))
+ : null)
+ .setHref(
+ hasAndNotNull(jsonObject, "href")
+ ? jsonObject.get("href").getAsString()
+ : null)
+ .setId(
+ hasAndNotNull(jsonObject, "id")
+ ? jsonObject.get("id").getAsString()
+ : null)
+ .setImages(
+ hasAndNotNull(jsonObject, "images")
+ ? new Image.JsonUtil().createModelObjectArray(
+ jsonObject.getAsJsonArray("images"))
+ : null)
+ .setProduct(
+ hasAndNotNull(jsonObject, "product")
+ ? ProductType.keyOf(
+ jsonObject.get("product").getAsString().toLowerCase())
+ : null)
+ .setType(
+ hasAndNotNull(jsonObject, "type")
+ ? ModelObjectType.keyOf(
+ jsonObject.get("type").getAsString().toLowerCase())
+ : null)
+ .setUri(
+ hasAndNotNull(jsonObject, "uri")
+ ? jsonObject.get("uri").getAsString()
+ : null)
+ .build();
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/AbstractRequest.java
@@ -0,0 +1,329 @@
+package com.wrapper.spotify.requests;
+
+import com.google.gson.*;
+import com.wrapper.spotify.IHttpManager;
+import com.wrapper.spotify.SpotifyApi;
+import com.wrapper.spotify.SpotifyApiThreading;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
+import org.apache.hc.core5.http.*;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.message.BasicHeader;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
+import org.apache.hc.core5.net.URIBuilder;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.logging.Level;
+import java.util.stream.Collectors;
+
+public abstract class AbstractRequest<T> implements IRequest<T> {
+
+ private final IHttpManager httpManager;
+ private final List<Header> headers;
+ private final ContentType contentType;
+ private final List<NameValuePair> bodyParameters;
+ private URI uri;
+ private HttpEntity body;
+
+ protected AbstractRequest(Builder<T, ?> builder) {
+ assert (builder != null);
+ assert (builder.path != null);
+ assert (!builder.path.equals(""));
+
+ this.httpManager = builder.httpManager;
+
+ URIBuilder uriBuilder = new URIBuilder();
+ uriBuilder
+ .setScheme(builder.scheme)
+ .setHost(builder.host)
+ .setPort(builder.port)
+ .setPath(builder.path);
+ if (builder.queryParameters.size() > 0) {
+ uriBuilder
+ .setParameters(builder.queryParameters);
+ }
+
+ try {
+ this.uri = uriBuilder.build();
+ } catch (URISyntaxException e) {
+ SpotifyApi.LOGGER.log(Level.SEVERE, e.getMessage());
+ }
+
+ this.headers = builder.headers;
+ this.contentType = builder.contentType;
+ this.body = builder.body;
+ this.bodyParameters = builder.bodyParameters;
+ }
+
+ /**
+ * Get something asynchronously.
+ *
+ * @return A {@link CompletableFuture} for a generic.
+ */
+ public CompletableFuture<T> executeAsync() {
+ return SpotifyApiThreading.executeAsync(
+ this::execute);
+ }
+
+ public void initializeBody() {
+ if (body == null && contentType != null) {
+ switch (contentType.getMimeType()) {
+ case "application/json":
+ body = new StringEntity(
+ bodyParametersToJson(bodyParameters),
+ ContentType.APPLICATION_JSON);
+ break;
+ case "application/x-www-form-urlencoded":
+ body = new UrlEncodedFormEntity(bodyParameters);
+ break;
+ }
+ }
+ }
+
+ public String bodyParametersToJson(List<NameValuePair> bodyParameters) {
+ JsonObject jsonObject = new JsonObject();
+ JsonElement jsonElement;
+
+ for (NameValuePair nameValuePair : bodyParameters) {
+ try {
+ jsonElement = JsonParser.parseString(nameValuePair.getValue());
+ } catch (JsonSyntaxException e) {
+ jsonElement = new JsonPrimitive(nameValuePair.getValue());
+ }
+
+ jsonObject.add(nameValuePair.getName(), jsonElement);
+ }
+
+ return jsonObject.toString();
+ }
+
+ public String getJson() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+
+ String json = httpManager.get(uri, headers.toArray(new Header[0]));
+
+ return returnJson(json);
+ }
+
+ public String postJson() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ initializeBody();
+
+ String json = httpManager.post(uri, headers.toArray(new Header[0]), body);
+
+ return returnJson(json);
+ }
+
+ public String putJson() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ initializeBody();
+
+ String json = httpManager.put(uri, headers.toArray(new Header[0]), body);
+
+ return returnJson(json);
+ }
+
+ public String deleteJson() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ initializeBody();
+
+ String json = httpManager.delete(uri, headers.toArray(new Header[0]), body);
+
+ return returnJson(json);
+ }
+
+ private String returnJson(String json) {
+ if (json == null) {
+ SpotifyApi.LOGGER.log(
+ Level.FINE,
+ "The httpManager returned json == null.");
+ return null;
+ } else if (json.equals("")) {
+ SpotifyApi.LOGGER.log(
+ Level.FINE,
+ "The httpManager returned json == \"\".");
+ return null;
+ } else {
+ SpotifyApi.LOGGER.log(
+ Level.FINE,
+ "The httpManager returned json == " + json + ".");
+ return json;
+ }
+ }
+
+ public IHttpManager getHttpManager() {
+ return httpManager;
+ }
+
+ public URI getUri() {
+ return uri;
+ }
+
+ public List<Header> getHeaders() {
+ return headers;
+ }
+
+ public ContentType getContentType() {
+ return contentType;
+ }
+
+ public HttpEntity getBody() {
+ return body;
+ }
+
+ public List<NameValuePair> getBodyParameters() {
+ return bodyParameters;
+ }
+
+ public static abstract class Builder<T, BT extends Builder<T, ?>> implements IRequest.Builder<T, BT> {
+
+ private final List<NameValuePair> pathParameters = new ArrayList<>();
+ private final List<NameValuePair> queryParameters = new ArrayList<>();
+ private final List<Header> headers = new ArrayList<>();
+ private final List<NameValuePair> bodyParameters = new ArrayList<>();
+ private IHttpManager httpManager = SpotifyApi.DEFAULT_HTTP_MANAGER;
+ private String scheme = SpotifyApi.DEFAULT_SCHEME;
+ private String host = SpotifyApi.DEFAULT_HOST;
+ private Integer port = SpotifyApi.DEFAULT_PORT;
+ private String path = null;
+ private ContentType contentType = null;
+ private HttpEntity body = null;
+
+ protected Builder() {
+ }
+
+ public BT setHttpManager(final IHttpManager httpManager) {
+ assert (httpManager != null);
+ this.httpManager = httpManager;
+ return self();
+ }
+
+ public BT setScheme(final String scheme) {
+ assert (scheme != null);
+ assert (!scheme.equals(""));
+ this.scheme = scheme;
+ return self();
+ }
+
+ public BT setHost(final String host) {
+ assert (host != null);
+ assert (!scheme.equals(""));
+ this.host = host;
+ return self();
+ }
+
+ public BT setPort(final Integer port) {
+ assert (port != null);
+ assert (port >= 0);
+ this.port = port;
+ return self();
+ }
+
+ public BT setPath(final String path) {
+ assert (path != null);
+ assert (!path.equals(""));
+
+ String builtPath = path;
+
+ for (NameValuePair nameValuePair : pathParameters) {
+ // Don't remove the "\\" before the "}" to prevent a regex issue on Android.
+ String key = "\\{" + nameValuePair.getName() + "\\}";
+ builtPath = builtPath.replaceAll(key, nameValuePair.getValue());
+ }
+
+ this.path = builtPath;
+ return self();
+ }
+
+ public BT setPathParameter(final String name, final String value) {
+ assert (name != null && value != null);
+ assert (!name.equals("") && !value.equals(""));
+
+ listAddOnce(this.pathParameters, new BasicNameValuePair(name, value));
+ return self();
+ }
+
+ public BT setDefaults(final IHttpManager httpManager,
+ final String scheme,
+ final String host,
+ final Integer port) {
+ setHttpManager(httpManager);
+ setScheme(scheme);
+ setHost(host);
+ setPort(port);
+
+ return self();
+ }
+
+ public <X> BT setQueryParameter(final String name, final X value) {
+ assert (name != null);
+ assert (!name.equals(""));
+ assert (value != null);
+ listAddOnce(this.queryParameters, new BasicNameValuePair(name, String.valueOf(value)));
+ return self();
+ }
+
+ public <X> BT setHeader(final String name, final X value) {
+ assert (name != null);
+ assert (!name.equals(""));
+ assert (value != null);
+ listAddOnce(this.headers, new BasicHeader(name, String.valueOf(value)));
+ return self();
+ }
+
+ public BT setContentType(final ContentType contentType) {
+ this.contentType = contentType;
+ setHeader("Content-Type", contentType.getMimeType());
+ return self();
+ }
+
+ public BT setBody(final HttpEntity httpEntity) {
+ this.body = httpEntity;
+ return self();
+ }
+
+ public <X> BT setBodyParameter(final String name, final X value) {
+ assert (name != null);
+ assert (!name.equals(""));
+ assert (value != null);
+ listAddOnce(this.bodyParameters, new BasicNameValuePair(name, String.valueOf(value)));
+ return self();
+ }
+
+ private void listAddOnce(List<NameValuePair> nameValuePairs, NameValuePair newNameValuePair) {
+ nameValuePairs.removeAll(nameValuePairs.stream()
+ .filter(nameValuePair -> nameValuePair.getName().equals(newNameValuePair.getName()))
+ .collect(Collectors.toList()));
+ nameValuePairs.add(newNameValuePair);
+ }
+
+ private void listAddOnce(List<Header> headers, Header newHeader) {
+ headers.removeAll(headers.stream()
+ .filter(header -> header.getName().equals(newHeader.getName()))
+ .collect(Collectors.toList()));
+ headers.add(newHeader);
+ }
+
+ /**
+ * Return this instance to simulate a self-type.
+ *
+ * @return This instance.
+ */
+ protected abstract BT self();
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/IRequest.java
@@ -0,0 +1,86 @@
+package com.wrapper.spotify.requests;
+
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+import com.wrapper.spotify.IHttpManager;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import org.apache.hc.core5.http.*;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+public interface IRequest<T> {
+
+ IHttpManager getHttpManager();
+
+ URI getUri();
+
+ List<Header> getHeaders();
+
+ ContentType getContentType();
+
+ HttpEntity getBody();
+
+ List<NameValuePair> getBodyParameters();
+
+ T execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException;
+
+ CompletableFuture<T> executeAsync();
+
+ String getJson() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException;
+
+ String postJson() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException;
+
+ String putJson() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException;
+
+ String deleteJson() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException;
+
+ @JsonPOJOBuilder(withPrefix = "set")
+ interface Builder<T, BT extends Builder<T, ?>> {
+
+ BT setHttpManager(final IHttpManager httpManager);
+
+ BT setScheme(final String scheme);
+
+ BT setHost(final String host);
+
+ BT setPort(final Integer port);
+
+ BT setPath(final String path);
+
+ BT setPathParameter(final String name, final String value);
+
+ BT setDefaults(final IHttpManager httpManager,
+ final String scheme,
+ final String host,
+ final Integer port);
+
+ <ST> BT setQueryParameter(final String name, final ST value);
+
+ <ST> BT setHeader(final String name, final ST value);
+
+ BT setContentType(final ContentType contentType);
+
+ BT setBody(final HttpEntity httpEntity);
+
+ <ST> BT setBodyParameter(final String name, final ST value);
+
+ IRequest<T> build();
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/authorization/AbstractAuthorizationRequest.java
@@ -0,0 +1,23 @@
+package com.wrapper.spotify.requests.authorization;
+
+import com.wrapper.spotify.Base64;
+import com.wrapper.spotify.requests.AbstractRequest;
+
+public abstract class AbstractAuthorizationRequest<T> extends AbstractRequest<T> {
+ protected AbstractAuthorizationRequest(final Builder<T, ?> builder) {
+ super(builder);
+ }
+
+ public static abstract class Builder<T, BT extends Builder<T, ?>> extends AbstractRequest.Builder<T, BT> {
+ protected Builder(final String clientId, final String clientSecret) {
+ super();
+
+ assert (clientId != null);
+ assert (clientSecret != null);
+ assert (!clientId.equals(""));
+ assert (!clientSecret.equals(""));
+
+ setHeader("Authorization", "Basic " + Base64.encode((clientId + ":" + clientSecret).getBytes()));
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/authorization/authorization_code/AuthorizationCodeRefreshRequest.java
@@ -0,0 +1,92 @@
+package com.wrapper.spotify.requests.authorization.authorization_code;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.SpotifyApi;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.credentials.AuthorizationCodeCredentials;
+import com.wrapper.spotify.requests.authorization.AbstractAuthorizationRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Refresh your access token by creating an
+ * <a href="https://developer.spotify.com/web-api/authorization-guide/#request-access-token-from-refresh-token">
+ * Authorization Code Refresh</a> request.
+ */
+@JsonDeserialize(builder = AuthorizationCodeRefreshRequest.Builder.class)
+public class AuthorizationCodeRefreshRequest extends AbstractAuthorizationRequest<AuthorizationCodeCredentials> {
+
+ private AuthorizationCodeRefreshRequest(Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Request new {@link AuthorizationCodeCredentials}.
+ *
+ * @return An {@link AuthorizationCodeCredentials} object containing a new/refreshed access code.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public AuthorizationCodeCredentials execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new AuthorizationCodeCredentials.JsonUtil().createModelObject(postJson());
+ }
+
+ /**
+ * Builder class for building an {@link AuthorizationCodeRefreshRequest}.
+ */
+ public static final class Builder extends AbstractAuthorizationRequest.Builder<AuthorizationCodeCredentials, Builder> {
+
+ public Builder(final String clientId, final String clientSecret) {
+ super(clientId, clientSecret);
+ }
+
+ /**
+ * The grant type setter.
+ *
+ * @param grant_type Required. Set it to {@code "refresh_token"}
+ * @return An {@link AuthorizationCodeRefreshRequest.Builder}.
+ */
+ public Builder grant_type(final String grant_type) {
+ assert (grant_type != null);
+ assert (grant_type.equals("refresh_token"));
+ return setBodyParameter("grant_type", grant_type);
+ }
+
+ /**
+ * The refresh token setter.
+ *
+ * @param refresh_token Required. The refresh token returned from the authorization code exchange.
+ * @return An {@link AuthorizationCodeRefreshRequest.Builder}.
+ */
+ public Builder refresh_token(final String refresh_token) {
+ assert (refresh_token != null);
+ assert (!refresh_token.equals(""));
+ return setBodyParameter("refresh_token", refresh_token);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return An {@link AuthorizationCodeRefreshRequest}.
+ */
+ public AuthorizationCodeRefreshRequest build() {
+ setContentType(ContentType.APPLICATION_FORM_URLENCODED);
+ setHost(SpotifyApi.DEFAULT_AUTHENTICATION_HOST);
+ setPort(SpotifyApi.DEFAULT_AUTHENTICATION_PORT);
+ setScheme(SpotifyApi.DEFAULT_AUTHENTICATION_SCHEME);
+ setPath("/api/token");
+
+ return new AuthorizationCodeRefreshRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/authorization/authorization_code/AuthorizationCodeRequest.java
@@ -0,0 +1,108 @@
+package com.wrapper.spotify.requests.authorization.authorization_code;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.SpotifyApi;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.credentials.AuthorizationCodeCredentials;
+import com.wrapper.spotify.requests.authorization.AbstractAuthorizationRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+import java.net.URI;
+
+/**
+ * Request an access token and refresh token by creating an
+ * <a href="https://developer.spotify.com/web-api/authorization-guide/#authorization_code_flow">Authorization Code</a>
+ * request.
+ */
+@JsonDeserialize(builder = AuthorizationCodeRequest.Builder.class)
+public class AuthorizationCodeRequest extends AbstractAuthorizationRequest<AuthorizationCodeCredentials> {
+
+ private AuthorizationCodeRequest(Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Request new {@link AuthorizationCodeCredentials}.
+ *
+ * @return An {@link AuthorizationCodeCredentials} object containing an access token and refresh token.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public AuthorizationCodeCredentials execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new AuthorizationCodeCredentials.JsonUtil().createModelObject(postJson());
+ }
+
+ /**
+ * Builder class for building an {@link AuthorizationCodeRequest}.
+ */
+ public static final class Builder extends AbstractAuthorizationRequest.Builder<AuthorizationCodeCredentials, Builder> {
+
+ public Builder(final String clientId, final String clientSecret) {
+ super(clientId, clientSecret);
+ }
+
+ /**
+ * The grant type setter.
+ *
+ * @param grant_type Required. As defined in the OAuth 2.0 specification, this field must contain the value
+ * {@code "authorization_code"}
+ * @return An {@link AuthorizationCodeRequest.Builder}.
+ */
+ public Builder grant_type(final String grant_type) {
+ assert (grant_type != null);
+ assert (grant_type.equals("authorization_code"));
+ return setBodyParameter("grant_type", grant_type);
+ }
+
+ /**
+ * The authorization code setter.
+ *
+ * @param code Required. The authorization code returned from the initial request to the Account's /authorize
+ * endpoint.
+ * @return An {@link AuthorizationCodeRequest.Builder}.
+ */
+ public Builder code(final String code) {
+ assert (code != null);
+ assert (!code.equals(""));
+ return setBodyParameter("code", code);
+ }
+
+ /**
+ * The redirect URI setter.
+ *
+ * @param redirect_uri Required. This parameter is used for validation only (there is no actual redirection). The
+ * value of this parameter must exactly match the value of {@code redirect_uri} supplied when
+ * requesting the authorization code.
+ * @return An {@link AuthorizationCodeRequest.Builder}.
+ */
+ public Builder redirect_uri(final URI redirect_uri) {
+ assert (redirect_uri != null);
+ return setBodyParameter("redirect_uri", redirect_uri.toString());
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return An {@link AuthorizationCodeRequest}.
+ */
+ public AuthorizationCodeRequest build() {
+ setContentType(ContentType.APPLICATION_FORM_URLENCODED);
+ setHost(SpotifyApi.DEFAULT_AUTHENTICATION_HOST);
+ setPort(SpotifyApi.DEFAULT_AUTHENTICATION_PORT);
+ setScheme(SpotifyApi.DEFAULT_AUTHENTICATION_SCHEME);
+ setPath("/api/token");
+
+ return new AuthorizationCodeRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/authorization/authorization_code/AuthorizationCodeUriRequest.java
@@ -0,0 +1,174 @@
+package com.wrapper.spotify.requests.authorization.authorization_code;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.SpotifyApi;
+import com.wrapper.spotify.requests.AbstractRequest;
+
+import java.net.URI;
+
+/**
+ * Request an authorization code by creating an
+ * <a href="https://developer.spotify.com/web-api/authorization-guide/#authorization_code_flow">Authorization Code
+ * URI</a> request.
+ */
+@JsonDeserialize(builder = AuthorizationCodeUriRequest.Builder.class)
+public class AuthorizationCodeUriRequest extends AbstractRequest<URI> {
+
+ private AuthorizationCodeUriRequest(Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Request an authorization URI.
+ *
+ * @return An authorization URI.
+ */
+ public URI execute() {
+ return this.getUri();
+ }
+
+ /**
+ * Builder class for building an {@link AuthorizationCodeUriRequest}.
+ */
+ public static final class Builder extends AbstractRequest.Builder<URI, Builder> {
+
+ public Builder() {
+ super();
+ }
+
+ /**
+ * The client ID setter.
+ *
+ * @param client_id Required. The client ID provided to you by Spotify when you register your application.
+ * @return An {@link AuthorizationCodeUriRequest.Builder}.
+ */
+ public Builder client_id(final String client_id) {
+ assert (client_id != null);
+ assert (!client_id.equals(""));
+ return setQueryParameter("client_id", client_id);
+ }
+
+ /**
+ * The response type setter.
+ *
+ * @param response_type Required. Set it to {@code "code"}.
+ * @return An {@link AuthorizationCodeUriRequest.Builder}.
+ */
+ public Builder response_type(final String response_type) {
+ assert (response_type != null);
+ assert (response_type.equals("code"));
+ return setQueryParameter("response_type", response_type);
+ }
+
+ /**
+ * The redirect URI setter.
+ *
+ * @param redirect_uri Required. The URI to redirect to after the user grants/denies permission. This URI needs to
+ * have been entered in the Redirect URI whitelist that you specified when you registered your
+ * application. The value of {@code redirect_uri} here must exactly match one of the values you
+ * entered when you registered your application, including upper/lowercase, terminating slashes,
+ * etc.
+ * @return An {@link AuthorizationCodeUriRequest.Builder}.
+ */
+ public Builder redirect_uri(final URI redirect_uri) {
+ assert (redirect_uri != null);
+ return setQueryParameter("redirect_uri", redirect_uri.toString());
+ }
+
+ /**
+ * The code challenge method setter.
+ *
+ * @param code_challenge_method Required if the Proof Key for Code Exchange (PKCE) flow is used. Set it to {@code "S256"}.
+ * @return An {@link AuthorizationCodeUriRequest.Builder}.
+ */
+ public Builder code_challenge_method(String code_challenge_method) {
+ assert (code_challenge_method != null);
+ assert (code_challenge_method.equals("S256"));
+ return setQueryParameter("code_challenge_method", code_challenge_method);
+ }
+
+ /**
+ * The code challenge setter.
+ *
+ * @param code_challenge Required if the Proof Key for Code Exchange (PKCE) flow is used.
+ * The code challenge that your app calculated beforehand.
+ * The code challenge is the base64url encoded sha256-hash of the code verifier,
+ * which is a cryptographically random string between 43 and 128 characters in length.
+ * It can contain letters, digits, underscores, periods, hyphens, or tildes and is generated
+ * by your app before each authentication request.
+ * @return An {@link AuthorizationCodeUriRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow-with-proof-key-for-code-exchange-pkce">
+ * Authorization Code Flow with Proof Key for Code Exchange (PKCE)</a>
+ */
+ public Builder code_challenge(String code_challenge) {
+ assert (code_challenge != null);
+ assert (!code_challenge.equals(""));
+ return setQueryParameter("code_challenge", code_challenge);
+ }
+
+ /**
+ * The state setter.
+ *
+ * @param state Optional, but strongly recommended. The state can be useful for correlating requests and responses.
+ * Because your {@code redirect_uri} can be guessed, using a state value can increase your assurance
+ * that an incoming connection is the result of an authentication request. If you generate a random
+ * string or encode the hash of some client state (e.g., a cookie) in this state variable, you can
+ * validate the response to additionally ensure that the request and response originated in the same
+ * browser. This provides protection against attacks such as cross-site request forgery.
+ * @return An {@link AuthorizationCodeUriRequest.Builder}.
+ * @see <a href="https://tools.ietf.org/html/rfc6749#section-10.12">RFC 6749: Cross-Site Request Forgery</a>
+ */
+ public Builder state(final String state) {
+ assert (state != null);
+ assert (!state.equals(""));
+ return setQueryParameter("state", state);
+ }
+
+ /**
+ * The scope setter.
+ *
+ * @param scope Optional. A space-separated list of scopes. If no scopes are specified, authorization will be
+ * granted only to access publicly available information: that is, only information normally visible in
+ * the Spotify desktop, web and mobile players.
+ * @return An {@link AuthorizationCodeUriRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/spotify-web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder scope(final String scope) {
+ assert (scope != null);
+ assert (!scope.equals(""));
+ return setQueryParameter("scope", scope);
+ }
+
+ /**
+ * The show dialog setter.
+ *
+ * @param show_dialog Optional. Whether or not to force the user to approve the app again if they’ve already done
+ * so. If {@code false} (default), a user who has already approved the application may be
+ * automatically redirected to the URI specified by {@code redirect_uri}. If {@code true}, the
+ * user will not be automatically redirected and will have to approve the app again.
+ * @return An {@link AuthorizationCodeUriRequest.Builder}.
+ */
+ public Builder show_dialog(final boolean show_dialog) {
+ return setQueryParameter("show_dialog", show_dialog);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return An {@link AuthorizationCodeUriRequest}.
+ */
+ public AuthorizationCodeUriRequest build() {
+ setHost(SpotifyApi.DEFAULT_AUTHENTICATION_HOST);
+ setPort(SpotifyApi.DEFAULT_AUTHENTICATION_PORT);
+ setScheme(SpotifyApi.DEFAULT_AUTHENTICATION_SCHEME);
+ setPath("/authorize");
+
+ return new AuthorizationCodeUriRequest(this);
+ }
+
+ @Override
+ protected AuthorizationCodeUriRequest.Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/authorization/authorization_code/pkce/AuthorizationCodePKCERefreshRequest.java
@@ -0,0 +1,105 @@
+package com.wrapper.spotify.requests.authorization.authorization_code.pkce;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.SpotifyApi;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.credentials.AuthorizationCodeCredentials;
+import com.wrapper.spotify.requests.AbstractRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Refresh your access token by creating an
+ * <a href="https://developer.spotify.com/documentation/general/guides/authorization-guide/#6-requesting-a-refreshed-access-token">
+ * Authorization Code Refresh</a> request.
+ */
+@JsonDeserialize(builder = AuthorizationCodePKCERefreshRequest.Builder.class)
+public class AuthorizationCodePKCERefreshRequest extends AbstractRequest<AuthorizationCodeCredentials> {
+
+ private AuthorizationCodePKCERefreshRequest(Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Request new {@link AuthorizationCodeCredentials}.
+ *
+ * @return An {@link AuthorizationCodeCredentials} object containing a new/refreshed access code.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public AuthorizationCodeCredentials execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new AuthorizationCodeCredentials.JsonUtil().createModelObject(postJson());
+ }
+
+
+ /**
+ * Builder class for building an {@link AuthorizationCodePKCERefreshRequest}.
+ */
+ public static final class Builder extends AbstractRequest.Builder<AuthorizationCodeCredentials, Builder> {
+
+ public Builder() {
+ super();
+ }
+
+ /**
+ * The grant type setter.
+ *
+ * @param grant_type Required. Set it to {@code "refresh_token"}
+ * @return An {@link AuthorizationCodePKCERefreshRequest.Builder}.
+ */
+ public Builder grant_type(final String grant_type) {
+ assert (grant_type != null);
+ assert (grant_type.equals("refresh_token"));
+ return setBodyParameter("grant_type", grant_type);
+ }
+
+ /**
+ * The refresh token setter.
+ *
+ * @param refresh_token Required. The refresh token returned from the authorization code exchange.
+ * @return An {@link AuthorizationCodePKCERefreshRequest.Builder}.
+ */
+ public Builder refresh_token(final String refresh_token) {
+ assert (refresh_token != null);
+ assert (!refresh_token.equals(""));
+ return setBodyParameter("refresh_token", refresh_token);
+ }
+
+ /**
+ * The client ID setter.
+ *
+ * @param client_id Required. The client ID for your app, available from the developer dashboard.
+ * @return An {@link AuthorizationCodePKCERefreshRequest.Builder}.
+ */
+ public Builder client_id(final String client_id) {
+ assert (client_id != null);
+ assert (!client_id.equals(""));
+ return setBodyParameter("client_id", client_id);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return An {@link AuthorizationCodePKCERefreshRequest}.
+ */
+ public AuthorizationCodePKCERefreshRequest build() {
+ setContentType(ContentType.APPLICATION_FORM_URLENCODED);
+ setHost(SpotifyApi.DEFAULT_AUTHENTICATION_HOST);
+ setPort(SpotifyApi.DEFAULT_AUTHENTICATION_PORT);
+ setScheme(SpotifyApi.DEFAULT_AUTHENTICATION_SCHEME);
+ setPath("/api/token");
+
+ return new AuthorizationCodePKCERefreshRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/authorization/authorization_code/pkce/AuthorizationCodePKCERequest.java
@@ -0,0 +1,132 @@
+package com.wrapper.spotify.requests.authorization.authorization_code.pkce;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.SpotifyApi;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.credentials.AuthorizationCodeCredentials;
+import com.wrapper.spotify.requests.AbstractRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+import java.net.URI;
+
+/**
+ * Request an access token by exchanging the authorization code for an access token with an
+ * <a href="https://developer.spotify.com/documentation/general/guides/authorization-guide/#4-your-app-exchanges-the-code-for-an-access-token">Authorization Code</a>
+ * request.
+ */
+@JsonDeserialize(builder = AuthorizationCodePKCERequest.Builder.class)
+public class AuthorizationCodePKCERequest extends AbstractRequest<AuthorizationCodeCredentials> {
+
+ private AuthorizationCodePKCERequest(Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Request new {@link AuthorizationCodeCredentials}.
+ *
+ * @return An {@link AuthorizationCodeCredentials} object containing an access token and refresh token.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public AuthorizationCodeCredentials execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new AuthorizationCodeCredentials.JsonUtil().createModelObject(postJson());
+ }
+
+ public static final class Builder extends AbstractRequest.Builder<AuthorizationCodeCredentials, Builder> {
+
+ public Builder() {
+ super();
+ }
+
+ /**
+ * The client ID setter.
+ *
+ * @param client_id Required. The client ID for your app, available from the developer dashboard.
+ * @return An {@link AuthorizationCodePKCERequest.Builder}.
+ */
+ public Builder client_id(final String client_id) {
+ assert (client_id != null);
+ assert (!client_id.equals(""));
+ return setBodyParameter("client_id", client_id);
+ }
+
+ /**
+ * The grant type setter.
+ *
+ * @param grant_type Required. As defined in the OAuth 2.0 specification, this field must contain the value
+ * {@code "authorization_code"}
+ * @return An {@link AuthorizationCodePKCERequest.Builder}.
+ */
+ public Builder grant_type(final String grant_type) {
+ assert (grant_type != null);
+ assert (grant_type.equals("authorization_code"));
+ return setBodyParameter("grant_type", grant_type);
+ }
+
+ /**
+ * The authorization code setter.
+ *
+ * @param code Required. The authorization code returned from the initial request to the Account's /authorize
+ * endpoint.
+ * @return An {@link AuthorizationCodePKCERequest.Builder}.
+ */
+ public Builder code(final String code) {
+ assert (code != null);
+ assert (!code.equals(""));
+ return setBodyParameter("code", code);
+ }
+
+ /**
+ * The redirect URI setter.
+ *
+ * @param redirect_uri Required. This parameter is used for validation only (there is no actual redirection). The
+ * value of this parameter must exactly match the value of {@code redirect_uri} supplied when
+ * requesting the authorization code.
+ * @return An {@link AuthorizationCodePKCERequest.Builder}.
+ */
+ public Builder redirect_uri(final URI redirect_uri) {
+ assert (redirect_uri != null);
+ return setBodyParameter("redirect_uri", redirect_uri.toString());
+ }
+
+ /**
+ * The code verifier setter.
+ *
+ * @param code_verifier Required. The value of this parameter must match the value of the code_verifier that your app generated beforehand.
+ * @return An {@link AuthorizationCodePKCERequest.Builder}.
+ * @see <a href="https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow-with-proof-key-for-code-exchange-pkce">
+ * Authorization Code Flow with Proof Key for Code Exchange (PKCE)</a>
+ */
+ public Builder code_verifier(String code_verifier) {
+ assert (code_verifier != null);
+ assert (!code_verifier.equals(""));
+ return setBodyParameter("code_verifier", code_verifier);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return An {@link AuthorizationCodePKCERequest}.
+ */
+ public AuthorizationCodePKCERequest build() {
+ setContentType(ContentType.APPLICATION_FORM_URLENCODED);
+ setHost(SpotifyApi.DEFAULT_AUTHENTICATION_HOST);
+ setPort(SpotifyApi.DEFAULT_AUTHENTICATION_PORT);
+ setScheme(SpotifyApi.DEFAULT_AUTHENTICATION_SCHEME);
+ setPath("/api/token");
+
+ return new AuthorizationCodePKCERequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/authorization/client_credentials/ClientCredentialsRequest.java
@@ -0,0 +1,80 @@
+package com.wrapper.spotify.requests.authorization.client_credentials;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.SpotifyApi;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.credentials.ClientCredentials;
+import com.wrapper.spotify.requests.authorization.AbstractAuthorizationRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Request an access token by creating a
+ * <a href="https://developer.spotify.com/web-api/authorization-guide/#client-credentials-flow">Client Credentials</a>
+ * request.
+ */
+@JsonDeserialize(builder = ClientCredentialsRequest.Builder.class)
+public class ClientCredentialsRequest extends AbstractAuthorizationRequest<ClientCredentials> {
+
+ public ClientCredentialsRequest(Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Request an access token.
+ *
+ * @return A {@link ClientCredentials} object containing an access token.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public ClientCredentials execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new ClientCredentials.JsonUtil().createModelObject(postJson());
+ }
+
+ /**
+ * Builder class for building a {@link ClientCredentialsRequest}.
+ */
+ public static final class Builder extends AbstractAuthorizationRequest.Builder<ClientCredentials, Builder> {
+
+ public Builder(final String clientId, final String clientSecret) {
+ super(clientId, clientSecret);
+ }
+
+ /**
+ * The grant type setter.
+ *
+ * @param grant_type Required. Set it to {@code "client_credentials"}.
+ * @return A {@link ClientCredentialsRequest.Builder}.
+ */
+ public Builder grant_type(final String grant_type) {
+ assert (grant_type != null);
+ assert (grant_type.equals("client_credentials"));
+ return setBodyParameter("grant_type", grant_type);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A {@link ClientCredentialsRequest}.
+ */
+ public ClientCredentialsRequest build() {
+ setContentType(ContentType.APPLICATION_FORM_URLENCODED);
+ setHost(SpotifyApi.DEFAULT_AUTHENTICATION_HOST);
+ setPort(SpotifyApi.DEFAULT_AUTHENTICATION_PORT);
+ setScheme(SpotifyApi.DEFAULT_AUTHENTICATION_SCHEME);
+ setPath("/api/token");
+
+ return new ClientCredentialsRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/AbstractDataPagingCursorbasedRequest.java
@@ -0,0 +1,19 @@
+package com.wrapper.spotify.requests.data;
+
+import com.wrapper.spotify.model_objects.specification.PagingCursorbased;
+
+public abstract class AbstractDataPagingCursorbasedRequest<T> extends AbstractDataRequest<T> {
+ protected AbstractDataPagingCursorbasedRequest(final AbstractDataRequest.Builder<T, ?> builder) {
+ super(builder);
+ }
+
+ public static abstract class Builder<T, A, BT extends Builder<T, A, ?>> extends AbstractDataRequest.Builder<PagingCursorbased<T>, BT> implements IPagingCursorbasedRequestBuilder<T, A, BT> {
+ protected Builder(String accessToken) {
+ super(accessToken);
+
+ assert (!accessToken.equals(""));
+
+ setHeader("Authorization", "Bearer " + accessToken);
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/AbstractDataPagingRequest.java
@@ -0,0 +1,21 @@
+package com.wrapper.spotify.requests.data;
+
+import com.wrapper.spotify.model_objects.specification.Paging;
+
+public abstract class AbstractDataPagingRequest<T> extends AbstractDataRequest<T> {
+ protected AbstractDataPagingRequest(final AbstractDataRequest.Builder<T, ?> builder) {
+ super(builder);
+ }
+
+ public static abstract class Builder<T, BT extends Builder<T, ?>>
+ extends AbstractDataRequest.Builder<Paging<T>, BT>
+ implements IPagingRequestBuilder<T, BT> {
+ protected Builder(String accessToken) {
+ super(accessToken);
+
+ assert (!accessToken.equals(""));
+
+ setHeader("Authorization", "Bearer " + accessToken);
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/AbstractDataRequest.java
@@ -0,0 +1,20 @@
+package com.wrapper.spotify.requests.data;
+
+import com.wrapper.spotify.requests.AbstractRequest;
+
+public abstract class AbstractDataRequest<T> extends AbstractRequest<T> {
+ protected AbstractDataRequest(final Builder<T, ?> builder) {
+ super(builder);
+ }
+
+ public static abstract class Builder<T, BT extends Builder<T, ?>> extends AbstractRequest.Builder<T, BT> {
+ protected Builder(String accessToken) {
+ super();
+
+ assert (accessToken != null);
+ assert (!accessToken.equals(""));
+
+ setHeader("Authorization", "Bearer " + accessToken);
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/IPagingCursorbasedRequestBuilder.java
@@ -0,0 +1,11 @@
+package com.wrapper.spotify.requests.data;
+
+import com.wrapper.spotify.model_objects.specification.PagingCursorbased;
+import com.wrapper.spotify.requests.IRequest;
+
+public interface IPagingCursorbasedRequestBuilder<T, A, BT extends IRequest.Builder<PagingCursorbased<T>, ?>>
+ extends IRequest.Builder<PagingCursorbased<T>, BT> {
+ BT limit(final Integer limit);
+
+ BT after(final A after);
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/IPagingRequestBuilder.java
@@ -0,0 +1,11 @@
+package com.wrapper.spotify.requests.data;
+
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.requests.IRequest;
+
+public interface IPagingRequestBuilder<T, BT extends IRequest.Builder<Paging<T>, ?>>
+ extends IRequest.Builder<Paging<T>, BT> {
+ BT limit(final Integer limit);
+
+ BT offset(final Integer offset);
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/albums/GetAlbumRequest.java
@@ -0,0 +1,99 @@
+package com.wrapper.spotify.requests.data.albums;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Album;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get Spotify catalog information for a single album.
+ */
+@JsonDeserialize(builder = GetAlbumRequest.Builder.class)
+public class GetAlbumRequest extends AbstractDataRequest<Album> {
+
+ /**
+ * The private {@link GetAlbumRequest} constructor.
+ *
+ * @param builder A {@link GetAlbumRequest.Builder}.
+ */
+ private GetAlbumRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get an {@link Album} synchronously.
+ *
+ * @return An {@link Album}.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Album execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Album.JsonUtil().createModelObject(getJson());
+ }
+
+
+ /**
+ * A builder class for a {@link GetAlbumRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<Album, Builder> {
+
+ /**
+ * Create a new {@link GetAlbumRequest.Builder} instance.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The ID path parameter setter.
+ *
+ * @param id The Spotify ID for the album.
+ * @return A {@link GetAlbumRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URIs &amp; IDs</a>
+ */
+ public Builder id(final String id) {
+ assert (id != null);
+ assert (!id.equals(""));
+ return setPathParameter("id", id);
+ }
+
+ /**
+ * The market query parameter setter.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply
+ * Track Relinking.
+ * @return A {@link GetAlbumRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ * @see <a href="https://developer.spotify.com/web-api/track-relinking-guide/">Spotify: Track Relinking Guide</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetAlbumRequest}.
+ */
+ @Override
+ public GetAlbumRequest build() {
+ setPath("/v1/albums/{id}");
+ return new GetAlbumRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/albums/GetAlbumsTracksRequest.java
@@ -0,0 +1,126 @@
+package com.wrapper.spotify.requests.data.albums;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.model_objects.specification.TrackSimplified;
+import com.wrapper.spotify.requests.data.AbstractDataPagingRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get Spotify catalog information about an album's tracks. Optional parameters can be used to limit the number of
+ * tracks returned.
+ */
+@JsonDeserialize(builder = GetAlbumsTracksRequest.Builder.class)
+public class GetAlbumsTracksRequest extends AbstractDataRequest<Paging<TrackSimplified>> {
+
+ /**
+ * The private {@link GetAlbumsTracksRequest} constructor.
+ *
+ * @param builder A {@link GetAlbumsTracksRequest.Builder}.
+ */
+ private GetAlbumsTracksRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get the tracks from the album.
+ *
+ * @return A track paging.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Paging<TrackSimplified> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new TrackSimplified.JsonUtil().createModelObjectPaging(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetAlbumsTracksRequest}.
+ */
+ public static final class Builder extends AbstractDataPagingRequest.Builder<TrackSimplified, Builder> {
+
+ /**
+ * Create a new {@link GetAlbumsTracksRequest.Builder} instance.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The ID path parameter setter.
+ *
+ * @param id The Spotify ID for the album.
+ * @return A {@link GetAlbumsTracksRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URIs &amp; IDs</a>
+ */
+ public Builder id(final String id) {
+ assert (id != null);
+ assert (!id.equals(""));
+ return setPathParameter("id", id);
+ }
+
+ /**
+ * The limit query parameter setter.
+ *
+ * @param limit Optional. The maximum number of tracks to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link GetAlbumsTracksRequest.Builder}.
+ */
+ @Override
+ public Builder limit(final Integer limit) {
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The offset query parameter setter.
+ *
+ * @param offset Optional. The index of the first track to return. Default: 0 (the first object). Use with limit to
+ * get the next set of tracks.
+ * @return A {@link GetAlbumsTracksRequest.Builder}.
+ */
+ @Override
+ public Builder offset(final Integer offset) {
+ assert (offset >= 0);
+ return setQueryParameter("offset", offset);
+ }
+
+ /**
+ * The market query parameter setter.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track
+ * Relinking.
+ * @return A {@link GetAlbumsTracksRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ * @see <a href="https://developer.spotify.com/web-api/track-relinking-guide/">Spotify: Track Relinking Guide</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetAlbumsTracksRequest}.
+ */
+ @Override
+ public GetAlbumsTracksRequest build() {
+ setPath("/v1/albums/{id}/tracks");
+ return new GetAlbumsTracksRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/albums/GetSeveralAlbumsRequest.java
@@ -0,0 +1,98 @@
+package com.wrapper.spotify.requests.data.albums;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Album;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get Spotify catalog information for multiple albums identified by their Spotify IDs.
+ */
+@JsonDeserialize(builder = GetSeveralAlbumsRequest.Builder.class)
+public class GetSeveralAlbumsRequest extends AbstractDataRequest<Album[]> {
+
+ /**
+ * The private {@link GetSeveralAlbumsRequest} constructor.
+ *
+ * @param builder A {@link GetSeveralAlbumsRequest.Builder}.
+ */
+ private GetSeveralAlbumsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get multiple albums.
+ *
+ * @return An array containing albums.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Album[] execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Album.JsonUtil().createModelObjectArray(getJson(), "albums");
+ }
+
+ /**
+ * Builder class for building a {@link GetSeveralAlbumsRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<Album[], Builder> {
+
+ /**
+ * Create a new {@link GetSeveralAlbumsRequest.Builder} instance.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The IDs query parameter setter.
+ *
+ * @param ids Required. A comma-separated list of the Spotify IDs for the albums. Maximum: 20 IDs.
+ * @return A {@link GetSeveralAlbumsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URIs &amp; IDs</a>
+ */
+ public Builder ids(final String ids) {
+ assert (ids != null);
+ assert (ids.split(",").length <= 20);
+ return setQueryParameter("ids", ids);
+ }
+
+ /**
+ * The market query parameter setter.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track
+ * Relinking.
+ * @return A {@link GetSeveralAlbumsRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ * @see <a href="https://developer.spotify.com/web-api/track-relinking-guide/">Spotify: Track Relinking Guide</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetSeveralAlbumsRequest}.
+ */
+ @Override
+ public GetSeveralAlbumsRequest build() {
+ setPath("/v1/albums");
+ return new GetSeveralAlbumsRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/artists/GetArtistRequest.java
@@ -0,0 +1,83 @@
+package com.wrapper.spotify.requests.data.artists;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Artist;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get Spotify catalog information for a single artist identified by their unique Spotify ID.
+ */
+@JsonDeserialize(builder = GetArtistRequest.Builder.class)
+public class GetArtistRequest extends AbstractDataRequest<Artist> {
+
+ /**
+ * The private {@link GetArtistRequest} constructor.
+ *
+ * @param builder A {@link GetArtistRequest.Builder}.
+ */
+ private GetArtistRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get an {@link Artist}.
+ *
+ * @return An {@link Artist}.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Artist execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Artist.JsonUtil().createModelObject(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetArtistRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<Artist, Builder> {
+
+ /**
+ * Create a new {@link GetArtistRequest.Builder} instance.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The artist ID setter.
+ *
+ * @param id The Spotify ID for the artist.
+ * @return A {@link GetArtistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URIs &amp; IDs</a>
+ */
+ public Builder id(final String id) {
+ assert (id != null);
+ assert (!id.equals(""));
+ return setPathParameter("id", id);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetArtistRequest}.
+ */
+ @Override
+ public GetArtistRequest build() {
+ setPath("/v1/artists/{id}");
+ return new GetArtistRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/artists/GetArtistsAlbumsRequest.java
@@ -0,0 +1,139 @@
+package com.wrapper.spotify.requests.data.artists;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.AlbumSimplified;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.requests.data.AbstractDataPagingRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get Spotify catalog information about an artist’s albums. Optional parameters can be specified in the query string to
+ * filter and sort the response.
+ */
+@JsonDeserialize(builder = GetArtistsAlbumsRequest.Builder.class)
+public class GetArtistsAlbumsRequest extends AbstractDataRequest<Paging<AlbumSimplified>> {
+
+ /**
+ * The private {@link GetArtistsAlbumsRequest} constructor.
+ *
+ * @param builder A {@link GetArtistsAlbumsRequest.Builder}.
+ */
+ private GetArtistsAlbumsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get the {@link AlbumSimplified} objects.
+ *
+ * @return An {@link AlbumSimplified} paging.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Paging<AlbumSimplified> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new AlbumSimplified.JsonUtil().createModelObjectPaging(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetArtistsAlbumsRequest}.
+ */
+ public static final class Builder extends AbstractDataPagingRequest.Builder<AlbumSimplified, Builder> {
+
+ /**
+ * Create a new {@link GetArtistsAlbumsRequest.Builder} instance.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The artist ID setter.
+ *
+ * @param id The Spotify ID for the artist.
+ * @return A {@link GetArtistsAlbumsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URIs &amp; IDs</a>
+ */
+ public Builder id(final String id) {
+ assert (id != null);
+ assert (!id.equals(""));
+ return setPathParameter("id", id);
+ }
+
+ /**
+ * The album type filter setter.
+ *
+ * @param album_type Optional. A comma-separated list of keywords that will be used to filter the response. If not
+ * supplied, all album types will be returned. Valid values are: {@code album}, {@code single},
+ * {@code appears_on} and {@code compilation}.
+ * @return A {@link GetArtistsAlbumsRequest.Builder}.
+ */
+ public Builder album_type(final String album_type) {
+ assert (album_type != null);
+ assert (album_type.matches("((^|,)(single|album|appears_on|compilation))+$"));
+ return setQueryParameter("album_type", album_type);
+ }
+
+ /**
+ * The market filter setter.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code. Supply this parameter to limit the response to one
+ * particular geographical market.
+ * @return A {@link GetArtistsAlbumsRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The number of album objects to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link GetArtistsAlbumsRequest.Builder}.
+ */
+ @Override
+ public Builder limit(final Integer limit) {
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The offset setter.
+ *
+ * @param offset Optional. The index of the first album to return. Default: 0 (i.e., the first album). Use with
+ * {@link #limit(Integer)} to get the next set of albums.
+ * @return A {@link GetArtistsAlbumsRequest.Builder}.
+ */
+ @Override
+ public Builder offset(final Integer offset) {
+ assert (offset >= 0);
+ return setQueryParameter("offset", offset);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetArtistsAlbumsRequest}.
+ */
+ @Override
+ public GetArtistsAlbumsRequest build() {
+ setPath("/v1/artists/{id}/albums");
+ return new GetArtistsAlbumsRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/artists/GetArtistsRelatedArtistsRequest.java
@@ -0,0 +1,84 @@
+package com.wrapper.spotify.requests.data.artists;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Artist;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get Spotify catalog information about artists similar to a given artist. Similarity is based on analysis of the
+ * Spotify community’s listening history.
+ */
+@JsonDeserialize(builder = GetArtistsRelatedArtistsRequest.Builder.class)
+public class GetArtistsRelatedArtistsRequest extends AbstractDataRequest<Artist[]> {
+
+ /**
+ * The private {@link GetArtistsRelatedArtistsRequest} constructor.
+ *
+ * @param builder A {@link GetArtistsRelatedArtistsRequest.Builder}.
+ */
+ private GetArtistsRelatedArtistsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get the related {@link Artist} objects.
+ *
+ * @return An array of {@link Artist} objects.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Artist[] execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Artist.JsonUtil().createModelObjectArray(getJson(), "artists");
+ }
+
+ /**
+ * Builder class for building a {@link GetArtistsRelatedArtistsRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<Artist[], Builder> {
+
+ /**
+ * Create a new {@link GetArtistsRelatedArtistsRequest.Builder} instance.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The artist ID setter.
+ *
+ * @param id The Spotify ID for the artist.
+ * @return A {@link GetArtistsRelatedArtistsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URIs &amp; IDs</a>
+ */
+ public Builder id(final String id) {
+ assert (id != null);
+ assert (!id.equals(""));
+ return setPathParameter("id", id);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetArtistsRelatedArtistsRequest}.
+ */
+ @Override
+ public GetArtistsRelatedArtistsRequest build() {
+ setPath("/v1/artists/{id}/related-artists");
+ return new GetArtistsRelatedArtistsRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/artists/GetArtistsTopTracksRequest.java
@@ -0,0 +1,96 @@
+package com.wrapper.spotify.requests.data.artists;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Track;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get Spotify catalog information about an artist’s top tracks by country.
+ */
+@JsonDeserialize(builder = GetArtistsTopTracksRequest.Builder.class)
+public class GetArtistsTopTracksRequest extends AbstractDataRequest<Track[]> {
+
+ /**
+ * The private {@link GetArtistsTopTracksRequest} constructor.
+ *
+ * @param builder A {@link GetArtistsTopTracksRequest.Builder}.
+ */
+ private GetArtistsTopTracksRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get the top {@link Track} objects.
+ *
+ * @return An array of {@link Track} objects.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Track[] execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Track.JsonUtil().createModelObjectArray(getJson(), "tracks");
+ }
+
+ /**
+ * Builder class for building a {@link GetArtistsTopTracksRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<Track[], Builder> {
+
+ /**
+ * Create a new {@link GetArtistsTopTracksRequest.Builder} instance.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The artist ID setter.
+ *
+ * @param id The Spotify ID for the artist.
+ * @return A {@link GetArtistsTopTracksRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URIs &amp; IDs</a>
+ */
+ public Builder id(final String id) {
+ assert (id != null);
+ assert (!id.equals(""));
+ return setPathParameter("id", id);
+ }
+
+ /**
+ * The country code setter.
+ *
+ * @param country Required. The country: an ISO 3166-1 alpha-2 country code.
+ * @return A {@link GetArtistsTopTracksRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public Builder country(final CountryCode country) {
+ assert (country != null);
+ return setQueryParameter("country", country);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetArtistsTopTracksRequest}.
+ */
+ @Override
+ public GetArtistsTopTracksRequest build() {
+ setPath("/v1/artists/{id}/top-tracks");
+ return new GetArtistsTopTracksRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/artists/GetSeveralArtistsRequest.java
@@ -0,0 +1,83 @@
+package com.wrapper.spotify.requests.data.artists;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Artist;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get Spotify catalog information for several artists based on their Spotify IDs.
+ */
+@JsonDeserialize(builder = GetSeveralArtistsRequest.Builder.class)
+public class GetSeveralArtistsRequest extends AbstractDataRequest<Artist[]> {
+
+ /**
+ * The private {@link GetSeveralArtistsRequest} constructor.
+ *
+ * @param builder A {@link GetSeveralArtistsRequest.Builder}.
+ */
+ private GetSeveralArtistsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get several {@link Artist} objects.
+ *
+ * @return An array with {@link Artist} objects.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Artist[] execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Artist.JsonUtil().createModelObjectArray(getJson(), "artists");
+ }
+
+ /**
+ * Builder class for building a {@link GetSeveralArtistsRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<Artist[], Builder> {
+
+ /**
+ * Create a new {@link GetSeveralArtistsRequest.Builder} instance.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The artist IDs setter.
+ *
+ * @param ids Required. A comma-separated list of the Spotify IDs for the artists. Maximum: 50 IDs.
+ * @return A {@link GetSeveralArtistsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URIs &amp; IDs</a>
+ */
+ public Builder ids(final String ids) {
+ assert (ids != null);
+ assert (ids.split(",").length <= 50);
+ return setQueryParameter("ids", ids);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetSeveralArtistsRequest}.
+ */
+ @Override
+ public GetSeveralArtistsRequest build() {
+ setPath("/v1/artists");
+ return new GetSeveralArtistsRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/browse/GetCategoryRequest.java
@@ -0,0 +1,124 @@
+package com.wrapper.spotify.requests.data.browse;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.neovisionaries.i18n.LanguageCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Category;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get a single category used to tag items in Spotify (on, for example, the Spotify player’s "Browse" tab).
+ */
+@JsonDeserialize(builder = GetCategoryRequest.Builder.class)
+public class GetCategoryRequest extends AbstractDataRequest<Category> {
+
+ /**
+ * The private {@link GetCategoryRequest} constructor.
+ *
+ * @param builder A {@link GetCategoryRequest.Builder}.
+ */
+ private GetCategoryRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get a {@link Category}.
+ *
+ * @return A {@link Category}.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Category execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Category.JsonUtil().createModelObject(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetCategoryRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<Category, Builder> {
+
+ /**
+ * Create a new {@link GetCategoryRequest.Builder} instance.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The category ID setter.
+ *
+ * @param category_id The Spotify category ID for the category.
+ * @return A {@link GetCategoryRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URIs &amp; IDs</a>
+ */
+ public Builder category_id(final String category_id) {
+ assert (category_id != null);
+ assert (category_id.matches("^[a-z]+$"));
+ return setPathParameter("category_id", category_id);
+ }
+
+ /**
+ * The country code setter.
+ *
+ * @param country Optional. A country: an ISO 3166-1 alpha-2 country code. Provide this parameter if you want to
+ * narrow the list of returned categories to those relevant to a particular country. If omitted, the
+ * returned items will be globally relevant.
+ * @return A {@link GetCategoryRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public Builder country(final CountryCode country) {
+ assert (country != null);
+ return setQueryParameter("country", country);
+ }
+
+ /**
+ * The language code setter.
+ *
+ * @param locale Optional. The desired language, consisting of an ISO 639 language code and an ISO 3166-1 alpha-2
+ * country code, joined by an underscore. For example: es_MX, meaning "Spanish (Mexico)". Provide this
+ * parameter if you want the category metadata returned in a particular language. Note that, if locale
+ * is not supplied, or if the specified language is not available, all strings will be returned in the
+ * Spotify default language (American English). The locale parameter, combined with the country
+ * parameter, may give odd results if not carefully matched. For example
+ * {@code country=SE&locale=de_DE} will return a list of categories relevant to Sweden but as German
+ * language strings.
+ * @return A {@link GetCategoryRequest.Builder}
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_639">Wikipedia: ISO 639 language code</a>
+ */
+ public Builder locale(final String locale) {
+ assert (locale != null);
+ assert (locale.contains("_"));
+ String[] localeParts = locale.split("_");
+ assert (localeParts.length == 2);
+ assert (LanguageCode.getByCode(localeParts[0]) != null);
+ assert (CountryCode.getByCode(localeParts[1]) != null);
+ return setQueryParameter("locale", locale);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetCategoryRequest}.
+ */
+ @Override
+ public GetCategoryRequest build() {
+ setPath("/v1/browse/categories/{category_id}");
+ return new GetCategoryRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/browse/GetCategorysPlaylistsRequest.java
@@ -0,0 +1,123 @@
+package com.wrapper.spotify.requests.data.browse;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.model_objects.specification.PlaylistSimplified;
+import com.wrapper.spotify.requests.data.AbstractDataPagingRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get a list of Spotify playlists tagged with a particular category.
+ */
+@JsonDeserialize(builder = GetCategorysPlaylistsRequest.Builder.class)
+public class GetCategorysPlaylistsRequest extends AbstractDataRequest<Paging<PlaylistSimplified>> {
+
+ /**
+ * The private {@link GetCategorysPlaylistsRequest} constructor.
+ *
+ * @param builder A {@link GetCategorysPlaylistsRequest.Builder}.
+ */
+ private GetCategorysPlaylistsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get multiple {@link PlaylistSimplified} objects.
+ *
+ * @return A {@link PlaylistSimplified} paging.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Paging<PlaylistSimplified> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new PlaylistSimplified.JsonUtil().createModelObjectPaging(getJson(), "playlists");
+ }
+
+ /**
+ * Builder class for building a {@link GetCategorysPlaylistsRequest}.
+ */
+ public static final class Builder extends AbstractDataPagingRequest.Builder<PlaylistSimplified, Builder> {
+
+ /**
+ * Create a new {@link GetCategorysPlaylistsRequest.Builder} instance.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The categroy ID setter.
+ *
+ * @param category_id The Spotify category ID for the category.
+ * @return A {@link GetCategorysPlaylistsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URIs &amp; IDs</a>
+ */
+ public Builder category_id(final String category_id) {
+ assert (category_id != null);
+ assert (category_id.matches("^[a-z]+$"));
+ return setPathParameter("category_id", category_id);
+ }
+
+ /**
+ * The country code setter.
+ *
+ * @param country Optional. A country: an ISO 3166-1 alpha-2 country code.
+ * @return A {@link GetCategorysPlaylistsRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public Builder country(final CountryCode country) {
+ assert (country != null);
+ return setQueryParameter("country", country);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link GetCategorysPlaylistsRequest.Builder}.
+ */
+ @Override
+ public Builder limit(final Integer limit) {
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The offset setter.
+ *
+ * @param offset Optional. The index of the first item to return. Default: 0 (the first object). Use with
+ * {@link #limit(Integer)} to get the next set of items.
+ * @return A {@link GetCategorysPlaylistsRequest.Builder}.
+ */
+ @Override
+ public Builder offset(final Integer offset) {
+ assert (offset >= 0);
+ return setQueryParameter("offset", offset);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetCategorysPlaylistsRequest}.
+ */
+ @Override
+ public GetCategorysPlaylistsRequest build() {
+ setPath("/v1/browse/categories/{category_id}/playlists");
+ return new GetCategorysPlaylistsRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/browse/GetListOfCategoriesRequest.java
@@ -0,0 +1,138 @@
+package com.wrapper.spotify.requests.data.browse;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.neovisionaries.i18n.LanguageCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Category;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.requests.data.AbstractDataPagingRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get a list of categories used to tag items in Spotify (on, for example, the Spotify player’s "Browse" tab).
+ */
+@JsonDeserialize(builder = GetListOfCategoriesRequest.Builder.class)
+public class GetListOfCategoriesRequest extends AbstractDataRequest<Paging<Category>> {
+
+ /**
+ * The private {@link GetListOfCategoriesRequest} constructor.
+ *
+ * @param builder A {@link GetListOfCategoriesRequest.Builder}.
+ */
+ private GetListOfCategoriesRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get a paging of {@link Category} objects.
+ *
+ * @return A {@link Category} paging.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Paging<Category> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Category.JsonUtil().createModelObjectPaging(getJson(), "categories");
+ }
+
+ /**
+ * Builder class for building a {@link GetListOfCategoriesRequest}.
+ */
+ public static final class Builder extends AbstractDataPagingRequest.Builder<Category, Builder> {
+
+ /**
+ * Create a new {@link GetListOfCategoriesRequest.Builder} instance.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The country code setter.
+ *
+ * @param country Optional. A country: an ISO 3166-1 alpha-2 country code. Provide this parameter if you want to
+ * narrow the list of returned categories to those relevant to a particular country. If omitted, the
+ * returned items will be globally relevant.
+ * @return A {@link GetListOfCategoriesRequest.Builder}
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public Builder country(final CountryCode country) {
+ assert (country != null);
+ return setQueryParameter("country", country);
+ }
+
+ /**
+ * The language code setter.
+ *
+ * @param locale Optional. The desired language, consisting of an ISO 639 language code and an ISO 3166-1 alpha-2
+ * country code, joined by an underscore. For example: es_MX, meaning "Spanish (Mexico)". Provide this
+ * parameter if you want the category metadata returned in a particular language. Note that, if locale
+ * is not supplied, or if the specified language is not available, all strings will be returned in the
+ * Spotify default language (American English). The locale parameter, combined with the country
+ * parameter, may give odd results if not carefully matched. For example
+ * {@code country=SE&locale=de_DE} will return a list of categories relevant to Sweden but as German
+ * language strings.
+ * @return A {@link GetListOfCategoriesRequest.Builder}
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_639">Wikipedia: ISO 639 language code</a>
+ */
+ public Builder locale(final String locale) {
+ assert (locale != null);
+ assert (locale.contains("_"));
+ String[] localeParts = locale.split("_");
+ assert (localeParts.length == 2);
+ assert (LanguageCode.getByCode(localeParts[0]) != null);
+ assert (CountryCode.getByCode(localeParts[1]) != null);
+ return setQueryParameter("locale", locale);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The maximum number of categories to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link GetListOfCategoriesRequest.Builder}.
+ */
+ @Override
+ public Builder limit(Integer limit) {
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The offset setter.
+ *
+ * @param offset Optional. The index of the first item to return. Default: 0 (the first object). Use with
+ * {@link #limit(Integer)} to get the next set of categories.
+ * @return A {@link GetListOfCategoriesRequest.Builder}.
+ */
+ @Override
+ public Builder offset(Integer offset) {
+ assert (offset >= 0);
+ return setQueryParameter("offset", offset);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetListOfCategoriesRequest}.
+ */
+ @Override
+ public GetListOfCategoriesRequest build() {
+ setPath("/v1/browse/categories");
+ return new GetListOfCategoriesRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/browse/GetListOfFeaturedPlaylistsRequest.java
@@ -0,0 +1,150 @@
+package com.wrapper.spotify.requests.data.browse;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.neovisionaries.i18n.LanguageCode;
+import com.wrapper.spotify.SpotifyApi;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.special.FeaturedPlaylists;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+import java.util.Date;
+
+/**
+ * Get a list of Spotify featured playlists (shown, for example, on a Spotify player’s "Browse" tab).
+ */
+@JsonDeserialize(builder = GetListOfFeaturedPlaylistsRequest.Builder.class)
+public class GetListOfFeaturedPlaylistsRequest extends AbstractDataRequest<FeaturedPlaylists> {
+
+ /**
+ * The private {@link GetListOfFeaturedPlaylistsRequest} constructor.
+ *
+ * @param builder A {@link GetListOfFeaturedPlaylistsRequest.Builder}.
+ */
+ private GetListOfFeaturedPlaylistsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get {@link FeaturedPlaylists} synchronously.
+ *
+ * @return A {@link FeaturedPlaylists} object.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public FeaturedPlaylists execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new FeaturedPlaylists.JsonUtil().createModelObject(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetListOfFeaturedPlaylistsRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<FeaturedPlaylists, Builder> {
+
+ /**
+ * Create a new {@link GetListOfFeaturedPlaylistsRequest.Builder} instance.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The language code setter.
+ *
+ * @param locale Optional. The desired language, consisting of an ISO 639 language code and an ISO 3166-1 alpha-2
+ * country code, joined by an underscore. For example: es_MX, meaning "Spanish (Mexico)". Provide this
+ * parameter if you want the category metadata returned in a particular language. Note that, if locale
+ * is not supplied, or if the specified language is not available, all strings will be returned in the
+ * Spotify default language (American English). The locale parameter, combined with the country
+ * parameter, may give odd results if not carefully matched. For example
+ * {@code country=SE&locale=de_DE} will return a list of categories relevant to Sweden but as German
+ * language strings.
+ * @return A {@link GetListOfFeaturedPlaylistsRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_639">Wikipedia: ISO 639 language code</a>
+ */
+ public Builder locale(final String locale) {
+ assert (locale != null);
+ assert (locale.contains("_"));
+ String[] localeParts = locale.split("_");
+ assert (localeParts.length == 2);
+ assert (LanguageCode.getByCode(localeParts[0]) != null);
+ assert (CountryCode.getByCode(localeParts[1]) != null);
+ return setQueryParameter("locale", locale);
+ }
+
+ /**
+ * The country code setter.
+ *
+ * @param country Optional. A country: an ISO 3166-1 alpha-2 country code. Provide this parameter if you want to
+ * narrow the list of returned categories to those relevant to a particular country. If omitted, the
+ * returned items will be globally relevant.
+ * @return A {@link GetListOfFeaturedPlaylistsRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public Builder country(final CountryCode country) {
+ assert (country != null);
+ return setQueryParameter("country", country);
+ }
+
+ /**
+ * The timestamp setter.
+ *
+ * @param timestamp Optional. A timestamp in ISO 8601 format. Use this parameter to specify the user's local time to
+ * get results tailored for that specific date and time in the day. If not provided, the response
+ * defaults to the current UTC time.
+ * @return A {@link GetListOfFeaturedPlaylistsRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_8601">Wikipedia: ISO 8601 timestamps</a>
+ */
+ public Builder timestamp(final Date timestamp) {
+ assert (timestamp != null);
+ return setQueryParameter("timestamp", SpotifyApi.formatDefaultDate(timestamp));
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link GetListOfFeaturedPlaylistsRequest.Builder}.
+ */
+ public Builder limit(final Integer limit) {
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The offset setter.
+ *
+ * @param offset Optional. The index of the first item to return. Default: 0 (the first object). Use with
+ * {@link #limit(Integer)} to get the next set of items.
+ * @return A {@link GetListOfFeaturedPlaylistsRequest.Builder}.
+ */
+ public Builder offset(final Integer offset) {
+ assert (offset >= 0);
+ return setQueryParameter("offset", offset);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetListOfFeaturedPlaylistsRequest}.
+ */
+ @Override
+ public GetListOfFeaturedPlaylistsRequest build() {
+ setPath("/v1/browse/featured-playlists");
+ return new GetListOfFeaturedPlaylistsRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/browse/GetListOfNewReleasesRequest.java
@@ -0,0 +1,112 @@
+package com.wrapper.spotify.requests.data.browse;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.AlbumSimplified;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.requests.data.AbstractDataPagingRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get a list of new album releases featured in Spotify (shown, for example, on a Spotify player’s "Browse" tab).
+ */
+@JsonDeserialize(builder = GetListOfNewReleasesRequest.Builder.class)
+public class GetListOfNewReleasesRequest extends AbstractDataRequest<Paging<AlbumSimplified>> {
+
+ /**
+ * The private {@link GetListOfNewReleasesRequest} constructor.
+ *
+ * @param builder A {@link GetListOfNewReleasesRequest.Builder}.
+ */
+ private GetListOfNewReleasesRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get a paging of new {@link AlbumSimplified} releases.
+ *
+ * @return An {@link AlbumSimplified} paging.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Paging<AlbumSimplified> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new AlbumSimplified.JsonUtil().createModelObjectPaging(getJson(), "albums");
+ }
+
+ /**
+ * Builder class for building a {@link GetListOfNewReleasesRequest}.
+ */
+ public static final class Builder extends AbstractDataPagingRequest.Builder<AlbumSimplified, Builder> {
+
+ /**
+ * Create a new {@link GetListOfNewReleasesRequest.Builder} instance.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The country code setter.
+ *
+ * @param country Optional. A country: an ISO 3166-1 alpha-2 country code. Provide this parameter if you want the
+ * list of returned items to be relevant to a particular country. If omitted, the returned items will
+ * be relevant to all countries.
+ * @return A {@link GetListOfNewReleasesRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public Builder country(final CountryCode country) {
+ assert (country != null);
+ return setQueryParameter("country", country);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link GetListOfNewReleasesRequest.Builder}.
+ */
+ @Override
+ public Builder limit(final Integer limit) {
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The offset setter.
+ *
+ * @param offset Optional. The index of the first item to return. Default: 0 (the first object). Use with
+ * {@link #limit(Integer)} to get the next set of items.
+ * @return A {@link GetListOfNewReleasesRequest.Builder}.
+ */
+ @Override
+ public Builder offset(final Integer offset) {
+ assert (offset >= 0);
+ return setQueryParameter("offset", offset);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetListOfNewReleasesRequest}.
+ */
+ @Override
+ public GetListOfNewReleasesRequest build() {
+ setPath("/v1/browse/new-releases");
+ return new GetListOfNewReleasesRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/browse/GetRecommendationsRequest.java
@@ -0,0 +1,749 @@
+package com.wrapper.spotify.requests.data.browse;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Recommendations;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Create a playlist-style listening experience based on seed artists, tracks and genres. <br><br>
+ * <p>
+ * Recommendations are generated based on the available information for a given seed entity and matched against similar
+ * artists and tracks. If there is sufficient information about the provided seeds, a list of tracks will be returned
+ * together with pool size details. <br><br>
+ * <p>
+ * For artists and tracks that are very new or obscure there might not be enough data to generate a list of tracks.
+ */
+@JsonDeserialize(builder = GetRecommendationsRequest.Builder.class)
+public class GetRecommendationsRequest extends AbstractDataRequest<Recommendations> {
+
+ /**
+ * The private {@link GetRecommendationsRequest} constructor.
+ *
+ * @param builder A {@link GetRecommendationsRequest.Builder}.
+ */
+ private GetRecommendationsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get the {@link Recommendations}.
+ *
+ * @return A {@link Recommendations} object.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Recommendations execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Recommendations.JsonUtil().createModelObject(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetRecommendationsRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<Recommendations, Builder> {
+
+ /**
+ * Create a new {@link GetRecommendationsRequest.Builder} instance.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The target size of the list of recommended tracks. For seeds with unusually small pools or
+ * when highly restrictive filtering is applied, it may be impossible to generate the requested number
+ * of recommended tracks. Debugging information for such cases is available in the response. Default:
+ * 20. Minimum: 1. Maximum: 100.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder limit(final Integer limit) {
+ assert (1 <= limit && limit <= 100);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The country code setter.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track
+ * Relinking. Because {@code min_*}, {@code max_*} and {@code target_*} are applied to pools before
+ * relinking, the generated results may not precisely match the filters applied. Original,
+ * non-relinked tracks are available via the {@code linked_from} attribute of the relinked track
+ * response.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The maximum acousticness setter. Tracks with the attribute value above the maximum value will be omitted.
+ *
+ * @param max_acousticness A confidence measure from 0.0 to 1.0 of whether the track is acoustic. 1.0 represents
+ * high confidence the track is acoustic.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder max_acousticness(final Float max_acousticness) {
+ assert (max_acousticness != null);
+ assert (0.0 <= max_acousticness && max_acousticness <= 1.0);
+ return setQueryParameter("max_acousticness", max_acousticness);
+ }
+
+ /**
+ * The maximum danceability setter. Tracks with the attribute value above the maximum value will be omitted.
+ *
+ * @param max_danceability Danceability describes how suitable a track is for dancing based on a combination of
+ * musical elements including tempo, rhythm stability, beat strength, and overall
+ * regularity. A value of 0.0 is least danceable and 1.0 is most danceable.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder max_danceability(final Float max_danceability) {
+ assert (max_danceability != null);
+ assert (0.0 <= max_danceability && max_danceability <= 1.0);
+ return setQueryParameter("max_danceability", max_danceability);
+ }
+
+ /**
+ * The maximum duration setter. Tracks with the attribute value above the maximum value will be omitted.
+ *
+ * @param max_duration_ms The duration of the track in milliseconds.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder max_duration_ms(final Integer max_duration_ms) {
+ assert (max_duration_ms != null);
+ return setQueryParameter("max_duration_ms", max_duration_ms);
+ }
+
+ /**
+ * The maximum energy setter. Tracks with the attribute value above the maximum value will be omitted.
+ *
+ * @param max_energy Energy is a measure from 0.0 to 1.0 and represents a perceptual measure of intensity and
+ * activity. Typically, energetic seed_tracks feel fast, loud, and noisy. For example, death metal
+ * has high energy, while a Bach prelude scores low on the scale. Perceptual features contributing
+ * to this attribute include dynamic range, perceived loudness, timbre, onset rate, and general
+ * entropy.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder max_energy(final Float max_energy) {
+ assert (max_energy != null);
+ assert (0.0 <= max_energy && max_energy <= 1.0);
+ return setQueryParameter("max_energy", max_energy);
+ }
+
+ /**
+ * The maximum instrumentalness setter. Tracks with the attribute value above the maximum value will be omitted.
+ *
+ * @param max_instrumentalness Predicts whether a track contains no vocals. "Ooh" and "aah" sounds are treated as
+ * instrumental in this context. Rap or spoken word seed_tracks are clearly "vocal". The
+ * closer the instrumentalness value is to 1.0, the greater likelihood the track
+ * contains no vocal content. Values above 0.5 are intended to represent instrumental
+ * seed_tracks, but confidence is higher as the value approaches 1.0.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder max_instrumentalness(final Float max_instrumentalness) {
+ assert (max_instrumentalness != null);
+ assert (0.0 <= max_instrumentalness && max_instrumentalness <= 1.0);
+ return setQueryParameter("max_instrumentalness", max_instrumentalness);
+ }
+
+ /**
+ * The maximum key setter. Tracks with the attribute value above the maximum value will be omitted.
+ *
+ * @param max_key The key the track is in. Integers map to pitches using standard
+ * <a href="https://en.wikipedia.org/wiki/Pitch_class">Pitch Class notation</a>. E.g. 0 = C, 1 =
+ * C♯/D♭, 2 = D, and so on.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder max_key(final Integer max_key) {
+ assert (max_key != null);
+ assert (0 <= max_key && max_key <= 11);
+ return setQueryParameter("max_key", max_key);
+ }
+
+ /**
+ * The maximum liveness setter. Tracks with the attribute value above the maximum value will be omitted.
+ *
+ * @param max_liveness Detects the presence of an audience in the recording. Higher liveness values represent an
+ * increased probability that the track was performed live. A value above 0.8 provides strong
+ * likelihood that the track is live.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder max_liveness(final Float max_liveness) {
+ assert (max_liveness != null);
+ assert (0.0 <= max_liveness && max_liveness <= 1.0);
+ return setQueryParameter("max_liveness", max_liveness);
+ }
+
+ /**
+ * The maximum loudness setter. Tracks with the attribute value above the maximum value will be omitted.
+ *
+ * @param max_loudness The overall loudness of a track in decibels (dB). Loudness values are averaged across the
+ * entire track and are useful for comparing relative loudness of seed_tracks. Loudness is the
+ * quality of a sound that is the primary psychological correlate of physical strength
+ * (amplitude). Values typical range between -60 and 0 db.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder max_loudness(final Float max_loudness) {
+ assert (max_loudness != null);
+ return setQueryParameter("max_loudness", max_loudness);
+ }
+
+ /**
+ * The maximum mode setter. Tracks with the attribute value above the maximum value will be omitted.
+ *
+ * @param max_mode Mode indicates the modality (major or minor) of a track, the type of scale from which its melodic
+ * content is derived. Major is represented by 1 and minor is 0.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder max_mode(final Integer max_mode) {
+ assert (max_mode != null);
+ assert (max_mode == 0 || max_mode == 1);
+ return setQueryParameter("max_mode", max_mode);
+ }
+
+ /**
+ * The maximum popularity setter. Tracks with the attribute value above the maximum value will be omitted.
+ *
+ * @param max_popularity The popularity of the track. The value will be between 0 and 100, with 100 being the most
+ * popular. The popularity is calculated by algorithm and is based, in the most part, on the
+ * total number of plays the track has had and how recent those plays are.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder max_popularity(final Integer max_popularity) {
+ assert (max_popularity != null);
+ assert (0 <= max_popularity && max_popularity <= 100);
+ return setQueryParameter("max_popularity", max_popularity);
+ }
+
+ /**
+ * The maximum speechiness setter. Tracks with the attribute value above the maximum value will be omitted.
+ *
+ * @param max_speechiness Speechiness detects the presence of spoken words in a track. The more exclusively
+ * speech-like the recording (e.g. talk show, audio book, poetry), the closer to 1.0 the
+ * attribute value. Values above 0.66 describe seed_tracks that are probably made entirely of
+ * spoken words. Values between 0.33 and 0.66 describe seed_tracks that may contain both
+ * music and speech, either in sections or layered, including such cases as rap music. Values
+ * below 0.33 most likely represent music and other non-speech-like seed_tracks.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder max_speechiness(final Float max_speechiness) {
+ assert (max_speechiness != null);
+ assert (0.0 <= max_speechiness && max_speechiness <= 1.0);
+ return setQueryParameter("max_speechiness", max_speechiness);
+ }
+
+ /**
+ * The maximum tempo setter. Tracks with the attribute value above the maximum value will be omitted.
+ *
+ * @param max_tempo The overall estimated tempo of a track in beats per minute (BPM). In musical terminology, tempo
+ * is the speed or pace of a given piece and derives directly from the average beat duration.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder max_tempo(final Float max_tempo) {
+ assert (max_tempo != null);
+ assert (max_tempo >= 0);
+ return setQueryParameter("max_tempo", max_tempo);
+ }
+
+ /**
+ * The maximum time signature setter. Tracks with the attribute value above the maximum value will be omitted.
+ *
+ * @param max_time_signature An estimated overall time signature of a track. The time signature (meter) is a
+ * notational convention to specify how many beats are in each bar (or measure).
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder max_time_signature(final Integer max_time_signature) {
+ assert (max_time_signature != null);
+ return setQueryParameter("max_time_signature", max_time_signature);
+ }
+
+ /**
+ * The maximum valence setter. Tracks with the attribute value above the maximum value will be omitted.
+ *
+ * @param max_valence A measure from 0.0 to 1.0 describing the musical positiveness conveyed by a track. Tracks with
+ * high valence sound more positive (e.g. happy, cheerful, euphoric), while seed_tracks with low
+ * valence sound more negative (e.g. sad, depressed, angry).
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder max_valence(final Float max_valence) {
+ assert (max_valence != null);
+ assert (0.0 <= max_valence && max_valence <= 1.0);
+ return setQueryParameter("max_valence", max_valence);
+ }
+
+ /**
+ * The minimum acousticness setter. Tracks with the attribute value below the minimum value will be omitted.
+ *
+ * @param min_acousticness A confidence measure from 0.0 to 1.0 of whether the track is acoustic. 1.0 represents
+ * high confidence the track is acoustic.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder min_acousticness(final Float min_acousticness) {
+ assert (min_acousticness != null);
+ assert (0.0 <= min_acousticness && min_acousticness <= 1.0);
+ return setQueryParameter("min_acousticness", min_acousticness);
+ }
+
+ /**
+ * The minimum danceability setter. Tracks with the attribute value below the minimum value will be omitted.
+ *
+ * @param min_danceability Danceability describes how suitable a track is for dancing based on a combination of
+ * musical elements including tempo, rhythm stability, beat strength, and overall
+ * regularity. A value of 0.0 is least danceable and 1.0 is most danceable.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder min_danceability(final Float min_danceability) {
+ assert (min_danceability != null);
+ assert (0.0 <= min_danceability && min_danceability <= 1.0);
+ return setQueryParameter("min_danceability", min_danceability);
+ }
+
+ /**
+ * The minimum duration setter. Tracks with the attribute value below the minimum value will be omitted.
+ *
+ * @param min_duration_ms The duration of the track in milliseconds.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder min_duration_ms(final Integer min_duration_ms) {
+ assert (min_duration_ms != null);
+ return setQueryParameter("min_duration_ms", min_duration_ms);
+ }
+
+ /**
+ * The minimum energy setter. Tracks with the attribute value below the minimum value will be omitted.
+ *
+ * @param min_energy Energy is a measure from 0.0 to 1.0 and represents a perceptual measure of intensity and
+ * activity. Typically, energetic seed_tracks feel fast, loud, and noisy. For example, death metal
+ * has high energy, while a Bach prelude scores low on the scale. Perceptual features contributing
+ * to this attribute include dynamic range, perceived loudness, timbre, onset rate, and general
+ * entropy.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder min_energy(final Float min_energy) {
+ assert (min_energy != null);
+ assert (0.0 <= min_energy && min_energy <= 1.0);
+ return setQueryParameter("min_energy", min_energy);
+ }
+
+ /**
+ * The minimum instrumentalness setter. Tracks with the attribute value below the minimum value will be omitted.
+ *
+ * @param min_instrumentalness Predicts whether a track contains no vocals. "Ooh" and "aah" sounds are treated as
+ * instrumental in this context. Rap or spoken word seed_tracks are clearly "vocal". The
+ * closer the instrumentalness value is to 1.0, the greater likelihood the track
+ * contains no vocal content. Values above 0.5 are intended to represent instrumental
+ * seed_tracks, but confidence is higher as the value approaches 1.0.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder min_instrumentalness(final Float min_instrumentalness) {
+ assert (min_instrumentalness != null);
+ assert (0.0 <= min_instrumentalness && min_instrumentalness <= 1.0);
+ return setQueryParameter("min_instrumentalness", min_instrumentalness);
+ }
+
+ /**
+ * The minimum key setter. Tracks with the attribute value below the minimum value will be omitted.
+ *
+ * @param min_key The key the track is in. Integers map to pitches using standard
+ * <a href="https://en.wikipedia.org/wiki/Pitch_class">Pitch Class notation</a>. E.g. 0 = C, 1 =
+ * C♯/D♭, 2 = D, and so on.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder min_key(final Integer min_key) {
+ assert (min_key != null);
+ assert (0 <= min_key && min_key <= 11);
+ return setQueryParameter("min_key", min_key);
+ }
+
+ /**
+ * The minimum liveness setter. Tracks with the attribute value below the minimum value will be omitted.
+ *
+ * @param min_liveness Detects the presence of an audience in the recording. Higher liveness values represent an
+ * increased probability that the track was performed live. A value above 0.8 provides strong
+ * likelihood that the track is live.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder min_liveness(final Float min_liveness) {
+ assert (min_liveness != null);
+ assert (0.0 <= min_liveness && min_liveness <= 1.0);
+ return setQueryParameter("min_liveness", min_liveness);
+ }
+
+ /**
+ * The minimum loudness setter. Tracks with the attribute value below the minimum value will be omitted.
+ *
+ * @param min_loudness The overall loudness of a track in decibels (dB). Loudness values are averaged across the
+ * entire track and are useful for comparing relative loudness of seed_tracks. Loudness is the
+ * quality of a sound that is the primary psychological correlate of physical strength
+ * (amplitude). Values typical range between -60 and 0 db.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder min_loudness(final Float min_loudness) {
+ assert (min_loudness != null);
+ return setQueryParameter("min_loudness", min_loudness);
+ }
+
+ /**
+ * The minimum mode setter. Tracks with the attribute value below the minimum value will be omitted.
+ *
+ * @param min_mode Mode indicates the modality (major or minor) of a track, the type of scale from which its melodic
+ * content is derived. Major is represented by 1 and minor is 0.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder min_mode(final Integer min_mode) {
+ assert (min_mode != null);
+ assert (min_mode == 0 || min_mode == 1);
+ return setQueryParameter("min_mode", min_mode);
+ }
+
+ /**
+ * The minimum popularity setter. Tracks with the attribute value below the minimum value will be omitted.
+ *
+ * @param min_popularity The popularity of the track. The value will be between 0 and 100, with 100 being the most
+ * popular. The popularity is calculated by algorithm and is based, in the most part, on the
+ * total number of plays the track has had and how recent those plays are.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder min_popularity(final Integer min_popularity) {
+ assert (min_popularity != null);
+ assert (0 <= min_popularity && min_popularity <= 100);
+ return setQueryParameter("min_popularity", min_popularity);
+ }
+
+ /**
+ * The minimum speechiness setter. Tracks with the attribute value below the minimum value will be omitted.
+ *
+ * @param min_speechiness Speechiness detects the presence of spoken words in a track. The more exclusively
+ * speech-like the recording (e.g. talk show, audio book, poetry), the closer to 1.0 the
+ * attribute value. Values above 0.66 describe seed_tracks that are probably made entirely of
+ * spoken words. Values between 0.33 and 0.66 describe seed_tracks that may contain both
+ * music and speech, either in sections or layered, including such cases as rap music. Values
+ * below 0.33 most likely represent music and other non-speech-like seed_tracks.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder min_speechiness(final Float min_speechiness) {
+ assert (min_speechiness != null);
+ assert (0.0 <= min_speechiness && min_speechiness <= 1.0);
+ return setQueryParameter("min_speechiness", min_speechiness);
+ }
+
+ /**
+ * The minimum tempo setter. Tracks with the attribute value below the minimum value will be omitted.
+ *
+ * @param min_tempo The overall estimated tempo of a track in beats per minute (BPM). In musical terminology, tempo
+ * is the speed or pace of a given piece and derives directly from the average beat duration.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder min_tempo(final Float min_tempo) {
+ assert (min_tempo != null);
+ assert (min_tempo >= 0);
+ return setQueryParameter("min_tempo", min_tempo);
+ }
+
+ /**
+ * The minimum time signature setter. Tracks with the attribute value below the minimum value will be omitted.
+ *
+ * @param min_time_signature An estimated overall time signature of a track. The time signature (meter) is a
+ * notational convention to specify how many beats are in each bar (or measure).
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder min_time_signature(final Integer min_time_signature) {
+ assert (min_time_signature != null);
+ return setQueryParameter("min_time_signature", min_time_signature);
+ }
+
+ /**
+ * The minimum valence setter. Tracks with the attribute value below the minimum value will be omitted.
+ *
+ * @param min_valence A measure from 0.0 to 1.0 describing the musical positiveness conveyed by a track. Tracks with
+ * high valence sound more positive (e.g. happy, cheerful, euphoric), while seed_tracks with low
+ * valence sound more negative (e.g. sad, depressed, angry).
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder min_valence(final Float min_valence) {
+ assert (min_valence != null);
+ assert (0.0 <= min_valence && min_valence <= 1.0);
+ return setQueryParameter("min_valence", min_valence);
+ }
+
+ /**
+ * The seed artists setter.
+ *
+ * @param seed_artists A comma separated list of Spotify IDs for seed seed_artists. Up to 5 seed values may be
+ * provided in any combination of seed_artists, seed_tracks and seed_genres.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URIs &amp; IDs</a>
+ */
+ public Builder seed_artists(final String seed_artists) {
+ assert (seed_artists != null);
+ assert (seed_artists.split(",").length <= 5);
+ return setQueryParameter("seed_artists", seed_artists);
+ }
+
+ /**
+ * The seed genres setter.
+ *
+ * @param seed_genres A comma separated list of any seed_genres in the set of available genre seeds. Up to 5 seed
+ * values may be provided in any combination of seed_artists, seed_tracks and seed_genres.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URIs &amp; IDs</a>
+ */
+ public Builder seed_genres(final String seed_genres) {
+ assert (seed_genres != null);
+ assert (seed_genres.split(",").length <= 5);
+ return setQueryParameter("seed_genres", seed_genres);
+ }
+
+ /**
+ * The seed tracks setter.
+ *
+ * @param seed_tracks A comma separated list of Spotify IDs for a seed track. Up to 5 seed values may be provided in
+ * any combination of seed_artists, seed_tracks and seed_genres.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URIs &amp; IDs</a>
+ */
+ public Builder seed_tracks(final String seed_tracks) {
+ assert (seed_tracks != null);
+ assert (seed_tracks.split(",").length <= 5);
+ return setQueryParameter("seed_tracks", seed_tracks);
+ }
+
+ /**
+ * The target acousticness setter. Tracks with the attribute value nearest to the target value will be preferred.
+ * All target values will be weighed equally in ranking results.
+ *
+ * @param target_acousticness A confidence measure from 0.0 to 1.0 of whether the track is acoustic. 1.0 represents
+ * high confidence the track is acoustic.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder target_acousticness(final Float target_acousticness) {
+ assert (target_acousticness != null);
+ assert (0.0 <= target_acousticness && target_acousticness <= 1.0);
+ return setQueryParameter("target_acousticness", target_acousticness);
+ }
+
+ /**
+ * The target danceability setter. Tracks with the attribute value nearest to the target value will be preferred.
+ * All target values will be weighed equally in ranking results.
+ *
+ * @param target_danceability Danceability describes how suitable a track is for dancing based on a combination of
+ * musical elements including tempo, rhythm stability, beat strength, and overall
+ * regularity. A value of 0.0 is least danceable and 1.0 is most danceable.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder target_danceability(final Float target_danceability) {
+ assert (target_danceability != null);
+ assert (0.0 <= target_danceability && target_danceability <= 1.0);
+ return setQueryParameter("target_danceability", target_danceability);
+ }
+
+ /**
+ * The target duration setter. Tracks with the attribute value nearest to the target value will be preferred. All
+ * target values will be weighed equally in ranking results.
+ *
+ * @param target_duration_ms The duration of the track in milliseconds.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder target_duration_ms(final Integer target_duration_ms) {
+ assert (target_duration_ms != null);
+ return setQueryParameter("target_duration_ms", target_duration_ms);
+ }
+
+ /**
+ * The target energy setter. Tracks with the attribute value nearest to the target value will be preferred. All
+ * target values will be weighed equally in ranking results.
+ *
+ * @param target_energy Energy is a measure from 0.0 to 1.0 and represents a perceptual measure of intensity and
+ * activity. Typically, energetic seed_tracks feel fast, loud, and noisy. For example, death
+ * metal has high energy, while a Bach prelude scores low on the scale. Perceptual features
+ * contributing to this attribute include dynamic range, perceived loudness, timbre, onset
+ * rate, and general entropy.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder target_energy(final Float target_energy) {
+ assert (target_energy != null);
+ assert (0.0 <= target_energy && target_energy <= 1.0);
+ return setQueryParameter("target_energy", target_energy);
+ }
+
+ /**
+ * The target instrumentalness setter. Tracks with the attribute value nearest to the target value will be
+ * preferred. All target values will be weighed equally in ranking results.
+ *
+ * @param target_instrumentalness Predicts whether a track contains no vocals. "Ooh" and "aah" sounds are treated as
+ * instrumental in this context. Rap or spoken word seed_tracks are clearly "vocal".
+ * The closer the instrumentalness value is to 1.0, the greater likelihood the track
+ * contains no vocal content. Values above 0.5 are intended to represent instrumental
+ * seed_tracks, but confidence is higher as the value approaches 1.0.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder target_instrumentalness(final Float target_instrumentalness) {
+ assert (target_instrumentalness != null);
+ assert (0.0 <= target_instrumentalness && target_instrumentalness <= 1.0);
+ return setQueryParameter("target_instrumentalness", target_instrumentalness);
+ }
+
+ /**
+ * The target key setter. Tracks with the attribute value nearest to the target value will be preferred. All target
+ * values will be weighed equally in ranking results.
+ *
+ * @param target_key The key the track is in. Integers map to pitches using standard
+ * <a href="https://en.wikipedia.org/wiki/Pitch_class">Pitch Class notation</a>. E.g. 0 = C, 1 =
+ * C♯/D♭, 2 = D, and so on.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder target_key(final Integer target_key) {
+ assert (target_key != null);
+ assert (0 <= target_key && target_key <= 11);
+ return setQueryParameter("target_key", target_key);
+ }
+
+ /**
+ * The target liveness setter. Tracks with the attribute value nearest to the target value will be preferred. All
+ * target values will be weighed equally in ranking results.
+ *
+ * @param target_liveness Detects the presence of an audience in the recording. Higher liveness values represent an
+ * increased probability that the track was performed live. A value above 0.8 provides strong
+ * likelihood that the track is live.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder target_liveness(final Float target_liveness) {
+ assert (target_liveness != null);
+ assert (0.0 <= target_liveness && target_liveness <= 1.0);
+ return setQueryParameter("target_liveness", target_liveness);
+ }
+
+ /**
+ * The target loudness setter. Tracks with the attribute value nearest to the target value will be preferred. All
+ * target values will be weighed equally in ranking results.
+ *
+ * @param target_loudness The overall loudness of a track in decibels (dB). Loudness values are averaged across the
+ * entire track and are useful for comparing relative loudness of seed_tracks. Loudness is
+ * the quality of a sound that is the primary psychological correlate of physical strength
+ * (amplitude). Values typical range between -60 and 0 db.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder target_loudness(final Float target_loudness) {
+ assert (target_loudness != null);
+ return setQueryParameter("target_loudness", target_loudness);
+ }
+
+ /**
+ * The target mode setter. Tracks with the attribute value nearest to the target value will be preferred. All target
+ * values will be weighed equally in ranking results.
+ *
+ * @param target_mode Mode indicates the modality (major or minor) of a track, the type of scale from which its
+ * melodic content is derived. Major is represented by 1 and minor is 0.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder target_mode(final Integer target_mode) {
+ assert (target_mode != null);
+ assert (target_mode == 0 || target_mode == 1);
+ return setQueryParameter("target_mode", target_mode);
+ }
+
+ /**
+ * The target popularity setter. Tracks with the attribute value nearest to the target value will be preferred. All
+ * target values will be weighed equally in ranking results.
+ *
+ * @param target_popularity The value will be between 0 and 100, with 100 being the most
+ * popular. The popularity is calculated by algorithm and is based, in the most part, on
+ * the total number of plays the track has had and how recent those plays are.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder target_popularity(final Integer target_popularity) {
+ assert (target_popularity != null);
+ assert (0 <= target_popularity && target_popularity <= 100);
+ return setQueryParameter("target_popularity", target_popularity);
+ }
+
+ /**
+ * The target speechiness setter. Tracks with the attribute value nearest to the target value will be preferred. All
+ * target values will be weighed equally in ranking results.
+ *
+ * @param target_speechiness Speechiness detects the presence of spoken words in a track. The more exclusively
+ * speech-like the recording (e.g. talk show, audio book, poetry), the closer to 1.0 the
+ * attribute value. Values above 0.66 describe seed_tracks that are probably made entirely
+ * of spoken words. Values between 0.33 and 0.66 describe seed_tracks that may contain
+ * both music and speech, either in sections or layered, including such cases as rap music.
+ * Values below 0.33 most likely represent music and other non-speech-like seed_tracks.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder target_speechiness(final Float target_speechiness) {
+ assert (target_speechiness != null);
+ assert (0.0 <= target_speechiness && target_speechiness <= 1.0);
+ return setQueryParameter("target_speechiness", target_speechiness);
+ }
+
+ /**
+ * The target tempo setter. Tracks with the attribute value nearest to the target value will be preferred. All
+ * target values will be weighed equally in ranking results.
+ *
+ * @param target_tempo The overall estimated tempo of a track in beats per minute (BPM). In musical terminology,
+ * tempo is the speed or pace of a given piece and derives directly from the average beat
+ * duration.
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder target_tempo(final Float target_tempo) {
+ assert (target_tempo != null);
+ assert (target_tempo >= 0);
+ return setQueryParameter("target_tempo", target_tempo);
+ }
+
+ /**
+ * The target time signature setter. Tracks with the attribute value nearest to the target value will be preferred.
+ * All target values will be weighed equally in ranking results.
+ *
+ * @param target_time_signature An estimated overall time signature of a track. The time signature (meter) is a
+ * notational convention to specify how many beats are in each bar (or measure).
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder target_time_signature(final Integer target_time_signature) {
+ assert (target_time_signature != null);
+ return setQueryParameter("target_time_signature", target_time_signature);
+ }
+
+ /**
+ * The target valence setter. Tracks with the attribute value nearest to the target value will be preferred. All
+ * target values will be weighed equally in ranking results.
+ *
+ * @param target_valence A measure from 0.0 to 1.0 describing the musical positiveness conveyed by a track. Tracks
+ * with high valence sound more positive (e.g. happy, cheerful, euphoric), while seed_tracks
+ * with low valence sound more negative (e.g. sad, depressed, angry).
+ * @return A {@link GetRecommendationsRequest.Builder}.
+ */
+ public Builder target_valence(final Float target_valence) {
+ assert (target_valence != null);
+ assert (0.0 <= target_valence && target_valence <= 1.0);
+ return setQueryParameter("target_valence", target_valence);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetRecommendationsRequest}.
+ */
+ @Override
+ public GetRecommendationsRequest build() {
+ setPath("/v1/recommendations");
+ return new GetRecommendationsRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/browse/miscellaneous/GetAvailableGenreSeedsRequest.java
@@ -0,0 +1,83 @@
+package com.wrapper.spotify.requests.data.browse.miscellaneous;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.Gson;
+import com.google.gson.JsonParser;
+import com.google.gson.reflect.TypeToken;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Retrieve a list of available genres seed parameter values for recommendations.
+ */
+@JsonDeserialize(builder = GetAvailableGenreSeedsRequest.Builder.class)
+public class GetAvailableGenreSeedsRequest extends AbstractDataRequest<String[]> {
+
+ /**
+ * The private {@link GetAvailableGenreSeedsRequest} constructor.
+ *
+ * @param builder A {@link GetAvailableGenreSeedsRequest.Builder}.
+ */
+ private GetAvailableGenreSeedsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get all available genre seeds.
+ *
+ * @return All available genre seeds.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public String[] execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ List<String> genres = new Gson().fromJson(
+ JsonParser
+ .parseString(getJson())
+ .getAsJsonObject()
+ .get("genres")
+ .getAsJsonArray(),
+ new TypeToken<List<String>>() {
+ }.getType()
+ );
+
+ return genres.toArray(new String[0]);
+ }
+
+ /**
+ * Builder class for building a {@link GetAvailableGenreSeedsRequest.Builder}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String[], Builder> {
+
+ /**
+ * Create a new {@link GetAvailableGenreSeedsRequest.Builder} instance.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetAvailableGenreSeedsRequest}.
+ */
+ @Override
+ public GetAvailableGenreSeedsRequest build() {
+ setPath("/v1/recommendations/available-genre-seeds");
+ return new GetAvailableGenreSeedsRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/episodes/GetEpisodeRequest.java
@@ -0,0 +1,103 @@
+package com.wrapper.spotify.requests.data.episodes;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Episode;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get Spotify catalog information for a single episode identified by its unique Spotify ID.
+ */
+@JsonDeserialize(builder = GetEpisodeRequest.Builder.class)
+public class GetEpisodeRequest extends AbstractDataRequest<Episode> {
+
+ /**
+ * The private {@link GetEpisodeRequest} constructor.
+ *
+ * @param builder A {@link GetEpisodeRequest.Builder}.
+ */
+ private GetEpisodeRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get an episode
+ *
+ * @return An {@link Episode}.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ @Override
+ public Episode execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Episode.JsonUtil().createModelObject(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetEpisodeRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<Episode, Builder> {
+
+ /**
+ * Create a new {@link GetEpisodeRequest.Builder}.
+ * <p>
+ * Reading the user’s resume points on episode objects requires the {@code user-read-playback-position} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The episode ID setter.
+ *
+ * @param id The Spotify ID for the episode.
+ * @return A {@link GetEpisodeRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder id(final String id) {
+ assert (id != null);
+ assert (!id.equals(""));
+ return setPathParameter("id", id);
+ }
+
+ /**
+ * The market country code setter.<p>
+ * If a country code is specified, only shows and episodes that are available in that market will be returned.
+ * If a valid user access token is specified in the request header, the country associated with the user account will take priority over this parameter.
+ * <i>Note: If neither market or user country are provided, the content is considered unavailable for the client.</i><p>
+ * Users can view the country that is associated with their account in the account settings.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code.
+ * @return A {@link GetEpisodeRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetEpisodeRequest}.
+ */
+ @Override
+ public GetEpisodeRequest build() {
+ setPath("/v1/episodes/{id}");
+ return new GetEpisodeRequest(this);
+ }
+
+ @Override
+ protected GetEpisodeRequest.Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/episodes/GetSeveralEpisodesRequest.java
@@ -0,0 +1,103 @@
+package com.wrapper.spotify.requests.data.episodes;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Episode;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get Spotify catalog information for multiple episodes based on their Spotify IDs.
+ */
+@JsonDeserialize(builder = GetSeveralEpisodesRequest.Builder.class)
+public class GetSeveralEpisodesRequest extends AbstractDataRequest<Episode[]> {
+
+ /**
+ * The private {@link GetSeveralEpisodesRequest} constructor.
+ *
+ * @param builder A {@link GetSeveralEpisodesRequest.Builder}.
+ */
+ private GetSeveralEpisodesRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get several episodes.
+ *
+ * @return Multiple {@link Episode} objects.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ @Override
+ public Episode[] execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Episode.JsonUtil().createModelObjectArray(getJson(), "episodes");
+ }
+
+ /**
+ * Builder class for building a {@link GetSeveralEpisodesRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<Episode[], Builder> {
+
+ /**
+ * Create a new {@link GetSeveralEpisodesRequest.Builder}.
+ * <p>
+ * Reading the user’s resume points on episode objects requires the {@code user-read-playback-position} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The episode IDs setter.
+ *
+ * @param ids Required. A comma-separated list of the Spotify IDs for the episodes. Maximum: 50 IDs.
+ * @return A {@link GetSeveralEpisodesRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder ids(final String ids) {
+ assert (ids != null);
+ assert (ids.split(",").length <= 50);
+ return setQueryParameter("ids", ids);
+ }
+
+ /**
+ * The market country code setter.<p>
+ * If a country code is specified, only shows and episodes that are available in that market will be returned.
+ * If a valid user access token is specified in the request header, the country associated with the user account will take priority over this parameter.
+ * <i>Note: If neither market or user country are provided, the content is considered unavailable for the client.</i><p>
+ * Users can view the country that is associated with their account in the account settings.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code.
+ * @return A {@link GetSeveralEpisodesRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetSeveralEpisodesRequest}.
+ */
+ @Override
+ public GetSeveralEpisodesRequest build() {
+ setPath("/v1/episodes");
+ return new GetSeveralEpisodesRequest(this);
+ }
+
+ @Override
+ protected GetSeveralEpisodesRequest.Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/follow/CheckCurrentUserFollowsArtistsOrUsersRequest.java
@@ -0,0 +1,99 @@
+package com.wrapper.spotify.requests.data.follow;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.Gson;
+import com.google.gson.JsonParser;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Check to see if the current user is following one or more artists or other Spotify users.
+ */
+@JsonDeserialize(builder = CheckCurrentUserFollowsArtistsOrUsersRequest.Builder.class)
+public class CheckCurrentUserFollowsArtistsOrUsersRequest extends AbstractDataRequest<Boolean[]> {
+
+ /**
+ * The private {@link CheckCurrentUserFollowsArtistsOrUsersRequest} constructor.
+ *
+ * @param builder A {@link CheckCurrentUserFollowsArtistsOrUsersRequest.Builder}.
+ */
+ private CheckCurrentUserFollowsArtistsOrUsersRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Check whether the user is following one or more users or artist or not.
+ *
+ * @return If the user is following more users or artists.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Boolean[] execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Gson().fromJson(JsonParser.parseString(getJson()).getAsJsonArray(), Boolean[].class);
+ }
+
+ /**
+ * Builder class for building a {@link CheckCurrentUserFollowsArtistsOrUsersRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<Boolean[], Builder> {
+
+ /**
+ * Create a new {@link CheckCurrentUserFollowsArtistsOrUsersRequest.Builder} instance.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The ID type setter.
+ *
+ * @param type Required. The ID type: either {@code artist} or {@code user}.
+ * @return A {@link CheckCurrentUserFollowsArtistsOrUsersRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URIs &amp; IDs</a>
+ */
+ public Builder type(final ModelObjectType type) {
+ assert (type != null);
+ assert (type.getType().equals("artist") || type.getType().equals("user"));
+ return setQueryParameter("type", type);
+ }
+
+ /**
+ * The artist or user IDs setter.
+ *
+ * @param ids Required. A comma-separated list of the artist or the user Spotify IDs to check. A maximum of 50 IDs
+ * can be sent in one request.
+ * @return A {@link CheckCurrentUserFollowsArtistsOrUsersRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URIs &amp; IDs</a>
+ */
+ public Builder ids(final String ids) {
+ assert (ids != null);
+ assert (ids.split(",").length <= 50);
+ return setQueryParameter("ids", ids);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link CheckCurrentUserFollowsArtistsOrUsersRequest}.
+ */
+ @Override
+ public CheckCurrentUserFollowsArtistsOrUsersRequest build() {
+ setPath("/v1/me/following/contains");
+ return new CheckCurrentUserFollowsArtistsOrUsersRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/follow/CheckUsersFollowPlaylistRequest.java
@@ -0,0 +1,118 @@
+package com.wrapper.spotify.requests.data.follow;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.Gson;
+import com.google.gson.JsonParser;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Check to see if one or more Spotify users are following a specified playlist.
+ */
+@JsonDeserialize(builder = CheckUsersFollowPlaylistRequest.Builder.class)
+public class CheckUsersFollowPlaylistRequest extends AbstractDataRequest<Boolean[]> {
+
+ /**
+ * The private {@link CheckUsersFollowPlaylistRequest} constructor.
+ *
+ * @param builder A {@link CheckUsersFollowPlaylistRequest.Builder}.
+ */
+ private CheckUsersFollowPlaylistRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Check whether a user is following a playlist or not.
+ *
+ * @return Whether a user is following a playlist or not.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Boolean[] execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Gson().fromJson(JsonParser.parseString(getJson()).getAsJsonArray(), Boolean[].class);
+ }
+
+ /**
+ * Builder class for building a {@link CheckUsersFollowPlaylistRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<Boolean[], Builder> {
+
+ /**
+ * Create a new {@link CheckUsersFollowPlaylistRequest.Builder} instance.
+ * <p>
+ * Following a playlist can be done publicly or privately. Checking if a user publicly follows a playlist doesn't
+ * require any scopes; if the user is publicly following the playlist, this endpoint returns {@code true}.
+ * <p>
+ * Checking if the user is privately following a playlist is only possible for the current user when that user has
+ * granted access to the {@code playlist-read-private scope}.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The playlists owner ID setter.
+ *
+ * @param owner_id The Spotify user ID of the person who owns the playlist.
+ * @return A {@link CheckUsersFollowPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder owner_id(final String owner_id) {
+ assert (owner_id != null);
+ assert (!owner_id.equals(""));
+ return setPathParameter("owner_id", owner_id);
+ }
+
+ /**
+ * The playlist ID setter.
+ *
+ * @param playlist_id The Spotify ID of the playlist.
+ * @return A {@link CheckUsersFollowPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder playlist_id(final String playlist_id) {
+ assert (playlist_id != null);
+ assert (!playlist_id.equals(""));
+ return setPathParameter("playlist_id", playlist_id);
+ }
+
+ /**
+ * The user IDs setter.
+ *
+ * @param ids Required. A comma-separated list of Spotify User IDs; the ids of the users that you want to check to
+ * see if they follow the playlist. Maximum: 5 ids.
+ * @return A {@link CheckUsersFollowPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder ids(final String ids) {
+ assert (ids != null);
+ assert (ids.split(",").length <= 5);
+ return setQueryParameter("ids", ids);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link CheckUsersFollowPlaylistRequest}.
+ */
+ @Override
+ public CheckUsersFollowPlaylistRequest build() {
+ setPath("/v1/users/{owner_id}/playlists/{playlist_id}/followers/contains");
+ return new CheckUsersFollowPlaylistRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/follow/FollowArtistsOrUsersRequest.java
@@ -0,0 +1,118 @@
+package com.wrapper.spotify.requests.data.follow;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonArray;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Add the current user as a follower of one or more artists or other Spotify users.
+ */
+@JsonDeserialize(builder = FollowArtistsOrUsersRequest.Builder.class)
+public class FollowArtistsOrUsersRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link FollowArtistsOrUsersRequest} constructor.
+ *
+ * @param builder A {@link FollowArtistsOrUsersRequest.Builder}.
+ */
+ private FollowArtistsOrUsersRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Follow an artist or user.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return putJson();
+ }
+
+ /**
+ * Builder class for building a {@link FollowArtistsOrUsersRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link FollowArtistsOrUsersRequest}.
+ * <p>
+ * Modifying the list of artists or users the current user follows requires authorization of the
+ * {@code user-follow-modify scope}.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The type setter.
+ *
+ * @param type Required. The ID type: either artist or user.
+ * @return A {@link FollowArtistsOrUsersRequest.Builder}.
+ */
+ public Builder type(final ModelObjectType type) {
+ assert (type != null);
+ assert (type.getType().equals("artist") || type.getType().equals("user"));
+ return setQueryParameter("type", type);
+ }
+
+ /**
+ * The user or artist IDs setter.
+ *
+ * @param ids Optional. A comma-separated list of the artist or the user Spotify IDs. A maximum of 50 IDs can be
+ * sent in one request.
+ * @return A {@link FollowArtistsOrUsersRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URIs &amp; IDs</a>
+ */
+ public Builder ids(final String ids) {
+ assert (ids != null);
+ assert (ids.split(",").length <= 50);
+ return setQueryParameter("ids", ids);
+ }
+
+ /**
+ * The user or artist IDs setter.
+ *
+ * @param ids Optional. A json array of the artist or the user Spotify IDs. A maximum of 50 IDs can be
+ * sent in one request.
+ * @return A {@link FollowArtistsOrUsersRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify URIs &amp; IDs</a>
+ */
+ public Builder ids(final JsonArray ids) {
+ assert (ids != null);
+ assert (!ids.isJsonNull());
+ assert (ids.size() <= 50);
+ return setBodyParameter("ids", ids);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link FollowArtistsOrUsersRequest}.
+ */
+ @Override
+ public FollowArtistsOrUsersRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/me/following");
+ return new FollowArtistsOrUsersRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/follow/FollowPlaylistRequest.java
@@ -0,0 +1,102 @@
+package com.wrapper.spotify.requests.data.follow;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Add the current user as a follower of a playlist.
+ */
+@JsonDeserialize(builder = FollowPlaylistRequest.Builder.class)
+public class FollowPlaylistRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link FollowPlaylistRequest} constructor.
+ *
+ * @param builder A {@link FollowPlaylistRequest.Builder}.
+ */
+ private FollowPlaylistRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Follow a playlist.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return putJson();
+ }
+
+ /**
+ * Builder class for building a {@link FollowPlaylistRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link FollowPlaylistRequest} instance.
+ * <p>
+ * Following a playlist publicly requires authorization of the {@code playlist-modify-public} scope; following it
+ * privately requires the {@code playlist-modify-private} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The playlist ID setter.
+ *
+ * @param playlist_id The Spotify ID of the playlist. Any playlist can be followed, regardless of its public/private
+ * status, as long as you know its playlist ID.
+ * @return A {@link FollowPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder playlist_id(final String playlist_id) {
+ assert (playlist_id != null);
+ assert (!playlist_id.equals(""));
+ return setPathParameter("playlist_id", playlist_id);
+ }
+
+ /**
+ * The public following state setter.
+ *
+ * @param public_ Optional, default {@code true}. If {@code true} the playlist will be included in user's public
+ * playlists, if {@code false} it will remain private. To be able to follow playlists privately, the
+ * user must have granted the {@code playlist-modify-private} scope.
+ * @return A {@link FollowPlaylistRequest.Builder}.
+ */
+ public Builder public_(final Boolean public_) {
+ assert (public_ != null);
+ return setBodyParameter("public", public_);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link FollowPlaylistRequest}.
+ */
+ @Override
+ public FollowPlaylistRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/playlists/{playlist_id}/followers");
+ return new FollowPlaylistRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/follow/GetUsersFollowedArtistsRequest.java
@@ -0,0 +1,115 @@
+package com.wrapper.spotify.requests.data.follow;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Artist;
+import com.wrapper.spotify.model_objects.specification.PagingCursorbased;
+import com.wrapper.spotify.requests.data.AbstractDataPagingCursorbasedRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get the current user’s followed artists.
+ */
+@JsonDeserialize(builder = GetUsersFollowedArtistsRequest.Builder.class)
+public class GetUsersFollowedArtistsRequest extends AbstractDataRequest<PagingCursorbased<Artist>> {
+
+ /**
+ * The private {@link GetUsersFollowedArtistsRequest} constructor.
+ *
+ * @param builder A {@link GetUsersFollowedArtistsRequest.Builder}.
+ */
+ private GetUsersFollowedArtistsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get a list of artists the user is following.
+ *
+ * @return An {@link Artist} paging.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public PagingCursorbased<Artist> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Artist.JsonUtil().createModelObjectPagingCursorbased(getJson(), "artists");
+ }
+
+ /**
+ * Builder class for building a {@link GetUsersFollowedArtistsRequest}.
+ */
+ public static final class Builder extends AbstractDataPagingCursorbasedRequest.Builder<Artist, String, Builder> {
+
+ /**
+ * Create a new {@link GetUsersFollowedArtistsRequest.Builder} instance.
+ * <p>
+ * Getting details of the artists or users the current user follows requires authorization of the
+ * {@code user-follow-read} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The type setter.
+ *
+ * @param type Required. The ID type: currently only {@code artist} is supported.
+ * @return A {@link GetUsersFollowedArtistsRequest.Builder}.
+ */
+ public Builder type(final ModelObjectType type) {
+ assert (type != null);
+ assert (type.getType().equals("artist"));
+ return setQueryParameter("type", type);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link GetUsersFollowedArtistsRequest.Builder}.
+ */
+ @Override
+ public Builder limit(final Integer limit) {
+ assert (limit != null);
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The after value setter.
+ *
+ * @param after Optional. The last artist ID retrieved from the previous request.
+ * @return A {@link GetUsersFollowedArtistsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ @Override
+ public Builder after(final String after) {
+ assert (after != null);
+ return setQueryParameter("after", after);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetUsersFollowedArtistsRequest}.
+ */
+ @Override
+ public GetUsersFollowedArtistsRequest build() {
+ setPath("/v1/me/following");
+ return new GetUsersFollowedArtistsRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/follow/UnfollowArtistsOrUsersRequest.java
@@ -0,0 +1,119 @@
+package com.wrapper.spotify.requests.data.follow;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonArray;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Remove the current user as a follower of one or more artists or other Spotify users.
+ */
+@JsonDeserialize(builder = UnfollowArtistsOrUsersRequest.Builder.class)
+public class UnfollowArtistsOrUsersRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link UnfollowArtistsOrUsersRequest} constructor.
+ *
+ * @param builder A {@link UnfollowArtistsOrUsersRequest.Builder}.
+ */
+ private UnfollowArtistsOrUsersRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Unfollow an artist or user.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return deleteJson();
+ }
+
+ /**
+ * Builder class for building an {@link UnfollowArtistsOrUsersRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link UnfollowArtistsOrUsersRequest.Builder} instance.
+ * <p>
+ * Modifying the list of artists or users the current user follows requires authorization of the
+ * {@code user-follow-modify} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The ID type setter.
+ *
+ * @param type Required. The ID type: either {@code artist} or {@code user}.
+ * @return An {@link UnfollowArtistsOrUsersRequest.Builder}.
+ */
+ public Builder type(final ModelObjectType type) {
+ assert (type != null);
+ assert (type.getType().equals("artist") || type.getType().equals("user"));
+ return setQueryParameter("type", type);
+ }
+
+ /**
+ * The artist or user IDs setter.
+ *
+ * @param ids Optional. A comma-separated list of the artist or the user Spotify IDs. A maximum of 50 IDs can be
+ * sent in one request.
+ * @return An {@link UnfollowArtistsOrUsersRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder ids(final String ids) {
+ assert (ids != null);
+ assert (ids.split(",").length <= 50);
+ return setQueryParameter("ids", ids);
+ }
+
+ /**
+ * The artist or user IDs setter.
+ * <p>
+ * <b>Note:</b> If the ids have already been set with {@link #ids(String)}, any ids added here will be ignored.
+ * @param ids Optional. A JSON array of the artist or the user Spotify IDs. A maximum of 50 IDs can be
+ * sent in one request.
+ * @return An {@link UnfollowArtistsOrUsersRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder ids(final JsonArray ids) {
+ assert (ids != null);
+ assert (!ids.isJsonNull());
+ assert (ids.size() <= 50);
+ return setBodyParameter("ids", ids);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link UnfollowArtistsOrUsersRequest}.
+ */
+ @Override
+ public UnfollowArtistsOrUsersRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/me/following");
+ return new UnfollowArtistsOrUsersRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/follow/UnfollowPlaylistRequest.java
@@ -0,0 +1,86 @@
+package com.wrapper.spotify.requests.data.follow;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Remove the current user as a follower of a playlist.
+ */
+@JsonDeserialize(builder = UnfollowPlaylistRequest.Builder.class)
+public class UnfollowPlaylistRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link UnfollowPlaylistRequest} constructor.
+ *
+ * @param builder A {@link UnfollowPlaylistRequest.Builder}.
+ */
+ private UnfollowPlaylistRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Unfollow a playlist.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return deleteJson();
+ }
+
+ /**
+ * Builder class for building an {@link UnfollowPlaylistRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link UnfollowPlaylistRequest.Builder} instance.
+ * <p>
+ * Unfollowing a publicly followed playlist for a user requires authorization of the {@code playlist-modify-public}
+ * scope; unfollowing a privately followed playlist requires the {@code playlist-modify-private} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The playlist ID setter.
+ *
+ * @param playlist_id The Spotify ID of the playlist that is to be no longer followed.
+ * @return An {@link UnfollowPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder playlist_id(final String playlist_id) {
+ assert (playlist_id != null);
+ assert (!playlist_id.equals(""));
+ return setPathParameter("playlist_id", playlist_id);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link UnfollowPlaylistRequest}.
+ */
+ @Override
+ public UnfollowPlaylistRequest build() {
+ setPath("/v1/playlists/{playlist_id}/followers");
+ return new UnfollowPlaylistRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/follow/legacy/FollowPlaylistRequest.java
@@ -0,0 +1,115 @@
+package com.wrapper.spotify.requests.data.follow.legacy;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Add the current user as a follower of a playlist.
+ */
+@JsonDeserialize(builder = FollowPlaylistRequest.Builder.class)
+public class FollowPlaylistRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link FollowPlaylistRequest} constructor.
+ *
+ * @param builder A {@link FollowPlaylistRequest.Builder}.
+ */
+ private FollowPlaylistRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Follow a playlist.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return putJson();
+ }
+
+ /**
+ * Builder class for building a {@link FollowPlaylistRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link FollowPlaylistRequest} instance.
+ * <p>
+ * Following a playlist publicly requires authorization of the {@code playlist-modify-public} scope; following it
+ * privately requires the {@code playlist-modify-private} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The playlist owner ID setter.
+ *
+ * @param owner_id The Spotify user ID of the person who owns the playlist.
+ * @return A {@link FollowPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder owner_id(final String owner_id) {
+ assert (owner_id != null);
+ assert (!owner_id.equals(""));
+ return setPathParameter("owner_id", owner_id);
+ }
+
+ /**
+ * The playlist ID setter.
+ *
+ * @param playlist_id The Spotify ID of the playlist. Any playlist can be followed, regardless of its public/private
+ * status, as long as you know its playlist ID.
+ * @return A {@link FollowPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder playlist_id(final String playlist_id) {
+ assert (playlist_id != null);
+ assert (!playlist_id.equals(""));
+ return setPathParameter("playlist_id", playlist_id);
+ }
+
+ /**
+ * The public following state setter.
+ *
+ * @param public_ Optional, default {@code true}. If {@code true} the playlist will be included in user's public
+ * playlists, if {@code false} it will remain private. To be able to follow playlists privately, the
+ * user must have granted the {@code playlist-modify-private} scope.
+ * @return A {@link FollowPlaylistRequest.Builder}.
+ */
+ public Builder public_(final Boolean public_) {
+ assert (public_ != null);
+ return setBodyParameter("public", public_);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link FollowPlaylistRequest}.
+ */
+ @Override
+ public FollowPlaylistRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/users/{owner_id}/playlists/{playlist_id}/followers");
+ return new FollowPlaylistRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/follow/legacy/UnfollowPlaylistRequest.java
@@ -0,0 +1,99 @@
+package com.wrapper.spotify.requests.data.follow.legacy;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Remove the specified user as a follower of a playlist.
+ */
+@JsonDeserialize(builder = UnfollowPlaylistRequest.Builder.class)
+public class UnfollowPlaylistRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link UnfollowPlaylistRequest} constructor.
+ *
+ * @param builder A {@link UnfollowPlaylistRequest.Builder}.
+ */
+ private UnfollowPlaylistRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Unfollow a playlist.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return deleteJson();
+ }
+
+ /**
+ * Builder class for building an {@link UnfollowPlaylistRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link UnfollowPlaylistRequest.Builder} instance.
+ * <p>
+ * Unfollowing a publicly followed playlist for a user requires authorization of the {@code playlist-modify-public}
+ * scope; unfollowing a privately followed playlist requires the {@code playlist-modify-private} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The playlist owner ID setter.
+ *
+ * @param owner_id The Spotify user ID of the person who owns the playlist.
+ * @return An {@link UnfollowPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder owner_id(final String owner_id) {
+ assert (owner_id != null);
+ assert (!owner_id.equals(""));
+ return setPathParameter("owner_id", owner_id);
+ }
+
+ /**
+ * The playlist ID setter.
+ *
+ * @param playlist_id The Spotify ID of the playlist that is to be no longer followed.
+ * @return An {@link UnfollowPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder playlist_id(final String playlist_id) {
+ assert (playlist_id != null);
+ assert (!playlist_id.equals(""));
+ return setPathParameter("playlist_id", playlist_id);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link UnfollowPlaylistRequest}.
+ */
+ @Override
+ public UnfollowPlaylistRequest build() {
+ setPath("/v1/users/{owner_id}/playlists/{playlist_id}/followers");
+ return new UnfollowPlaylistRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/library/CheckUsersSavedAlbumsRequest.java
@@ -0,0 +1,87 @@
+package com.wrapper.spotify.requests.data.library;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.Gson;
+import com.google.gson.JsonParser;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Check if one or more albums is already saved in the current Spotify user’s "Your Music" library.
+ */
+@JsonDeserialize(builder = CheckUsersSavedAlbumsRequest.Builder.class)
+public class CheckUsersSavedAlbumsRequest extends AbstractDataRequest<Boolean[]> {
+
+ /**
+ * The private {@link CheckUsersSavedAlbumsRequest} constructor.
+ *
+ * @param builder A {@link CheckUsersSavedAlbumsRequest.Builder}.
+ */
+ private CheckUsersSavedAlbumsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Check whether an album is present in the current user's "Your Music" library.
+ *
+ * @return Whether an album is present in the current user's "Your Music" library.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Boolean[] execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Gson().fromJson(JsonParser.parseString(getJson()).getAsJsonArray(), Boolean[].class);
+ }
+
+ /**
+ * Builder class for building a {@link CheckUsersSavedAlbumsRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<Boolean[], Builder> {
+
+ /**
+ * Create a new {@link CheckUsersSavedAlbumsRequest.Builder} instance.
+ * <p>
+ * The {@code user-library-read} scope must have been authorized by the user.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The album IDs setter.
+ *
+ * @param ids Required. A comma-separated list of the Spotify IDs for the albums. Maximum: 50 IDs.
+ * @return A {@link CheckUsersSavedAlbumsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder ids(final String ids) {
+ assert (ids != null);
+ assert (ids.split(",").length <= 50);
+ return setQueryParameter("ids", ids);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link CheckUsersSavedAlbumsRequest}.
+ */
+ @Override
+ public CheckUsersSavedAlbumsRequest build() {
+ setPath("/v1/me/albums/contains");
+ return new CheckUsersSavedAlbumsRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/library/CheckUsersSavedShowsRequest.java
@@ -0,0 +1,87 @@
+package com.wrapper.spotify.requests.data.library;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.Gson;
+import com.google.gson.JsonParser;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Check if one or more shows is already saved in the current Spotify user’s library.
+ */
+@JsonDeserialize(builder = CheckUsersSavedShowsRequest.Builder.class)
+public class CheckUsersSavedShowsRequest extends AbstractDataRequest<Boolean[]> {
+
+ /**
+ * The private {@link CheckUsersSavedShowsRequest} constructor.
+ *
+ * @param builder A {@link CheckUsersSavedShowsRequest.Builder}.
+ */
+ private CheckUsersSavedShowsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Check whether a show is present in the current user's "Your Music" library.
+ *
+ * @return Whether an show is present in the current user's "Your Music" library.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ @Override
+ public Boolean[] execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Gson().fromJson(JsonParser.parseString(getJson()).getAsJsonArray(), Boolean[].class);
+ }
+
+ /**
+ * Builder class for building a {@link CheckUsersSavedShowsRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<Boolean[], Builder> {
+ /**
+ * Create a new {@link CheckUsersSavedShowsRequest.Builder} instance.
+ * <p>
+ * The {@code user-library-read} scope must have been authorized by the user.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The show IDs setter.
+ *
+ * @param ids Required. A comma-separated list of the Spotify IDs for the shows. Maximum: 50 IDs.
+ * @return A {@link CheckUsersSavedShowsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public CheckUsersSavedShowsRequest.Builder ids(final String ids) {
+ assert (ids != null);
+ assert (ids.split(",").length <= 50);
+ return setQueryParameter("ids", ids);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link CheckUsersSavedShowsRequest}.
+ */
+ @Override
+ public CheckUsersSavedShowsRequest build() {
+ setPath("/v1/me/shows/contains");
+ return new CheckUsersSavedShowsRequest(this);
+ }
+
+ @Override
+ protected CheckUsersSavedShowsRequest.Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/library/CheckUsersSavedTracksRequest.java
@@ -0,0 +1,87 @@
+package com.wrapper.spotify.requests.data.library;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.Gson;
+import com.google.gson.JsonParser;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Check if one or more tracks is already saved in the current Spotify user’s "Your Music" library.
+ */
+@JsonDeserialize(builder = CheckUsersSavedTracksRequest.Builder.class)
+public class CheckUsersSavedTracksRequest extends AbstractDataRequest<Boolean[]> {
+
+ /**
+ * The private {@link CheckUsersSavedTracksRequest} constructor.
+ *
+ * @param builder A {@link CheckUsersSavedTracksRequest.Builder}.
+ */
+ private CheckUsersSavedTracksRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Check whether an track is present in the current user's "Your Music" library.
+ *
+ * @return Whether an track is present in the current user's "Your Music" library.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Boolean[] execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Gson().fromJson(JsonParser.parseString(getJson()).getAsJsonArray(), Boolean[].class);
+ }
+
+ /**
+ * Builder class for building a {@link CheckUsersSavedTracksRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<Boolean[], Builder> {
+
+ /**
+ * Create a new {@link CheckUsersSavedTracksRequest.Builder} instance.
+ * <p>
+ * The {@code user-library-read} scope must have been authorized by the user.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The track IDs setter.
+ *
+ * @param ids Required. A comma-separated list of the Spotify IDs for the tracks. Maximum: 50 IDs.
+ * @return A {@link CheckUsersSavedTracksRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder ids(final String ids) {
+ assert (ids != null);
+ assert (ids.split(",").length <= 50);
+ return setQueryParameter("ids", ids);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link CheckUsersSavedTracksRequest}.
+ */
+ @Override
+ public CheckUsersSavedTracksRequest build() {
+ setPath("/v1/me/tracks/contains");
+ return new CheckUsersSavedTracksRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/library/GetCurrentUsersSavedAlbumsRequest.java
@@ -0,0 +1,115 @@
+package com.wrapper.spotify.requests.data.library;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.model_objects.specification.SavedAlbum;
+import com.wrapper.spotify.requests.data.AbstractDataPagingRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get a list of the albums saved in the current Spotify user’s "Your Music" library.
+ */
+@JsonDeserialize(builder = GetCurrentUsersSavedAlbumsRequest.Builder.class)
+public class GetCurrentUsersSavedAlbumsRequest extends AbstractDataRequest<Paging<SavedAlbum>> {
+
+ /**
+ * The private {@link GetCurrentUsersSavedAlbumsRequest} constructor.
+ *
+ * @param builder A {@link GetCurrentUsersSavedAlbumsRequest.Builder}.
+ */
+ private GetCurrentUsersSavedAlbumsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get the saved albums of the current user.
+ *
+ * @return A {@link SavedAlbum} paging.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Paging<SavedAlbum> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new SavedAlbum.JsonUtil().createModelObjectPaging(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetCurrentUsersSavedAlbumsRequest}.
+ */
+ public static final class Builder extends AbstractDataPagingRequest.Builder<SavedAlbum, Builder> {
+
+ /**
+ * Create a new {@link GetCurrentUsersSavedAlbumsRequest.Builder} instance.
+ * <p>
+ * The {@code user-library-read} scope must have been authorized by the user.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The maximum number of objects to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link GetCurrentUsersSavedAlbumsRequest.Builder}.
+ */
+ @Override
+ public Builder limit(final Integer limit) {
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The offset setter.
+ *
+ * @param offset Optional. The index of the first object to return. Default: 0 (i.e., the first object). Use with
+ * {@link #limit(Integer)} to get the next set of objects.
+ * @return A {@link GetCurrentUsersSavedAlbumsRequest.Builder}.
+ */
+ @Override
+ public Builder offset(final Integer offset) {
+ assert (offset >= 0);
+ return setQueryParameter("offset", offset);
+ }
+
+ /**
+ * The market country code setter.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply
+ * Track Relinking.
+ * @return A {@link GetCurrentUsersSavedAlbumsRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ * @see <a href="https://developer.spotify.com/web-api/track-relinking-guide/">Spotify: Track Relinking Guide</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetCurrentUsersSavedAlbumsRequest}.
+ */
+ @Override
+ public GetCurrentUsersSavedAlbumsRequest build() {
+ setPath("/v1/me/albums");
+ return new GetCurrentUsersSavedAlbumsRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/library/GetUsersSavedShowsRequest.java
@@ -0,0 +1,102 @@
+package com.wrapper.spotify.requests.data.library;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.model_objects.specification.SavedShow;
+import com.wrapper.spotify.requests.data.AbstractDataPagingRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get a list of shows saved in the current Spotify user’s library.
+ */
+@JsonDeserialize(builder = GetUsersSavedShowsRequest.Builder.class)
+public class GetUsersSavedShowsRequest extends AbstractDataRequest<Paging<SavedShow>> {
+
+ /**
+ * The private {@link GetUsersSavedShowsRequest} constructor.
+ *
+ * @param builder A {@link GetUsersSavedShowsRequest.Builder}.
+ */
+ private GetUsersSavedShowsRequest(Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get a list of the current user’s saved shows.
+ *
+ * @return A {@link SavedShow} paging object.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ @Override
+ public Paging<SavedShow> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new SavedShow.JsonUtil().createModelObjectPaging(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetUsersSavedShowsRequest}.
+ */
+ public static final class Builder extends AbstractDataPagingRequest.Builder<SavedShow, Builder> {
+
+ /**
+ * Create a new {@link GetUsersSavedShowsRequest.Builder} instance.
+ * <p>
+ * The {@code user-library-read} scope must have been authorized by the user.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The maximum number of shows to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link GetUsersSavedShowsRequest.Builder}.
+ */
+ @Override
+ public Builder limit(final Integer limit) {
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+
+ /**
+ * The offset setter.
+ *
+ * @param offset Optional. The index of the first show to return. Default: 0 (i.e., the first object). Use with
+ * {@link #limit(Integer)} to get the next set of objects.
+ * @return A {@link GetUsersSavedShowsRequest.Builder}.
+ */
+ @Override
+ public Builder offset(final Integer offset) {
+ assert (offset >= 0);
+ return setQueryParameter("offset", offset);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetUsersSavedShowsRequest}.
+ */
+ @Override
+ public GetUsersSavedShowsRequest build() {
+ setPath("/v1/me/shows");
+ return new GetUsersSavedShowsRequest(this);
+ }
+
+ @Override
+ protected GetUsersSavedShowsRequest.Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/library/GetUsersSavedTracksRequest.java
@@ -0,0 +1,115 @@
+package com.wrapper.spotify.requests.data.library;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.model_objects.specification.SavedTrack;
+import com.wrapper.spotify.requests.data.AbstractDataPagingRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get a list of the songs saved in the current Spotify user’s "Your Music" library.
+ */
+@JsonDeserialize(builder = GetUsersSavedTracksRequest.Builder.class)
+public class GetUsersSavedTracksRequest extends AbstractDataRequest<Paging<SavedTrack>> {
+
+ /**
+ * The private {@link GetUsersSavedTracksRequest} constructor.
+ *
+ * @param builder A {@link GetUsersSavedTracksRequest.Builder}.
+ */
+ private GetUsersSavedTracksRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get the songs from the current users "Your Music" library.
+ *
+ * @return A {@link SavedTrack} paging.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Paging<SavedTrack> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new SavedTrack.JsonUtil().createModelObjectPaging(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetUsersSavedTracksRequest}.
+ */
+ public static final class Builder extends AbstractDataPagingRequest.Builder<SavedTrack, Builder> {
+
+ /**
+ * Create a new {@link GetUsersSavedTracksRequest.Builder} instance.
+ * <p>
+ * The {@code user-library-read} scope must have been authorized by the user.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The maximum number of objects to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link GetUsersSavedTracksRequest.Builder}.
+ */
+ @Override
+ public Builder limit(final Integer limit) {
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The offset setter.
+ *
+ * @param offset Optional. The index of the first object to return. Default: 0 (i.e., the first object). Use with
+ * {@link #limit(Integer)} to get the next set of objects.
+ * @return A {@link GetUsersSavedTracksRequest.Builder}.
+ */
+ @Override
+ public Builder offset(final Integer offset) {
+ assert (offset >= 0);
+ return setQueryParameter("offset", offset);
+ }
+
+ /**
+ * The market country code setter.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply
+ * Track Relinking.
+ * @return A {@link GetUsersSavedTracksRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ * @see <a href="https://developer.spotify.com/web-api/track-relinking-guide/">Spotify: Track Relinking Guide</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetUsersSavedTracksRequest}.
+ */
+ @Override
+ public GetUsersSavedTracksRequest build() {
+ setPath("/v1/me/tracks");
+ return new GetUsersSavedTracksRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/library/RemoveAlbumsForCurrentUserRequest.java
@@ -0,0 +1,104 @@
+package com.wrapper.spotify.requests.data.library;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonArray;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Remove one or more albums from the current user’s "Your Music" library.
+ */
+@JsonDeserialize(builder = RemoveAlbumsForCurrentUserRequest.Builder.class)
+public class RemoveAlbumsForCurrentUserRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link RemoveAlbumsForCurrentUserRequest} constructor.
+ *
+ * @param builder A {@link RemoveAlbumsForCurrentUserRequest.Builder}.
+ */
+ private RemoveAlbumsForCurrentUserRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Remove one or more albums from the current user’s ‘Your Music’ library.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return deleteJson();
+ }
+
+ /**
+ * Builder class for building a {@link RemoveAlbumsForCurrentUserRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link RemoveAlbumsForCurrentUserRequest.Builder} instance.
+ * <p>
+ * Modification of the current user's "Your Music" collection requires authorization of the
+ * {@code user-library-modify} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The album IDs setter.
+ *
+ * @param ids Optional. A comma-separated list of the Spotify IDs. Maximum: 50 IDs.
+ * @return A {@link RemoveAlbumsForCurrentUserRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder ids(final String ids) {
+ assert (ids != null);
+ assert (ids.split(",").length <= 50);
+ return setQueryParameter("ids", ids);
+ }
+
+ /**
+ * The album IDs setter.
+ * <p>
+ * <b>Note:</b> If the ids have already been set with {@link #ids(String)}, any ids added here will be ignored.
+ * @param ids Optional. A JSON array of the Spotify IDs. Maximum: 50 IDs.
+ * @return A {@link RemoveAlbumsForCurrentUserRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder ids(final JsonArray ids) {
+ assert (ids != null);
+ assert (!ids.isJsonNull());
+ assert (ids.size() <= 50);
+ return setBodyParameter("ids", ids);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link RemoveAlbumsForCurrentUserRequest.Builder}.
+ */
+ @Override
+ public RemoveAlbumsForCurrentUserRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/me/albums");
+ return new RemoveAlbumsForCurrentUserRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/library/RemoveUsersSavedShowsRequest.java
@@ -0,0 +1,122 @@
+package com.wrapper.spotify.requests.data.library;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonArray;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Delete one or more shows from current Spotify user’s library.
+ */
+@JsonDeserialize(builder = RemoveUsersSavedShowsRequest.Builder.class)
+public class RemoveUsersSavedShowsRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link RemoveUsersSavedShowsRequest} constructor.
+ *
+ * @param builder A {@link RemoveUsersSavedShowsRequest.Builder}.
+ */
+ private RemoveUsersSavedShowsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Delete one or more shows from current Spotify user’s library.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ @Override
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return deleteJson();
+ }
+
+ /**
+ * Builder class for building a {@link RemoveUsersSavedShowsRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link RemoveUsersSavedShowsRequest.Builder} instance.
+ * <p>
+ * Modification of the current user's "Your Music" collection requires authorization of the
+ * {@code user-library-modify} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The show IDs setter.
+ *
+ * @param ids Optional. A comma-separated list of Spotify IDs for the shows to be deleted from the user’s library. Maximum: 50 IDs.
+ * @return A {@link RemoveUsersSavedShowsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder ids(final String ids) {
+ assert (ids != null);
+ assert (ids.split(",").length <= 50);
+ return setQueryParameter("ids", ids);
+ }
+
+ /**
+ * The market country code setter.<p>
+ * If a country code is specified, only shows that are available in that market will be removed.
+ * If a valid user access token is specified in the request header, the country associated with the user account will take priority over this parameter.
+ * <i>Note: If neither market or user country are provided, the content is considered unavailable for the client.</i><p>
+ * Users can view the country that is associated with their account in the account settings.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code.
+ * @return A {@link RemoveUsersSavedShowsRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The show IDs setter.
+ * <p>
+ * <b>Note:</b> If the ids have already been set with {@link #ids(String)}, any ids added here will be ignored.
+ * @param ids Optional. A JSON array of Spotify IDs for the shows to be deleted from the user’s library. Maximum: 50 IDs.
+ * @return A {@link RemoveUsersSavedShowsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder ids(final JsonArray ids) {
+ assert (ids != null);
+ assert (!ids.isJsonNull());
+ assert (ids.size() <= 50);
+ return setBodyParameter("ids", ids);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link RemoveUsersSavedShowsRequest}.
+ */
+ @Override
+ public RemoveUsersSavedShowsRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/me/shows");
+ return new RemoveUsersSavedShowsRequest(this);
+ }
+
+ @Override
+ protected RemoveUsersSavedShowsRequest.Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/library/RemoveUsersSavedTracksRequest.java
@@ -0,0 +1,104 @@
+package com.wrapper.spotify.requests.data.library;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonArray;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Remove one or more tracks from the current user’s "Your Music" library.
+ */
+@JsonDeserialize(builder = RemoveUsersSavedTracksRequest.Builder.class)
+public class RemoveUsersSavedTracksRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link RemoveUsersSavedTracksRequest} constructor.
+ *
+ * @param builder A {@link RemoveUsersSavedTracksRequest.Builder}.
+ */
+ private RemoveUsersSavedTracksRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Remove one or more tracks from the current user’s ‘Your Music’ library.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return deleteJson();
+ }
+
+ /**
+ * Builder class for building a {@link RemoveUsersSavedTracksRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link RemoveUsersSavedTracksRequest.Builder} instance.
+ * <p>
+ * Modification of the current user's "Your Music" collection requires authorization of the
+ * {@code user-library-modify} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The track IDs setter.
+ *
+ * @param ids Optional. A comma-separated list of the Spotify IDs. Maximum: 50 IDs.
+ * @return A {@link RemoveUsersSavedTracksRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder ids(final String ids) {
+ assert (ids != null);
+ assert (ids.split(",").length <= 50);
+ return setQueryParameter("ids", ids);
+ }
+
+ /**
+ * The track IDs setter.
+ * <p>
+ * <b>Note:</b> If the ids have already been set with {@link #ids(String)}, any ids added here will be ignored.
+ * @param ids Optional. A JSON array of the Spotify IDs. Maximum: 50 IDs.
+ * @return A {@link RemoveUsersSavedTracksRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder ids(final JsonArray ids) {
+ assert (ids != null);
+ assert (!ids.isJsonNull());
+ assert (ids.size() <= 50);
+ return setBodyParameter("ids", ids);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link RemoveUsersSavedTracksRequest}.
+ */
+ @Override
+ public RemoveUsersSavedTracksRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/me/tracks");
+ return new RemoveUsersSavedTracksRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/library/SaveAlbumsForCurrentUserRequest.java
@@ -0,0 +1,103 @@
+package com.wrapper.spotify.requests.data.library;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonArray;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Save one or more albums to the current user’s "Your Music" library.
+ */
+@JsonDeserialize(builder = SaveAlbumsForCurrentUserRequest.Builder.class)
+public class SaveAlbumsForCurrentUserRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link SaveAlbumsForCurrentUserRequest} constructor.
+ *
+ * @param builder A {@link SaveAlbumsForCurrentUserRequest.Builder}.
+ */
+ private SaveAlbumsForCurrentUserRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Save an album to the "Your Music" library.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return putJson();
+ }
+
+ /**
+ * Builder class for building a {@link SaveAlbumsForCurrentUserRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link SaveAlbumsForCurrentUserRequest.Builder} instance.
+ * <p>
+ * Modification of the current user's "Your Music" collection requires authorization of the
+ * {@code user-library-modify} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The album IDs setter.
+ *
+ * @param ids Optional. A comma-separated list of the Spotify IDs. Maximum: 50 IDs.
+ * @return A {@link SaveAlbumsForCurrentUserRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder ids(final String ids) {
+ assert (ids != null);
+ assert (ids.split(",").length <= 50);
+ return setQueryParameter("ids", ids);
+ }
+
+ /**
+ * The album IDs setter.
+ *
+ * @param ids Optional. A json array consisting of the Spotify IDs. Maximum: 50 IDs.
+ * @return A {@link SaveAlbumsForCurrentUserRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder ids(final JsonArray ids) {
+ assert (ids != null);
+ assert (!ids.isJsonNull());
+ assert (ids.size() <= 50);
+ return setBodyParameter("ids", ids);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link SaveAlbumsForCurrentUserRequest}.
+ */
+ @Override
+ public SaveAlbumsForCurrentUserRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/me/albums");
+ return new SaveAlbumsForCurrentUserRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/library/SaveShowsForCurrentUserRequest.java
@@ -0,0 +1,105 @@
+package com.wrapper.spotify.requests.data.library;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonArray;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Save one or more shows to current Spotify user’s library.
+ */
+@JsonDeserialize(builder = SaveShowsForCurrentUserRequest.Builder.class)
+public class SaveShowsForCurrentUserRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link SaveShowsForCurrentUserRequest} constructor.
+ *
+ * @param builder A {@link SaveShowsForCurrentUserRequest.Builder}.
+ */
+ private SaveShowsForCurrentUserRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Save one or more shows.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ @Override
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return putJson();
+ }
+
+ /**
+ * Builder class for building a {@link SaveShowsForCurrentUserRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link SaveShowsForCurrentUserRequest.Builder} instance.
+ * <p>
+ * Modification of the current user's "Your Music" collection requires authorization of the
+ * {@code user-library-modify} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The show IDs setter.
+ *
+ * @param ids Optional. A comma-separated list of Spotify IDs for the shows to be added to the user’s library.
+ * @return A {@link SaveShowsForCurrentUserRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder ids(final String ids) {
+ assert (ids != null);
+ assert (ids.split(",").length <= 50);
+ return setQueryParameter("ids", ids);
+ }
+
+ /**
+ * The show IDs setter.
+ * <p>
+ * <b>Note:</b> If the ids have already been set with {@link #ids(String)}, any ids added here will be ignored.
+ * @param ids Optional. A JSON array of Spotify IDs for the shows to be added to the user’s library.
+ * @return A {@link SaveShowsForCurrentUserRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder ids(final JsonArray ids) {
+ assert (ids != null);
+ assert (!ids.isJsonNull());
+ assert (ids.size() <= 50);
+ return setBodyParameter("ids", ids);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link SaveShowsForCurrentUserRequest}.
+ */
+ @Override
+ public SaveShowsForCurrentUserRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/me/shows");
+ return new SaveShowsForCurrentUserRequest(this);
+ }
+
+ @Override
+ protected SaveShowsForCurrentUserRequest.Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/library/SaveTracksForUserRequest.java
@@ -0,0 +1,103 @@
+package com.wrapper.spotify.requests.data.library;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonArray;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Save one or more tracks to the current user’s "Your Music" library.
+ */
+@JsonDeserialize(builder = SaveTracksForUserRequest.Builder.class)
+public class SaveTracksForUserRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link SaveTracksForUserRequest} constructor.
+ *
+ * @param builder A {@link SaveTracksForUserRequest.Builder}.
+ */
+ private SaveTracksForUserRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Save one or more tracks.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return putJson();
+ }
+
+ /**
+ * Builder class for building a {@link SaveTracksForUserRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link SaveTracksForUserRequest.Builder} instance.
+ * <p>
+ * Modification of the current user's "Your Music" collection requires authorization of the
+ * {@code user-library-modify} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The track IDs setter.
+ *
+ * @param ids Optional. A comma-separated list of the Spotify IDs. Maximum: 50 IDs.
+ * @return A {@link SaveTracksForUserRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder ids(final String ids) {
+ assert (ids != null);
+ assert (ids.split(",").length <= 50);
+ return setQueryParameter("ids", ids);
+ }
+
+ /**
+ * The track IDs setter.
+ *
+ * @param ids Optional. A json array consisting of the Spotify IDs. Maximum: 50 IDs.
+ * @return A {@link SaveTracksForUserRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder ids(final JsonArray ids) {
+ assert (ids != null);
+ assert (!ids.isJsonNull());
+ assert (ids.size() <= 50);
+ return setBodyParameter("ids", ids);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link SaveTracksForUserRequest}.
+ */
+ @Override
+ public SaveTracksForUserRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/me/tracks");
+ return new SaveTracksForUserRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/personalization/GetUsersTopArtistsAndTracksRequest.java
@@ -0,0 +1,159 @@
+package com.wrapper.spotify.requests.data.personalization;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.enums.ModelObjectType;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.AbstractModelObject;
+import com.wrapper.spotify.model_objects.specification.Artist;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.model_objects.specification.Track;
+import com.wrapper.spotify.requests.data.AbstractDataPagingRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import com.wrapper.spotify.requests.data.personalization.interfaces.IArtistTrackModelObject;
+import com.wrapper.spotify.requests.data.personalization.simplified.GetUsersTopArtistsRequest;
+import com.wrapper.spotify.requests.data.personalization.simplified.GetUsersTopTracksRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * This class only exists for theoretical purposes. Please use {@link GetUsersTopArtistsRequest} and
+ * {@link GetUsersTopTracksRequest} instead.
+ *
+ * @param <T> The request {@link ModelObjectType}: artist or track.
+ */
+@JsonDeserialize(builder = GetUsersTopArtistsAndTracksRequest.Builder.class)
+public class GetUsersTopArtistsAndTracksRequest<T extends IArtistTrackModelObject> extends AbstractDataRequest<Paging<T>> {
+
+ private final AbstractModelObject.JsonUtil<T> tClass;
+
+ /**
+ * The private {@link GetUsersTopArtistsAndTracksRequest} constructor.
+ *
+ * @param builder A {@link GetUsersTopArtistsAndTracksRequest.Builder}.
+ * @param tClass A {@link AbstractModelObject.JsonUtil}.
+ */
+ private GetUsersTopArtistsAndTracksRequest(final Builder<T> builder, final AbstractModelObject.JsonUtil<T> tClass) {
+ super(builder);
+ this.tClass = tClass;
+ }
+
+ /**
+ * Get the top artists and tracks.
+ *
+ * @return Top artists and tracks.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Paging<T> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return tClass.createModelObjectPaging(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetUsersTopArtistsAndTracksRequest}.
+ * <p>
+ * This class only exists for theoretical purposes. Please use {@link GetUsersTopArtistsRequest.Builder} and
+ * {@link GetUsersTopTracksRequest.Builder} instead.
+ *
+ * @param <T> The request {@link ModelObjectType}: artist or track.
+ */
+ public static final class Builder<T extends IArtistTrackModelObject> extends AbstractDataPagingRequest.Builder<T, Builder<T>> {
+
+ private AbstractModelObject.JsonUtil<T> tClass;
+
+ /**
+ * Create a new {@link GetUsersTopArtistsAndTracksRequest.Builder} instance.
+ * <p>
+ * Getting details of a user's top artists and tracks requires authorization of the {@code user-top-read} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The model object type setter.
+ *
+ * @param type The type of entity to return. Valid values: {@code artists} or {@code tracks}.
+ * @return A {@link GetUsersTopArtistsAndTracksRequest.Builder}.
+ */
+ @SuppressWarnings("unchecked")
+ public Builder<T> type(final ModelObjectType type) {
+ assert (type != null);
+ assert (type.getType().equals("artists") || type.getType().equals("tracks"));
+
+ switch (type.getType()) {
+ case "artists":
+ tClass = (AbstractModelObject.JsonUtil<T>) new Artist.JsonUtil();
+ break;
+ case "tracks":
+ tClass = (AbstractModelObject.JsonUtil<T>) new Track.JsonUtil();
+ break;
+ }
+
+ return setPathParameter("type", type.getType());
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The number of entities to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link GetUsersTopArtistsAndTracksRequest.Builder}.
+ */
+ @Override
+ public Builder<T> limit(final Integer limit) {
+ assert (limit != null);
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The offset setter.
+ *
+ * @param offset Optional. The index of the first entity to return. Default: 0 (i.e., the first track). Use with
+ * {@link #limit(Integer)} to get the next set of entities.
+ * @return A {@link GetUsersTopArtistsAndTracksRequest.Builder}.
+ */
+ @Override
+ public Builder<T> offset(final Integer offset) {
+ assert (offset >= 0);
+ return setQueryParameter("offset", offset);
+ }
+
+ /**
+ * The time range setter.
+ *
+ * @param time_range Optional. Over what time frame the affinities are computed. Valid values: {@code long_term}
+ * (calculated from several years of data and including all new data as it becomes available),
+ * {@code medium_term} (approximately last 6 months), {@code short_term} (approximately last 4
+ * weeks). Default: {@code medium_term}.
+ * @return A {@link GetUsersTopArtistsAndTracksRequest.Builder}.
+ */
+ public Builder<T> time_range(final String time_range) {
+ assert (time_range != null);
+ assert (time_range.equals("long_term") || time_range.equals("medium_term") || time_range.equals("short_term"));
+ return setQueryParameter("time_range", time_range);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetUsersTopArtistsAndTracksRequest}.
+ */
+ @Override
+ public GetUsersTopArtistsAndTracksRequest<T> build() {
+ setPath("/v1/me/top/{type}");
+ return new GetUsersTopArtistsAndTracksRequest<>(this, tClass);
+ }
+
+ @Override
+ protected Builder<T> self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/personalization/interfaces/IArtistTrackModelObject.java
@@ -0,0 +1,6 @@
+package com.wrapper.spotify.requests.data.personalization.interfaces;
+
+import com.wrapper.spotify.model_objects.IModelObject;
+
+public interface IArtistTrackModelObject extends IModelObject {
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/personalization/simplified/GetUsersTopArtistsRequest.java
@@ -0,0 +1,126 @@
+package com.wrapper.spotify.requests.data.personalization.simplified;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Artist;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.requests.data.AbstractDataPagingRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get the current user’s top artists based on calculated affinity.
+ * <p>
+ * Affinity is a measure of the expected preference a user has for a particular track or artist. It is based on user
+ * behavior, including play history, but does not include actions made while in incognito mode.
+ * Light or infrequent users of Spotify may not have sufficient play history to generate a full affinity data set.
+ * <p>
+ * As a user’s behavior is likely to shift over time, this preference data is available over three time spans. See
+ * {@link Builder#time_range(String)} for more information.
+ * <p>
+ * For each time range, the top 50 tracks and artists are available for each user. In the future, it is likely that this
+ * restriction will be relaxed. This data is typically updated once each day for each user.
+ */
+@JsonDeserialize(builder = GetUsersTopArtistsRequest.Builder.class)
+public class GetUsersTopArtistsRequest extends AbstractDataRequest<Paging<Artist>> {
+
+ /**
+ * The private {@link GetUsersTopArtistsRequest} constructor.
+ *
+ * @param builder A {@link GetUsersTopArtistsRequest.Builder}.
+ */
+ private GetUsersTopArtistsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get an user's top artists.
+ *
+ * @return An user's top artists.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Paging<Artist> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Artist.JsonUtil().createModelObjectPaging(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetUsersTopArtistsRequest}.
+ */
+ public static final class Builder extends AbstractDataPagingRequest.Builder<Artist, Builder> {
+
+ /**
+ * Create a new {@link GetUsersTopArtistsRequest.Builder} instance.
+ * <p>
+ * Getting details of a user's top artists requires authorization of the {@code user-top-read} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The number of entities to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link GetUsersTopArtistsRequest.Builder}.
+ */
+ @Override
+ public Builder limit(final Integer limit) {
+ assert (limit != null);
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The offset setter.
+ *
+ * @param offset Optional. The index of the first entity to return. Default: 0 (i.e., the first track). Use with
+ * {@link #limit(Integer)} to get the next set of entities.
+ * @return A {@link GetUsersTopArtistsRequest.Builder}.
+ */
+ @Override
+ public Builder offset(final Integer offset) {
+ assert (offset >= 0);
+ return setQueryParameter("offset", offset);
+ }
+
+ /**
+ * The time range setter.
+ *
+ * @param time_range Optional. Over what time frame the affinities are computed. Valid values: {@code long_term}
+ * (calculated from several years of data and including all new data as it becomes available),
+ * {@code medium_term} (approximately last 6 months), {@code short_term} (approximately last 4
+ * weeks). Default: {@code medium_term}.
+ * @return A {@link GetUsersTopArtistsRequest.Builder}.
+ */
+ public Builder time_range(final String time_range) {
+ assert (time_range != null);
+ assert (time_range.equals("long_term") || time_range.equals("medium_term") || time_range.equals("short_term"));
+ return setQueryParameter("time_range", time_range);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetUsersTopArtistsRequest}.
+ */
+ @Override
+ public GetUsersTopArtistsRequest build() {
+ setPath("/v1/me/top/artists");
+ return new GetUsersTopArtistsRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/personalization/simplified/GetUsersTopTracksRequest.java
@@ -0,0 +1,126 @@
+package com.wrapper.spotify.requests.data.personalization.simplified;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.model_objects.specification.Track;
+import com.wrapper.spotify.requests.data.AbstractDataPagingRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get the current user’s top tracks based on calculated affinity.
+ * <p>
+ * Affinity is a measure of the expected preference a user has for a particular track or artist. It is based on user
+ * behavior, including play history, but does not include actions made while in incognito mode.
+ * Light or infrequent users of Spotify may not have sufficient play history to generate a full affinity data set.
+ * <p>
+ * As a user’s behavior is likely to shift over time, this preference data is available over three time spans. See
+ * {@link Builder#time_range(String)} for more information.
+ * <p>
+ * For each time range, the top 50 tracks and artists are available for each user. In the future, it is likely that this
+ * restriction will be relaxed. This data is typically updated once each day for each user.
+ */
+@JsonDeserialize(builder = GetUsersTopTracksRequest.Builder.class)
+public class GetUsersTopTracksRequest extends AbstractDataRequest<Paging<Track>> {
+
+ /**
+ * The private {@link GetUsersTopTracksRequest} constructor.
+ *
+ * @param builder A {@link GetUsersTopTracksRequest.Builder}.
+ */
+ private GetUsersTopTracksRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get an user's top tracks.
+ *
+ * @return An user's top tracks.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Paging<Track> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Track.JsonUtil().createModelObjectPaging(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetUsersTopTracksRequest}.
+ */
+ public static final class Builder extends AbstractDataPagingRequest.Builder<Track, Builder> {
+
+ /**
+ * Create a new {@link GetUsersTopTracksRequest.Builder} instance.
+ * <p>
+ * Getting details of a user's top tracks requires authorization of the {@code user-top-read} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The number of entities to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link GetUsersTopTracksRequest.Builder}.
+ */
+ @Override
+ public Builder limit(final Integer limit) {
+ assert (limit != null);
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The offset setter.
+ *
+ * @param offset Optional. The index of the first entity to return. Default: 0 (i.e., the first track). Use with
+ * {@link #limit(Integer)} to get the next set of entities.
+ * @return A {@link GetUsersTopTracksRequest.Builder}.
+ */
+ @Override
+ public Builder offset(final Integer offset) {
+ assert (offset >= 0);
+ return setQueryParameter("offset", offset);
+ }
+
+ /**
+ * The time range setter.
+ *
+ * @param time_range Optional. Over what time frame the affinities are computed. Valid values: {@code long_term}
+ * (calculated from several years of data and including all new data as it becomes available),
+ * {@code medium_term} (approximately last 6 months), {@code short_term} (approximately last 4
+ * weeks). Default: {@code medium_term}.
+ * @return A {@link GetUsersTopTracksRequest.Builder}.
+ */
+ public Builder time_range(final String time_range) {
+ assert (time_range != null);
+ assert (time_range.equals("long_term") || time_range.equals("medium_term") || time_range.equals("short_term"));
+ return setQueryParameter("time_range", time_range);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetUsersTopTracksRequest}
+ */
+ @Override
+ public GetUsersTopTracksRequest build() {
+ setPath("/v1/me/top/tracks");
+ return new GetUsersTopTracksRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/player/AddItemToUsersPlaybackQueueRequest.java
@@ -0,0 +1,105 @@
+package com.wrapper.spotify.requests.data.player;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Add a track or an episode to the end of the user's current playback queue.
+ */
+@JsonDeserialize(builder = AddItemToUsersPlaybackQueueRequest.Builder.class)
+public class AddItemToUsersPlaybackQueueRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link AddItemToUsersPlaybackQueueRequest} constructor.
+ *
+ * @param builder A {@link AddItemToUsersPlaybackQueueRequest.Builder}.
+ */
+ private AddItemToUsersPlaybackQueueRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Add an item to the user's playback queue.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ @Override
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return postJson();
+ }
+
+ /**
+ * Builder class for building a {@link AddItemToUsersPlaybackQueueRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link AddItemToUsersPlaybackQueueRequest.Builder}.
+ * <p>
+ * Your access token must have the {@code user-modify-playback-state} scope authorized in order to control playback.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The device ID setter.
+ *
+ * @param device_id Optional. The ID of the device this command is targeting. If not supplied, the
+ * user's currently active device is the target.
+ * @return A {@link AddItemToUsersPlaybackQueueRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder device_id(final String device_id) {
+ assert (device_id != null);
+ assert (!device_id.equals(""));
+ return setQueryParameter("device_id", device_id);
+ }
+
+ /**
+ * The uri setter.
+ *
+ * @param uri Required. The uri of the item to add to the queue.
+ * Must be a track or an episode uri.
+ * @return A {@link AddItemToUsersPlaybackQueueRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder uri(final String uri) {
+ assert (uri != null);
+ assert (!uri.equals(""));
+ return setQueryParameter("uri", uri);
+ }
+
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link AddItemToUsersPlaybackQueueRequest}.
+ */
+ @Override
+ public AddItemToUsersPlaybackQueueRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/me/player/queue");
+ return new AddItemToUsersPlaybackQueueRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/player/GetCurrentUsersRecentlyPlayedTracksRequest.java
@@ -0,0 +1,127 @@
+package com.wrapper.spotify.requests.data.player;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.SpotifyApi;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.PagingCursorbased;
+import com.wrapper.spotify.model_objects.specification.PlayHistory;
+import com.wrapper.spotify.requests.data.AbstractDataPagingCursorbasedRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+import java.util.Date;
+
+/**
+ * Get tracks from the current user’s recently played tracks.
+ * <p>
+ * Returns the most recent 50 tracks played by a user. Note that a track currently playing will not be visible in play
+ * history until it has completed. A track must be played for more than 30 seconds to be included in play history.
+ * <p>
+ * Any tracks listened to while the user had "Private Session" enabled in their client will not be returned in the list
+ * of recently played tracks.
+ * <p>
+ * The endpoint uses a bidirectional cursor for paging. Follow the {@code next} field with the {@code before} parameter
+ * to move back in time, or use the after parameter to move forward in time. If you supply no {@code before} or
+ * {@code after} parameter, the endpoint will return the most recently played songs, and the {@code next}
+ * link will page back in time.
+ */
+@JsonDeserialize(builder = GetCurrentUsersRecentlyPlayedTracksRequest.Builder.class)
+public class GetCurrentUsersRecentlyPlayedTracksRequest extends AbstractDataRequest<PagingCursorbased<PlayHistory>> {
+
+ /**
+ * The private {@link GetCurrentUsersRecentlyPlayedTracksRequest} constructor.
+ *
+ * @param builder A {@link GetCurrentUsersRecentlyPlayedTracksRequest.Builder}.
+ */
+ private GetCurrentUsersRecentlyPlayedTracksRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get an user's recently played tracks.
+ *
+ * @return An user's recently played tracks.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public PagingCursorbased<PlayHistory> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new PlayHistory.JsonUtil().createModelObjectPagingCursorbased(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetCurrentUsersRecentlyPlayedTracksRequest}.
+ */
+ public static final class Builder extends AbstractDataPagingCursorbasedRequest.Builder<PlayHistory, Date, Builder> {
+
+ /**
+ * Create a new {@link GetCurrentUsersRecentlyPlayedTracksRequest.Builder}.
+ * <p>
+ * Your access token must have the {@code user-read-recently-played} scope authorized in order to read
+ * the user's recently played track.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link GetCurrentUsersRecentlyPlayedTracksRequest.Builder}.
+ */
+ @Override
+ public Builder limit(final Integer limit) {
+ assert (limit != null);
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The after date setter.
+ *
+ * @param after Optional. A {@link Date} object. Returns all items after (but not including) this cursor position.
+ * If this is specified, {@link #before(Date)} must not be specified.
+ * @return A {@link GetCurrentUsersRecentlyPlayedTracksRequest.Builder}.
+ */
+ @Override
+ public Builder after(final Date after) {
+ assert (after != null);
+ return setQueryParameter("after", SpotifyApi.formatDefaultDate(after));
+ }
+
+ /**
+ * The before date setter.
+ *
+ * @param before Optional. A {@link Date} object. Returns all items before (but not including) this cursor position.
+ * If this is specified, {@link #after(Date)} must not be specified.
+ * @return A {@link GetCurrentUsersRecentlyPlayedTracksRequest.Builder}.
+ */
+ public Builder before(final Date before) {
+ assert (before != null);
+ return setQueryParameter("before", SpotifyApi.formatDefaultDate(before));
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetCurrentUsersRecentlyPlayedTracksRequest}.
+ */
+ @Override
+ public GetCurrentUsersRecentlyPlayedTracksRequest build() {
+ setPath("/v1/me/player/recently-played");
+ return new GetCurrentUsersRecentlyPlayedTracksRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/player/GetInformationAboutUsersCurrentPlaybackRequest.java
@@ -0,0 +1,102 @@
+package com.wrapper.spotify.requests.data.player;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.miscellaneous.CurrentlyPlayingContext;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get information about the user’s current playback state, including track, track progress, and active device.
+ */
+@JsonDeserialize(builder = GetInformationAboutUsersCurrentPlaybackRequest.Builder.class)
+public class GetInformationAboutUsersCurrentPlaybackRequest extends AbstractDataRequest<CurrentlyPlayingContext> {
+
+ /**
+ * The private {@link GetInformationAboutUsersCurrentPlaybackRequest} constructor.
+ *
+ * @param builder A {@link GetInformationAboutUsersCurrentPlaybackRequest.Builder}.
+ */
+ private GetInformationAboutUsersCurrentPlaybackRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get information about a user's playback.
+ *
+ * @return Information about a users playback.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public CurrentlyPlayingContext execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new CurrentlyPlayingContext.JsonUtil().createModelObject(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetInformationAboutUsersCurrentPlaybackRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<CurrentlyPlayingContext, Builder> {
+
+ /**
+ * Create a new {@link GetInformationAboutUsersCurrentPlaybackRequest.Builder}.
+ * <p>
+ * Your access token must have the {@code user-read-playback-state} scope authorized in order to read information.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The market country code setter.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code. Provide this parameter if you
+ * want to apply Track Relinking.
+ * @return A {@link GetInformationAboutUsersCurrentPlaybackRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/track-relinking-guide/">Spotify: Track Relinking Guide</a>
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The additional types setter.
+ *
+ * @param additionalTypes Optional. A comma-separated list of item types that your client supports
+ * besides the default track type. Valid types are: {@code track} and {@code episode}.
+ * An unsupported type in the response is expected to be represented as {@code null} value in the {@code item} field.
+ * @return A {@link GetInformationAboutUsersCurrentPlaybackRequest.Builder}.
+ */
+ public Builder additionalTypes(final String additionalTypes) {
+ assert (additionalTypes != null);
+ assert (additionalTypes.matches("((^|,)(episode|track))+$"));
+ return setQueryParameter("additional_types", additionalTypes);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetInformationAboutUsersCurrentPlaybackRequest}.
+ */
+ @Override
+ public GetInformationAboutUsersCurrentPlaybackRequest build() {
+ setPath("/v1/me/player");
+ return new GetInformationAboutUsersCurrentPlaybackRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/player/GetUsersAvailableDevicesRequest.java
@@ -0,0 +1,73 @@
+package com.wrapper.spotify.requests.data.player;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.miscellaneous.Device;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get information about a user’s available devices.
+ */
+@JsonDeserialize(builder = GetUsersAvailableDevicesRequest.Builder.class)
+public class GetUsersAvailableDevicesRequest extends AbstractDataRequest<Device[]> {
+
+ /**
+ * The private {@link GetUsersAvailableDevicesRequest} constructor.
+ *
+ * @param builder A {@link GetUsersAvailableDevicesRequest.Builder}.
+ */
+ private GetUsersAvailableDevicesRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get an user's available devices.
+ *
+ * @return An user's available devices.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Device[] execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Device.JsonUtil().createModelObjectArray(getJson(), "devices");
+ }
+
+ /**
+ * Builder class for building a {@link GetUsersAvailableDevicesRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<Device[], Builder> {
+
+ /**
+ * Create a new {@link GetUsersAvailableDevicesRequest.Builder}.
+ * <p>
+ * Your access token must have the {@code user-read-playback-state} scope authorized in order to read information.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetUsersAvailableDevicesRequest}.
+ */
+ @Override
+ public GetUsersAvailableDevicesRequest build() {
+ setPath("/v1/me/player/devices");
+ return new GetUsersAvailableDevicesRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/player/GetUsersCurrentlyPlayingTrackRequest.java
@@ -0,0 +1,103 @@
+package com.wrapper.spotify.requests.data.player;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.miscellaneous.CurrentlyPlaying;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get the object currently being played on the user’s Spotify account.
+ */
+@JsonDeserialize(builder = GetUsersCurrentlyPlayingTrackRequest.Builder.class)
+public class GetUsersCurrentlyPlayingTrackRequest extends AbstractDataRequest<CurrentlyPlaying> {
+
+ /**
+ * The private {@link GetUsersCurrentlyPlayingTrackRequest} constructor.
+ *
+ * @param builder A {@link GetUsersCurrentlyPlayingTrackRequest.Builder}.
+ */
+ private GetUsersCurrentlyPlayingTrackRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get an user's currently playing track.
+ *
+ * @return An user's currently playing track.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public CurrentlyPlaying execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new CurrentlyPlaying.JsonUtil().createModelObject(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetUsersCurrentlyPlayingTrackRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<CurrentlyPlaying, Builder> {
+
+ /**
+ * Create a new {@link GetUsersCurrentlyPlayingTrackRequest.Builder}.
+ * <p>
+ * Your access token must have the {@code user-read-currently-playing} scope and/or the
+ * {@code user-read-playback-state} authorized in order to read information.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The market country code setter.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code. Provide this parameter if you
+ * want to apply Track Relinking.
+ * @return A {@link GetUsersCurrentlyPlayingTrackRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/track-relinking-guide/">Spotify: Track Relinking Guide</a>
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The additional types setter.
+ *
+ * @param additionalTypes Optional. A comma-separated list of item types that your client supports
+ * besides the default track type. Valid types are: {@code track} and {@code episode}.
+ * An unsupported type in the response is expected to be represented as {@code null} value in the {@code item} field.
+ * @return A {@link GetUsersCurrentlyPlayingTrackRequest.Builder}.
+ */
+ public GetUsersCurrentlyPlayingTrackRequest.Builder additionalTypes(final String additionalTypes) {
+ assert (additionalTypes != null);
+ assert (additionalTypes.matches("((^|,)(episode|track))+$"));
+ return setQueryParameter("additional_types", additionalTypes);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetUsersCurrentlyPlayingTrackRequest}.
+ */
+ @Override
+ public GetUsersCurrentlyPlayingTrackRequest build() {
+ setPath("/v1/me/player/currently-playing");
+ return new GetUsersCurrentlyPlayingTrackRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/player/PauseUsersPlaybackRequest.java
@@ -0,0 +1,88 @@
+package com.wrapper.spotify.requests.data.player;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Pause playback on the user’s account.
+ */
+@JsonDeserialize(builder = PauseUsersPlaybackRequest.Builder.class)
+public class PauseUsersPlaybackRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link PauseUsersPlaybackRequest} constructor.
+ *
+ * @param builder A {@link PauseUsersPlaybackRequest.Builder}.
+ */
+ private PauseUsersPlaybackRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Pause an user's playback.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return putJson();
+ }
+
+ /**
+ * Builder class for building a {@link PauseUsersPlaybackRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link PauseUsersPlaybackRequest.Builder}.
+ * <p>
+ * Your access token must have the {@code user-modify-playback-state} scope authorized in order to control playback.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The device ID setter.
+ *
+ * @param device_id Optional. The ID of the device this command is targeting. If not supplied, the
+ * user's currently active device is the target.
+ * @return A {@link PauseUsersPlaybackRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder device_id(final String device_id) {
+ assert (device_id != null);
+ assert (!device_id.equals(""));
+ return setQueryParameter("device_id", device_id);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link PauseUsersPlaybackRequest}.
+ */
+ @Override
+ public PauseUsersPlaybackRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/me/player/pause");
+ return new PauseUsersPlaybackRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/player/SeekToPositionInCurrentlyPlayingTrackRequest.java
@@ -0,0 +1,102 @@
+package com.wrapper.spotify.requests.data.player;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Seeks to the given position in the user’s currently playing track.
+ */
+@JsonDeserialize(builder = SeekToPositionInCurrentlyPlayingTrackRequest.Builder.class)
+public class SeekToPositionInCurrentlyPlayingTrackRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link SeekToPositionInCurrentlyPlayingTrackRequest} constructor.
+ *
+ * @param builder A {@link SeekToPositionInCurrentlyPlayingTrackRequest.Builder}.
+ */
+ private SeekToPositionInCurrentlyPlayingTrackRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Seek to a position in the user's currently playing track.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return putJson();
+ }
+
+ /**
+ * Builder class for building a {@link SeekToPositionInCurrentlyPlayingTrackRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link SeekToPositionInCurrentlyPlayingTrackRequest.Builder}.
+ * <p>
+ * Your access token must have the {@code user-modify-playback-state} scope authorized in order to control playback.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The position setter.
+ *
+ * @param position_ms Required. The position in milliseconds to seek to. Must be a positive number. Passing in a
+ * position that is greater than the length of the track will cause the player to start
+ * playing the next song.
+ * @return A {@link SeekToPositionInCurrentlyPlayingTrackRequest.Builder}.
+ */
+ public Builder position_ms(final Integer position_ms) {
+ assert (position_ms != null);
+ assert (position_ms >= 0);
+ return setQueryParameter("position_ms", position_ms);
+ }
+
+ /**
+ * The device ID setter.
+ *
+ * @param device_id Optional. The ID of the device this command is targeting. If not supplied, the
+ * user's currently active device is the target.
+ * @return A {@link SeekToPositionInCurrentlyPlayingTrackRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder device_id(final String device_id) {
+ assert (device_id != null);
+ assert (!device_id.equals(""));
+ return setQueryParameter("device_id", device_id);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link SeekToPositionInCurrentlyPlayingTrackRequest}.
+ */
+ @Override
+ public SeekToPositionInCurrentlyPlayingTrackRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/me/player/seek");
+ return new SeekToPositionInCurrentlyPlayingTrackRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/player/SetRepeatModeOnUsersPlaybackRequest.java
@@ -0,0 +1,101 @@
+package com.wrapper.spotify.requests.data.player;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Set the repeat mode for the user’s playback.
+ */
+@JsonDeserialize(builder = SetRepeatModeOnUsersPlaybackRequest.Builder.class)
+public class SetRepeatModeOnUsersPlaybackRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link SetRepeatModeOnUsersPlaybackRequest} constructor.
+ *
+ * @param builder A {@link SetRepeatModeOnUsersPlaybackRequest.Builder}.
+ */
+ private SetRepeatModeOnUsersPlaybackRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Set the repeat mode on a user's playback.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return putJson();
+ }
+
+ /**
+ * Builder class for building a {@link SetRepeatModeOnUsersPlaybackRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link SetRepeatModeOnUsersPlaybackRequest.Builder}.
+ * <p>
+ * Your access token must have the {@code user-modify-playback-state} scope authorized in order to control playback.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The repeat state setter.
+ *
+ * @param state Required. {@code track}, {@code context} or {@code off}. {@code track} will repeat the current
+ * track. {@code context} will repeat the current context. {@code off} will turn repeat off.
+ * @return A {@link SetRepeatModeOnUsersPlaybackRequest.Builder}.
+ */
+ public Builder state(final String state) {
+ assert (state != null);
+ assert (state.equals("track") || state.equals("context") || state.equals("off"));
+ return setQueryParameter("state", state);
+ }
+
+ /**
+ * The device ID setter.
+ *
+ * @param device_id Optional. The ID of the device this command is targeting. If not supplied, the
+ * user's currently active device is the target.
+ * @return A {@link SetRepeatModeOnUsersPlaybackRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder device_id(final String device_id) {
+ assert (device_id != null);
+ assert (!device_id.equals(""));
+ return setQueryParameter("device_id", device_id);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link SetRepeatModeOnUsersPlaybackRequest}.
+ */
+ @Override
+ public SetRepeatModeOnUsersPlaybackRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/me/player/repeat");
+ return new SetRepeatModeOnUsersPlaybackRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/player/SetVolumeForUsersPlaybackRequest.java
@@ -0,0 +1,100 @@
+package com.wrapper.spotify.requests.data.player;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Set the volume for the user’s current playback device.
+ */
+@JsonDeserialize(builder = SetVolumeForUsersPlaybackRequest.Builder.class)
+public class SetVolumeForUsersPlaybackRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link SetVolumeForUsersPlaybackRequest} constructor.
+ *
+ * @param builder A {@link SetVolumeForUsersPlaybackRequest.Builder}.
+ */
+ private SetVolumeForUsersPlaybackRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Set the volume for the user’s current playback device.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return putJson();
+ }
+
+ /**
+ * Builder class for building a {@link SetVolumeForUsersPlaybackRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link SetVolumeForUsersPlaybackRequest.Builder}.
+ * <p>
+ * Your access token must have the {@code user-modify-playback-state} scope authorized in order to control playback.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The volume percentage setter.
+ *
+ * @param volume_percent Required. The volume to set. Must be a value from 0 to 100 inclusive.
+ * @return A {@link SetVolumeForUsersPlaybackRequest.Builder}.
+ */
+ public Builder volume_percent(final Integer volume_percent) {
+ assert (volume_percent != null);
+ assert (0 <= volume_percent && volume_percent <= 100);
+ return setQueryParameter("volume_percent", volume_percent);
+ }
+
+ /**
+ * The device ID setter.
+ *
+ * @param device_id Optional. The ID of the device this command is targeting. If not supplied, the
+ * user's currently active device is the target.
+ * @return A {@link SetVolumeForUsersPlaybackRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder device_id(final String device_id) {
+ assert (device_id != null);
+ assert (!device_id.equals(""));
+ return setQueryParameter("device_id", device_id);
+ }
+
+ /**
+ * The build method.
+ *
+ * @return A custom {@link SetVolumeForUsersPlaybackRequest}.
+ */
+ @Override
+ public SetVolumeForUsersPlaybackRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/me/player/volume");
+ return new SetVolumeForUsersPlaybackRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/player/SkipUsersPlaybackToNextTrackRequest.java
@@ -0,0 +1,88 @@
+package com.wrapper.spotify.requests.data.player;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Skips to next track in the user’s queue.
+ */
+@JsonDeserialize(builder = SkipUsersPlaybackToNextTrackRequest.Builder.class)
+public class SkipUsersPlaybackToNextTrackRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link SkipUsersPlaybackToNextTrackRequest} constructor.
+ *
+ * @param builder A {@link SkipUsersPlaybackToNextTrackRequest.Builder}.
+ */
+ private SkipUsersPlaybackToNextTrackRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Skip to the next track in the user’s queue.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return postJson();
+ }
+
+ /**
+ * Builder class for building a {@link SkipUsersPlaybackToNextTrackRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link SkipUsersPlaybackToNextTrackRequest.Builder}.
+ * <p>
+ * Your access token must have the {@code user-modify-playback-state} scope authorized in order to control playback.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The device ID setter.
+ *
+ * @param device_id Optional. The ID of the device this command is targeting. If not supplied, the
+ * user's currently active device is the target.
+ * @return A {@link SkipUsersPlaybackToNextTrackRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder device_id(final String device_id) {
+ assert (device_id != null);
+ assert (!device_id.equals(""));
+ return setQueryParameter("device_id", device_id);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link SkipUsersPlaybackToNextTrackRequest}.
+ */
+ @Override
+ public SkipUsersPlaybackToNextTrackRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/me/player/next");
+ return new SkipUsersPlaybackToNextTrackRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/player/SkipUsersPlaybackToPreviousTrackRequest.java
@@ -0,0 +1,92 @@
+package com.wrapper.spotify.requests.data.player;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Skips to previous track in the user’s queue.
+ * <p>
+ * <b>Note:</b> This will ALWAYS skip to the previous track, regardless of the current track’s progress.
+ * Returning to the start of the current track should be performed using a
+ * {@link SeekToPositionInCurrentlyPlayingTrackRequest}.
+ */
+@JsonDeserialize(builder = SkipUsersPlaybackToPreviousTrackRequest.Builder.class)
+public class SkipUsersPlaybackToPreviousTrackRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link SkipUsersPlaybackToPreviousTrackRequest} constructor.
+ *
+ * @param builder A {@link SkipUsersPlaybackToPreviousTrackRequest.Builder}.
+ */
+ private SkipUsersPlaybackToPreviousTrackRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Skip to the previous track in the user’s queue.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return postJson();
+ }
+
+ /**
+ * Builder class for building a {@link SkipUsersPlaybackToPreviousTrackRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link SkipUsersPlaybackToPreviousTrackRequest.Builder}.
+ * <p>
+ * Your access token must have the {@code user-modify-playback-state} scope authorized in order to control playback.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The device ID setter.
+ *
+ * @param device_id Optional. The ID of the device this command is targeting. If not supplied, the
+ * user's currently active device is the target.
+ * @return A {@link SkipUsersPlaybackToPreviousTrackRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder device_id(final String device_id) {
+ assert (device_id != null);
+ assert (!device_id.equals(""));
+ return setQueryParameter("device_id", device_id);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link SkipUsersPlaybackToPreviousTrackRequest}.
+ */
+ @Override
+ public SkipUsersPlaybackToPreviousTrackRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/me/player/previous");
+ return new SkipUsersPlaybackToPreviousTrackRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/player/StartResumeUsersPlaybackRequest.java
@@ -0,0 +1,152 @@
+package com.wrapper.spotify.requests.data.player;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Start a new context or resume current playback on the user’s active device.
+ */
+@JsonDeserialize(builder = StartResumeUsersPlaybackRequest.Builder.class)
+public class StartResumeUsersPlaybackRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link StartResumeUsersPlaybackRequest} constructor.
+ *
+ * @param builder A {@link StartResumeUsersPlaybackRequest.Builder}.
+ */
+ private StartResumeUsersPlaybackRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Start or resume a playback.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return putJson();
+ }
+
+ /**
+ * Builder class for building a {@link StartResumeUsersPlaybackRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link StartResumeUsersPlaybackRequest.Builder}.
+ * <p>
+ * Your access token must have the {@code user-modify-playback-state} scope authorized in order to control playback.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The device ID setter.
+ *
+ * @param device_id Optional. The ID of the device this command is targeting. If not supplied, the
+ * user's currently active device is the target.
+ * @return A {@link StartResumeUsersPlaybackRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder device_id(final String device_id) {
+ assert (device_id != null);
+ assert (!device_id.equals(""));
+ return setQueryParameter("device_id", device_id);
+ }
+
+ /**
+ * The context URI setter.
+ *
+ * @param context_uri Optional. Spotify URI of the context to play.
+ * Valid contexts are albums, artists and playlists.
+ * @return A {@link StartResumeUsersPlaybackRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder context_uri(final String context_uri) {
+ assert (context_uri != null);
+ assert (!context_uri.equals(""));
+ return setBodyParameter("context_uri", context_uri);
+ }
+
+ /**
+ * The URI setter.
+ *
+ * @param uris Optional. A JSON array of the Spotify track URIs to play.
+ * @return A {@link StartResumeUsersPlaybackRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder uris(final JsonArray uris) {
+ assert (uris != null);
+ assert (!uris.isJsonNull());
+ return setBodyParameter("uris", uris);
+ }
+
+ /**
+ * The offset setter.
+ * <p>
+ * <b>Note:</b> If {@link #context_uri(String)} has been set and corresponds to an album or playlist object, an
+ * offset can be specified either by track {@code uri} OR {@code position}. If both are present the request will
+ * return an error. If incorrect values are provided for {@code position} or {@code uri}, the request may be
+ * accepted but with an unpredictable resulting action on playback.
+ *
+ * @param offset Optional. Indicates from where in the context playback should start. Only available when
+ * {@link #context_uri(String)} corresponds to an album or playlist object, or when the
+ * {@link #uris(JsonArray)} parameter is used. <br> The {@code position} parameter in the
+ * {@code offset} object is zero based and can’t be negative. <br> The {@code uri} parameter in the
+ * {@code offset} object is a string representing the URI of the item to start at.
+ * @return A {@link StartResumeUsersPlaybackRequest.Builder}.
+ */
+ public Builder offset(final JsonObject offset) {
+ assert (offset != null);
+ assert (!offset.isJsonNull());
+ return setBodyParameter("offset", offset);
+ }
+
+ /**
+ * The position setter.
+ *
+ * @param position_ms Optional. Indicates from what position to start playback. Must be a positive number. Passing
+ * in a position that is greater than the length of the track will cause the player to start
+ * playing the next song.
+ * @return A {@link StartResumeUsersPlaybackRequest.Builder}.
+ */
+ public Builder position_ms(final Integer position_ms) {
+ assert (position_ms != null);
+ assert (position_ms >= 0);
+ return setBodyParameter("position_ms", position_ms);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link StartResumeUsersPlaybackRequest}.
+ */
+ @Override
+ public StartResumeUsersPlaybackRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/me/player/play");
+ return new StartResumeUsersPlaybackRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/player/ToggleShuffleForUsersPlaybackRequest.java
@@ -0,0 +1,98 @@
+package com.wrapper.spotify.requests.data.player;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Toggle shuffle on or off for user’s playback.
+ */
+@JsonDeserialize(builder = ToggleShuffleForUsersPlaybackRequest.Builder.class)
+public class ToggleShuffleForUsersPlaybackRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link ToggleShuffleForUsersPlaybackRequest} constructor.
+ *
+ * @param builder A {@link ToggleShuffleForUsersPlaybackRequest.Builder}.
+ */
+ private ToggleShuffleForUsersPlaybackRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Toggle the shuffle state of an user's playback.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return putJson();
+ }
+
+ /**
+ * Builder class for building a {@link ToggleShuffleForUsersPlaybackRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link ToggleShuffleForUsersPlaybackRequest.Builder}.
+ * <p>
+ * Your access token must have the {@code user-modify-playback-state} scope authorized in order to control playback.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The toggle state setter.
+ *
+ * @param state Required. {@code true}: Shuffle user's playback. {@code false}: Do not shuffle user's playback.
+ * @return A {@link ToggleShuffleForUsersPlaybackRequest.Builder}.
+ */
+ public Builder state(final Boolean state) {
+ return setQueryParameter("state", state);
+ }
+
+ /**
+ * The device ID setter.
+ *
+ * @param device_id Optional. The ID of the device this command is targeting. If not supplied, the
+ * user's currently active device is the target.
+ * @return A {@link ToggleShuffleForUsersPlaybackRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder device_id(final String device_id) {
+ assert (device_id != null);
+ assert (!device_id.equals(""));
+ return setQueryParameter("device_id", device_id);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link ToggleShuffleForUsersPlaybackRequest}.
+ */
+ @Override
+ public ToggleShuffleForUsersPlaybackRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/me/player/shuffle");
+ return new ToggleShuffleForUsersPlaybackRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/player/TransferUsersPlaybackRequest.java
@@ -0,0 +1,102 @@
+package com.wrapper.spotify.requests.data.player;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonArray;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Transfer playback to a new device and determine if it should start playing.
+ */
+@JsonDeserialize(builder = TransferUsersPlaybackRequest.Builder.class)
+public class TransferUsersPlaybackRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link TransferUsersPlaybackRequest} constructor.
+ *
+ * @param builder A {@link TransferUsersPlaybackRequest.Builder}.
+ */
+ private TransferUsersPlaybackRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Transfer playback to a new device.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return putJson();
+ }
+
+ /**
+ * Builder class for building a {@link TransferUsersPlaybackRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link TransferUsersPlaybackRequest.Builder}.
+ * <p>
+ * Your access token must have the {@code user-modify-playback-state} scope authorized in order to control playback.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The device ID setter.
+ *
+ * @param device_ids Required. A JSON array containing the ID of the device on which playback should be
+ * started/transferred. <b>Note:</b> Although an array is accepted, only a single
+ * {@code device_id} is currently supported.
+ * @return A {@link TransferUsersPlaybackRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder device_ids(final JsonArray device_ids) {
+ assert (device_ids != null);
+ assert (!device_ids.isJsonNull());
+ assert (device_ids.size() == 1);
+ return setBodyParameter("device_ids", device_ids);
+ }
+
+ /**
+ * The playing state setter.
+ *
+ * @param play Optional. {@code true}: ensure playback happens on new device.
+ * {@code false} or not provided: keep the current playback state.
+ * @return A {@link TransferUsersPlaybackRequest.Builder}.
+ */
+ public Builder play(final Boolean play) {
+ return setBodyParameter("play", play);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link TransferUsersPlaybackRequest}.
+ */
+ @Override
+ public TransferUsersPlaybackRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/me/player");
+ return new TransferUsersPlaybackRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/playlists/AddItemsToPlaylistRequest.java
@@ -0,0 +1,178 @@
+package com.wrapper.spotify.requests.data.playlists;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonArray;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.special.SnapshotResult;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Add one or more items to a user’s playlist.
+ * <p>
+ * Use this endpoint to add Spotify tracks or episodes to a user’s playlist, sending their Spotify URI.
+ * Note that local tracks can’t be added.
+ */
+@JsonDeserialize(builder = AddItemsToPlaylistRequest.Builder.class)
+public class AddItemsToPlaylistRequest extends AbstractDataRequest<SnapshotResult> {
+
+ /**
+ * The private {@link AddItemsToPlaylistRequest} constructor.
+ *
+ * @param builder A {@link AddItemsToPlaylistRequest.Builder}.
+ */
+ private AddItemsToPlaylistRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Add items to playlist.
+ *
+ * @return A playlist snapshot ID. The snapshot ID can be used to identify your playlist version in future requests.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ * @see <a href="https://developer.spotify.com/web-api/working-with-playlists/#version-control-and-snapshots">
+ * Spotify: Version Control and Snapshots</a>
+ */
+ public SnapshotResult execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new SnapshotResult.JsonUtil().createModelObject(postJson());
+ }
+
+ /**
+ * Builder class for building an {@link AddItemsToPlaylistRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<SnapshotResult, Builder> {
+
+ /**
+ * Create a new {@link AddItemsToPlaylistRequest.Builder}.
+ * <p>
+ * Adding items to the current user's public playlists requires authorization of the {@code playlist-modify-public}
+ * scope; adding items to the current user's private playlist (including collaborative playlists) requires the
+ * {@code playlist-modify-private} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The user ID setter.
+ *
+ * @param user_id The user's Spotify user ID.
+ * @return An {@link AddItemsToPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ * @deprecated Playlist IDs are unique for themselves. This parameter is thus no longer used.
+ * (https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/)
+ */
+ @Deprecated
+ public Builder user_id(final String user_id) {
+ assert (user_id != null);
+ assert (!user_id.equals(""));
+ return setPathParameter("user_id", user_id);
+ }
+
+ /**
+ * The playlist ID setter.
+ *
+ * @param playlist_id The Spotify ID for the playlist.
+ * @return An {@link AddItemsToPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder playlist_id(final String playlist_id) {
+ assert (playlist_id != null);
+ assert (!playlist_id.equals(""));
+ return setPathParameter("playlist_id", playlist_id);
+ }
+
+ /**
+ * The item URIs setter.
+ * <p>
+ * <b>Note:</b> It is likely that passing a large number of item URIs as a query parameter will exceed the maximum
+ * length of the request URI. When adding a large number of items it is recommended to pass them
+ * with {@link #uris(JsonArray)}.
+ *
+ * @param uris Optional. A comma-separated list of Spotify track or episode URIs to add. Maximum: 100 item URIs.
+ * @return An {@link AddItemsToPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder uris(final String uris) {
+ assert (uris != null);
+ assert (!uris.equals(""));
+ assert (uris.split(",").length <= 100);
+ return setQueryParameter("uris", uris);
+ }
+
+ /**
+ * The position setter.
+ *
+ * @param position Optional. The position to insert the items, a zero-based index. If omitted, the
+ * items will be appended to the playlist. Items are added in the order they are
+ * listed in the query string or request body.
+ * @return An {@link AddItemsToPlaylistRequest.Builder}.
+ */
+ public Builder position(final Integer position) {
+ return position(position, false);
+ }
+
+ /**
+ * The item URIs setter.
+ * <p>
+ * <b>Note:</b> If the URIs already have been set with {@link #uris(String)}, any URIs listed here will be ignored.
+ *
+ * @param uris Optional. A JSON array of the Spotify track or episode URIs to add. maximum: 100 item URIs.
+ * @return An {@link AddItemsToPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder uris(final JsonArray uris) {
+ assert (uris != null);
+ assert (!uris.isJsonNull());
+ assert (uris.size() <= 100);
+ return setBodyParameter("uris", uris);
+ }
+
+ /**
+ * The position setter. You can set it in the request query string or request body, depending on the
+ * {@code use_body} parameter.
+ *
+ * @param position Optional. The position to insert the items, a zero-based index. If omitted, the
+ * items will be appended to the playlist. Items are added in the order they are
+ * listed in the query string or request body.
+ * @param use_body Whether to include the position in the request query string or body.
+ * @return An {@link AddItemsToPlaylistRequest.Builder}.
+ */
+ public Builder position(final Integer position, final Boolean use_body) {
+ assert (position >= 0);
+
+ if (use_body) {
+ return setBodyParameter("position", position);
+ } else {
+ return setQueryParameter("position", position);
+ }
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link AddItemsToPlaylistRequest}.
+ */
+ @Override
+ public AddItemsToPlaylistRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/playlists/{playlist_id}/tracks");
+ return new AddItemsToPlaylistRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/playlists/ChangePlaylistsDetailsRequest.java
@@ -0,0 +1,151 @@
+package com.wrapper.spotify.requests.data.playlists;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Change a playlist’s name and public/private state. (The user must, of course, own the playlist.)
+ */
+@JsonDeserialize(builder = ChangePlaylistsDetailsRequest.Builder.class)
+public class ChangePlaylistsDetailsRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link ChangePlaylistsDetailsRequest} constructor.
+ *
+ * @param builder A {@link ChangePlaylistsDetailsRequest.Builder}.
+ */
+ private ChangePlaylistsDetailsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Change a playlist's details.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return putJson();
+ }
+
+ /**
+ * Builder class for building a {@link ChangePlaylistsDetailsRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link ChangePlaylistsDetailsRequest.Builder}.
+ * <p>
+ * Modifying an user's public playlists requires authorization of the {@code playlist-modify-public}
+ * scope; Modifying an user's private playlist (including collaborative playlists) requires the
+ * {@code playlist-modify-private} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The user ID setter.
+ *
+ * @param user_id The user's Spotify user ID.
+ * @return A {@link ChangePlaylistsDetailsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ * @deprecated Playlist IDs are unique for themselves. This parameter is thus no longer used.
+ * (https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/)
+ */
+ @Deprecated
+ public Builder user_id(final String user_id) {
+ assert (user_id != null);
+ assert (!user_id.equals(""));
+ return setPathParameter("user_id", user_id);
+ }
+
+ /**
+ * The playlist ID setter.
+ *
+ * @param playlist_id The Spotify ID for the playlist.
+ * @return A {@link ChangePlaylistsDetailsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder playlist_id(final String playlist_id) {
+ assert (playlist_id != null);
+ assert (!playlist_id.equals(""));
+ return setPathParameter("playlist_id", playlist_id);
+ }
+
+ /**
+ * The playlist name setter.
+ *
+ * @param name Optional. The new name for the playlist.
+ * @return A {@link ChangePlaylistsDetailsRequest.Builder}.
+ */
+ public Builder name(final String name) {
+ assert (name != null);
+ assert (!name.equals(""));
+ return setBodyParameter("name", name);
+ }
+
+ /**
+ * The public status setter.
+ *
+ * @param public_ Optional. If {@code true} the playlist will be public, if {@code false} it will be private.
+ * @return A {@link ChangePlaylistsDetailsRequest.Builder}.
+ */
+ public Builder public_(final Boolean public_) {
+ return setBodyParameter("public", public_);
+ }
+
+ /**
+ * The collaborative state setter.
+ *
+ * @param collaborative Optional. If {@code true}, the playlist will become collaborative and other users will be
+ * able to modify the playlist in their Spotify client. <b>Note:</b> You can only set
+ * this to {@code true} on non-public playlists.
+ * @return A {@link ChangePlaylistsDetailsRequest.Builder}.
+ */
+ public Builder collaborative(final Boolean collaborative) {
+ return setBodyParameter("collaborative", collaborative);
+ }
+
+ /**
+ * The playlist description setter.
+ *
+ * @param description Optional, value for playlist description as displayed in Spotify Clients and in the Web API.
+ * @return A {@link ChangePlaylistsDetailsRequest.Builder}.
+ */
+ public Builder description(final String description) {
+ assert (description != null);
+ assert (!description.equals(""));
+ return setBodyParameter("description", description);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link ChangePlaylistsDetailsRequest}.
+ */
+ @Override
+ public ChangePlaylistsDetailsRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/playlists/{playlist_id}");
+ return new ChangePlaylistsDetailsRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/playlists/CreatePlaylistRequest.java
@@ -0,0 +1,141 @@
+package com.wrapper.spotify.requests.data.playlists;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Playlist;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Create a playlist for a Spotify user. (The playlist will be empty until you add tracks
+ * with an {@link AddItemsToPlaylistRequest}.)
+ */
+@JsonDeserialize(builder = CreatePlaylistRequest.Builder.class)
+public class CreatePlaylistRequest extends AbstractDataRequest<Playlist> {
+
+ /**
+ * The private {@link CreatePlaylistRequest} constructor.
+ *
+ * @param builder A {@link CreatePlaylistRequest.Builder}.
+ */
+ private CreatePlaylistRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Create a new playlist.
+ *
+ * @return The newly created {@link Playlist}.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Playlist execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Playlist.JsonUtil().createModelObject(postJson());
+ }
+
+ /**
+ * Builder class for building a {@link CreatePlaylistRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<Playlist, Builder> {
+
+ /**
+ * Create a new {@link CreatePlaylistRequest.Builder}.
+ * <p>
+ * Creating a public playlists requires authorization of the {@code playlist-modify-public}
+ * scope; Creating a private playlist requires the {@code playlist-modify-private} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The user ID setter.
+ *
+ * @param user_id The user's Spotify user ID.
+ * @return A {@link CreatePlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder user_id(final String user_id) {
+ assert (user_id != null);
+ assert (!user_id.equals(""));
+ return setPathParameter("user_id", user_id);
+ }
+
+ /**
+ * The playlist name setter.
+ *
+ * @param name Optional. The name for the playlist. This name does not need
+ * to be unique; a user may have several playlists with the same name.
+ * @return A {@link CreatePlaylistRequest.Builder}.
+ */
+ public Builder name(final String name) {
+ assert (name != null);
+ assert (!name.equals(""));
+ return setBodyParameter("name", name);
+ }
+
+ /**
+ * The public status setter.
+ * <p>
+ * <b>Note:</b> To be able to create private playlists, the user must have
+ * granted the {@code playlist-modify-private} scope.
+ *
+ * @param public_ Optional. If {@code true} the playlist will be public, if {@code false} it will be private.
+ * @return A {@link CreatePlaylistRequest.Builder}.
+ */
+ public Builder public_(final Boolean public_) {
+ return setBodyParameter("public", public_);
+ }
+
+ /**
+ * The collaborative state setter.
+ *
+ * @param collaborative Optional, default {@code false}. If {@code true} the playlist will be collaborative.
+ * <b>Note:</b> To create a collaborative playlist you must also set {@link #public_(Boolean)}
+ * to {@code false}. To create collaborative playlists you must have granted
+ * {@code playlist-modify-private} and {@code playlist-modify-public} scopes.
+ * @return A {@link CreatePlaylistRequest.Builder}.
+ */
+ public Builder collaborative(final Boolean collaborative) {
+ return setBodyParameter("collaborative", collaborative);
+ }
+
+ /**
+ * The playlist description setter.
+ *
+ * @param description Optional, value for playlist description as displayed in Spotify Clients and in the Web API.
+ * @return A {@link CreatePlaylistRequest.Builder}.
+ */
+ public Builder description(final String description) {
+ assert (description != null);
+ assert (!description.equals(""));
+ return setBodyParameter("description", description);
+ }
+
+ /**
+ * the request build method.
+ *
+ * @return A custom {@link CreatePlaylistRequest}.
+ */
+ @Override
+ public CreatePlaylistRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/users/{user_id}/playlists");
+ return new CreatePlaylistRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/playlists/GetListOfCurrentUsersPlaylistsRequest.java
@@ -0,0 +1,105 @@
+package com.wrapper.spotify.requests.data.playlists;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.model_objects.specification.PlaylistSimplified;
+import com.wrapper.spotify.requests.data.AbstractDataPagingRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get a list of the playlists owned or followed by the current Spotify user.
+ */
+@JsonDeserialize(builder = GetListOfCurrentUsersPlaylistsRequest.Builder.class)
+public class GetListOfCurrentUsersPlaylistsRequest extends AbstractDataRequest<Paging<PlaylistSimplified>> {
+
+ /**
+ * The private {@link GetListOfCurrentUsersPlaylistsRequest} constructor.
+ *
+ * @param builder A {@link GetListOfCurrentUsersPlaylistsRequest.Builder}.
+ */
+ private GetListOfCurrentUsersPlaylistsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get a list of the current user's playlists.
+ *
+ * @return A {@link PlaylistSimplified} paging.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Paging<PlaylistSimplified> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new PlaylistSimplified.JsonUtil().createModelObjectPaging(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetListOfCurrentUsersPlaylistsRequest}
+ */
+ public static final class Builder extends AbstractDataPagingRequest.Builder<PlaylistSimplified, Builder> {
+
+ /**
+ * Create a new {@link GetListOfCurrentUsersPlaylistsRequest.Builder}.
+ * <p>
+ * Private playlists are only retrievable for the current user and requires the {@code playlist-read-private}
+ * scope to have been authorized by the user. <b>Note:</b> This scope alone will not return collaborative playlists,
+ * even though they are always private.
+ * <p>
+ * Collaborative playlists are only retrievable for the current user and requires the
+ * {@code playlist-read-collaborative} scope to have been authorized by the user.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The maximum number of playlists to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link GetListOfCurrentUsersPlaylistsRequest.Builder}.
+ */
+ @Override
+ public Builder limit(final Integer limit) {
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The offset setter.
+ *
+ * @param offset Optional. The index of the first playlist to return. Default: 0 (the first object). Maximum offset:
+ * 100.000. Use with {@link #limit(Integer)} to get the next set of playlists.
+ * @return A {@link GetListOfCurrentUsersPlaylistsRequest.Builder}.
+ */
+ @Override
+ public Builder offset(final Integer offset) {
+ assert (0 <= offset && offset <= 100000);
+ return setQueryParameter("offset", offset);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetListOfCurrentUsersPlaylistsRequest}.
+ */
+ @Override
+ public GetListOfCurrentUsersPlaylistsRequest build() {
+ setPath("/v1/me/playlists");
+ return new GetListOfCurrentUsersPlaylistsRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/playlists/GetListOfUsersPlaylistsRequest.java
@@ -0,0 +1,118 @@
+package com.wrapper.spotify.requests.data.playlists;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.model_objects.specification.PlaylistSimplified;
+import com.wrapper.spotify.requests.data.AbstractDataPagingRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get a list of the playlists owned or followed by a Spotify user.
+ */
+@JsonDeserialize(builder = GetListOfUsersPlaylistsRequest.Builder.class)
+public class GetListOfUsersPlaylistsRequest extends AbstractDataRequest<Paging<PlaylistSimplified>> {
+
+ /**
+ * The private {@link GetListOfUsersPlaylistsRequest} constructor.
+ *
+ * @param builder A {@link GetListOfUsersPlaylistsRequest.Builder}.
+ */
+ private GetListOfUsersPlaylistsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get an user's playlists.
+ *
+ * @return A {@link PlaylistSimplified} paging.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Paging<PlaylistSimplified> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new PlaylistSimplified.JsonUtil().createModelObjectPaging(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetListOfUsersPlaylistsRequest}.
+ */
+ public static final class Builder extends AbstractDataPagingRequest.Builder<PlaylistSimplified, Builder> {
+
+ /**
+ * Create a new {@link GetListOfUsersPlaylistsRequest.Builder}.
+ * <p>
+ * Private playlists are only retrievable for the current user and requires the {@code playlist-read-private}
+ * scope to have been authorized by the user. <b>Note:</b> This scope alone will not return collaborative playlists,
+ * even though they are always private.
+ * <p>
+ * Collaborative playlists are only retrievable for the current user and requires the
+ * {@code playlist-read-collaborative} scope to have been authorized by the user.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The user ID setter.
+ *
+ * @param user_id The user's Spotify user ID.
+ * @return A {@link GetListOfUsersPlaylistsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder user_id(final String user_id) {
+ assert (user_id != null);
+ assert (!user_id.equals(""));
+ return setPathParameter("user_id", user_id);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The maximum number of playlists to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link GetListOfUsersPlaylistsRequest.Builder}.
+ */
+ @Override
+ public Builder limit(final Integer limit) {
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The offset setter.
+ *
+ * @param offset Optional. The index of the first playlist to return. Default: 0 (the first object). Maximum offset:
+ * 100.000. Use with {@link #limit(Integer)} to get the next set of playlists.
+ * @return A {@link GetListOfUsersPlaylistsRequest.Builder}.
+ */
+ @Override
+ public Builder offset(final Integer offset) {
+ assert (0 <= offset && offset <= 100000);
+ return setQueryParameter("offset", offset);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetListOfUsersPlaylistsRequest}.
+ */
+ @Override
+ public GetListOfUsersPlaylistsRequest build() {
+ setPath("/v1/users/{user_id}/playlists");
+ return new GetListOfUsersPlaylistsRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/playlists/GetPlaylistCoverImageRequest.java
@@ -0,0 +1,99 @@
+package com.wrapper.spotify.requests.data.playlists;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Image;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get the current image associated with a specific playlist.
+ */
+@JsonDeserialize(builder = GetPlaylistCoverImageRequest.Builder.class)
+public class GetPlaylistCoverImageRequest extends AbstractDataRequest<Image[]> {
+
+ /**
+ * The private {@link GetPlaylistCoverImageRequest} constructor.
+ *
+ * @param builder A {@link GetPlaylistCoverImageRequest.Builder}.
+ */
+ private GetPlaylistCoverImageRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get the cover image of a playlist.
+ *
+ * @return Multiple {@link Image} objects.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Image[] execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Image.JsonUtil().createModelObjectArray(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetPlaylistCoverImageRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<Image[], Builder> {
+
+ /**
+ * Create a new {@link GetPlaylistCoverImageRequest.Builder}.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The user ID setter.
+ *
+ * @param user_id The user's Spotify user ID.
+ * @return A {@link GetPlaylistCoverImageRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ * @deprecated Playlist IDs are unique for themselves. This parameter is thus no longer used.
+ * (https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/)
+ */
+ @Deprecated
+ public Builder user_id(final String user_id) {
+ assert (user_id != null);
+ assert (!user_id.equals(""));
+ return setPathParameter("user_id", user_id);
+ }
+
+ /**
+ * The playlist ID setter.
+ *
+ * @param playlist_id The Spotify ID for the playlist.
+ * @return A {@link GetPlaylistCoverImageRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder playlist_id(final String playlist_id) {
+ assert (playlist_id != null);
+ assert (!playlist_id.equals(""));
+ return setPathParameter("playlist_id", playlist_id);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetPlaylistCoverImageRequest}.
+ */
+ @Override
+ public GetPlaylistCoverImageRequest build() {
+ setPath("/v1/playlists/{playlist_id}/images");
+ return new GetPlaylistCoverImageRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/playlists/GetPlaylistRequest.java
@@ -0,0 +1,145 @@
+package com.wrapper.spotify.requests.data.playlists;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Playlist;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get a playlist owned by a Spotify user.
+ */
+@JsonDeserialize(builder = GetPlaylistRequest.Builder.class)
+public class GetPlaylistRequest extends AbstractDataRequest<Playlist> {
+
+ /**
+ * The private {@link GetPlaylistRequest} constructor.
+ *
+ * @param builder A {@link GetPlaylistRequest.Builder}.
+ */
+ private GetPlaylistRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get a playlist.
+ *
+ * @return A {@link Playlist}.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Playlist execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Playlist.JsonUtil().createModelObject(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetPlaylistRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<Playlist, Builder> {
+
+ /**
+ * Create a new {@link GetPlaylistRequest.Builder}.
+ * <p>
+ * Both Public and Private playlists belonging to any user are retrievable on provision of a valid access token.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The user ID setter.
+ *
+ * @param user_id The user's Spotify user ID.
+ * @return A {@link GetPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ * @deprecated Playlist IDs are unique for themselves. This parameter is thus no longer used
+ * (https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/)
+ */
+ @Deprecated
+ public Builder user_id(final String user_id) {
+ assert (user_id != null);
+ assert (!user_id.equals(""));
+ return setPathParameter("user_id", user_id);
+ }
+
+ /**
+ * The playlist ID setter.
+ *
+ * @param playlist_id The Spotify ID for the playlist.
+ * @return A {@link GetPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder playlist_id(final String playlist_id) {
+ assert (playlist_id != null);
+ assert (!playlist_id.equals(""));
+ return setPathParameter("playlist_id", playlist_id);
+ }
+
+ /**
+ * The fields filter setter.
+ *
+ * @param fields Optional. Filters for the query: a comma-separated list of the fields to return.
+ * If omitted, all fields are returned.
+ * @return A {@link GetPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/get-playlist/#tablepress-101">
+ * Spotify: More Details on Playlist Fields</a>
+ */
+ public Builder fields(final String fields) {
+ assert (fields != null);
+ assert (!fields.equals(""));
+ return setQueryParameter("fields", fields);
+ }
+
+ /**
+ * The market country code setter.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code. Provide this
+ * parameter if you want to apply Track Relinking.
+ * @return A {@link GetPlaylistRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ * @see <a href="https://developer.spotify.com/web-api/track-relinking-guide/">Spotify: Track Relinking Guide</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The additional types setter.
+ *
+ * @param additionalTypes Optional. A comma-separated list of item types that your client supports
+ * besides the default track type. Valid types are: {@code track} and {@code episode}.
+ * An unsupported type in the response is expected to be represented as {@code null} value in the {@code item} field.
+ * @return A {@link GetPlaylistRequest.Builder}.
+ */
+ public Builder additionalTypes(final String additionalTypes) {
+ assert (additionalTypes != null);
+ assert (additionalTypes.matches("((^|,)(episode|track))+$"));
+ return setQueryParameter("additional_types", additionalTypes);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetPlaylistRequest}.
+ */
+ @Override
+ public GetPlaylistRequest build() {
+ setPath("/v1/playlists/{playlist_id}");
+ return new GetPlaylistRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/playlists/GetPlaylistsItemsRequest.java
@@ -0,0 +1,171 @@
+package com.wrapper.spotify.requests.data.playlists;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.model_objects.specification.PlaylistTrack;
+import com.wrapper.spotify.requests.data.AbstractDataPagingRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get full details of the tracks or episodes of a playlist owned by a Spotify user.
+ */
+@JsonDeserialize(builder = GetPlaylistsItemsRequest.Builder.class)
+public class GetPlaylistsItemsRequest extends AbstractDataRequest<Paging<PlaylistTrack>> {
+
+ /**
+ * The private {@link GetPlaylistsItemsRequest} constructor.
+ *
+ * @param builder A {@link GetPlaylistsItemsRequest.Builder}.
+ */
+ private GetPlaylistsItemsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get a playlist's items.
+ *
+ * @return A playlist's items.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Paging<PlaylistTrack> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new PlaylistTrack.JsonUtil().createModelObjectPaging(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetPlaylistsItemsRequest}.
+ */
+ public static final class Builder extends AbstractDataPagingRequest.Builder<PlaylistTrack, Builder> {
+
+ /**
+ * Create a new {@link GetPlaylistsItemsRequest.Builder}.
+ * <p>
+ * Both Public and Private playlists belonging to any user are retrievable on provision of a valid access token.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The user ID setter.
+ *
+ * @param user_id The user's Spotify user ID.
+ * @return A {@link GetPlaylistsItemsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ * @deprecated Playlist IDs are unique for themselves. This parameter is thus no longer used
+ * (https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/)
+ */
+ @Deprecated
+ public Builder user_id(final String user_id) {
+ assert (user_id != null);
+ assert (!user_id.equals(""));
+ return setPathParameter("user_id", user_id);
+ }
+
+ /**
+ * The playlist ID setter.
+ *
+ * @param playlist_id The Spotify ID for the playlist.
+ * @return A {@link GetPlaylistsItemsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder playlist_id(final String playlist_id) {
+ assert (playlist_id != null);
+ assert (!playlist_id.equals(""));
+ return setPathParameter("playlist_id", playlist_id);
+ }
+
+ /**
+ * The fields filter setter.
+ *
+ * @param fields Optional. Filters for the query: a comma-separated list of the fields to return.
+ * If omitted, all fields are returned.
+ * @return A {@link GetPlaylistsItemsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/get-playlists-tracks/#tablepress-107">
+ * Spotify: More Details on Playlist Fields</a>
+ */
+ public Builder fields(final String fields) {
+ assert (fields != null);
+ assert (!fields.equals(""));
+ return setQueryParameter("fields", fields);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The maximum number of items to return. Default: 100. Minimum: 1. Maximum: 100.
+ * @return A {@link GetPlaylistsItemsRequest.Builder}.
+ */
+ @Override
+ public Builder limit(final Integer limit) {
+ assert (1 <= limit && limit <= 100);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The offset setter.
+ *
+ * @param offset Optional. The index of the first item to return. Default: 0 (the first object).
+ * @return A {@link GetPlaylistsItemsRequest.Builder}.
+ */
+ @Override
+ public Builder offset(final Integer offset) {
+ assert (offset >= 0);
+ return setQueryParameter("offset", offset);
+ }
+
+ /**
+ * The market country code setter.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code. Provide this
+ * parameter if you want to apply Track Relinking.
+ * @return A {@link GetPlaylistsItemsRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ * @see <a href="https://developer.spotify.com/web-api/track-relinking-guide/">Spotify: Track Relinking Guide</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The additional types setter.
+ *
+ * @param additionalTypes Optional. A comma-separated list of item types that your client supports
+ * besides the default track type. Valid types are: {@code track} and {@code episode}.
+ * An unsupported type in the response is expected to be represented as {@code null} value in the {@code item} field.
+ * @return A {@link GetPlaylistsItemsRequest.Builder}.
+ */
+ public Builder additionalTypes(final String additionalTypes) {
+ assert (additionalTypes != null);
+ assert (additionalTypes.matches("((^|,)(episode|track))+$"));
+ return setQueryParameter("additional_types", additionalTypes);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetPlaylistsItemsRequest}.
+ */
+ @Override
+ public GetPlaylistsItemsRequest build() {
+ setPath("/v1/playlists/{playlist_id}/tracks");
+ return new GetPlaylistsItemsRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/playlists/RemoveItemsFromPlaylistRequest.java
@@ -0,0 +1,153 @@
+package com.wrapper.spotify.requests.data.playlists;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonArray;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.special.SnapshotResult;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Remove one or more items from a user’s playlist.
+ */
+@JsonDeserialize(builder = RemoveItemsFromPlaylistRequest.Builder.class)
+public class RemoveItemsFromPlaylistRequest extends AbstractDataRequest<SnapshotResult> {
+
+ /**
+ * The private {@link RemoveItemsFromPlaylistRequest} constructor.
+ *
+ * @param builder A {@link RemoveItemsFromPlaylistRequest.Builder}.
+ */
+ private RemoveItemsFromPlaylistRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Remove items from a playlist.
+ *
+ * @return A playlist snapshot ID. The snapshot ID can be used to identify your playlist version in future requests.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ * @see <a href="https://developer.spotify.com/web-api/working-with-playlists/#version-control-and-snapshots">
+ * Spotify: Version Control and Snapshots</a>
+ */
+ public SnapshotResult execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new SnapshotResult.JsonUtil().createModelObject(deleteJson());
+ }
+
+ /**
+ * Builder class for building a {@link RemoveItemsFromPlaylistRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<SnapshotResult, Builder> {
+
+ /**
+ * Create a new {@link RemoveItemsFromPlaylistRequest.Builder}.
+ * <p>
+ * Removing items from an user's public playlists requires authorization of the {@code playlist-modify-public}
+ * scope; removing items from an user's private playlist (including collaborative playlists) requires the
+ * {@code playlist-modify-private} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The user ID setter.
+ *
+ * @param user_id The user's Spotify user ID.
+ * @return An {@link RemoveItemsFromPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ * @deprecated Playlist IDs are unique for themselves. This parameter is thus no longer used.
+ * (https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/)
+ */
+ @Deprecated
+ public Builder user_id(final String user_id) {
+ assert (user_id != null);
+ assert (!user_id.equals(""));
+ return setPathParameter("user_id", user_id);
+ }
+
+ /**
+ * The playlist ID setter.
+ *
+ * @param playlist_id The Spotify ID for the playlist.
+ * @return An {@link RemoveItemsFromPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder playlist_id(final String playlist_id) {
+ assert (playlist_id != null);
+ assert (!playlist_id.equals(""));
+ return setPathParameter("playlist_id", playlist_id);
+ }
+
+ /**
+ * The item URIs setter.
+ * <p>
+ * There are several ways to specify which tracks or episodes to remove, determined by the request parameters.
+ * Removing all occurrences of specific items: <br>
+ * {@code [{ "uri": "spotify:track:4iV5W9uYEdYUVa79Axb7Rh" },
+ * {"uri": "spotify:episode:512ojhOuo1ktJprKbVcKyQ" }] } <br>
+ * Removing a specific occurrence of an item: <br>
+ * {@code [{ "uri": "spotify:track:4iV5W9uYEdYUVa79Axb7Rh", "positions": [0,3] },
+ * { "uri": "spotify:track:1301WleyT98MSxVHPZCA6M", "positions": [7] }] }
+ *
+ * @param tracks Required. An array of objects containing Spotify URIs of the items to remove. A maximum of
+ * 100 objects can be sent at once
+ * @return A {@link RemoveItemsFromPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder tracks(final JsonArray tracks) {
+ assert (tracks != null);
+ assert (!tracks.isJsonNull());
+ assert (tracks.size() <= 100);
+ return setBodyParameter("tracks", tracks);
+ }
+
+ /**
+ * The playlist snapshot ID setter.
+ * <p>
+ * To guard against errors when concurrent edits happen to the same playlist, we recommend specifying a snapshot ID.
+ * The snapshot ID lets us know which version of the playlist you are trying to edit. Concurrent edits by another
+ * user will be automatically resolved. If a given item in a given position is not found in the specified snapshot,
+ * the entire request will fail an no edits will take place.
+ *
+ * @param snapshotId Optional. The playlist's snapshot ID against which you want to make the changes. The API will
+ * validate that the specified items exist and in the specified positions and make the changes,
+ * even if more recent changes have been made to the playlist.
+ * @return A {@link RemoveItemsFromPlaylistRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/working-with-playlists/#version-control-and-snapshots">
+ * Spotify: Version Control and Snapshots</a>
+ */
+ public Builder snapshotId(final String snapshotId) {
+ assert (snapshotId != null);
+ assert (!snapshotId.equals(""));
+ return setBodyParameter("snapshot_id", snapshotId);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link RemoveItemsFromPlaylistRequest}.
+ */
+ @Override
+ public RemoveItemsFromPlaylistRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/playlists/{playlist_id}/tracks");
+ return new RemoveItemsFromPlaylistRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/playlists/ReorderPlaylistsItemsRequest.java
@@ -0,0 +1,163 @@
+package com.wrapper.spotify.requests.data.playlists;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.special.SnapshotResult;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Reorder an item or a group of items in a playlist.
+ * <p>
+ * When reordering items, the timestamp indicating when they were added and the user who added them will
+ * be kept untouched. In addition, the users following the playlists won’t be notified about changes in
+ * the playlists when the items are reordered.
+ */
+@JsonDeserialize(builder = ReorderPlaylistsItemsRequest.Builder.class)
+public class ReorderPlaylistsItemsRequest extends AbstractDataRequest<SnapshotResult> {
+
+ /**
+ * The private {@link ReorderPlaylistsItemsRequest} constructor.
+ *
+ * @param builder A {@link ReorderPlaylistsItemsRequest.Builder}.
+ */
+ private ReorderPlaylistsItemsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Reorder the items in a playlist.
+ *
+ * @return A playlist snapshot ID. The snapshot ID can be used to identify your playlist version in future requests.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ * @see <a href="https://developer.spotify.com/web-api/working-with-playlists/#version-control-and-snapshots">
+ * Spotify: Version Control and Snapshots</a>
+ */
+ public SnapshotResult execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new SnapshotResult.JsonUtil().createModelObject(putJson());
+ }
+
+ /**
+ * Builder class for building a {@link ReorderPlaylistsItemsRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<SnapshotResult, Builder> {
+
+ /**
+ * Create a new {@link ReorderPlaylistsItemsRequest.Builder}.
+ * <p>
+ * Reordering items in the current user's public playlists requires authorization of the
+ * {@code playlist-modify-public} scope; reordering items in the current user's private playlist (including
+ * collaborative playlists) requires the {@code playlist-modify-private} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The user ID setter.
+ *
+ * @param user_id The user's Spotify user ID.
+ * @return A {@link ReorderPlaylistsItemsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ * @deprecated Playlist IDs are unique for themselves. This parameter is thus no longer used.
+ * (https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/)
+ */
+ @Deprecated
+ public Builder user_id(final String user_id) {
+ assert (user_id != null);
+ assert (!user_id.equals(""));
+ return setPathParameter("user_id", user_id);
+ }
+
+ /**
+ * The playlist ID setter.
+ *
+ * @param playlist_id The Spotify ID for the playlist.
+ * @return A {@link ReorderPlaylistsItemsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder playlist_id(final String playlist_id) {
+ assert (playlist_id != null);
+ assert (!playlist_id.equals(""));
+ return setPathParameter("playlist_id", playlist_id);
+ }
+
+ /**
+ * The range start setter.
+ *
+ * @param range_start Required. The position of the first item to be reordered.
+ * @return A {@link ReorderPlaylistsItemsRequest.Builder}.
+ */
+ public Builder range_start(final Integer range_start) {
+ assert (range_start != null);
+ assert (range_start >= 0);
+ return setBodyParameter("range_start", range_start);
+ }
+
+ /**
+ * The range length setter.
+ *
+ * @param range_length Optional. The amount of items to be reordered. Defaults to 1 if not set.
+ * @return A {@link ReorderPlaylistsItemsRequest.Builder}.
+ */
+ public Builder range_length(final Integer range_length) {
+ assert (range_length != null);
+ assert (range_length >= 1);
+ return setBodyParameter("range_length", range_length);
+ }
+
+ /**
+ * The insert before setter.
+ *
+ * @param insert_before Required. The position where the items should be inserted. To reorder the items to the
+ * end of the playlist, simply set insert_before to the position after the last item.
+ * @return A {@link ReorderPlaylistsItemsRequest.Builder}.
+ */
+ public Builder insert_before(final Integer insert_before) {
+ assert (insert_before != null);
+ assert (insert_before >= 0);
+ return setBodyParameter("insert_before", insert_before);
+ }
+
+ /**
+ * The playlist snapshot ID setter.
+ *
+ * @param snapshot_id Optional. The playlist's snapshot ID against which you want to make the changes.
+ * @return A {@link ReorderPlaylistsItemsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/working-with-playlists/#version-control-and-snapshots">
+ * Spotify: Version Control and Snapshots</a>
+ */
+ public Builder snapshot_id(final String snapshot_id) {
+ assert (snapshot_id != null);
+ assert (!snapshot_id.equals(""));
+ return setBodyParameter("snapshot_id", snapshot_id);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link ReorderPlaylistsItemsRequest}.
+ */
+ @Override
+ public ReorderPlaylistsItemsRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/playlists/{playlist_id}/tracks");
+ return new ReorderPlaylistsItemsRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/playlists/ReplacePlaylistsItemsRequest.java
@@ -0,0 +1,137 @@
+package com.wrapper.spotify.requests.data.playlists;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonArray;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Replace all the items in a playlist, overwriting its existing items. This powerful request can be useful for
+ * replacing items, re-ordering existing items, or clearing the playlist.
+ */
+@JsonDeserialize(builder = ReplacePlaylistsItemsRequest.Builder.class)
+public class ReplacePlaylistsItemsRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link ReplacePlaylistsItemsRequest} constructor.
+ *
+ * @param builder A {@link ReplacePlaylistsItemsRequest.Builder}.
+ */
+ private ReplacePlaylistsItemsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Replace items in a playlist.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return putJson();
+ }
+
+ /**
+ * Builder class for building a {@link ReplacePlaylistsItemsRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link ReplacePlaylistsItemsRequest.Builder}.
+ * <p>
+ * Replacing items in the current user's public playlists requires authorization of the
+ * {@code playlist-modify-public} scope; replacing items in the current user's private playlist (including
+ * collaborative playlists) requires the {@code playlist-modify-private} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The user ID setter.
+ *
+ * @param user_id The user's Spotify user ID.
+ * @return A {@link ReplacePlaylistsItemsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ * @deprecated Playlist IDs are unique for themselves. This parameter is thus no longer used.
+ * (https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/)
+ */
+ @Deprecated
+ public Builder user_id(final String user_id) {
+ assert (user_id != null);
+ assert (!user_id.equals(""));
+ return setPathParameter("user_id", user_id);
+ }
+
+ /**
+ * The playlist ID setter.
+ *
+ * @param playlist_id The Spotify ID for the playlist.
+ * @return A {@link ReplacePlaylistsItemsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder playlist_id(final String playlist_id) {
+ assert (playlist_id != null);
+ assert (!playlist_id.equals(""));
+ return setPathParameter("playlist_id", playlist_id);
+ }
+
+ /**
+ * The item URIs setter.
+ *
+ * @param uris Optional. A comma-separated list of Spotify track or episode URIs to set. Maximum: 100 track or episode URIs.
+ * @return A {@link ReplacePlaylistsItemsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder uris(final String uris) {
+ assert (uris != null);
+ assert (!uris.equals(""));
+ assert (uris.split(",").length <= 100);
+ return setQueryParameter("uris", uris);
+ }
+
+ /**
+ * The item URIs setter.
+ * <p>
+ * <b>Note:</b> If the URIs have already been set with {@link #uris(String)}, any URIs set here will be ignored.
+ *
+ * @param uris Optional. A JSON array of Spotify track or episode URIs to set. Maximum: 100 track or episode URIs.
+ * @return A {@link ReplacePlaylistsItemsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder uris(final JsonArray uris) {
+ assert (uris != null);
+ assert (!uris.isJsonNull());
+ assert (uris.size() <= 100);
+ return setBodyParameter("uris", uris);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link ReplacePlaylistsItemsRequest}.
+ */
+ @Override
+ public ReplacePlaylistsItemsRequest build() {
+ setContentType(ContentType.APPLICATION_JSON);
+ setPath("/v1/playlists/{playlist_id}/tracks");
+ return new ReplacePlaylistsItemsRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/playlists/UploadCustomPlaylistCoverImageRequest.java
@@ -0,0 +1,119 @@
+package com.wrapper.spotify.requests.data.playlists;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.ParseException;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+
+import java.io.IOException;
+
+/**
+ * Replace the image used to represent a specific playlist.
+ */
+@JsonDeserialize(builder = UploadCustomPlaylistCoverImageRequest.Builder.class)
+public class UploadCustomPlaylistCoverImageRequest extends AbstractDataRequest<String> {
+
+ /**
+ * The private {@link UploadCustomPlaylistCoverImageRequest} constructor.
+ *
+ * @param builder A {@link UploadCustomPlaylistCoverImageRequest.Builder}.
+ */
+ private UploadCustomPlaylistCoverImageRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Upload a new playlist image.
+ *
+ * @return A string. <b>Note:</b> This endpoint doesn't return something in its response body.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public String execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return putJson();
+ }
+
+ /**
+ * Builder class for building an {@link UploadCustomPlaylistCoverImageRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<String, Builder> {
+
+ /**
+ * Create a new {@link UploadCustomPlaylistCoverImageRequest.Builder}.
+ * <p>
+ * This access token must be tied to the user who owns the playlist, and must have the scope
+ * {@code ugc-image-upload} granted. In addition, the token must also contain {@code playlist-modify-public}
+ * and/or {@code playlist-modify-private}, depending the public status of the playlist you want to update.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The user ID setter.
+ *
+ * @param user_id The user's Spotify user ID.
+ * @return A {@link UploadCustomPlaylistCoverImageRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ * @deprecated Playlist IDs are unique for themselves. This parameter is thus no longer used.
+ * (https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/)
+ */
+ @Deprecated
+ public Builder user_id(final String user_id) {
+ assert (user_id != null);
+ assert (!user_id.equals(""));
+ return setPathParameter("user_id", user_id);
+ }
+
+ /**
+ * The playlist ID setter.
+ *
+ * @param playlist_id The Spotify ID for the playlist.
+ * @return A {@link UploadCustomPlaylistCoverImageRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder playlist_id(final String playlist_id) {
+ assert (playlist_id != null);
+ assert (!playlist_id.equals(""));
+ return setPathParameter("playlist_id", playlist_id);
+ }
+
+ /**
+ * The image data setter.
+ *
+ * @param image_data Base64 encoded JPEG image data, maximum payload size is 256 KB.
+ * @return A {@link UploadCustomPlaylistCoverImageRequest.Builder}.
+ */
+ public Builder image_data(final String image_data) {
+ assert (image_data != null);
+ assert (!image_data.equals(""));
+ assert (image_data.getBytes().length <= 256000);
+ return setBody(new StringEntity(image_data, ContentType.IMAGE_JPEG));
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link UploadCustomPlaylistCoverImageRequest}.
+ */
+ @Override
+ public UploadCustomPlaylistCoverImageRequest build() {
+ setContentType(ContentType.IMAGE_JPEG);
+ setPath("/v1/playlists/{playlist_id}/images");
+ return new UploadCustomPlaylistCoverImageRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/search/SearchItemRequest.java
@@ -0,0 +1,150 @@
+package com.wrapper.spotify.requests.data.search;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.special.SearchResult;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get Spotify catalog information about artists, albums, episodes, playlists, shows or tracks that match a keyword string.
+ */
+@JsonDeserialize(builder = SearchItemRequest.Builder.class)
+public class SearchItemRequest extends AbstractDataRequest<SearchResult> {
+
+ /**
+ * The private {@link SearchItemRequest} constructor.
+ *
+ * @param builder A {@link SearchItemRequest.Builder}.
+ */
+ private SearchItemRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Search for an item.
+ *
+ * @return A {@link SearchResult}.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public SearchResult execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new SearchResult.JsonUtil().createModelObject(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link SearchItemRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<SearchResult, Builder> {
+
+ /**
+ * Create a new {@link SearchItemRequest.Builder}.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The search query setter.
+ *
+ * @param q Required. The search query's keywords (and optional field filters and operators).
+ * @return A {@link SearchItemRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/search-item/#tablepress-47">Spotify: Search Query Options</a>
+ */
+ public Builder q(final String q) {
+ assert (q != null);
+ assert (!q.equals(""));
+ return setQueryParameter("q", q);
+ }
+
+ /**
+ * The type setter.
+ *
+ * @param type Required. A comma-separated list of item types to search across. Valid types are: {@code album},
+ * {@code artist}, {@code episode}, {@code playlist}, {@code show} and {@code track}.
+ * @return A {@link SearchItemRequest.Builder}.
+ */
+ public Builder type(final String type) {
+ assert (type != null);
+ assert (type.matches("((^|,)(album|artist|episode|playlist|show|track))+$"));
+ return setQueryParameter("type", type);
+ }
+
+ /**
+ * The market country code setter.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code. If a country code is given, only artists,
+ * albums, and tracks with content playable in that market will be returned. (Playlist
+ * results are not affected by the market parameter.)
+ * @return A {@link SearchItemRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The maximum number of results to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link SearchItemRequest.Builder}.
+ */
+ public Builder limit(final Integer limit) {
+ assert (limit != null);
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The offset setter.
+ *
+ * @param offset Optional. The index of the first result to return. Default: 0 (i.e., the first result). Maximum
+ * offset: 100.000. Use with {@link #limit(Integer)} to get the next page of search results.
+ * @return A {@link SearchItemRequest.Builder}.
+ */
+ public Builder offset(final Integer offset) {
+ assert (offset != null);
+ assert (0 <= offset && offset <= 100000);
+ return setQueryParameter("offset", offset);
+ }
+
+ /**
+ * The include external setter.
+ *
+ * @param includeExternal Optional. Possible values: {@code audio}. If {@code audio} is set
+ * the response will include any relevant audio content that is hosted externally.
+ * By default external content is filtered out from responses.
+ * @return A {@link SearchItemRequest.Builder}.
+ */
+ public Builder includeExternal(String includeExternal) {
+ assert (includeExternal != null);
+ assert (includeExternal.matches("audio"));
+ return setQueryParameter("include_external", includeExternal);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A {@link SearchItemRequest.Builder}.
+ */
+ @Override
+ public SearchItemRequest build() {
+ setPath("/v1/search");
+ return new SearchItemRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/search/interfaces/ISearchModelObject.java
@@ -0,0 +1,7 @@
+package com.wrapper.spotify.requests.data.search.interfaces;
+
+import com.wrapper.spotify.model_objects.IModelObject;
+
+public interface ISearchModelObject extends IModelObject {
+
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/search/simplified/SearchAlbumsRequest.java
@@ -0,0 +1,143 @@
+package com.wrapper.spotify.requests.data.search.simplified;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.AlbumSimplified;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.requests.data.AbstractDataPagingRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import com.wrapper.spotify.requests.data.search.SearchItemRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get Spotify catalog information about albums that match a keyword string.
+ */
+@JsonDeserialize(builder = SearchAlbumsRequest.Builder.class)
+public class SearchAlbumsRequest extends AbstractDataRequest<Paging<AlbumSimplified>> {
+
+ /**
+ * The private {@link SearchAlbumsRequest} constructor.
+ *
+ * @param builder A {@link SearchAlbumsRequest.Builder}.
+ */
+ private SearchAlbumsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Search for albums.
+ *
+ * @return An {@link AlbumSimplified} paging.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Paging<AlbumSimplified> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new AlbumSimplified.JsonUtil().createModelObjectPaging(getJson(), "albums");
+ }
+
+ /**
+ * Builder class for building a {@link SearchAlbumsRequest}.
+ */
+ public static final class Builder extends AbstractDataPagingRequest.Builder<AlbumSimplified, Builder> {
+
+ /**
+ * Create a new {@link SearchAlbumsRequest.Builder}.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The search query setter.
+ *
+ * @param q Required. The search query's keywords (and optional field filters and operators).
+ * @return A {@link SearchAlbumsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/search-item/#tablepress-47">Spotify: Search Query Options</a>
+ */
+ public Builder q(final String q) {
+ assert (q != null);
+ assert (!q.equals(""));
+ return setQueryParameter("q", q);
+ }
+
+ /**
+ * The market country code setter.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code. If a country code is given, only artists,
+ * albums, and tracks with content playable in that market will be returned. (Playlist
+ * results are not affected by the market parameter.)
+ * @return A {@link SearchAlbumsRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The maximum number of results to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link SearchAlbumsRequest.Builder}.
+ */
+ @Override
+ public Builder limit(final Integer limit) {
+ assert (limit != null);
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The offset setter.
+ *
+ * @param offset Optional. The index of the first result to return. Default: 0 (i.e., the first result). Maximum
+ * offset: 100.000. Use with {@link #limit(Integer)} to get the next page of search results.
+ * @return A {@link SearchAlbumsRequest.Builder}.
+ */
+ @Override
+ public Builder offset(final Integer offset) {
+ assert (offset != null);
+ assert (0 <= offset && offset <= 100000);
+ return setQueryParameter("offset", offset);
+ }
+
+ /**
+ * The include external setter.
+ *
+ * @param includeExternal Optional. Possible values: {@code audio}. If {@code audio} is set
+ * the response will include any relevant audio content that is hosted externally.
+ * By default external content is filtered out from responses.
+ * @return A {@link SearchItemRequest.Builder}.
+ */
+ public Builder includeExternal(String includeExternal) {
+ assert (includeExternal != null);
+ assert (includeExternal.matches("audio"));
+ return setQueryParameter("include_external", includeExternal);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A {@link SearchAlbumsRequest.Builder}.
+ */
+ @Override
+ public SearchAlbumsRequest build() {
+ setPath("/v1/search");
+ setQueryParameter("type", "album");
+ return new SearchAlbumsRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/search/simplified/SearchArtistsRequest.java
@@ -0,0 +1,143 @@
+package com.wrapper.spotify.requests.data.search.simplified;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Artist;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.requests.data.AbstractDataPagingRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import com.wrapper.spotify.requests.data.search.SearchItemRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get Spotify catalog information about artists that match a keyword string.
+ */
+@JsonDeserialize(builder = SearchArtistsRequest.Builder.class)
+public class SearchArtistsRequest extends AbstractDataRequest<Paging<Artist>> {
+
+ /**
+ * The private {@link SearchArtistsRequest} constructor.
+ *
+ * @param builder A {@link SearchArtistsRequest.Builder}.
+ */
+ private SearchArtistsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Search for artists.
+ *
+ * @return An {@link Artist} paging.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Paging<Artist> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Artist.JsonUtil().createModelObjectPaging(getJson(), "artists");
+ }
+
+ /**
+ * Builder class for building a {@link SearchArtistsRequest}.
+ */
+ public static final class Builder extends AbstractDataPagingRequest.Builder<Artist, Builder> {
+
+ /**
+ * Create a new {@link SearchArtistsRequest.Builder}.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The search query setter.
+ *
+ * @param q Required. The search query's keywords (and optional field filters and operators).
+ * @return A {@link SearchArtistsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/search-item/#tablepress-47">Spotify: Search Query Options</a>
+ */
+ public Builder q(final String q) {
+ assert (q != null);
+ assert (!q.equals(""));
+ return setQueryParameter("q", q);
+ }
+
+ /**
+ * The market country code setter.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code. If a country code is given, only artists,
+ * albums, and tracks with content playable in that market will be returned. (Playlist
+ * results are not affected by the market parameter.)
+ * @return A {@link SearchArtistsRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The maximum number of results to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link SearchArtistsRequest.Builder}.
+ */
+ @Override
+ public Builder limit(final Integer limit) {
+ assert (limit != null);
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The offset setter.
+ *
+ * @param offset Optional. The index of the first result to return. Default: 0 (i.e., the first result). Maximum
+ * offset: 100.000. Use with {@link #limit(Integer)} to get the next page of search results.
+ * @return A {@link SearchArtistsRequest.Builder}.
+ */
+ @Override
+ public Builder offset(final Integer offset) {
+ assert (offset != null);
+ assert (0 <= offset && offset <= 100000);
+ return setQueryParameter("offset", offset);
+ }
+
+ /**
+ * The include external setter.
+ *
+ * @param includeExternal Optional. Possible values: {@code audio}. If {@code audio} is set
+ * the response will include any relevant audio content that is hosted externally.
+ * By default external content is filtered out from responses.
+ * @return A {@link SearchItemRequest.Builder}.
+ */
+ public Builder includeExternal(String includeExternal) {
+ assert (includeExternal != null);
+ assert (includeExternal.matches("audio"));
+ return setQueryParameter("include_external", includeExternal);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A {@link SearchArtistsRequest.Builder}.
+ */
+ @Override
+ public SearchArtistsRequest build() {
+ setPath("/v1/search");
+ setQueryParameter("type", "artist");
+ return new SearchArtistsRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/search/simplified/SearchEpisodesRequest.java
@@ -0,0 +1,143 @@
+package com.wrapper.spotify.requests.data.search.simplified;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.EpisodeSimplified;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.requests.data.AbstractDataPagingRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import com.wrapper.spotify.requests.data.search.SearchItemRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get Spotify catalog information about episodes that match a keyword string.
+ */
+@JsonDeserialize(builder = SearchEpisodesRequest.Builder.class)
+public class SearchEpisodesRequest extends AbstractDataRequest<Paging<EpisodeSimplified>> {
+
+ /**
+ * The private {@link SearchEpisodesRequest} constructor.
+ *
+ * @param builder A {@link SearchEpisodesRequest.Builder}.
+ */
+ private SearchEpisodesRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Search for episodes.
+ *
+ * @return An array of {@link EpisodeSimplified} objects wrapped in a {@link Paging} object.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Paging<EpisodeSimplified> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new EpisodeSimplified.JsonUtil().createModelObjectPaging(getJson(), "episodes");
+ }
+
+ /**
+ * Builder class for building a {@link SearchEpisodesRequest}.
+ */
+ public static final class Builder extends AbstractDataPagingRequest.Builder<EpisodeSimplified, Builder> {
+
+ /**
+ * Create a new {@link SearchEpisodesRequest.Builder}.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The search query setter.
+ *
+ * @param q Required. The search query's keywords (and optional field filters and operators).
+ * @return A {@link SearchEpisodesRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/search-item/#tablepress-47">Spotify: Search Query Options</a>
+ */
+ public Builder q(final String q) {
+ assert (q != null);
+ assert (!q.equals(""));
+ return setQueryParameter("q", q);
+ }
+
+ /**
+ * The market country code setter.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code. If a country code is given, only artists,
+ * albums, and tracks with content playable in that market will be returned. (Playlist
+ * results are not affected by the market parameter.)
+ * @return A {@link SearchEpisodesRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The maximum number of results to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link SearchEpisodesRequest.Builder}.
+ */
+ @Override
+ public Builder limit(final Integer limit) {
+ assert (limit != null);
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The offset setter.
+ *
+ * @param offset Optional. The index of the first result to return. Default: 0 (i.e., the first result). Maximum
+ * offset: 100.000. Use with {@link #limit(Integer)} to get the next page of search results.
+ * @return A {@link SearchEpisodesRequest.Builder}.
+ */
+ @Override
+ public Builder offset(final Integer offset) {
+ assert (offset != null);
+ assert (0 <= offset && offset <= 100000);
+ return setQueryParameter("offset", offset);
+ }
+
+ /**
+ * The include external setter.
+ *
+ * @param includeExternal Optional. Possible values: {@code audio}. If {@code audio} is set
+ * the response will include any relevant audio content that is hosted externally.
+ * By default external content is filtered out from responses.
+ * @return A {@link SearchItemRequest.Builder}.
+ */
+ public Builder includeExternal(String includeExternal) {
+ assert (includeExternal != null);
+ assert (includeExternal.matches("audio"));
+ return setQueryParameter("include_external", includeExternal);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A {@link SearchEpisodesRequest.Builder}.
+ */
+ @Override
+ public SearchEpisodesRequest build() {
+ setPath("/v1/search");
+ setQueryParameter("type", "episode");
+ return new SearchEpisodesRequest(this);
+ }
+
+ @Override
+ protected SearchEpisodesRequest.Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/search/simplified/SearchPlaylistsRequest.java
@@ -0,0 +1,143 @@
+package com.wrapper.spotify.requests.data.search.simplified;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.model_objects.specification.PlaylistSimplified;
+import com.wrapper.spotify.requests.data.AbstractDataPagingRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import com.wrapper.spotify.requests.data.search.SearchItemRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get Spotify catalog information about playlists that match a keyword string.
+ */
+@JsonDeserialize(builder = SearchPlaylistsRequest.Builder.class)
+public class SearchPlaylistsRequest extends AbstractDataRequest<Paging<PlaylistSimplified>> {
+
+ /**
+ * The private {@link SearchPlaylistsRequest} constructor.
+ *
+ * @param builder A {@link SearchPlaylistsRequest.Builder}.
+ */
+ private SearchPlaylistsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Search for playlists.
+ *
+ * @return A {@link PlaylistSimplified} paging.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Paging<PlaylistSimplified> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new PlaylistSimplified.JsonUtil().createModelObjectPaging(getJson(), "playlists");
+ }
+
+ /**
+ * Builder class for building a {@link SearchPlaylistsRequest}.
+ */
+ public static final class Builder extends AbstractDataPagingRequest.Builder<PlaylistSimplified, Builder> {
+
+ /**
+ * Create a new {@link SearchPlaylistsRequest.Builder}.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The search query setter.
+ *
+ * @param q Required. The search query's keywords (and optional field filters and operators).
+ * @return A {@link SearchPlaylistsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/search-item/#tablepress-47">Spotify: Search Query Options</a>
+ */
+ public Builder q(final String q) {
+ assert (q != null);
+ assert (!q.equals(""));
+ return setQueryParameter("q", q);
+ }
+
+ /**
+ * The market country code setter.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code. If a country code is given, only artists,
+ * albums, and tracks with content playable in that market will be returned. (Playlist
+ * results are not affected by the market parameter.)
+ * @return A {@link SearchPlaylistsRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The maximum number of results to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link SearchPlaylistsRequest.Builder}.
+ */
+ @Override
+ public Builder limit(final Integer limit) {
+ assert (limit != null);
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The offset setter.
+ *
+ * @param offset Optional. The index of the first result to return. Default: 0 (i.e., the first result). Maximum
+ * offset: 100.000. Use with {@link #limit(Integer)} to get the next page of search results.
+ * @return A {@link SearchPlaylistsRequest.Builder}.
+ */
+ @Override
+ public Builder offset(final Integer offset) {
+ assert (offset != null);
+ assert (0 <= offset && offset <= 100000);
+ return setQueryParameter("offset", offset);
+ }
+
+ /**
+ * The include external setter.
+ *
+ * @param includeExternal Optional. Possible values: {@code audio}. If {@code audio} is set
+ * the response will include any relevant audio content that is hosted externally.
+ * By default external content is filtered out from responses.
+ * @return A {@link SearchItemRequest.Builder}.
+ */
+ public Builder includeExternal(String includeExternal) {
+ assert (includeExternal != null);
+ assert (includeExternal.matches("audio"));
+ return setQueryParameter("include_external", includeExternal);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A {@link SearchPlaylistsRequest.Builder}.
+ */
+ @Override
+ public SearchPlaylistsRequest build() {
+ setPath("/v1/search");
+ setQueryParameter("type", "playlist");
+ return new SearchPlaylistsRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/search/simplified/SearchShowsRequest.java
@@ -0,0 +1,144 @@
+package com.wrapper.spotify.requests.data.search.simplified;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.model_objects.specification.ShowSimplified;
+import com.wrapper.spotify.requests.data.AbstractDataPagingRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import com.wrapper.spotify.requests.data.search.SearchItemRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get Spotify catalog information about shows that match a keyword string.
+ */
+@JsonDeserialize(builder = SearchShowsRequest.Builder.class)
+public class SearchShowsRequest extends AbstractDataRequest<Paging<ShowSimplified>> {
+
+ /**
+ * The private {@link SearchShowsRequest} constructor.
+ *
+ * @param builder A {@link SearchShowsRequest.Builder}.
+ */
+ private SearchShowsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Search for shows.
+ *
+ * @return An array of {@link ShowSimplified} objects wrapped in a {@link Paging} object.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Paging<ShowSimplified> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new ShowSimplified.JsonUtil().createModelObjectPaging(getJson(), "shows");
+ }
+
+ /**
+ * Builder class for building a {@link SearchShowsRequest}.
+ */
+ public static final class Builder extends AbstractDataPagingRequest.Builder<ShowSimplified, Builder> {
+
+ /**
+ * Create a new {@link SearchShowsRequest.Builder}.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The search query setter.
+ *
+ * @param q Required. The search query's keywords (and optional field filters and operators).
+ * @return A {@link SearchShowsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/search-item/#tablepress-47">Spotify: Search Query Options</a>
+ */
+ public Builder q(final String q) {
+ assert (q != null);
+ assert (!q.equals(""));
+ return setQueryParameter("q", q);
+ }
+
+ /**
+ * The market country code setter.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code. If a country code is given, only artists,
+ * albums, and tracks with content playable in that market will be returned. (Playlist
+ * results are not affected by the market parameter.)
+ * @return A {@link SearchShowsRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The maximum number of results to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link SearchShowsRequest.Builder}.
+ */
+ @Override
+ public Builder limit(final Integer limit) {
+ assert (limit != null);
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The offset setter.
+ *
+ * @param offset Optional. The index of the first result to return. Default: 0 (i.e., the first result). Maximum
+ * offset: 100.000. Use with {@link #limit(Integer)} to get the next page of search results.
+ * @return A {@link SearchShowsRequest.Builder}.
+ */
+ @Override
+ public Builder offset(final Integer offset) {
+ assert (offset != null);
+ assert (0 <= offset && offset <= 100000);
+ return setQueryParameter("offset", offset);
+ }
+
+
+ /**
+ * The include external setter.
+ *
+ * @param includeExternal Optional. Possible values: {@code audio}. If {@code audio} is set
+ * the response will include any relevant audio content that is hosted externally.
+ * By default external content is filtered out from responses.
+ * @return A {@link SearchItemRequest.Builder}.
+ */
+ public Builder includeExternal(String includeExternal) {
+ assert (includeExternal != null);
+ assert (includeExternal.matches("audio"));
+ return setQueryParameter("include_external", includeExternal);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A {@link SearchShowsRequest.Builder}.
+ */
+ @Override
+ public SearchShowsRequest build() {
+ setPath("/v1/search");
+ setQueryParameter("type", "show");
+ return new SearchShowsRequest(this);
+ }
+
+ @Override
+ protected SearchShowsRequest.Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/search/simplified/SearchTracksRequest.java
@@ -0,0 +1,143 @@
+package com.wrapper.spotify.requests.data.search.simplified;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.model_objects.specification.Track;
+import com.wrapper.spotify.requests.data.AbstractDataPagingRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import com.wrapper.spotify.requests.data.search.SearchItemRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get Spotify catalog information about tracks that match a keyword string.
+ */
+@JsonDeserialize(builder = SearchTracksRequest.Builder.class)
+public class SearchTracksRequest extends AbstractDataRequest<Paging<Track>> {
+
+ /**
+ * The private {@link SearchTracksRequest} constructor.
+ *
+ * @param builder A {@link SearchTracksRequest.Builder}.
+ */
+ private SearchTracksRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Search for tracks.
+ *
+ * @return A {@link Track} paging.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Paging<Track> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Track.JsonUtil().createModelObjectPaging(getJson(), "tracks");
+ }
+
+ /**
+ * Builder class for building a {@link SearchTracksRequest}.
+ */
+ public static final class Builder extends AbstractDataPagingRequest.Builder<Track, Builder> {
+
+ /**
+ * Create a new {@link SearchTracksRequest.Builder}.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The search query setter.
+ *
+ * @param q Required. The search query's keywords (and optional field filters and operators).
+ * @return A {@link SearchTracksRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/search-item/#tablepress-47">Spotify: Search Query Options</a>
+ */
+ public Builder q(final String q) {
+ assert (q != null);
+ assert (!q.equals(""));
+ return setQueryParameter("q", q);
+ }
+
+ /**
+ * The market country code setter.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code. If a country code is given, only artists,
+ * albums, and tracks with content playable in that market will be returned. (Playlist
+ * results are not affected by the market parameter.)
+ * @return A {@link SearchTracksRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The maximum number of results to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link SearchTracksRequest.Builder}.
+ */
+ @Override
+ public Builder limit(final Integer limit) {
+ assert (limit != null);
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The offset setter.
+ *
+ * @param offset Optional. The index of the first result to return. Default: 0 (i.e., the first result). Maximum
+ * offset: 100.000. Use with {@link #limit(Integer)} to get the next page of search results.
+ * @return A {@link SearchTracksRequest.Builder}.
+ */
+ @Override
+ public Builder offset(final Integer offset) {
+ assert (offset != null);
+ assert (0 <= offset && offset <= 100000);
+ return setQueryParameter("offset", offset);
+ }
+
+ /**
+ * The include external setter.
+ *
+ * @param includeExternal Optional. Possible values: {@code audio}. If {@code audio} is set
+ * the response will include any relevant audio content that is hosted externally.
+ * By default external content is filtered out from responses.
+ * @return A {@link SearchItemRequest.Builder}.
+ */
+ public Builder includeExternal(String includeExternal) {
+ assert (includeExternal != null);
+ assert (includeExternal.matches("audio"));
+ return setQueryParameter("include_external", includeExternal);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A {@link SearchTracksRequest.Builder}.
+ */
+ @Override
+ public SearchTracksRequest build() {
+ setPath("/v1/search");
+ setQueryParameter("type", "track");
+ return new SearchTracksRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/search/simplified/special/SearchAlbumsSpecialRequest.java
@@ -0,0 +1,131 @@
+package com.wrapper.spotify.requests.data.search.simplified.special;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.special.AlbumSimplifiedSpecial;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.requests.data.AbstractDataPagingRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get Spotify catalog information about albums that match a keyword string.
+ * <p>
+ * This class exists because it includes the property {@code totalTracks}, which is not documented in the official
+ * specification, although the albums object as returned by the searches API includes it.
+ */
+@JsonDeserialize(builder = SearchAlbumsSpecialRequest.Builder.class)
+public class SearchAlbumsSpecialRequest extends AbstractDataRequest<Paging<AlbumSimplifiedSpecial>> {
+
+ /**
+ * The private {@link SearchAlbumsSpecialRequest} constructor.
+ *
+ * @param builder A {@link Builder}.
+ */
+ private SearchAlbumsSpecialRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Search for albums.
+ *
+ * @return An {@link AlbumSimplifiedSpecial} paging.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Paging<AlbumSimplifiedSpecial> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new AlbumSimplifiedSpecial.JsonUtil().createModelObjectPaging(getJson(), "albums");
+ }
+
+ /**
+ * Builder class for building a {@link SearchAlbumsSpecialRequest}.
+ */
+ public static final class Builder extends AbstractDataPagingRequest.Builder<AlbumSimplifiedSpecial, Builder> {
+
+ /**
+ * Create a new {@link Builder}.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The search query setter.
+ *
+ * @param q Required. The search query's keywords (and optional field filters and operators).
+ * @return A {@link Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/search-item/#tablepress-47">Spotify: Search Query Options</a>
+ */
+ public Builder q(final String q) {
+ assert (q != null);
+ assert (!q.equals(""));
+ return setQueryParameter("q", q);
+ }
+
+ /**
+ * The market country code setter.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code. If a country code is given, only artists,
+ * albums, and tracks with content playable in that market will be returned. (Playlist
+ * results are not affected by the market parameter.)
+ * @return A {@link Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The maximum number of results to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link Builder}.
+ */
+ @Override
+ public Builder limit(final Integer limit) {
+ assert (limit != null);
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The offset setter.
+ *
+ * @param offset Optional. The index of the first result to return. Default: 0 (i.e., the first result). Maximum
+ * offset: 100.000. Use with {@link #limit(Integer)} to get the next page of search results.
+ * @return A {@link Builder}.
+ */
+ @Override
+ public Builder offset(final Integer offset) {
+ assert (offset != null);
+ assert (0 <= offset && offset <= 100000);
+ return setQueryParameter("offset", offset);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A {@link Builder}.
+ */
+ @Override
+ public SearchAlbumsSpecialRequest build() {
+ setPath("/v1/search");
+ setQueryParameter("type", "album");
+ return new SearchAlbumsSpecialRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/shows/GetSeveralShowsRequest.java
@@ -0,0 +1,103 @@
+package com.wrapper.spotify.requests.data.shows;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.ShowSimplified;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get Spotify catalog information for multiple shows based on their Spotify IDs.
+ */
+@JsonDeserialize(builder = GetSeveralShowsRequest.Builder.class)
+public class GetSeveralShowsRequest extends AbstractDataRequest<ShowSimplified[]> {
+
+ /**
+ * The private {@link GetSeveralShowsRequest} constructor.
+ *
+ * @param builder A {@link GetSeveralShowsRequest.Builder}.
+ */
+ private GetSeveralShowsRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get several shows.
+ *
+ * @return Multiple {@link ShowSimplified} objects.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ @Override
+ public ShowSimplified[] execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new ShowSimplified.JsonUtil().createModelObjectArray(getJson(), "shows");
+ }
+
+ /**
+ * Builder class for building a {@link GetSeveralShowsRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<ShowSimplified[], Builder> {
+
+ /**
+ * Create a new {@link GetSeveralShowsRequest.Builder}.
+ * <p>
+ * Reading the user’s resume points on episode objects requires the {@code user-read-playback-position} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The show IDs setter.
+ *
+ * @param ids Required. A comma-separated list of the Spotify IDs for the shows. Maximum: 50 IDs.
+ * @return A {@link GetSeveralShowsRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public GetSeveralShowsRequest.Builder ids(final String ids) {
+ assert (ids != null);
+ assert (ids.split(",").length <= 50);
+ return setQueryParameter("ids", ids);
+ }
+
+ /**
+ * The market country code setter.<p>
+ * If a country code is specified, only shows and episodes that are available in that market will be returned.
+ * If a valid user access token is specified in the request header, the country associated with the user account will take priority over this parameter.
+ * <i>Note: If neither market or user country are provided, the content is considered unavailable for the client.</i><p>
+ * Users can view the country that is associated with their account in the account settings.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code.
+ * @return A {@link GetSeveralShowsRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public GetSeveralShowsRequest.Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetSeveralShowsRequest}.
+ */
+ @Override
+ public GetSeveralShowsRequest build() {
+ setPath("/v1/shows");
+ return new GetSeveralShowsRequest(this);
+ }
+
+ @Override
+ protected GetSeveralShowsRequest.Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/shows/GetShowRequest.java
@@ -0,0 +1,103 @@
+package com.wrapper.spotify.requests.data.shows;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Show;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get Spotify catalog information for a single show identified by its unique Spotify ID.
+ */
+@JsonDeserialize(builder = GetShowRequest.Builder.class)
+public class GetShowRequest extends AbstractDataRequest<Show> {
+
+ /**
+ * The private {@link GetShowRequest} constructor.
+ *
+ * @param builder A {@link GetShowRequest.Builder}.
+ */
+ private GetShowRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get a show.
+ *
+ * @return An {@link Show}.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ @Override
+ public Show execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Show.JsonUtil().createModelObject(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetShowRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<Show, Builder> {
+
+ /**
+ * Create a new {@link GetShowRequest.Builder}.
+ * <p>
+ * Reading the user’s resume points on episode objects requires the {@code user-read-playback-position} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The show ID setter.
+ *
+ * @param id The Spotify ID for the show.
+ * @return A {@link GetShowRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder id(final String id) {
+ assert (id != null);
+ assert (!id.equals(""));
+ return setPathParameter("id", id);
+ }
+
+ /**
+ * The market country code setter.<p>
+ * If a country code is specified, only shows and episodes that are available in that market will be returned.
+ * If a valid user access token is specified in the request header, the country associated with the user account will take priority over this parameter.
+ * <i>Note: If neither market or user country are provided, the content is considered unavailable for the client.</i><p>
+ * Users can view the country that is associated with their account in the account settings.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code.
+ * @return A {@link GetShowRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetShowRequest}.
+ */
+ @Override
+ public GetShowRequest build() {
+ setPath("/v1/shows/{id}");
+ return new GetShowRequest(this);
+ }
+
+ @Override
+ protected GetShowRequest.Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/shows/GetShowsEpisodesRequest.java
@@ -0,0 +1,131 @@
+package com.wrapper.spotify.requests.data.shows;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.EpisodeSimplified;
+import com.wrapper.spotify.model_objects.specification.Paging;
+import com.wrapper.spotify.requests.data.AbstractDataPagingRequest;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get Spotify catalog information about an show’s episodes.
+ */
+@JsonDeserialize(builder = GetShowsEpisodesRequest.Builder.class)
+public class GetShowsEpisodesRequest extends AbstractDataRequest<Paging<EpisodeSimplified>> {
+
+ /**
+ * The private {@link GetShowsEpisodesRequest} constructor.
+ *
+ * @param builder A {@link GetShowsEpisodesRequest.Builder}.
+ */
+ private GetShowsEpisodesRequest(Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get episodes of a show.
+ *
+ * @return An array of {@link EpisodeSimplified} objects wrapped in a {@link Paging} object.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ @Override
+ public Paging<EpisodeSimplified> execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new EpisodeSimplified.JsonUtil().createModelObjectPaging(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetShowsEpisodesRequest}.
+ */
+ public static final class Builder extends AbstractDataPagingRequest.Builder<EpisodeSimplified, Builder> {
+
+ /**
+ * Create a new {@link GetShowsEpisodesRequest.Builder} instance.
+ * <p>
+ * Reading the user’s resume points on episode objects requires the {@code user-read-playback-position} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The show ID setter.
+ *
+ * @param id The Spotify ID for the show.
+ * @return A {@link GetShowsEpisodesRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder id(final String id) {
+ assert (id != null);
+ assert (!id.equals(""));
+ return setPathParameter("id", id);
+ }
+
+ /**
+ * The limit setter.
+ *
+ * @param limit Optional. The maximum number of episodes to return. Default: 20. Minimum: 1. Maximum: 50.
+ * @return A {@link GetShowsEpisodesRequest.Builder}.
+ */
+ @Override
+ public Builder limit(final Integer limit) {
+ assert (1 <= limit && limit <= 50);
+ return setQueryParameter("limit", limit);
+ }
+
+ /**
+ * The offset setter.
+ *
+ * @param offset Optional. The index of the first episode to return. Default: 0 (i.e., the first object). Use with
+ * {@link #limit(Integer)} to get the next set of objects.
+ * @return A {@link GetShowsEpisodesRequest.Builder}.
+ */
+ @Override
+ public Builder offset(final Integer offset) {
+ assert (offset >= 0);
+ return setQueryParameter("offset", offset);
+ }
+
+ /**
+ * The market country code setter.<p>
+ * If a country code is specified, only shows and episodes that are available in that market will be returned.
+ * If a valid user access token is specified in the request header, the country associated with the user account will take priority over this parameter.
+ * <i>Note: If neither market or user country are provided, the content is considered unavailable for the client.</i><p>
+ * Users can view the country that is associated with their account in the account settings.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code.
+ * @return A {@link GetShowsEpisodesRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetShowsEpisodesRequest}.
+ */
+ @Override
+ public GetShowsEpisodesRequest build() {
+ setPath("/v1/shows/{id}/episodes");
+ return new GetShowsEpisodesRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/tracks/GetAudioAnalysisForTrackRequest.java
@@ -0,0 +1,83 @@
+package com.wrapper.spotify.requests.data.tracks;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.miscellaneous.AudioAnalysis;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get a detailed audio analysis for a single track identified by its unique Spotify ID.
+ */
+@JsonDeserialize(builder = GetAudioAnalysisForTrackRequest.Builder.class)
+public class GetAudioAnalysisForTrackRequest extends AbstractDataRequest<AudioAnalysis> {
+
+ /**
+ * The private {@link GetAudioAnalysisForTrackRequest} constructor.
+ *
+ * @param builder A {@link GetAudioAnalysisForTrackRequest.Builder}.
+ */
+ private GetAudioAnalysisForTrackRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get an audio analysis for a track.
+ *
+ * @return An {@link AudioAnalysis}.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public AudioAnalysis execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new AudioAnalysis.JsonUtil().createModelObject(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetAudioAnalysisForTrackRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<AudioAnalysis, Builder> {
+
+ /**
+ * Create a new {@link GetAudioAnalysisForTrackRequest.Builder}.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The track ID setter.
+ *
+ * @param id Required. The Spotify ID for the track.
+ * @return A {@link GetAudioAnalysisForTrackRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder id(final String id) {
+ assert (id != null);
+ assert (!id.equals(""));
+ return setPathParameter("id", id);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetAudioAnalysisForTrackRequest}.
+ */
+ @Override
+ public GetAudioAnalysisForTrackRequest build() {
+ setPath("/v1/audio-analysis/{id}");
+ return new GetAudioAnalysisForTrackRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/tracks/GetAudioFeaturesForSeveralTracksRequest.java
@@ -0,0 +1,83 @@
+package com.wrapper.spotify.requests.data.tracks;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.AudioFeatures;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get audio features for multiple tracks based on their Spotify IDs.
+ */
+@JsonDeserialize(builder = GetAudioFeaturesForSeveralTracksRequest.Builder.class)
+public class GetAudioFeaturesForSeveralTracksRequest extends AbstractDataRequest<AudioFeatures[]> {
+
+ /**
+ * The private {@link GetAudioFeaturesForSeveralTracksRequest} constructor.
+ *
+ * @param builder A {@link GetAudioFeaturesForSeveralTracksRequest.Builder}.
+ */
+ private GetAudioFeaturesForSeveralTracksRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get audio features for several tracks.
+ *
+ * @return Multiple {@link AudioFeatures} objects.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public AudioFeatures[] execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new AudioFeatures.JsonUtil().createModelObjectArray(getJson(), "audio_features");
+ }
+
+ /**
+ * Builder class for building a {@link GetAudioFeaturesForSeveralTracksRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<AudioFeatures[], Builder> {
+
+ /**
+ * Create a new {@link GetAudioFeaturesForSeveralTracksRequest.Builder}.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The track IDs setter.
+ *
+ * @param ids Required. A comma-separated list of the Spotify IDs for the tracks. Maximum: 100 IDs.
+ * @return A {@link GetAudioFeaturesForSeveralTracksRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder ids(final String ids) {
+ assert (ids != null);
+ assert (ids.split(",").length <= 100);
+ return setQueryParameter("ids", ids);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetAudioFeaturesForSeveralTracksRequest}.
+ */
+ @Override
+ public GetAudioFeaturesForSeveralTracksRequest build() {
+ setPath("/v1/audio-features");
+ return new GetAudioFeaturesForSeveralTracksRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/tracks/GetAudioFeaturesForTrackRequest.java
@@ -0,0 +1,83 @@
+package com.wrapper.spotify.requests.data.tracks;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.AudioFeatures;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get audio feature information for a single track identified by its unique Spotify ID.
+ */
+@JsonDeserialize(builder = GetAudioFeaturesForTrackRequest.Builder.class)
+public class GetAudioFeaturesForTrackRequest extends AbstractDataRequest<AudioFeatures> {
+
+ /**
+ * The private {@link GetAudioFeaturesForTrackRequest} constructor.
+ *
+ * @param builder A {@link GetAudioFeaturesForTrackRequest.Builder}.
+ */
+ private GetAudioFeaturesForTrackRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get audio features for a track.
+ *
+ * @return An {@link AudioFeatures} object.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public AudioFeatures execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new AudioFeatures.JsonUtil().createModelObject(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetAudioFeaturesForTrackRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<AudioFeatures, Builder> {
+
+ /**
+ * Create a new {@link GetAudioFeaturesForTrackRequest.Builder}.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The track ID setter.
+ *
+ * @param id Required. The Spotify ID for the track.
+ * @return A {@link GetAudioFeaturesForTrackRequest.Builder}..
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder id(final String id) {
+ assert (id != null);
+ assert (!id.equals(""));
+ return setPathParameter("id", id);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetAudioFeaturesForTrackRequest}.
+ */
+ @Override
+ public GetAudioFeaturesForTrackRequest build() {
+ setPath("/v1/audio-features/{id}");
+ return new GetAudioFeaturesForTrackRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/tracks/GetSeveralTracksRequest.java
@@ -0,0 +1,98 @@
+package com.wrapper.spotify.requests.data.tracks;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Track;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get Spotify catalog information for multiple tracks based on their Spotify IDs.
+ */
+@JsonDeserialize(builder = GetSeveralTracksRequest.Builder.class)
+public class GetSeveralTracksRequest extends AbstractDataRequest<Track[]> {
+
+ /**
+ * The private {@link GetSeveralTracksRequest} constructor.
+ *
+ * @param builder A {@link GetSeveralTracksRequest.Builder}.
+ */
+ private GetSeveralTracksRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get several tracks.
+ *
+ * @return Multiple {@link Track} objects.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Track[] execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Track.JsonUtil().createModelObjectArray(getJson(), "tracks");
+ }
+
+ /**
+ * Builder class for building a {@link GetSeveralTracksRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<Track[], Builder> {
+
+ /**
+ * Create a new {@link GetSeveralTracksRequest.Builder}.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The track IDs setter.
+ *
+ * @param ids Required. A comma-separated list of the Spotify IDs for the tracks. Maximum: 50 IDs.
+ * @return A {@link GetSeveralTracksRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder ids(final String ids) {
+ assert (ids != null);
+ assert (ids.split(",").length <= 50);
+ return setQueryParameter("ids", ids);
+ }
+
+ /**
+ * The market country code setter.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code. Provide this
+ * parameter if you want to apply Track Relinking.
+ * @return A {@link GetSeveralTracksRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ * @see <a href="https://developer.spotify.com/web-api/track-relinking-guide/">Spotify: Track Relinking Guide</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetSeveralTracksRequest}.
+ */
+ @Override
+ public GetSeveralTracksRequest build() {
+ setPath("/v1/tracks");
+ return new GetSeveralTracksRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/tracks/GetTrackRequest.java
@@ -0,0 +1,98 @@
+package com.wrapper.spotify.requests.data.tracks;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.neovisionaries.i18n.CountryCode;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.Track;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get Spotify catalog information for a single track identified by its unique Spotify ID.
+ */
+@JsonDeserialize(builder = GetTrackRequest.Builder.class)
+public class GetTrackRequest extends AbstractDataRequest<Track> {
+
+ /**
+ * The private {@link GetTrackRequest} constructor.
+ *
+ * @param builder A {@link GetTrackRequest.Builder}.
+ */
+ private GetTrackRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get a track.
+ *
+ * @return A {@link Track}.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public Track execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new Track.JsonUtil().createModelObject(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetTrackRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<Track, Builder> {
+
+ /**
+ * Create a new {@link GetTrackRequest.Builder}.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The track ID setter.
+ *
+ * @param id The Spotify ID for the track.
+ * @return A {@link GetTrackRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder id(final String id) {
+ assert (id != null);
+ assert (!id.equals(""));
+ return setPathParameter("id", id);
+ }
+
+ /**
+ * The market country code setter.
+ *
+ * @param market Optional. An ISO 3166-1 alpha-2 country code. Provide this
+ * parameter if you want to apply Track Relinking.
+ * @return A {@link GetSeveralTracksRequest.Builder}.
+ * @see <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">Wikipedia: ISO 3166-1 alpha-2 country codes</a>
+ * @see <a href="https://developer.spotify.com/web-api/track-relinking-guide/">Spotify: Track Relinking Guide</a>
+ */
+ public Builder market(final CountryCode market) {
+ assert (market != null);
+ return setQueryParameter("market", market);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetTrackRequest}.
+ */
+ @Override
+ public GetTrackRequest build() {
+ setPath("/v1/tracks/{id}");
+ return new GetTrackRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/users_profile/GetCurrentUsersProfileRequest.java
@@ -0,0 +1,75 @@
+package com.wrapper.spotify.requests.data.users_profile;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.User;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get detailed profile information about the current user (including the current user’s username).
+ */
+@JsonDeserialize(builder = GetCurrentUsersProfileRequest.Builder.class)
+public class GetCurrentUsersProfileRequest extends AbstractDataRequest<User> {
+
+ /**
+ * The private {@link GetCurrentUsersProfileRequest} constructor.
+ *
+ * @param builder A {@link GetCurrentUsersProfileRequest.Builder}.
+ */
+ private GetCurrentUsersProfileRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get the profile of the current user.
+ *
+ * @return A {@link User}.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public User execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new User.JsonUtil().createModelObject(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetCurrentUsersProfileRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<User, Builder> {
+
+ /**
+ * Create a new {@link GetCurrentUsersProfileRequest.Builder}.
+ * <p>
+ * Reading the user's email address requires the {@code user-read-email} scope; reading
+ * country and product subscription level requires the {@code user-read-private} scope. Reading
+ * the user's birthdate requires the {@code user-read-birthdate} scope.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ * @see <a href="https://developer.spotify.com/web-api/using-scopes/">Spotify: Using Scopes</a>
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetCurrentUsersProfileRequest}.
+ */
+ @Override
+ public GetCurrentUsersProfileRequest build() {
+ setPath("/v1/me");
+ return new GetCurrentUsersProfileRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/com/stream_pi/spotify/requests/data/users_profile/GetUsersProfileRequest.java
@@ -0,0 +1,83 @@
+package com.wrapper.spotify.requests.data.users_profile;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.wrapper.spotify.exceptions.SpotifyWebApiException;
+import com.wrapper.spotify.model_objects.specification.User;
+import com.wrapper.spotify.requests.data.AbstractDataRequest;
+import org.apache.hc.core5.http.ParseException;
+
+import java.io.IOException;
+
+/**
+ * Get public profile information about a Spotify user.
+ */
+@JsonDeserialize(builder = GetUsersProfileRequest.Builder.class)
+public class GetUsersProfileRequest extends AbstractDataRequest<User> {
+
+ /**
+ * The private {@link GetUsersProfileRequest} constructor.
+ *
+ * @param builder A {@link GetUsersProfileRequest.Builder}.
+ */
+ private GetUsersProfileRequest(final Builder builder) {
+ super(builder);
+ }
+
+ /**
+ * Get the profile of a current user.
+ *
+ * @return A {@link User}.
+ * @throws IOException In case of networking issues.
+ * @throws SpotifyWebApiException The Web API returned an error further specified in this exception's root cause.
+ */
+ public User execute() throws
+ IOException,
+ SpotifyWebApiException,
+ ParseException {
+ return new User.JsonUtil().createModelObject(getJson());
+ }
+
+ /**
+ * Builder class for building a {@link GetUsersProfileRequest}.
+ */
+ public static final class Builder extends AbstractDataRequest.Builder<User, Builder> {
+
+ /**
+ * Create a new {@link GetUsersProfileRequest.Builder}.
+ *
+ * @param accessToken Required. A valid access token from the Spotify Accounts service.
+ */
+ public Builder(final String accessToken) {
+ super(accessToken);
+ }
+
+ /**
+ * The user ID setter.
+ *
+ * @param user_id The user's Spotify user ID.
+ * @return A {@link GetUsersProfileRequest.Builder}.
+ * @see <a href="https://developer.spotify.com/web-api/user-guide/#spotify-uris-and-ids">Spotify: URIs &amp; IDs</a>
+ */
+ public Builder user_id(final String user_id) {
+ assert (user_id != null);
+ assert (!user_id.equals(""));
+ return setPathParameter("user_id", user_id);
+ }
+
+ /**
+ * The request build method.
+ *
+ * @return A custom {@link GetUsersProfileRequest}.
+ */
+ @Override
+ public GetUsersProfileRequest build() {
+ setPath("/v1/users/{user_id}");
+ return new GetUsersProfileRequest(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
--- /dev/null
+++ b/spotify/src/main/java/module-info.java
@@ -0,0 +1,7 @@
+module com.stream_pi.spotify
+{
+ requires com.stream_pi.action_api;
+ requires org.kordamp.ikonli.javafx;
+
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with com.stream_pi.spotify.Spotify;
+}
\ No newline at end of file
--- 'a/textblockaction/src/main/java/com/stream_pi/textblockaction/TextBlockAction.java'
+++ b/textblockaction/src/main/java/com/stream_pi/textblockaction/TextBlockAction.java
@@ -2,7 +2,7 @@ package com.stream_pi.textblockaction;
import com.stream_pi.action_api.actionproperty.property.Property;
import com.stream_pi.action_api.actionproperty.property.Type;
-import com.stream_pi.action_api.normalaction.NormalAction;
+import com.stream_pi.action_api.externalplugin.NormalAction;
import com.stream_pi.util.version.Version;
import static java.awt.event.KeyEvent.*;
--- 'a/textblockaction/src/main/java/module-info.java'
+++ b/textblockaction/src/main/java/module-info.java
@@ -9,6 +9,6 @@ module com.stream_pi.textblockaction {
requires java.desktop;
- provides com.stream_pi.action_api.normalaction.NormalAction with TextBlockAction;
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with TextBlockAction;
}
\ No newline at end of file
A twitch/README.md
+39 −0
--- /dev/null
+++ b/twitch/README.md
@@ -0,0 +1,39 @@
+# Twitch Chat Integration
+
+The first step is to acquire an [OAuth token](https://twitchapps.com/tmi/), the generated OAuth token should look something like `oauth:xxxxx`.
+
+Then in the Stream-Pi Server's Plugin page you will need to enter your Twitch username with the generated OAuth token then click on `Save Twitch Chat credentials` button.
+You should then be able to use the pre-bundled Twitch chat actions.
+
+## Supported actions (see [Chat Commands](https://help.twitch.tv/s/article/chat-commands?language=en_US) for full documentation)
+
+### All Users
+
+- Set username color
+ - Normal users can choose between Blue, Coral, DodgerBlue, SpringGreen, YellowGreen, Green, OrangeRed, Red, GoldenRod, HotPink, CadetBlue, SeaGreen, Chocolate, BlueViolet, and Firebrick. Twitch Turbo users can use any Hex value (i.e: #000000).
+- Send channel message
+- Whisper (send user message)
+
+### Broadcaster and Mods
+
+- Clear chat
+
+### Broadcaster and channel editors
+
+- Run commercial
+- Host
+- Unhost
+- Raid
+- Unraid
+- Add stream marker
+
+### TODO
+
+- Toggle slow mode
+- Toggle followers-only mode
+- Toggle subs-only mode
+- Toggle emotes-only mode
+
+## Running locally
+
+Copy the `Java-Twirk` and the `twitch-xxx-action` jar files from the `PreBuiltPlugins` directory to your Stream-Pi server plugins' directory.
\ No newline at end of file
--- /dev/null
+++ b/twitch/add-stream-marker/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>com.stream-pi</groupId>
+ <artifactId>twitch-add-stream-marker</artifactId>
+ <version>1.0.0</version>
+
+ <properties>
+ <maven.compiler.source>11</maven.compiler.source>
+ <maven.compiler.target>11</maven.compiler.target>
+ <streamPiActionApiVersion>1.0.0-SNAPSHOT</streamPiActionApiVersion>
+ <streamPiUtilVersion>1.0.0-SNAPSHOT</streamPiUtilVersion>
+ <streamPiTwitchChatConnectVersion>1.0.0</streamPiTwitchChatConnectVersion>
+ <javaTwirkVersion>0.6.3</javaTwirkVersion>
+ </properties>
+
+ <repositories>
+ <repository>
+ <id>jitpack.io</id>
+ <url>https://jitpack.io</url>
+ </repository>
+ </repositories>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>3.1.0</version>
+ <executions>
+ <execution>
+ <id>test-jar</id>
+ <phase>package</phase>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.8.1</version>
+ <configuration>
+ <release>11</release>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>util</artifactId>
+ <version>${streamPiUtilVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>action-api</artifactId>
+ <version>${streamPiActionApiVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.github.gikkman</groupId>
+ <artifactId>Java-Twirk</artifactId>
+ <version>${javaTwirkVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>twitch-chat-connect</artifactId>
+ <version>${streamPiTwitchChatConnectVersion}</version>
+ </dependency>
+ </dependencies>
+
+</project>
--- /dev/null
+++ b/twitch/add-stream-marker/src/main/java/addstreammarker/AddStreamMarkerAction.java
@@ -0,0 +1,89 @@
+package addstreammarker;
+
+import com.gikk.twirk.Twirk;
+import com.gikk.twirk.TwirkBuilder;
+import com.stream_pi.action_api.actionproperty.property.Property;
+import com.stream_pi.action_api.actionproperty.property.Type;
+import com.stream_pi.action_api.externalplugin.NormalAction;
+import com.stream_pi.util.exception.StreamPiException;
+import com.stream_pi.util.version.Version;
+import connect.chat.TwitchChatCredentials;
+
+public class AddStreamMarkerAction extends NormalAction
+{
+
+ private final String channelNameKey = "channel_name_asm";
+ private final String descriptionKey = "description_asm";
+
+ private Twirk twirk;
+
+ public AddStreamMarkerAction()
+ {
+ setName("Add stream marker");
+ setCategory("Twitch Chat");
+ setVisibilityInServerSettingsPane(false);
+ setAuthor("j4ckofalltrades");
+ setVersion(new Version(1, 0, 0));
+ setHelpLink("https://github.com/stream-pi/essentialactions#twitch-chat-integration");
+ }
+
+ @Override
+ public void initProperties() throws Exception
+ {
+ Property channelName = new Property(channelNameKey, Type.STRING);
+ channelName.setDisplayName("Channel Name");
+ channelName.setDefaultValueStr("channel_name");
+ channelName.setCanBeBlank(false);
+
+ Property description = new Property(descriptionKey, Type.STRING);
+ description.setDisplayName("Description (Optional, max 140 chars)");
+
+ addClientProperties(channelName, description);
+ }
+
+ @Override
+ public void initAction() throws Exception
+ {
+
+ }
+
+ @Override
+ public void onActionClicked() throws Exception
+ {
+ final TwitchChatCredentials.ChatCredentials credentials = TwitchChatCredentials.getCredentials();
+ credentials.ensureCredentialsInitialized();
+
+ final String channel = getClientProperties().getSingleProperty(channelNameKey).getStringValue();
+ final String description = getClientProperties().getSingleProperty(descriptionKey).getStringValue();
+
+ try
+ {
+ twirk = new TwirkBuilder(channel, credentials.getNickname(), credentials.getOauthToken()).build();
+ twirk.connect();
+
+ if (description != null && !description.isEmpty()) {
+ twirk.channelMessage(String.format("/marker %s", description));
+ } else {
+ twirk.channelMessage("/marker");
+ }
+ } catch (Exception ex)
+ {
+ throw new StreamPiException(
+ "Failed to add marker to stream", "Could not add stream marker, please try again."
+ );
+ }
+ }
+
+ @Override
+ public void onShutDown() throws Exception
+ {
+ if (twirk != null) {
+ try
+ {
+ twirk.disconnect();
+ } catch (Exception ex) {
+ throw new StreamPiException("Twitch connection error", "Please try again.");
+ }
+ }
+ }
+}
--- /dev/null
+++ b/twitch/add-stream-marker/src/main/java/module-info.java
@@ -0,0 +1,9 @@
+module com.stream_pi.twitch.addstreammarkeraction {
+ requires com.stream_pi.util;
+ requires com.stream_pi.action_api;
+
+ requires com.stream_pi.twitchchatconnectaction;
+ requires Java.Twirk;
+
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with addstreammarker.AddStreamMarkerAction;
+}
--- /dev/null
+++ b/twitch/clear-chat/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>com.stream-pi</groupId>
+ <artifactId>twitch-clear-chat</artifactId>
+ <version>1.0.0</version>
+
+ <properties>
+ <maven.compiler.source>11</maven.compiler.source>
+ <maven.compiler.target>11</maven.compiler.target>
+ <streamPiActionApiVersion>1.0.0-SNAPSHOT</streamPiActionApiVersion>
+ <streamPiUtilVersion>1.0.0-SNAPSHOT</streamPiUtilVersion>
+ <streamPiTwitchChatConnectVersion>1.0.0</streamPiTwitchChatConnectVersion>
+ <javaTwirkVersion>0.6.3</javaTwirkVersion>
+ </properties>
+
+ <repositories>
+ <repository>
+ <id>jitpack.io</id>
+ <url>https://jitpack.io</url>
+ </repository>
+ </repositories>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>3.1.0</version>
+ <executions>
+ <execution>
+ <id>test-jar</id>
+ <phase>package</phase>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.8.1</version>
+ <configuration>
+ <release>11</release>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>util</artifactId>
+ <version>${streamPiUtilVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>action-api</artifactId>
+ <version>${streamPiActionApiVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.github.gikkman</groupId>
+ <artifactId>Java-Twirk</artifactId>
+ <version>${javaTwirkVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>twitch-chat-connect</artifactId>
+ <version>${streamPiTwitchChatConnectVersion}</version>
+ </dependency>
+ </dependencies>
+
+</project>
--- /dev/null
+++ b/twitch/clear-chat/src/main/java/clearchat/ClearChatAction.java
@@ -0,0 +1,77 @@
+package clearchat;
+
+import com.gikk.twirk.Twirk;
+import com.gikk.twirk.TwirkBuilder;
+import com.stream_pi.action_api.actionproperty.property.Property;
+import com.stream_pi.action_api.actionproperty.property.Type;
+import com.stream_pi.action_api.externalplugin.NormalAction;
+import com.stream_pi.util.exception.StreamPiException;
+import com.stream_pi.util.version.Version;
+import connect.chat.TwitchChatCredentials;
+
+public class ClearChatAction extends NormalAction
+{
+
+ private final String channelNameKey = "channel_name_cc";
+
+ private Twirk twirk;
+
+ @Override
+ public void initProperties() throws Exception
+ {
+ setName("Clear Chat");
+ setCategory("Twitch Chat");
+ setVisibilityInServerSettingsPane(false);
+ setAuthor("j4ckofalltrades");
+ setVersion(new Version(1, 0, 0));
+ setHelpLink("https://github.com/stream-pi/essentialactions#twitch-chat-integration");
+ }
+
+ @Override
+ public void initAction() throws Exception
+ {
+ Property channelName = new Property(channelNameKey, Type.STRING);
+ channelName.setDisplayName("Channel Name");
+ channelName.setDefaultValueStr("channel_name");
+ channelName.setCanBeBlank(false);
+
+ addClientProperties(channelName);
+ }
+
+ @Override
+ public void onActionClicked() throws Exception
+ {
+ final TwitchChatCredentials.ChatCredentials credentials = TwitchChatCredentials.getCredentials();
+ credentials.ensureCredentialsInitialized();
+
+ final String channel = getClientProperties().getSingleProperty(channelNameKey).getStringValue();
+
+ try
+ {
+ twirk = new TwirkBuilder(channel, credentials.getNickname(), credentials.getOauthToken()).build();
+ twirk.connect();
+ twirk.channelMessage("/clear");
+ } catch (Exception ex)
+ {
+ throw new StreamPiException(
+ "Failed to clear channel chat",
+ String.format("Could not clear chat for '%s' channel, please try again.", channel)
+ );
+ }
+ }
+
+ @Override
+ public void onShutDown() throws Exception
+ {
+ if (twirk != null)
+ {
+ try
+ {
+ twirk.disconnect();
+ } catch (Exception ex)
+ {
+ throw new StreamPiException("Twitch Connection error", "Please try again.");
+ }
+ }
+ }
+}
--- /dev/null
+++ b/twitch/clear-chat/src/main/java/module-info.java
@@ -0,0 +1,9 @@
+module com.stream_pi.twitch.clearchataction {
+ requires com.stream_pi.util;
+ requires com.stream_pi.action_api;
+
+ requires com.stream_pi.twitchchatconnectaction;
+ requires Java.Twirk;
+
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with clearchat.ClearChatAction;
+}
--- /dev/null
+++ b/twitch/host-channel/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>com.stream-pi</groupId>
+ <artifactId>twitch-host-channel</artifactId>
+ <version>1.0.0</version>
+
+ <properties>
+ <maven.compiler.source>11</maven.compiler.source>
+ <maven.compiler.target>11</maven.compiler.target>
+ <streamPiActionApiVersion>1.0.0-SNAPSHOT</streamPiActionApiVersion>
+ <streamPiUtilVersion>1.0.0-SNAPSHOT</streamPiUtilVersion>
+ <streamPiTwitchChatConnectVersion>1.0.0</streamPiTwitchChatConnectVersion>
+ <javaTwirkVersion>0.6.3</javaTwirkVersion>
+ </properties>
+
+ <repositories>
+ <repository>
+ <id>jitpack.io</id>
+ <url>https://jitpack.io</url>
+ </repository>
+ </repositories>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>3.1.0</version>
+ <executions>
+ <execution>
+ <id>test-jar</id>
+ <phase>package</phase>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.8.1</version>
+ <configuration>
+ <release>11</release>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>util</artifactId>
+ <version>${streamPiUtilVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>action-api</artifactId>
+ <version>${streamPiActionApiVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.github.gikkman</groupId>
+ <artifactId>Java-Twirk</artifactId>
+ <version>${javaTwirkVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>twitch-chat-connect</artifactId>
+ <version>${streamPiTwitchChatConnectVersion}</version>
+ </dependency>
+ </dependencies>
+
+</project>
--- /dev/null
+++ b/twitch/host-channel/src/main/java/hostchannel/HostChannelAction.java
@@ -0,0 +1,86 @@
+package hostchannel;
+
+import com.gikk.twirk.Twirk;
+import com.gikk.twirk.TwirkBuilder;
+import com.stream_pi.action_api.actionproperty.property.Property;
+import com.stream_pi.action_api.actionproperty.property.Type;
+import com.stream_pi.action_api.externalplugin.NormalAction;
+import com.stream_pi.util.exception.StreamPiException;
+import com.stream_pi.util.version.Version;
+import connect.chat.TwitchChatCredentials;
+
+public class HostChannelAction extends NormalAction
+{
+
+ private final String channelKey = "channel_hc";
+ private final String channelToHostKey = "channel_to_host_hc";
+
+ private Twirk twirk;
+
+ public HostChannelAction()
+ {
+ setName("Host Channel");
+ setCategory("Twitch Chat");
+ setVisibilityInServerSettingsPane(false);
+ setAuthor("j4ckofalltrades");
+ setVersion(new Version(1, 0, 0));
+ setHelpLink("https://github.com/stream-pi/essentialactions#twitch-chat-integration");
+ }
+
+ @Override
+ public void initProperties() throws Exception
+ {
+ Property channel = new Property(channelKey, Type.STRING);
+ channel.setDisplayName("Channel");
+ channel.setDefaultValueStr("channel");
+ channel.setCanBeBlank(false);
+
+ Property channelToHost = new Property(channelToHostKey, Type.STRING);
+ channelToHost.setDisplayName("Channel to host");
+ channelToHost.setDefaultValueStr("channel_to_host");
+ channelToHost.setCanBeBlank(false);
+
+ addClientProperties(channel, channelToHost);
+ }
+
+ @Override
+ public void initAction() throws Exception
+ {
+
+ }
+
+ @Override
+ public void onActionClicked() throws Exception
+ {
+ final TwitchChatCredentials.ChatCredentials credentials = TwitchChatCredentials.getCredentials();
+ credentials.ensureCredentialsInitialized();
+
+ final String channel = getClientProperties().getSingleProperty(channelKey).getStringValue();
+ final String channelToHost = getClientProperties().getSingleProperty(channelToHostKey).getStringValue();
+
+ try
+ {
+ twirk = new TwirkBuilder(channel, credentials.getNickname(), credentials.getOauthToken()).build();
+ twirk.connect();
+ twirk.channelMessage(String.format("/host %s", channelToHost));
+ } catch (Exception ex)
+ {
+ throw new StreamPiException(
+ "Failed to host channel",
+ String.format("Could not host channel '%s', please try again.", channelToHost));
+ }
+ }
+
+ @Override
+ public void onShutDown() throws Exception
+ {
+ if (twirk != null) {
+ try
+ {
+ twirk.disconnect();
+ } catch (Exception ex) {
+ throw new StreamPiException("Twitch connection error", "Please try again.");
+ }
+ }
+ }
+}
--- /dev/null
+++ b/twitch/host-channel/src/main/java/module-info.java
@@ -0,0 +1,9 @@
+module com.stream_pi.twitch.hostchannelaction {
+ requires com.stream_pi.util;
+ requires com.stream_pi.action_api;
+
+ requires com.stream_pi.twitchchatconnectaction;
+ requires Java.Twirk;
+
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with hostchannel.HostChannelAction;
+}
--- /dev/null
+++ b/twitch/raid-channel/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>com.stream-pi</groupId>
+ <artifactId>twitch-raid-channel</artifactId>
+ <version>1.0.0</version>
+
+ <properties>
+ <maven.compiler.source>11</maven.compiler.source>
+ <maven.compiler.target>11</maven.compiler.target>
+ <streamPiActionApiVersion>1.0.0-SNAPSHOT</streamPiActionApiVersion>
+ <streamPiUtilVersion>1.0.0-SNAPSHOT</streamPiUtilVersion>
+ <streamPiTwitchChatConnectVersion>1.0.0</streamPiTwitchChatConnectVersion>
+ <javaTwirkVersion>0.6.3</javaTwirkVersion>
+ </properties>
+
+ <repositories>
+ <repository>
+ <id>jitpack.io</id>
+ <url>https://jitpack.io</url>
+ </repository>
+ </repositories>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>3.1.0</version>
+ <executions>
+ <execution>
+ <id>test-jar</id>
+ <phase>package</phase>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.8.1</version>
+ <configuration>
+ <release>11</release>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>util</artifactId>
+ <version>${streamPiUtilVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>action-api</artifactId>
+ <version>${streamPiActionApiVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.github.gikkman</groupId>
+ <artifactId>Java-Twirk</artifactId>
+ <version>${javaTwirkVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>twitch-chat-connect</artifactId>
+ <version>${streamPiTwitchChatConnectVersion}</version>
+ </dependency>
+ </dependencies>
+
+</project>
--- /dev/null
+++ b/twitch/raid-channel/src/main/java/module-info.java
@@ -0,0 +1,9 @@
+module com.stream_pi.twitch.raidchannelaction {
+ requires com.stream_pi.util;
+ requires com.stream_pi.action_api;
+
+ requires com.stream_pi.twitchchatconnectaction;
+ requires Java.Twirk;
+
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with raidchannel.RaidChannelAction;
+}
--- /dev/null
+++ b/twitch/raid-channel/src/main/java/raidchannel/RaidChannelAction.java
@@ -0,0 +1,86 @@
+package raidchannel;
+
+import com.gikk.twirk.Twirk;
+import com.gikk.twirk.TwirkBuilder;
+import com.stream_pi.action_api.actionproperty.property.Property;
+import com.stream_pi.action_api.actionproperty.property.Type;
+import com.stream_pi.action_api.externalplugin.NormalAction;
+import com.stream_pi.util.exception.StreamPiException;
+import com.stream_pi.util.version.Version;
+import connect.chat.TwitchChatCredentials;
+
+public class RaidChannelAction extends NormalAction
+{
+
+ private final String channelKey = "channel_rc";
+ private final String channelToRaidKey = "channel_to_raid_rc";
+
+ private Twirk twirk;
+
+ public RaidChannelAction()
+ {
+ setName("Raid Channel");
+ setCategory("Twitch Chat");
+ setVisibilityInServerSettingsPane(false);
+ setAuthor("j4ckofalltrades");
+ setVersion(new Version(1, 0, 0));
+ setHelpLink("https://github.com/stream-pi/essentialactions#twitch-chat-integration");
+ }
+
+ @Override
+ public void initProperties() throws Exception
+ {
+ Property channel = new Property(channelKey, Type.STRING);
+ channel.setDisplayName("Channel");
+ channel.setDefaultValueStr("channel");
+ channel.setCanBeBlank(false);
+
+ Property channelToRaid = new Property(channelToRaidKey, Type.STRING);
+ channelToRaid.setDisplayName("Channel to raid");
+ channelToRaid.setDefaultValueStr("channel_to_raid");
+ channelToRaid.setCanBeBlank(false);
+
+ addClientProperties(channel, channelToRaid);
+ }
+
+ @Override
+ public void initAction() throws Exception
+ {
+
+ }
+
+ @Override
+ public void onActionClicked() throws Exception
+ {
+ final TwitchChatCredentials.ChatCredentials credentials = TwitchChatCredentials.getCredentials();
+ credentials.ensureCredentialsInitialized();
+
+ final String channel = getClientProperties().getSingleProperty(channelKey).getStringValue();
+ final String channelToRaid = getClientProperties().getSingleProperty(channelToRaidKey).getStringValue();
+
+ try
+ {
+ twirk = new TwirkBuilder(channel, credentials.getNickname(), credentials.getOauthToken()).build();
+ twirk.connect();
+ twirk.channelMessage(String.format("/raid %s", channelToRaid));
+ } catch (Exception ex)
+ {
+ throw new StreamPiException(
+ "Failed to raid channel",
+ String.format("Could not raid channel '%s', please try again.", channelToRaid));
+ }
+ }
+
+ @Override
+ public void onShutDown() throws Exception
+ {
+ if (twirk != null) {
+ try
+ {
+ twirk.disconnect();
+ } catch (Exception ex) {
+ throw new StreamPiException("Twitch connection error", "Please try again.");
+ }
+ }
+ }
+}
--- 'a/twitch/send-channel-msg/src/main/java/module-info.java'
+++ b/twitch/send-channel-msg/src/main/java/module-info.java
@@ -5,5 +5,5 @@ module com.stream_pi.twitch.sendchannelm
requires com.stream_pi.twitchchatconnectaction;
requires Java.Twirk;
- provides com.stream_pi.action_api.normalaction.NormalAction with sendchannelmsg.SendChannelMessageAction;
-}
\ No newline at end of file
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with sendchannelmsg.SendChannelMessageAction;
+}
--- 'a/twitch/send-channel-msg/src/main/java/sendchannelmsg/SendChannelMessageAction.java'
+++ b/twitch/send-channel-msg/src/main/java/sendchannelmsg/SendChannelMessageAction.java
@@ -4,7 +4,7 @@ import com.gikk.twirk.Twirk;
import com.gikk.twirk.TwirkBuilder;
import com.stream_pi.action_api.actionproperty.property.Property;
import com.stream_pi.action_api.actionproperty.property.Type;
-import com.stream_pi.action_api.normalaction.NormalAction;
+import com.stream_pi.action_api.externalplugin.NormalAction;
import com.stream_pi.util.exception.StreamPiException;
import com.stream_pi.util.version.Version;
import connect.chat.TwitchChatCredentials;
@@ -12,8 +12,8 @@ import connect.chat.TwitchChatCredential
public class SendChannelMessageAction extends NormalAction
{
- private static final String TWITCH_CHANNEL_NAME_KEY = "twitch_channel_name";
- private static final String TWITCH_CHANNEL_MSG_KEY = "twitch_channel_msg";
+ private final String channelNameKey = "channel_name_scm";
+ private final String channelMsgKey = "channel_msg_scm";
private Twirk twirk;
@@ -24,18 +24,18 @@ public class SendChannelMessageAction ex
setVisibilityInServerSettingsPane(false);
setAuthor("j4ckofalltrades");
setVersion(new Version(1, 0, 0));
- setHelpLink("https://github.com/stream-pi/essentialactions");
+ setHelpLink("https://github.com/stream-pi/essentialactions#twitch-chat-integration");
}
@Override
public void initProperties() throws Exception
{
- Property channelName = new Property(TWITCH_CHANNEL_NAME_KEY, Type.STRING);
+ Property channelName = new Property(channelNameKey, Type.STRING);
channelName.setDisplayName("Channel Name");
channelName.setDefaultValueStr("channel_name");
channelName.setCanBeBlank(false);
- Property channelMessage = new Property(TWITCH_CHANNEL_MSG_KEY, Type.STRING);
+ Property channelMessage = new Property(channelMsgKey, Type.STRING);
channelMessage.setDisplayName("Message");
channelMessage.setDefaultValueStr("channel_msg");
channelMessage.setCanBeBlank(false);
@@ -52,22 +52,15 @@ public class SendChannelMessageAction ex
@Override
public void onActionClicked() throws Exception
{
- TwitchChatCredentials.ChatCredentials credentials = TwitchChatCredentials.getCredentials();
- if (!isChatCredentialsInitialized(credentials))
- {
- throw new StreamPiException("Twitch Chat uninitialized.","Please check that the Twitch Chat plugin configuration is correct.");
- }
+ final TwitchChatCredentials.ChatCredentials credentials = TwitchChatCredentials.getCredentials();
+ credentials.ensureCredentialsInitialized();
- final String channel = getClientProperties().getSingleProperty(TWITCH_CHANNEL_NAME_KEY).getStringValue();
- final String message = getClientProperties().getSingleProperty(TWITCH_CHANNEL_MSG_KEY).getStringValue();
+ final String channel = getClientProperties().getSingleProperty(channelNameKey).getStringValue();
+ final String message = getClientProperties().getSingleProperty(channelMsgKey).getStringValue();
try
{
- twirk = new TwirkBuilder(
- getClientProperties().getSingleProperty(TWITCH_CHANNEL_NAME_KEY).getStringValue(),
- credentials.getNickname(),
- credentials.getOauthToken())
- .build();
+ twirk = new TwirkBuilder(channel, credentials.getNickname(), credentials.getOauthToken()).build();
twirk.connect();
twirk.channelMessage(message);
} catch (Exception ex)
@@ -80,31 +73,16 @@ public class SendChannelMessageAction ex
}
}
- private boolean isChatCredentialsInitialized(TwitchChatCredentials.ChatCredentials credentials)
- {
- if (credentials == null)
- {
- return false;
- }
-
- final String twitchNickname = credentials.getNickname();
- boolean isNicknameInitialized = twitchNickname != null &&
- !twitchNickname.isEmpty() &&
- !twitchNickname.equalsIgnoreCase("twitch_nickname");
-
- final String twitchChatOauthToken = credentials.getOauthToken();
- boolean isTokenInitialized = twitchChatOauthToken != null &&
- !twitchChatOauthToken.isEmpty() &&
- !twitchChatOauthToken.equalsIgnoreCase("twitch_oauth_token");
-
- return isNicknameInitialized && isTokenInitialized;
- }
-
@Override
public void onShutDown() throws Exception
{
- twirk.close();
+ if (twirk != null) {
+ try
+ {
+ twirk.disconnect();
+ } catch (Exception ex) {
+ throw new StreamPiException("Twitch connection error", "Please try again.");
+ }
+ }
}
}
-
-
--- /dev/null
+++ b/twitch/set-color/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>com.stream-pi</groupId>
+ <artifactId>twitch-set-color</artifactId>
+ <version>1.0.0</version>
+
+ <properties>
+ <maven.compiler.source>11</maven.compiler.source>
+ <maven.compiler.target>11</maven.compiler.target>
+ <streamPiActionApiVersion>1.0.0-SNAPSHOT</streamPiActionApiVersion>
+ <streamPiUtilVersion>1.0.0-SNAPSHOT</streamPiUtilVersion>
+ <streamPiTwitchChatConnectVersion>1.0.0</streamPiTwitchChatConnectVersion>
+ <javaTwirkVersion>0.6.3</javaTwirkVersion>
+ </properties>
+
+ <repositories>
+ <repository>
+ <id>jitpack.io</id>
+ <url>https://jitpack.io</url>
+ </repository>
+ </repositories>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>3.1.0</version>
+ <executions>
+ <execution>
+ <id>test-jar</id>
+ <phase>package</phase>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.8.1</version>
+ <configuration>
+ <release>11</release>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>util</artifactId>
+ <version>${streamPiUtilVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>action-api</artifactId>
+ <version>${streamPiActionApiVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.github.gikkman</groupId>
+ <artifactId>Java-Twirk</artifactId>
+ <version>${javaTwirkVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>twitch-chat-connect</artifactId>
+ <version>${streamPiTwitchChatConnectVersion}</version>
+ </dependency>
+ </dependencies>
+
+</project>
--- /dev/null
+++ b/twitch/set-color/src/main/java/module-info.java
@@ -0,0 +1,9 @@
+module com.stream_pi.twitch.setcoloraction {
+ requires com.stream_pi.util;
+ requires com.stream_pi.action_api;
+
+ requires com.stream_pi.twitchchatconnectaction;
+ requires Java.Twirk;
+
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with setcolor.SetColorAction;
+}
--- /dev/null
+++ b/twitch/set-color/src/main/java/setcolor/SetColorAction.java
@@ -0,0 +1,83 @@
+package setcolor;
+
+import com.gikk.twirk.Twirk;
+import com.gikk.twirk.TwirkBuilder;
+import com.stream_pi.action_api.actionproperty.property.Property;
+import com.stream_pi.action_api.actionproperty.property.Type;
+import com.stream_pi.action_api.externalplugin.NormalAction;
+import com.stream_pi.util.exception.StreamPiException;
+import com.stream_pi.util.version.Version;
+import connect.chat.TwitchChatCredentials;
+
+public class SetColorAction extends NormalAction
+{
+
+ private final String channelNameKey = "channel_name_sc";
+ private final String usernameColorKey = "username_color_sc";
+
+ private Twirk twirk;
+
+ @Override
+ public void initProperties() throws Exception
+ {
+ setName("Set Color");
+ setCategory("Twitch Chat");
+ setVisibilityInServerSettingsPane(false);
+ setAuthor("j4ckofalltrades");
+ setVersion(new Version(1, 0, 0));
+ setHelpLink("https://github.com/stream-pi/essentialactions#twitch-chat-integration");
+ }
+
+ @Override
+ public void initAction() throws Exception
+ {
+ Property channelName = new Property(channelNameKey, Type.STRING);
+ channelName.setDisplayName("Channel Name");
+ channelName.setDefaultValueStr("channel_name");
+ channelName.setCanBeBlank(false);
+
+ Property usernameColor = new Property(usernameColorKey, Type.STRING);
+ usernameColor.setDisplayName("Color");
+ usernameColor.setDefaultValueStr("color");
+ usernameColor.setCanBeBlank(false);
+
+ addClientProperties(channelName, usernameColor);
+ }
+
+ @Override
+ public void onActionClicked() throws Exception
+ {
+ final TwitchChatCredentials.ChatCredentials credentials = TwitchChatCredentials.getCredentials();
+ credentials.ensureCredentialsInitialized();
+
+ final String channel = getClientProperties().getSingleProperty(channelNameKey).getStringValue();
+ final String color = getClientProperties().getSingleProperty(usernameColorKey).getStringValue();
+
+ try
+ {
+ twirk = new TwirkBuilder(channel, credentials.getNickname(), credentials.getOauthToken()).build();
+ twirk.connect();
+ twirk.channelMessage(String.format("/color %s", color));
+ } catch (Exception ex)
+ {
+ throw new StreamPiException(
+ "Failed to change username color",
+ String.format("Could not change username color to '%s' for '%s' channel, please try again.",
+ color, channel)
+ );
+ }
+ }
+
+ @Override
+ public void onShutDown() throws Exception
+ {
+ if (twirk != null) {
+ try
+ {
+ twirk.disconnect();
+ } catch (Exception ex) {
+ throw new StreamPiException("Twitch connection error", "Please try again.");
+ }
+ }
+ }
+}
--- /dev/null
+++ b/twitch/start-commercial/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>com.stream-pi</groupId>
+ <artifactId>twitch-start-commercial</artifactId>
+ <version>1.0.0</version>
+
+ <properties>
+ <maven.compiler.source>11</maven.compiler.source>
+ <maven.compiler.target>11</maven.compiler.target>
+ <streamPiActionApiVersion>1.0.0-SNAPSHOT</streamPiActionApiVersion>
+ <streamPiUtilVersion>1.0.0-SNAPSHOT</streamPiUtilVersion>
+ <streamPiTwitchChatConnectVersion>1.0.0</streamPiTwitchChatConnectVersion>
+ <javaTwirkVersion>0.6.3</javaTwirkVersion>
+ </properties>
+
+ <repositories>
+ <repository>
+ <id>jitpack.io</id>
+ <url>https://jitpack.io</url>
+ </repository>
+ </repositories>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>3.1.0</version>
+ <executions>
+ <execution>
+ <id>test-jar</id>
+ <phase>package</phase>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.8.1</version>
+ <configuration>
+ <release>11</release>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>util</artifactId>
+ <version>${streamPiUtilVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>action-api</artifactId>
+ <version>${streamPiActionApiVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.github.gikkman</groupId>
+ <artifactId>Java-Twirk</artifactId>
+ <version>${javaTwirkVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>twitch-chat-connect</artifactId>
+ <version>${streamPiTwitchChatConnectVersion}</version>
+ </dependency>
+ </dependencies>
+
+</project>
--- /dev/null
+++ b/twitch/start-commercial/src/main/java/module-info.java
@@ -0,0 +1,9 @@
+module com.stream_pi.twitch.startcommercialaction {
+ requires com.stream_pi.util;
+ requires com.stream_pi.action_api;
+
+ requires com.stream_pi.twitchchatconnectaction;
+ requires Java.Twirk;
+
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with startcommercial.StartCommercialAction;
+}
--- /dev/null
+++ b/twitch/start-commercial/src/main/java/startcommercial/StartCommercialAction.java
@@ -0,0 +1,92 @@
+package startcommercial;
+
+import com.gikk.twirk.Twirk;
+import com.gikk.twirk.TwirkBuilder;
+import com.stream_pi.action_api.actionproperty.property.Property;
+import com.stream_pi.action_api.actionproperty.property.Type;
+import com.stream_pi.action_api.externalplugin.NormalAction;
+import com.stream_pi.util.exception.StreamPiException;
+import com.stream_pi.util.version.Version;
+import connect.chat.TwitchChatCredentials;
+
+import java.util.List;
+
+public class StartCommercialAction extends NormalAction
+{
+
+ private final String channelNameKey = "channel_name_sca";
+ private final String durationKey = "duration_sca";
+
+ private Twirk twirk;
+
+ public StartCommercialAction()
+ {
+ setName("Start commercial");
+ setCategory("Twitch Chat");
+ setVisibilityInServerSettingsPane(false);
+ setAuthor("j4ckofalltrades");
+ setVersion(new Version(1, 0, 0));
+ setHelpLink("https://github.com/stream-pi/essentialactions#twitch-chat-integration");
+ }
+
+ @Override
+ public void initProperties() throws Exception
+ {
+ Property channelName = new Property(channelNameKey, Type.STRING);
+ channelName.setDisplayName("Channel Name");
+ channelName.setDefaultValueStr("channel_name");
+ channelName.setCanBeBlank(false);
+
+ Property duration = new Property(durationKey, Type.LIST);
+ duration.setDisplayName("Duration");
+ duration.setListValue(List.of(
+ String.valueOf(30),
+ String.valueOf(60),
+ String.valueOf(90),
+ String.valueOf(120),
+ String.valueOf(150),
+ String.valueOf(180)));
+
+ addClientProperties(channelName, duration);
+ }
+
+ @Override
+ public void initAction() throws Exception
+ {
+
+ }
+
+ @Override
+ public void onActionClicked() throws Exception
+ {
+ final TwitchChatCredentials.ChatCredentials credentials = TwitchChatCredentials.getCredentials();
+ credentials.ensureCredentialsInitialized();
+
+ final String channel = getClientProperties().getSingleProperty(channelNameKey).getStringValue();
+ final String duration = getClientProperties().getSingleProperty(durationKey).getStringValue();
+
+ try
+ {
+ twirk = new TwirkBuilder(channel, credentials.getNickname(), credentials.getOauthToken()).build();
+ twirk.connect();
+ twirk.channelMessage(String.format("/commercial %s", duration));
+ } catch (Exception ex)
+ {
+ throw new StreamPiException(
+ "Failed to start commercial", "Could not start commercial, please try again.");
+ }
+ }
+
+ @Override
+ public void onShutDown() throws Exception
+ {
+ if (twirk != null) {
+ try
+ {
+ twirk.disconnect();
+ } catch (Exception ex) {
+ throw new StreamPiException("Twitch connection error", "Please try again.");
+ }
+ }
+ }
+}
--- 'a/twitch/twitch-chat-connect/pom.xml'
+++ b/twitch/twitch-chat-connect/pom.xml
@@ -11,8 +11,8 @@
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
- <streamPiActionApiVersion>1.0.0</streamPiActionApiVersion>
- <streamPiUtilVersion>1.0.0</streamPiUtilVersion>
+ <streamPiActionApiVersion>1.0.0-SNAPSHOT</streamPiActionApiVersion>
+ <streamPiUtilVersion>1.0.0-SNAPSHOT</streamPiUtilVersion>
<javaTwirkVersion>0.6.3</javaTwirkVersion>
</properties>
@@ -71,4 +71,4 @@
</dependency>
</dependencies>
-</project>
\ No newline at end of file
+</project>
--- 'a/twitch/twitch-chat-connect/src/main/java/connect/TwitchChatConnectAction.java'
+++ b/twitch/twitch-chat-connect/src/main/java/connect/TwitchChatConnectAction.java
@@ -1,8 +1,9 @@
package connect;
+import com.stream_pi.action_api.actionproperty.property.ControlType;
import com.stream_pi.action_api.actionproperty.property.Property;
import com.stream_pi.action_api.actionproperty.property.Type;
-import com.stream_pi.action_api.normalaction.NormalAction;
+import com.stream_pi.action_api.externalplugin.NormalAction;
import com.stream_pi.util.alert.StreamPiAlert;
import com.stream_pi.util.alert.StreamPiAlertType;
import com.stream_pi.util.exception.MinorException;
@@ -13,10 +14,10 @@ import javafx.scene.control.Button;
public class TwitchChatConnectAction extends NormalAction
{
- private static final String TWITCH_ACCESS_TOKEN_KEY = "twitch_access_token";
- private static final String TWITCH_NICKNAME_KEY = "twitch_nickname";
+ private static final String ACCESS_TOKEN_KEY = "twitch_chat_access_token";
+ private static final String NICKNAME_KEY = "twitch_chat_nickname";
- private final Button saveCredentialsBtn;
+ private final Button clearCredentialsBtn;
public TwitchChatConnectAction()
{
@@ -25,63 +26,67 @@ public class TwitchChatConnectAction ext
setVisibilityInPluginsPane(false);
setAuthor("j4ckofalltrades");
setVersion(new Version(1, 0, 0));
- setHelpLink("https://github.com/Stream-Pi/essentialactions");
+ setHelpLink("https://github.com/stream-pi/essentialactions#twitch-chat-integration");
- saveCredentialsBtn = new Button("Save Twitch chat credentials");
- setButtonBar(saveCredentialsBtn);
+ clearCredentialsBtn = new Button("Clear Twitch chat credentials");
+ onClearCredentials();
+ setServerSettingsButtonBar(clearCredentialsBtn);
}
@Override
- public void initProperties() throws Exception
+ public void initProperties() throws MinorException
{
- Property twitchNicknameProp = new Property(TWITCH_NICKNAME_KEY, Type.STRING);
+ Property twitchNicknameProp = new Property(NICKNAME_KEY, Type.STRING);
twitchNicknameProp.setDisplayName("Twitch Username");
- twitchNicknameProp.setDefaultValueStr("twitch_nickname");
- twitchNicknameProp.setCanBeBlank(false);
- Property twitchAccessTokenProp = new Property(TWITCH_ACCESS_TOKEN_KEY, Type.STRING);
+ Property twitchAccessTokenProp = new Property(ACCESS_TOKEN_KEY, Type.STRING);
+ twitchAccessTokenProp.setControlType(ControlType.TEXT_FIELD_MASKED);
twitchAccessTokenProp.setDisplayName("Access Token");
- twitchAccessTokenProp.setDefaultValueStr("twitch_oauth_token");
- twitchAccessTokenProp.setCanBeBlank(false);
addServerProperties(twitchNicknameProp, twitchAccessTokenProp);
}
@Override
- public void initAction() throws Exception
+ public void initAction() throws MinorException
{
- saveCredentialsBtn.setOnAction(action ->
+ clearCredentialsBtn.setDisable(isEmptyCredentials());
+
+ TwitchChatCredentials.setCredentials(
+ new TwitchChatCredentials.ChatCredentials()
+ .setOauthToken(getServerProperties().getSingleProperty(ACCESS_TOKEN_KEY).getStringValue())
+ .setNickname(getServerProperties().getSingleProperty(NICKNAME_KEY).getStringValue()));
+ }
+
+ private boolean isEmptyCredentials() throws MinorException {
+ final String twitchNickname = getServerProperties().getSingleProperty(NICKNAME_KEY).getStringValue();
+ final String twitchChatOauthToken = getServerProperties().getSingleProperty(ACCESS_TOKEN_KEY).getStringValue();
+ return (twitchNickname == null || twitchNickname.isEmpty()) &&
+ (twitchChatOauthToken == null || twitchChatOauthToken.isEmpty());
+ }
+
+ private void onClearCredentials()
+ {
+ clearCredentialsBtn.setOnAction(action ->
{
try
{
- persistCredentials();
-
+ getServerProperties().getSingleProperty(ACCESS_TOKEN_KEY).setStringValue("");
+ getServerProperties().getSingleProperty(NICKNAME_KEY).setStringValue("");
+ saveServerProperties();
new StreamPiAlert(
- "Twitch chat credentials saved",
- "Chat credentials been saved, you can now start using Twitch chat integration actions.",
+ "Twitch chat credentials cleared",
+ "To revoke token access, disconnect \"Twitch Chat OAuth Token Generator\" from your Twitch settings (https://www.twitch.tv/settings/connections).",
StreamPiAlertType.INFORMATION)
.show();
} catch (Exception e)
{
new StreamPiAlert(
"Failed to save chat credentials",
- "An error has occurred while saving chat credentials, please try again.",
+ "An error has occurred while clearing chat credentials, please try again.",
StreamPiAlertType.WARNING)
.show();
}
});
-
- persistCredentials();
- }
-
- private void persistCredentials() throws MinorException
- {
- final String token = getServerProperties().getSingleProperty(TWITCH_ACCESS_TOKEN_KEY).getStringValue();
- final String nickname = getServerProperties().getSingleProperty(TWITCH_NICKNAME_KEY).getStringValue();
- TwitchChatCredentials.setCredentials(
- new TwitchChatCredentials.ChatCredentials()
- .setOauthToken(token)
- .setNickname(nickname));
}
@Override
--- 'a/twitch/twitch-chat-connect/src/main/java/connect/chat/TwitchChatCredentials.java'
+++ b/twitch/twitch-chat-connect/src/main/java/connect/chat/TwitchChatCredentials.java
@@ -1,5 +1,7 @@
package connect.chat;
+import com.stream_pi.util.exception.StreamPiException;
+
public final class TwitchChatCredentials
{
@@ -20,6 +22,20 @@ public final class TwitchChatCredentials
String nickname;
String oauthToken;
+ public void ensureCredentialsInitialized() throws Exception {
+ final String twitchNickname = nickname;
+ boolean isNicknameInitialized = twitchNickname != null && !twitchNickname.isEmpty();
+
+ final String twitchChatOauthToken = oauthToken;
+ boolean isTokenInitialized = twitchChatOauthToken != null && !twitchChatOauthToken.isEmpty();
+
+ if (!isNicknameInitialized && !isTokenInitialized) {
+ throw new StreamPiException(
+ "Twitch Chat config uninitialized.",
+ "Please check that the Twitch Chat plugin configuration is correct.");
+ }
+ }
+
public String getNickname()
{
return nickname;
--- 'a/twitch/twitch-chat-connect/src/main/java/module-info.java'
+++ b/twitch/twitch-chat-connect/src/main/java/module-info.java
@@ -7,5 +7,5 @@ module com.stream_pi.twitchchatconnectac
exports connect.chat;
- provides com.stream_pi.action_api.normalaction.NormalAction with connect.TwitchChatConnectAction;
-}
\ No newline at end of file
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with connect.TwitchChatConnectAction;
+}
--- /dev/null
+++ b/twitch/unhost/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>com.stream-pi</groupId>
+ <artifactId>twitch-unhost</artifactId>
+ <version>1.0.0</version>
+
+ <properties>
+ <maven.compiler.source>11</maven.compiler.source>
+ <maven.compiler.target>11</maven.compiler.target>
+ <streamPiActionApiVersion>1.0.0-SNAPSHOT</streamPiActionApiVersion>
+ <streamPiUtilVersion>1.0.0-SNAPSHOT</streamPiUtilVersion>
+ <streamPiTwitchChatConnectVersion>1.0.0</streamPiTwitchChatConnectVersion>
+ <javaTwirkVersion>0.6.3</javaTwirkVersion>
+ </properties>
+
+ <repositories>
+ <repository>
+ <id>jitpack.io</id>
+ <url>https://jitpack.io</url>
+ </repository>
+ </repositories>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>3.1.0</version>
+ <executions>
+ <execution>
+ <id>test-jar</id>
+ <phase>package</phase>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.8.1</version>
+ <configuration>
+ <release>11</release>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>util</artifactId>
+ <version>${streamPiUtilVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>action-api</artifactId>
+ <version>${streamPiActionApiVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.github.gikkman</groupId>
+ <artifactId>Java-Twirk</artifactId>
+ <version>${javaTwirkVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>twitch-chat-connect</artifactId>
+ <version>${streamPiTwitchChatConnectVersion}</version>
+ </dependency>
+ </dependencies>
+
+</project>
--- /dev/null
+++ b/twitch/unhost/src/main/java/module-info.java
@@ -0,0 +1,9 @@
+module com.stream_pi.twitch.unhostaction {
+ requires com.stream_pi.util;
+ requires com.stream_pi.action_api;
+
+ requires com.stream_pi.twitchchatconnectaction;
+ requires Java.Twirk;
+
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with unhost.UnhostAction;
+}
--- /dev/null
+++ b/twitch/unhost/src/main/java/unhost/UnhostAction.java
@@ -0,0 +1,79 @@
+package unhost;
+
+import com.gikk.twirk.Twirk;
+import com.gikk.twirk.TwirkBuilder;
+import com.stream_pi.action_api.actionproperty.property.Property;
+import com.stream_pi.action_api.actionproperty.property.Type;
+import com.stream_pi.action_api.externalplugin.NormalAction;
+import com.stream_pi.util.exception.StreamPiException;
+import com.stream_pi.util.version.Version;
+import connect.chat.TwitchChatCredentials;
+
+public class UnhostAction extends NormalAction
+{
+
+ private final String channelNameKey = "channel_name_uh";
+
+ private Twirk twirk;
+
+ public UnhostAction()
+ {
+ setName("Unhost");
+ setCategory("Twitch Chat");
+ setVisibilityInServerSettingsPane(false);
+ setAuthor("j4ckofalltrades");
+ setVersion(new Version(1, 0, 0));
+ setHelpLink("https://github.com/stream-pi/essentialactions#twitch-chat-integration");
+ }
+
+ @Override
+ public void initProperties() throws Exception
+ {
+ Property channel = new Property(channelNameKey, Type.STRING);
+ channel.setDisplayName("Channel");
+ channel.setDefaultValueStr("channel_name");
+ channel.setCanBeBlank(false);
+
+ addClientProperties(channel);
+ }
+
+ @Override
+ public void initAction() throws Exception
+ {
+
+ }
+
+ @Override
+ public void onActionClicked() throws Exception
+ {
+ final TwitchChatCredentials.ChatCredentials credentials = TwitchChatCredentials.getCredentials();
+ credentials.ensureCredentialsInitialized();
+
+ final String channel = getClientProperties().getSingleProperty(channelNameKey).getStringValue();
+
+ try
+ {
+ twirk = new TwirkBuilder(channel, credentials.getNickname(), credentials.getOauthToken()).build();
+ twirk.connect();
+ twirk.channelMessage("/unhost");
+ } catch (Exception ex)
+ {
+ throw new StreamPiException(
+ "Failed to cancel channel hosting",
+ "Could not cancel channel hosting, please try again.");
+ }
+ }
+
+ @Override
+ public void onShutDown() throws Exception
+ {
+ if (twirk != null) {
+ try
+ {
+ twirk.disconnect();
+ } catch (Exception ex) {
+ throw new StreamPiException("Twitch connection error", "Please try again.");
+ }
+ }
+ }
+}
--- /dev/null
+++ b/twitch/unraid/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>com.stream-pi</groupId>
+ <artifactId>twitch-unraid</artifactId>
+ <version>1.0.0</version>
+
+ <properties>
+ <maven.compiler.source>11</maven.compiler.source>
+ <maven.compiler.target>11</maven.compiler.target>
+ <streamPiActionApiVersion>1.0.0-SNAPSHOT</streamPiActionApiVersion>
+ <streamPiUtilVersion>1.0.0-SNAPSHOT</streamPiUtilVersion>
+ <streamPiTwitchChatConnectVersion>1.0.0</streamPiTwitchChatConnectVersion>
+ <javaTwirkVersion>0.6.3</javaTwirkVersion>
+ </properties>
+
+ <repositories>
+ <repository>
+ <id>jitpack.io</id>
+ <url>https://jitpack.io</url>
+ </repository>
+ </repositories>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>3.1.0</version>
+ <executions>
+ <execution>
+ <id>test-jar</id>
+ <phase>package</phase>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.8.1</version>
+ <configuration>
+ <release>11</release>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>util</artifactId>
+ <version>${streamPiUtilVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>action-api</artifactId>
+ <version>${streamPiActionApiVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.github.gikkman</groupId>
+ <artifactId>Java-Twirk</artifactId>
+ <version>${javaTwirkVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>twitch-chat-connect</artifactId>
+ <version>${streamPiTwitchChatConnectVersion}</version>
+ </dependency>
+ </dependencies>
+
+</project>
--- /dev/null
+++ b/twitch/unraid/src/main/java/module-info.java
@@ -0,0 +1,9 @@
+module com.stream_pi.twitch.unraidaction {
+ requires com.stream_pi.util;
+ requires com.stream_pi.action_api;
+
+ requires com.stream_pi.twitchchatconnectaction;
+ requires Java.Twirk;
+
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with unraid.UnraidAction;
+}
--- /dev/null
+++ b/twitch/unraid/src/main/java/unraid/UnraidAction.java
@@ -0,0 +1,79 @@
+package unraid;
+
+import com.gikk.twirk.Twirk;
+import com.gikk.twirk.TwirkBuilder;
+import com.stream_pi.action_api.actionproperty.property.Property;
+import com.stream_pi.action_api.actionproperty.property.Type;
+import com.stream_pi.action_api.externalplugin.NormalAction;
+import com.stream_pi.util.exception.StreamPiException;
+import com.stream_pi.util.version.Version;
+import connect.chat.TwitchChatCredentials;
+
+public class UnraidAction extends NormalAction
+{
+
+ private final String channelNameKey = "channel_name_ur";
+
+ private Twirk twirk;
+
+ public UnraidAction()
+ {
+ setName("Unraid");
+ setCategory("Twitch Chat");
+ setVisibilityInServerSettingsPane(false);
+ setAuthor("j4ckofalltrades");
+ setVersion(new Version(1, 0, 0));
+ setHelpLink("https://github.com/stream-pi/essentialactions#twitch-chat-integration");
+ }
+
+ @Override
+ public void initProperties() throws Exception
+ {
+ Property channel = new Property(channelNameKey, Type.STRING);
+ channel.setDisplayName("Channel");
+ channel.setDefaultValueStr("channel_name");
+ channel.setCanBeBlank(false);
+
+ addClientProperties(channel);
+ }
+
+ @Override
+ public void initAction() throws Exception
+ {
+
+ }
+
+ @Override
+ public void onActionClicked() throws Exception
+ {
+ final TwitchChatCredentials.ChatCredentials credentials = TwitchChatCredentials.getCredentials();
+ credentials.ensureCredentialsInitialized();
+
+ final String channel = getClientProperties().getSingleProperty(channelNameKey).getStringValue();
+
+ try
+ {
+ twirk = new TwirkBuilder(channel, credentials.getNickname(), credentials.getOauthToken()).build();
+ twirk.connect();
+ twirk.channelMessage("/unraid");
+ } catch (Exception ex)
+ {
+ throw new StreamPiException(
+ "Failed to cancel channel raid",
+ "Could not cancel channel raid, please try again.");
+ }
+ }
+
+ @Override
+ public void onShutDown() throws Exception
+ {
+ if (twirk != null) {
+ try
+ {
+ twirk.disconnect();
+ } catch (Exception ex) {
+ throw new StreamPiException("Twitch connection error", "Please try again.");
+ }
+ }
+ }
+}
--- /dev/null
+++ b/twitch/whisper/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>com.stream-pi</groupId>
+ <artifactId>twitch-whisper</artifactId>
+ <version>1.0.0</version>
+
+ <properties>
+ <maven.compiler.source>11</maven.compiler.source>
+ <maven.compiler.target>11</maven.compiler.target>
+ <streamPiActionApiVersion>1.0.0-SNAPSHOT</streamPiActionApiVersion>
+ <streamPiUtilVersion>1.0.0-SNAPSHOT</streamPiUtilVersion>
+ <streamPiTwitchChatConnectVersion>1.0.0</streamPiTwitchChatConnectVersion>
+ <javaTwirkVersion>0.6.3</javaTwirkVersion>
+ </properties>
+
+ <repositories>
+ <repository>
+ <id>jitpack.io</id>
+ <url>https://jitpack.io</url>
+ </repository>
+ </repositories>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>3.1.0</version>
+ <executions>
+ <execution>
+ <id>test-jar</id>
+ <phase>package</phase>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.8.1</version>
+ <configuration>
+ <release>11</release>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>util</artifactId>
+ <version>${streamPiUtilVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>action-api</artifactId>
+ <version>${streamPiActionApiVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.github.gikkman</groupId>
+ <artifactId>Java-Twirk</artifactId>
+ <version>${javaTwirkVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.stream-pi</groupId>
+ <artifactId>twitch-chat-connect</artifactId>
+ <version>${streamPiTwitchChatConnectVersion}</version>
+ </dependency>
+ </dependencies>
+
+</project>
--- /dev/null
+++ b/twitch/whisper/src/main/java/module-info.java
@@ -0,0 +1,9 @@
+module com.stream_pi.twitch.whisperaction {
+ requires com.stream_pi.util;
+ requires com.stream_pi.action_api;
+
+ requires com.stream_pi.twitchchatconnectaction;
+ requires Java.Twirk;
+
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with whisper.WhisperAction;
+}
--- /dev/null
+++ b/twitch/whisper/src/main/java/whisper/WhisperAction.java
@@ -0,0 +1,88 @@
+package whisper;
+
+import com.gikk.twirk.Twirk;
+import com.gikk.twirk.TwirkBuilder;
+import com.stream_pi.action_api.actionproperty.property.Property;
+import com.stream_pi.action_api.actionproperty.property.Type;
+import com.stream_pi.action_api.externalplugin.NormalAction;
+import com.stream_pi.util.exception.StreamPiException;
+import com.stream_pi.util.version.Version;
+import connect.chat.TwitchChatCredentials;
+
+public class WhisperAction extends NormalAction
+{
+
+ private final String usernameKey = "username_wa";
+ private final String messageKey = "message_wa";
+
+ private Twirk twirk;
+
+ public WhisperAction()
+ {
+ setName("Whisper");
+ setCategory("Twitch Chat");
+ setVisibilityInServerSettingsPane(false);
+ setAuthor("j4ckofalltrades");
+ setVersion(new Version(1, 0, 0));
+ setHelpLink("https://github.com/stream-pi/essentialactions#twitch-chat-integration");
+ }
+
+ @Override
+ public void initProperties() throws Exception
+ {
+ Property usernameProp = new Property(usernameKey, Type.STRING);
+ usernameProp.setDisplayName("Twitch Username");
+ usernameProp.setDefaultValueStr("username");
+ usernameProp.setCanBeBlank(false);
+
+ Property messageProp = new Property(messageKey, Type.STRING);
+ messageProp.setDisplayName("Message");
+ messageProp.setDefaultValueStr("message");
+ messageProp.setCanBeBlank(false);
+
+ addClientProperties(usernameProp, messageProp);
+ }
+
+ @Override
+ public void initAction() throws Exception
+ {
+
+ }
+
+ @Override
+ public void onActionClicked() throws Exception
+ {
+ final TwitchChatCredentials.ChatCredentials credentials = TwitchChatCredentials.getCredentials();
+ credentials.ensureCredentialsInitialized();
+
+ final String username = getClientProperties().getSingleProperty(usernameKey).getStringValue();
+ final String message = getClientProperties().getSingleProperty(messageKey).getStringValue();
+
+ try
+ {
+ twirk = new TwirkBuilder(username, credentials.getNickname(), credentials.getOauthToken()).build();
+ twirk.connect();
+ twirk.whisper(username, message);
+ } catch (Exception ex)
+ {
+ throw new StreamPiException(
+ "Failed to send message to user",
+ String.format("Could not send message '%s' to user '%s', please try again.",
+ username, message)
+ );
+ }
+ }
+
+ @Override
+ public void onShutDown() throws Exception
+ {
+ if (twirk != null) {
+ try
+ {
+ twirk.disconnect();
+ } catch (Exception ex) {
+ throw new StreamPiException("Twitch connection error", "Please try again.");
+ }
+ }
+ }
+}
--- 'a/twitteraction/src/main/java/com/stream_pi/twitteraction/TwitterAction.java'
+++ b/twitteraction/src/main/java/com/stream_pi/twitteraction/TwitterAction.java
@@ -1,8 +1,9 @@
package com.stream_pi.twitteraction;
+import com.stream_pi.action_api.actionproperty.property.ControlType;
import com.stream_pi.action_api.actionproperty.property.Property;
import com.stream_pi.action_api.actionproperty.property.Type;
-import com.stream_pi.action_api.normalaction.NormalAction;
+import com.stream_pi.action_api.externalplugin.NormalAction;
import com.stream_pi.util.alert.StreamPiAlert;
import com.stream_pi.util.alert.StreamPiAlertListener;
import com.stream_pi.util.alert.StreamPiAlertType;
@@ -97,7 +98,7 @@ public class TwitterAction extends Norma
}
}).start());
- setButtonBar(loginAsNewUserButton, logoutButton);
+ setServerSettingsButtonBar(loginAsNewUserButton, logoutButton);
}
@Override
@@ -107,6 +108,7 @@ public class TwitterAction extends Norma
oAuthConsumerKey.setDisplayName("API Key");
Property oAuthConsumerKeySecret = new Property("consumer_key_secret", Type.STRING);
+ oAuthConsumerKeySecret.setControlType(ControlType.TEXT_FIELD_MASKED);
oAuthConsumerKeySecret.setDisplayName("API Key Secret");
Property oAuthAccessToken = new Property("access_token", Type.STRING);
--- 'a/twitteraction/src/main/java/module-info.java'
+++ b/twitteraction/src/main/java/module-info.java
@@ -10,5 +10,5 @@ module com.stream_pi.twitteraction {
requires java.desktop;
- provides com.stream_pi.action_api.normalaction.NormalAction with com.stream_pi.twitteraction.TwitterAction;
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with com.stream_pi.twitteraction.TwitterAction;
}
\ No newline at end of file
--- 'a/websiteaction/src/main/java/com/stream_pi/websiteaction/WebsiteAction.java'
+++ b/websiteaction/src/main/java/com/stream_pi/websiteaction/WebsiteAction.java
@@ -2,14 +2,15 @@ package com.stream_pi.websiteaction;
import com.stream_pi.action_api.actionproperty.property.Property;
import com.stream_pi.action_api.actionproperty.property.Type;
-import com.stream_pi.action_api.normalaction.NormalAction;
+import com.stream_pi.action_api.externalplugin.NormalAction;
import com.stream_pi.util.exception.MinorException;
import com.stream_pi.util.version.Version;
import java.awt.*;
import java.net.URI;
-public class WebsiteAction extends NormalAction {
+public class WebsiteAction extends NormalAction
+{
public WebsiteAction()
{
@@ -22,6 +23,18 @@ public class WebsiteAction extends Norma
}
@Override
+ public void onActionSavedFromServer() throws Exception
+ {
+ String website = getClientProperties().getSingleProperty("websiteURL").getStringValue();
+
+ if(website != null)
+ {
+ if(website.contains("google.com"))
+ setDisplayText("GOOGLE");
+ }
+ }
+
+ @Override
public void initProperties() throws Exception {
Property websiteUrl = new Property("websiteURL", Type.STRING);
websiteUrl.setDisplayName("Website URL");
--- 'a/websiteaction/src/main/java/module-info.java'
+++ b/websiteaction/src/main/java/module-info.java
@@ -7,6 +7,6 @@ module com.stream_pi.websiteaction {
requires java.desktop;
- provides com.stream_pi.action_api.normalaction.NormalAction with com.stream_pi.websiteaction.WebsiteAction;
+ provides com.stream_pi.action_api.externalplugin.ExternalPlugin with com.stream_pi.websiteaction.WebsiteAction;
}
\ No newline at end of file