From: EVNPC <39675973+EVNPC@users.noreply.github.com> Date: Sun, 18 Apr 2021 00:36:48 +0530 Subject: New Plugins And Discontinued Ones --- New Plugins And Discontinued Ones --- --- 'a/documentopen/src/main/java/com/stream_pi/documentopen/DocumentOpen.java' +++ b/documentopen/src/main/java/com/stream_pi/documentopen/DocumentOpen.java @@ -24,7 +24,7 @@ public class DocumentOpen extends Normal setHelpLink("https://github.com/stream-pi/essentialactions"); setVersion(new Version(1,0,0)); - setCategory("Essential Actions"); + setCategory("Essentials"); } --- 'a/playaudioclipaction/src/main/java/com/stream_pi/playaudioclipaction/PlayAudioClipAction.java' +++ b/playaudioclipaction/src/main/java/com/stream_pi/playaudioclipaction/PlayAudioClipAction.java @@ -2,7 +2,6 @@ package com.stream_pi.playaudioclipactio 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; @@ -15,9 +14,6 @@ import com.stream_pi.util.version.Versio import javafx.application.Platform; import javafx.scene.media.AudioClip; -import java.awt.*; -import java.net.URI; - import java.io.File; public class PlayAudioClipAction extends NormalAction { @@ -30,20 +26,11 @@ public class PlayAudioClipAction extends setServerButtonGraphic("fas-volume-up"); setHelpLink("https://github.com/Stream-Pi/EssentialActions"); setVersion(new Version(2,0,0)); - - states = new ArrayList<>(); - states.add("Audio Clip"); - states.add("Music Clip"); } - private ArrayList states; - @Override public void initProperties() throws Exception { - Property audioOrMusic = new Property("selection", Type.LIST); - audioOrMusic.setListValue(states); - audioOrMusic.setDisplayName("Audio Clip or Music Clip"); Property audioFileLocationProperty = new Property("audio_location", Type.STRING); audioFileLocationProperty.setControlType(ControlType.FILE_PATH); audioFileLocationProperty.setDisplayName("Audio File Location"); @@ -57,7 +44,7 @@ public class PlayAudioClipAction extends new FileExtensionFilter("HLS","*.m3u8") ); - addClientProperties(audioOrMusic, audioFileLocationProperty); + addClientProperties(audioFileLocationProperty); } public AudioClip mediaPlayer = null; @@ -66,47 +53,7 @@ public class PlayAudioClipAction extends @Override public void onActionClicked() throws Exception { - String state = states.get(getClientProperties().getSingleProperty("selection").getSelectedIndex()); - - if(state.equals("Audio Clip")) - { - audioClipPlay(); - } - else if(state.equals("Music Clip")) - { - musicClipPlay(); - } - - } - - @Override - 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); - } - } - - public void audioClipPlay() throws Exception - { - + Property audioFileLocationProperty = getClientProperties().getSingleProperty("audio_location"); if (audioFileLocationProperty.getStringValue().isBlank()) @@ -132,28 +79,31 @@ public class PlayAudioClipAction extends } Platform.runLater(mediaPlayer::play); + } - public void musicClipPlay() throws Exception + @Override + public void onShutDown() { + shutDown(); + } - Property audioFileLocationProperty = getClientProperties().getSingleProperty("audio_location"); + public void onActionDeleted() + { + shutDown(); + } - if (audioFileLocationProperty.getStringValue().isBlank()) - { - new StreamPiAlert("Media Action", "No file specified", StreamPiAlertType.ERROR).show(); - return; - } + public void onClientDisconnected() + { + shutDown(); + } - if(!audioFileLocationProperty.getStringValue().equals(path)) + private void shutDown() + { + if(mediaPlayer != null) { - path = audioFileLocationProperty.getStringValue(); + if(mediaPlayer.isPlaying()) + Platform.runLater(mediaPlayer::stop); } - - File file = new File(path); - - Desktop.getDesktop().open(file); } - - } --- 'a/playaudioclipaction/src/main/java/module-info.java' +++ b/playaudioclipaction/src/main/java/module-info.java @@ -6,8 +6,6 @@ module com.stream_pi.playaudioclipaction requires javafx.media; 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.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/ --- /dev/null +++ b/playmusicclipaction/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file --- /dev/null +++ b/playmusicclipaction/pom.xml @@ -0,0 +1,84 @@ + + + 4.0.0 + + com.stream-pi + playmusicclipaction + 1.0.0 + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.1.0 + + + test-jar + package + + test-jar + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 11 + + + + + + + 1.0.0-SNAPSHOT + 1.0.0-SNAPSHOT + + 16 + + UTF-8 + 11 + 11 + + 11.5.0 + 11.5.0 + + + + + com.stream-pi + util + ${UtilVersion} + + + + com.stream-pi + action-api + ${ActionAPIVersion} + + + + org.openjfx + javafx-media + ${JavaFXVersion} + + + + org.kordamp.ikonli + ikonli-fontawesome5-pack + ${IkonliFA5PackVersion} + + + + org.kordamp.ikonli + ikonli-javafx + ${IkonliVersion} + + + \ No newline at end of file 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 --- /dev/null +++ b/spotify/.gitignore @@ -0,0 +1,9 @@ +.idea/ +DemoCustomNormalAction.iml +target/ + + +.settings/ +.project +.factorypath +.classpath --- /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. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file --- /dev/null +++ b/spotify/pom.xml @@ -0,0 +1,95 @@ + + + 4.0.0 + + com.stream-pi + Spotify + 1.0.0 + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.1.0 + + + test-jar + package + + test-jar + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 11 + + + + + + + 1.0.0-SNAPSHOT + + 16-ea+6 + + UTF-8 + 11 + 11 + + + + + + com.github.thelinmichael + spotify-web-api-java + master-SNAPSHOT + + + + org.openjfx + javafx-controls + ${JavaFXVersion} + + + + org.openjfx + javafx-graphics + ${JavaFXVersion} + + + + org.openjfx + javafx-base + ${JavaFXVersion} + + + + + + com.stream-pi + action-api + ${ActionAPIVersion} + + + + + + + org.kordamp.ikonli + ikonli-material-pack + 11.5.0 + + + + + + \ 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 (<=1.7) class and Base64 (>=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 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.
+ * 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).
+ * 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.
+ * 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.
+ * 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.
+ * 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 + * Authorization Code Flow with Proof Key for Code Exchange (PKCE) + */ + 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.
+ * 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 + * Authorization Code Flow with Proof Key for Code Exchange (PKCE) + */ + 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 + * Authorization Code Flow with Proof Key for Code Exchange (PKCE) + */ + 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 + * Authorization Code Flow with Proof Key for Code Exchange (PKCE) + */ + 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.
+ * 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + * @see Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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.

+ *

+ * 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 Either {@link com.wrapper.spotify.model_objects.specification.Artist} or + * {@link com.wrapper.spotify.model_objects.specification.Track} + * @return A {@link GetUsersTopArtistsAndTracksRequest.Builder}. + */ + public GetUsersTopArtistsAndTracksRequest.Builder getUsersTopArtistsAndTracks(ModelObjectType type) { + return new GetUsersTopArtistsAndTracksRequest.Builder(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.

+ *

+ * 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. + *

+ * 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. + *

+ * Note: 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. + *
Note: 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + * @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. + *

+ * Note: If you want to add a large number of items (>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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + * @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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + * @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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + * @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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + * @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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + * @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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + * @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 Spotify: URLs & IDs + */ + 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.

+ *

+ * 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 Spotify: URLs & IDs + * @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.

+ *

+ * 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + * @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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + * @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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + * @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 Spotify: URLs & IDs + */ + 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. + *

+ * 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 Spotify: URLs & IDs + */ + 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 CompletableFuture executeAsync(final Callable callable) { + CompletableFuture 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 Disallows object + */ +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 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 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 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 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 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 Wikipedia: Mode (Music) + */ +public enum Modality { + + MAJOR(1), + MINOR(0); + + private static final Map 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 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 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 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:

+ * {@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.
+ * + * @param The model object type of the corresponding JsonUtil. + */ + public static abstract class JsonUtil implements IModelObject.IJsonUtil { + + /** + * {@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) ((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[] createModelObjectArray(final JsonArray jsonArray, Class 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 createModelObjectPaging(final JsonObject jsonObject) { + return new Paging.Builder() + .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 createModelObjectPaging(final String json) { + return createModelObjectPaging(JsonParser.parseString(json).getAsJsonObject()); + } + + /** + * {@inheritDoc} + */ + public Paging createModelObjectPaging(final String json, final String key) { + return createModelObjectPaging(JsonParser.parseString(json).getAsJsonObject().get(key).getAsJsonObject()); + } + + /** + * {@inheritDoc} + */ + public PagingCursorbased createModelObjectPagingCursorbased(final JsonObject jsonObject) { + return new PagingCursorbased.Builder() + .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 createModelObjectPagingCursorbased(final String json) { + return createModelObjectPagingCursorbased(JsonParser.parseString(json).getAsJsonObject()); + } + + /** + * {@inheritDoc} + */ + public PagingCursorbased 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.
+ * 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.
+ * 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 Type of the corresponding model object. + */ + interface IJsonUtil { + + /** + * 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.
+ * 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.
+ * 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 The model object type of the array and class object. + * @return A model object array. + */ + X[] createModelObjectArray(final JsonArray jsonArray, final Class clazz); + + /** + * Create a paging of model objects out of a json object. + * + * @param jsonObject A json object. + * @return A model object paging. + */ + Paging 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 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 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 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 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 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.
+ * 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 Spotify ID 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 Spotify URI 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 + * Authorization Code + * Credentials 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 "Bearer". + */ + public String getTokenType() { + return tokenType; + } + + + /** + * Get the Scopes 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 "Bearer". + * @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 { + 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 + * Client Credentials 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 "Bearer". + */ + 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 "Bearer". + * @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 { + 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 Spotify: Authorization Guide + */ +@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 + * RFC 6749 Section 5.2. + * + * @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 + * RFC 6749 Section 4.1.2.1. + * + * @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 { + 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 + * Audio Analysis objects by building instances from this class.
+ * These objects contain a great amount of additional information to + * {@link com.wrapper.spotify.model_objects.specification.AudioFeatures} objects.

+ *

+ * Note: 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 { + 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.
+ * 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.
+ * 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.
+ * 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 { + 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.
+ * 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 { + 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.
+ * 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 Wikipedia: Pitch class notation + */ + 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 Wikipedia: Mode (music) + */ + 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.
+ * 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 { + 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.
+ * 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.

+ *

+ * 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.

+ *

+ * 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 { + 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.
+ * The total number of samples is calculated by multiplying the duration of the track with the sample rate.
+ * + * @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.

+ *

+ * Note: The sample MD5 is probably 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.
+ * + * @return The sample MD5. + */ + public String getSampleMd5() { + return sampleMd5; + } + + /** + * Get the offset seconds.
+ * Note: There is no public documentation available for this field.
+ * + * @return The offset seconds. + */ + public Integer getOffsetSeconds() { + return offsetSeconds; + } + + /** + * Get the window seconds.
+ * Note: There is no public documentation available for this field.
+ * + * @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.
+ * 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 Wikipedia: Pitch class notation + */ + 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 Wikipedia: Mode (music) + */ + 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.

+ *

+ * Note: 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.

+ *

+ * Note: 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.
+ *

+ * 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. + *

+ * + * @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 { + 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 + * Currently Playing objects 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 { + 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 + * Currently Playing + * Context objects 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 { + 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 Device + * objects 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 { + 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 { + 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.

+ *

+ * Part of the response when Track Relinking + * 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 { + 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 { + @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 + * simplified Album objects by building instances from this class. + *

+ * 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 ISO 3166-1 alpha-2 country + * codes. + */ + public CountryCode[] getAvailableMarkets() { + return availableMarkets; + } + + /** + * Get the external URLs of the album.
+ * Example: Spotify-URL + * + * @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 Spotify album URI. + */ + 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 + * ISO 3166-1 alpha-2 country codes. + * @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 Spotify album ID. + * @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 + * Spotify album URI. + * @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 { + 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 + * Featured Playlist objects by building instances from this class. + */ +@JsonDeserialize(builder = FeaturedPlaylists.Builder.class) +public class FeaturedPlaylists extends AbstractModelObject { + private final String message; + private final Paging 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.
+ * 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 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 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 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 { + 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 Spotify URI 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 { + 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 albums; + private final Paging artists; + private final Paging episodes; + private final Paging playlists; + private final Paging shows; + private final Paging 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.
+ * Note: 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 getAlbums() { + return albums; + } + + /** + * Get the artist objects contained in the search result object.
+ * Note: 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 getArtists() { + return artists; + } + + /** + * Get the episode objects contained in the search result object.
+ * Note: 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 getEpisodes() { + return episodes; + } + + /** + * Get the playlist objects contained in the search result object.
+ * Note: 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 getPlaylists() { + return playlists; + } + + /** + * Get the show objects contained in the search result object.
+ * Note: 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 getShows() { + return shows; + } + + /** + * Get the track objects contained in the search result object.
+ * Note: 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 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 albums; + private Paging artists; + private Paging episodes; + private Paging playlists; + private Paging shows; + private Paging tracks; + + /** + * The albums setter. + * + * @param albums Albums from the search result. + * @return A {@link SearchResult.Builder}. + */ + public Builder setAlbums(Paging 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 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 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 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 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 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 { + 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. + *

+ * + * Spotify: Working With Playlists + */ +@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 { + 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 + * Album objects 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 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 ISO 3166-1 alpha-2 country + * codes. + */ + 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.
+ * Example: upc -> "Universal Product Code". + * + * @return An array of {@link ExternalId} objects. + */ + public ExternalId getExternalIds() { + return externalIds; + } + + /** + * Get the external URLs of the album.
+ * Example: Spotify-URL + * + * @return An {@link ExternalUrl} object. + */ + public ExternalUrl getExternalUrls() { + return externalUrls; + } + + /** + * Get a list of all genres of the album.
+ * 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 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 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)
+ * 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 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 Spotify album URI. + */ + 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 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 + * ISO 3166-1 alpha-2 country codes. + * @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 Spotify album ID. + * @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 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 + * Spotify album URI. + * @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 { + 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 + * simplified Album objects 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 ISO 3166-1 alpha-2 country + * codes. + */ + public CountryCode[] getAvailableMarkets() { + return availableMarkets; + } + + /** + * Get the external URLs of the album.
+ * Example: Spotify-URL + * + * @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 Spotify album URI. + */ + 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 + * ISO 3166-1 alpha-2 country codes. + * @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 Spotify album ID. + * @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 + * Spotify album URI. + * @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 { + 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 + * Artist objects 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.
+ * Example: Spotify-URL + * + * @return An {@link ExternalUrl} object. + */ + public ExternalUrl getExternalUrls() { + return externalUrls; + } + + /** + * Get information about the followers of the artist.
+ * Example: Follower count. + * + * @return A {@link Followers} object. + */ + public Followers getFollowers() { + return followers; + } + + /** + * Get a list of all genres of the artist.
+ * 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 Spotify artist ID. + */ + 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)
+ * 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 Spotify artist URI. + */ + 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 Spotify artist ID. + * @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 + * Spotify artist URI. + * @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 { + 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 + * simplified Artist objects 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.
+ * Example: Spotify-URL + * + * @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 Spotify artist ID. + */ + 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 Spotify artist URI. + */ + 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 Spotify artist ID. + * @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 + * Spotify artist URI. + * @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 { + 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 + * Audio Feature objects 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.
+ * 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.
+ * 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.
+ * 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 Spotify ID + * 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.
+ * 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 Wikipedia: Pitch class notation + */ + public Integer getKey() { + return key; + } + + /** + * Get the liveness of the track as a value between 0.0 and 1.0.
+ * 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 Wikipedia: Mode (music) + */ + public Modality getMode() { + return mode; + } + + /** + * Get the speechiness of the track as a value between 0.0 and 1.0.
+ * 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.
+ * 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 Spotify URI + * 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.
+ * 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 Spotify URI + * 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 { + 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 + * Category objects 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 Spotify ID + * 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 { + 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 + * Context objects 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 Spotify URI + * 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 { + 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 + * Copyright objects + * 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 { + 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 + * Cursor objects + * 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 + * + * cursor-based paging object. + * + * @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 { + 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 + * Disallows objects + * by building instances from this class. + */ +@JsonDeserialize(builder = Actions.Builder.class) +public class Disallows extends AbstractModelObject { + private final EnumSet 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 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 disallowedActions; + + /** + * Set the set of disallowed actions. + * + * @param disallowedActions The set of disallowed actions. + * @return A {@link Disallows.Builder}. + */ + public Builder setDisallowedActions(EnumSet 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 { + @Override + public Disallows createModelObject(JsonObject jsonObject) { + if (jsonObject == null || jsonObject.isJsonNull()) { + return null; + } + + EnumSet disallowedActions = EnumSet.noneOf(Action.class); + for (Map.Entry 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 + * episode objects 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 + * OR unknown). + */ + public Boolean getExplicit() { + return explicit; + } + + /** + * Get the external URLs of the episode.
+ * Example: Spotify-URL + * + * @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 Spotify episode ID. + */ + @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 ISO 3166-1 alpha-2 country codes. + */ + 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 Spotify episode URI. + */ + @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 Spotify episode ID. + * @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 ISO 3166-1 alpha-2 country codes. + * @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 Spotify URI 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 { + @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 + * simplified Episode objects 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 + * OR unknown). + */ + public Boolean getExplicit() { + return explicit; + } + + /** + * Get the external URLs of the episode.
+ * Example: Spotify-URL + * + * @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 Spotify episode ID. + */ + 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 ISO 3166-1 alpha-2 country codes. + */ + 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 Spotify episode URI. + */ + 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 Spotify episode ID. + * @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 ISO 3166-1 alpha-2 country codes. + * @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 Spotify URI 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 { + @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 + * Error objects 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 HTTP status code + * of the {@link Error} object. + * + * @return The HTTP status code. + */ + 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 HTTP status + * code setter. + * + * @param status The + * HTTP status code. + * @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 { + 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 + * External ID objects + * by building instances from this class. + */ +@JsonDeserialize(builder = ExternalId.Builder.class) +public class ExternalId extends AbstractModelObject { + private final Map externalIds; + + private ExternalId(final Builder builder) { + super(builder); + + this.externalIds = builder.externalIds; + } + + /** + * Get the external IDs from this + * External ID object.

+ *

+ * External ID examples:
+ * "isrc" - + * International Standard Recording Code
+ * "ean" - International Article Number + *
+ * "upc" - Universal Product Code + * + * @return A {@link Map} of external IDs, containing external identifiers for the object. + */ + public Map 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 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 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 { + public ExternalId createModelObject(JsonObject jsonObject) { + if (jsonObject == null || jsonObject.isJsonNull()) { + return null; + } + + Map 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 + * External URL objects + * by building instances from this class. + */ +@JsonDeserialize(builder = ExternalUrl.Builder.class) +public class ExternalUrl extends AbstractModelObject { + private final Map 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 + * External URL object. + *

+ *

+ * External URL example:
+ * "spotify" - The Spotify URL + * for the object. + * + * @return A {@link Map} of external public URLs to its objects. + */ + public Map 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 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 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 { + public ExternalUrl createModelObject(JsonObject jsonObject) { + if (jsonObject == null || jsonObject.isJsonNull()) { + return null; + } + + Map 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 + * Follower objects + * 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.
+ * Please note: 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 { + 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 + * Image objects + * 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 { + 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 + * Paging objects + * by building instances from this class.
+ * This offset-based paging object is a container for a set of objects. + * + * @param The type of the objects contained in a paging object. + */ +@JsonDeserialize(builder = Paging.Builder.class) +public class Paging 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 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 builder() { + return new Builder<>(); + } + + /** + * Builder class for building {@link Paging} instances. + * + * @param The type of the objects contained in a paging object. + */ + public static final class Builder 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 setHref(String href) { + this.href = href; + return this; + } + + /** + * The items setter. + * + * @param items A page of items. + * @return A {@link Paging.Builder}. + */ + public Builder 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 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 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 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 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 setTotal(Integer total) { + this.total = total; + return this; + } + + @Override + public Paging build() { + return new Paging<>(this); + } + } + + /** + * JsonUtil class for building {@link Paging} instances. + * + * @param The type of the objects contained in a paging object. + */ + @SuppressWarnings("unchecked") + public static final class JsonUtil extends AbstractModelObject.JsonUtil> { + public Paging createModelObject(JsonObject jsonObject) { + if (jsonObject == null || jsonObject.isJsonNull()) { + return null; + } + + return new Paging.Builder() + .setHref( + hasAndNotNull(jsonObject, "href") + ? jsonObject.get("href").getAsString() + : null) + .setItems( + createModelObjectArray( + jsonObject.getAsJsonArray("items"), (Class) ((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 + * + * cursor-based Paging objects by building instances from this class.
+ * This cursor-based paging object is a container for a set of objects. + * + * @param The type of the objects contained in a paging object. + */ +@JsonDeserialize(builder = PagingCursorbased.Builder.class) +public class PagingCursorbased 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 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 builder() { + return new Builder<>(); + } + + /** + * Builder class for building {@link PagingCursorbased} instances. + * + * @param The type of the objects contained in a paging object. + */ + public static final class Builder 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 setHref(String href) { + this.href = href; + return this; + } + + /** + * The items setter. + * + * @param items A page of items. + * @return A {@link PagingCursorbased.Builder}. + */ + public Builder 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 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 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 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 setTotal(Integer total) { + this.total = total; + return this; + } + + @Override + public PagingCursorbased build() { + return new PagingCursorbased<>(this); + } + } + + /** + * JsonUtil class for building {@link PagingCursorbased} instances. + * + * @param The type of the objects contained in a paging object. + */ + @SuppressWarnings("unchecked") + public static final class JsonUtil extends AbstractModelObject.JsonUtil> { + public PagingCursorbased createModelObject(JsonObject jsonObject) { + if (jsonObject == null || jsonObject.isJsonNull()) { + return null; + } + + return new Builder() + .setHref( + hasAndNotNull(jsonObject, "href") + ? jsonObject.get("href").getAsString() + : null) + .setItems( + hasAndNotNull(jsonObject, "items") + ? createModelObjectArray( + jsonObject.getAsJsonArray("items"), (Class) ((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 + * Play History objects 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 { + 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 + * Playlist objects 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 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 + * Spotify: Working With Playlists + */ + 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.
+ * Example: Spotify-URL. + * + * @return Known external URLs for this playlist. + */ + public ExternalUrl getExternalUrls() { + return externalUrls; + } + + /** + * Get information about the followers of the playlist.
+ * 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 Spotify ID + * 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.
+ * Note: 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 + * Spotify: Working With Playlists + */ + 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 + * Spotify: Working With Playlists + */ + 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 getTracks() { + return tracks; + } + + /** + * Get the model object type. In this case "playlist". + * + * @return The object type: "playlist" + */ + public ModelObjectType getType() { + return type; + } + + /** + * Get the Spotify URI + * 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 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 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 Spotify URI + * 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 { + 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 + * simplified Playlist objects 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 + * Spotify: Working With Playlists + */ + public Boolean getIsCollaborative() { + return collaborative; + } + + /** + * Get the external URLs of the playlist.
+ * 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 Spotify ID + * 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.
+ * Note: 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 + * Spotify: Working With Playlists + */ + 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 + * Spotify: Working With Playlists + */ + 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 Spotify URI + * 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 Spotify URI + * 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 { + 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 + * Playlist Track objects 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. + * Note: 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. + * Note: 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.
+ * 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 { + 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 + * Recommendation objects 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 { + 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 + * Recommendation Seed objects 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 { + 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 + * Resume Point objects + * 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 { + @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 + * Saved Album objects 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 { + 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 + * Saved Show objects + * 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 { + @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 + * Saved Track objects 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 { + 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 + * Show objects 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 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 ISO 3166-1 alpha-2 country + * * codes. + */ + 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 + * OR 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 getEpisodes() { + return episodes; + } + + /** + * Get the external URLs of the show.
+ * Example: Spotify-URL + * + * @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 Spotify show ID. + */ + 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 ISO 3166-1 alpha-2 country codes. + */ + 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 Spotify show URI. + */ + 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 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 + * ISO 3166-1 alpha-2 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 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 Spotify show ID. + * @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 ISO 3166-1 alpha-2 country codes. + * @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 Spotify URI 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 { + @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 + * simplified Show objects 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 ISO 3166-1 alpha-2 country + * * codes. + */ + 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 + * OR unknown). + */ + public Boolean getExplicit() { + return explicit; + } + + /** + * Get the external URLs of the show.
+ * Example: Spotify-URL + * + * @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 Spotify show ID. + */ + 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 ISO 3166-1 alpha-2 country codes. + */ + 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 Spotify show URI. + */ + 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 + * ISO 3166-1 alpha-2 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 Spotify show ID. + * @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 ISO 3166-1 alpha-2 country codes. + * @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 Spotify URI 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 { + @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 + * Track objects 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 + * ISO 3166-1 alpha-2 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 + * OR unknown). + */ + public Boolean getIsExplicit() { + return explicit; + } + + /** + * Get the external IDs of the track.
+ * Example: isrc -> "International Standard Recording Code". + * + * @return Known external IDs for the track. + */ + public ExternalId getExternalIds() { + return externalIds; + } + + /** + * Get the external URLs of the track.
+ * 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 Spotify ID 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 Track Relinking + * 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 + * Track Relinking 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 + * Track Relinking 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.
+ * 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.

+ *

+ * 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.

+ *

+ * 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 Spotify URI 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 + * ISO 3166-1 alpha-2 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 OR 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 Spotify URI + * 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 { + 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 + * Track Link objects by building instances from this class.
+ * Track Link objects contain information about originally requested tracks, when the given track is not available in + * your market region. + * + * @see Spotify: Track Relinking Guide + */ +@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.
+ * 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 Spotify ID 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 Spotify URI 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 { + 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 + * simplified Track objects 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 + * ISO 3166-1 alpha-2 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 + * OR unknown). + */ + public Boolean getIsExplicit() { + return explicit; + } + + /** + * Get the external URLs of the track.
+ * 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 Spotify ID 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 Track Relinking + * 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 + * Track Relinking 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 Spotify URI 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 + * ISO 3166-1 alpha-2 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 OR 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 Spotify URI + * 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 { + 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 + * User objects by building instances from this class.
+ * Note: Many methods of this model object may return {@code null}, depending on the scopes specified in the + * authentication request. + * + * @see Spotify: Using Scopes + */ +@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.
+ * Note: 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 Spotify: Using Scopes + */ + public String getBirthdate() { + return birthdate; + } + + /** + * Get the country code of the users set home country.
+ * Note: This field is only available when the current user has granted access to the {@code user-read-private} + * scope. + * + * @return An ISO 3166-1 alpha-2 country code. + * @see Spotify: Using Scopes + */ + public CountryCode getCountry() { + return country; + } + + /** + * Get the users display name if available.
+ * 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.
+ * Important! This email address is unverified; there is no proof that it actually belongs to the user.
+ * Note: 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 Spotify: Using Scopes + */ + public String getEmail() { + return email; + } + + /** + * Get the external URLs of the user.
+ * Example: Spotify-URL. + * + * @return Known external URLs for this user. + */ + public ExternalUrl getExternalUrls() { + return externalUrls; + } + + /** + * Get information about the followers of the user.
+ * 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 Spotify user ID + * 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.
+ * Product type refers to premium account, free account, etc.
+ * Note: 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 Spotify: Using Scopes + */ + 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 Spotify URI 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 Spotify user ID + * 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 { + 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 implements IRequest { + + private final IHttpManager httpManager; + private final List

headers; + private final ContentType contentType; + private final List bodyParameters; + private URI uri; + private HttpEntity body; + + protected AbstractRequest(Builder 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 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 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
getHeaders() { + return headers; + } + + public ContentType getContentType() { + return contentType; + } + + public HttpEntity getBody() { + return body; + } + + public List getBodyParameters() { + return bodyParameters; + } + + public static abstract class Builder> implements IRequest.Builder { + + private final List pathParameters = new ArrayList<>(); + private final List queryParameters = new ArrayList<>(); + private final List
headers = new ArrayList<>(); + private final List 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 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 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 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 nameValuePairs, NameValuePair newNameValuePair) { + nameValuePairs.removeAll(nameValuePairs.stream() + .filter(nameValuePair -> nameValuePair.getName().equals(newNameValuePair.getName())) + .collect(Collectors.toList())); + nameValuePairs.add(newNameValuePair); + } + + private void listAddOnce(List
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 { + + IHttpManager getHttpManager(); + + URI getUri(); + + List
getHeaders(); + + ContentType getContentType(); + + HttpEntity getBody(); + + List getBodyParameters(); + + T execute() throws + IOException, + SpotifyWebApiException, + ParseException; + + CompletableFuture 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> { + + 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); + + BT setQueryParameter(final String name, final ST value); + + BT setHeader(final String name, final ST value); + + BT setContentType(final ContentType contentType); + + BT setBody(final HttpEntity httpEntity); + + BT setBodyParameter(final String name, final ST value); + + IRequest 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 extends AbstractRequest { + protected AbstractAuthorizationRequest(final Builder builder) { + super(builder); + } + + public static abstract class Builder> extends AbstractRequest.Builder { + 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 + * + * Authorization Code Refresh request. + */ +@JsonDeserialize(builder = AuthorizationCodeRefreshRequest.Builder.class) +public class AuthorizationCodeRefreshRequest extends AbstractAuthorizationRequest { + + 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 { + + 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 + * Authorization Code + * request. + */ +@JsonDeserialize(builder = AuthorizationCodeRequest.Builder.class) +public class AuthorizationCodeRequest extends AbstractAuthorizationRequest { + + 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 { + + 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 + * Authorization Code + * URI request. + */ +@JsonDeserialize(builder = AuthorizationCodeUriRequest.Builder.class) +public class AuthorizationCodeUriRequest extends AbstractRequest { + + 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 { + + 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 + * Authorization Code Flow with Proof Key for Code Exchange (PKCE) + */ + 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 RFC 6749: Cross-Site Request Forgery + */ + 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 Spotify: Using Scopes + */ + 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 + * + * Authorization Code Refresh request. + */ +@JsonDeserialize(builder = AuthorizationCodePKCERefreshRequest.Builder.class) +public class AuthorizationCodePKCERefreshRequest extends AbstractRequest { + + 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 { + + 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 + * Authorization Code + * request. + */ +@JsonDeserialize(builder = AuthorizationCodePKCERequest.Builder.class) +public class AuthorizationCodePKCERequest extends AbstractRequest { + + 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 { + + 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 + * Authorization Code Flow with Proof Key for Code Exchange (PKCE) + */ + 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 + * Client Credentials + * request. + */ +@JsonDeserialize(builder = ClientCredentialsRequest.Builder.class) +public class ClientCredentialsRequest extends AbstractAuthorizationRequest { + + 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 { + + 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 extends AbstractDataRequest { + protected AbstractDataPagingCursorbasedRequest(final AbstractDataRequest.Builder builder) { + super(builder); + } + + public static abstract class Builder> extends AbstractDataRequest.Builder, BT> implements IPagingCursorbasedRequestBuilder { + 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 extends AbstractDataRequest { + protected AbstractDataPagingRequest(final AbstractDataRequest.Builder builder) { + super(builder); + } + + public static abstract class Builder> + extends AbstractDataRequest.Builder, BT> + implements IPagingRequestBuilder { + 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 extends AbstractRequest { + protected AbstractDataRequest(final Builder builder) { + super(builder); + } + + public static abstract class Builder> extends AbstractRequest.Builder { + 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, ?>> + extends IRequest.Builder, 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, ?>> + extends IRequest.Builder, 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 { + + /** + * 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 { + + /** + * 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 Spotify URIs & IDs + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + * @see Spotify: Track Relinking Guide + */ + 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> { + + /** + * 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 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 { + + /** + * 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 Spotify URIs & IDs + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + * @see Spotify: Track Relinking Guide + */ + 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 { + + /** + * 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 { + + /** + * 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 Spotify URIs & IDs + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + * @see Spotify: Track Relinking Guide + */ + 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 { + + /** + * 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 { + + /** + * 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 Spotify URIs & IDs + */ + 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> { + + /** + * 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 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 { + + /** + * 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 Spotify URIs & IDs + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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 { + + /** + * 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 { + + /** + * 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 Spotify URIs & IDs + */ + 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 { + + /** + * 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 { + + /** + * 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 Spotify URIs & IDs + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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 { + + /** + * 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 { + + /** + * 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 Spotify URIs & IDs + */ + 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 { + + /** + * 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 { + + /** + * 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 Spotify URIs & IDs + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + * @see Wikipedia: ISO 639 language code + */ + 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> { + + /** + * 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 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 { + + /** + * 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 Spotify URIs & IDs + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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> { + + /** + * 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 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 { + + /** + * 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 Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + * @see Wikipedia: ISO 639 language code + */ + 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 { + + /** + * 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 { + + /** + * 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 Wikipedia: ISO 3166-1 alpha-2 country codes + * @see Wikipedia: ISO 639 language code + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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 Wikipedia: ISO 8601 timestamps + */ + 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> { + + /** + * 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 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 { + + /** + * 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 Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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.

+ *

+ * 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.

+ *

+ * 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 { + + /** + * 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 { + + /** + * 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 Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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 + * Pitch Class notation. 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 + * Pitch Class notation. 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 Spotify URIs & IDs + */ + 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 Spotify URIs & IDs + */ + 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 Spotify URIs & IDs + */ + 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 + * Pitch Class notation. 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 { + + /** + * 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 genres = new Gson().fromJson( + JsonParser + .parseString(getJson()) + .getAsJsonObject() + .get("genres") + .getAsJsonArray(), + new TypeToken>() { + }.getType() + ); + + return genres.toArray(new String[0]); + } + + /** + * Builder class for building a {@link GetAvailableGenreSeedsRequest.Builder}. + */ + public static final class Builder extends AbstractDataRequest.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 { + + /** + * 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 { + + /** + * Create a new {@link GetEpisodeRequest.Builder}. + *

+ * 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 Spotify: URIs & IDs + */ + public Builder id(final String id) { + assert (id != null); + assert (!id.equals("")); + return setPathParameter("id", id); + } + + /** + * The market country code setter.

+ * 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. + * Note: If neither market or user country are provided, the content is considered unavailable for the client.

+ * 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 Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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 { + + /** + * 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 { + + /** + * Create a new {@link GetSeveralEpisodesRequest.Builder}. + *

+ * 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 Spotify: URIs & IDs + */ + public Builder ids(final String ids) { + assert (ids != null); + assert (ids.split(",").length <= 50); + return setQueryParameter("ids", ids); + } + + /** + * The market country code setter.

+ * 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. + * Note: If neither market or user country are provided, the content is considered unavailable for the client.

+ * 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 Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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 { + + /** + * 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 { + + /** + * 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 Spotify URIs & IDs + */ + 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 Spotify URIs & IDs + */ + 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 { + + /** + * 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 { + + /** + * Create a new {@link CheckUsersFollowPlaylistRequest.Builder} instance. + *

+ * 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}. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + 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 Spotify: URIs & IDs + */ + 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 Spotify: URIs & IDs + */ + 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 { + + /** + * 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. Note: 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 { + + /** + * Create a new {@link FollowArtistsOrUsersRequest}. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify URIs & IDs + */ + 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 Spotify URIs & IDs + */ + 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 { + + /** + * The private {@link FollowPlaylistRequest} constructor. + * + * @param builder A {@link FollowPlaylistRequest.Builder}. + */ + private FollowPlaylistRequest(final Builder builder) { + super(builder); + } + + /** + * Follow a playlist. + * + * @return A string. Note: 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 { + + /** + * Create a new {@link FollowPlaylistRequest} instance. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + 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> { + + /** + * 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 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 { + + /** + * Create a new {@link GetUsersFollowedArtistsRequest.Builder} instance. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + @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 { + + /** + * 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. Note: 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 { + + /** + * Create a new {@link UnfollowArtistsOrUsersRequest.Builder} instance. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + public Builder ids(final String ids) { + assert (ids != null); + assert (ids.split(",").length <= 50); + return setQueryParameter("ids", ids); + } + + /** + * The artist or user IDs setter. + *

+ * Note: 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 Spotify: URIs & IDs + */ + 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 { + + /** + * The private {@link UnfollowPlaylistRequest} constructor. + * + * @param builder A {@link UnfollowPlaylistRequest.Builder}. + */ + private UnfollowPlaylistRequest(final Builder builder) { + super(builder); + } + + /** + * Unfollow a playlist. + * + * @return A string. Note: 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 { + + /** + * Create a new {@link UnfollowPlaylistRequest.Builder} instance. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + 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 { + + /** + * The private {@link FollowPlaylistRequest} constructor. + * + * @param builder A {@link FollowPlaylistRequest.Builder}. + */ + private FollowPlaylistRequest(final Builder builder) { + super(builder); + } + + /** + * Follow a playlist. + * + * @return A string. Note: 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 { + + /** + * Create a new {@link FollowPlaylistRequest} instance. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + 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 Spotify: URIs & IDs + */ + 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 { + + /** + * The private {@link UnfollowPlaylistRequest} constructor. + * + * @param builder A {@link UnfollowPlaylistRequest.Builder}. + */ + private UnfollowPlaylistRequest(final Builder builder) { + super(builder); + } + + /** + * Unfollow a playlist. + * + * @return A string. Note: 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 { + + /** + * Create a new {@link UnfollowPlaylistRequest.Builder} instance. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + 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 Spotify: URIs & IDs + */ + 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 { + + /** + * 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 { + + /** + * Create a new {@link CheckUsersSavedAlbumsRequest.Builder} instance. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + 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 { + + /** + * 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 { + /** + * Create a new {@link CheckUsersSavedShowsRequest.Builder} instance. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + 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 { + + /** + * 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 { + + /** + * Create a new {@link CheckUsersSavedTracksRequest.Builder} instance. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + 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> { + + /** + * 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 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 { + + /** + * Create a new {@link GetCurrentUsersSavedAlbumsRequest.Builder} instance. + *

+ * 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 Spotify: Using Scopes + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + * @see Spotify: Track Relinking Guide + */ + 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> { + + /** + * 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 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 { + + /** + * Create a new {@link GetUsersSavedShowsRequest.Builder} instance. + *

+ * 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 Spotify: Using Scopes + */ + 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> { + + /** + * 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 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 { + + /** + * Create a new {@link GetUsersSavedTracksRequest.Builder} instance. + *

+ * 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 Spotify: Using Scopes + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + * @see Spotify: Track Relinking Guide + */ + 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 { + + /** + * 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. Note: 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 { + + /** + * Create a new {@link RemoveAlbumsForCurrentUserRequest.Builder} instance. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + public Builder ids(final String ids) { + assert (ids != null); + assert (ids.split(",").length <= 50); + return setQueryParameter("ids", ids); + } + + /** + * The album IDs setter. + *

+ * Note: 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 Spotify: URIs & IDs + */ + 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 { + + /** + * 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. Note: 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 { + + /** + * Create a new {@link RemoveUsersSavedShowsRequest.Builder} instance. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + public Builder ids(final String ids) { + assert (ids != null); + assert (ids.split(",").length <= 50); + return setQueryParameter("ids", ids); + } + + /** + * The market country code setter.

+ * 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. + * Note: If neither market or user country are provided, the content is considered unavailable for the client.

+ * 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 Wikipedia: ISO 3166-1 alpha-2 country codes + */ + public Builder market(final CountryCode market) { + assert (market != null); + return setQueryParameter("market", market); + } + + /** + * The show IDs setter. + *

+ * Note: 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 Spotify: URIs & IDs + */ + 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 { + + /** + * 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. Note: 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 { + + /** + * Create a new {@link RemoveUsersSavedTracksRequest.Builder} instance. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + public Builder ids(final String ids) { + assert (ids != null); + assert (ids.split(",").length <= 50); + return setQueryParameter("ids", ids); + } + + /** + * The track IDs setter. + *

+ * Note: 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 Spotify: URIs & IDs + */ + 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 { + + /** + * 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. Note: 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 { + + /** + * Create a new {@link SaveAlbumsForCurrentUserRequest.Builder} instance. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + 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 Spotify: URIs & IDs + */ + 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 { + + /** + * 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. Note: 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 { + + /** + * Create a new {@link SaveShowsForCurrentUserRequest.Builder} instance. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + public Builder ids(final String ids) { + assert (ids != null); + assert (ids.split(",").length <= 50); + return setQueryParameter("ids", ids); + } + + /** + * The show IDs setter. + *

+ * Note: 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 Spotify: URIs & IDs + */ + 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 { + + /** + * 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. Note: 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 { + + /** + * Create a new {@link SaveTracksForUserRequest.Builder} instance. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + 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 Spotify: URIs & IDs + */ + 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 The request {@link ModelObjectType}: artist or track. + */ +@JsonDeserialize(builder = GetUsersTopArtistsAndTracksRequest.Builder.class) +public class GetUsersTopArtistsAndTracksRequest extends AbstractDataRequest> { + + private final AbstractModelObject.JsonUtil tClass; + + /** + * The private {@link GetUsersTopArtistsAndTracksRequest} constructor. + * + * @param builder A {@link GetUsersTopArtistsAndTracksRequest.Builder}. + * @param tClass A {@link AbstractModelObject.JsonUtil}. + */ + private GetUsersTopArtistsAndTracksRequest(final Builder builder, final AbstractModelObject.JsonUtil 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 execute() throws + IOException, + SpotifyWebApiException, + ParseException { + return tClass.createModelObjectPaging(getJson()); + } + + /** + * Builder class for building a {@link GetUsersTopArtistsAndTracksRequest}. + *

+ * This class only exists for theoretical purposes. Please use {@link GetUsersTopArtistsRequest.Builder} and + * {@link GetUsersTopTracksRequest.Builder} instead. + * + * @param The request {@link ModelObjectType}: artist or track. + */ + public static final class Builder extends AbstractDataPagingRequest.Builder> { + + private AbstractModelObject.JsonUtil tClass; + + /** + * Create a new {@link GetUsersTopArtistsAndTracksRequest.Builder} instance. + *

+ * 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 Spotify: Using Scopes + */ + 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 type(final ModelObjectType type) { + assert (type != null); + assert (type.getType().equals("artists") || type.getType().equals("tracks")); + + switch (type.getType()) { + case "artists": + tClass = (AbstractModelObject.JsonUtil) new Artist.JsonUtil(); + break; + case "tracks": + tClass = (AbstractModelObject.JsonUtil) 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 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 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 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 build() { + setPath("/v1/me/top/{type}"); + return new GetUsersTopArtistsAndTracksRequest<>(this, tClass); + } + + @Override + protected Builder 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. + *

+ * 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. + *

+ * 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. + *

+ * 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> { + + /** + * 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 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 { + + /** + * Create a new {@link GetUsersTopArtistsRequest.Builder} instance. + *

+ * 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 Spotify: Using Scopes + */ + 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. + *

+ * 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. + *

+ * 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. + *

+ * 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> { + + /** + * 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 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 { + + /** + * Create a new {@link GetUsersTopTracksRequest.Builder} instance. + *

+ * 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 Spotify: Using Scopes + */ + 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 { + + /** + * 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. Note: 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 { + + /** + * Create a new {@link AddItemToUsersPlaybackQueueRequest.Builder}. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + 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 Spotify: URIs & IDs + */ + 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. + *

+ * 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. + *

+ * 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. + *

+ * 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> { + + /** + * 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 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 { + + /** + * Create a new {@link GetCurrentUsersRecentlyPlayedTracksRequest.Builder}. + *

+ * 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 Spotify: Using Scopes + */ + 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 { + + /** + * 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 { + + /** + * Create a new {@link GetInformationAboutUsersCurrentPlaybackRequest.Builder}. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: Track Relinking Guide + * @see Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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 { + + /** + * 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 { + + /** + * Create a new {@link GetUsersAvailableDevicesRequest.Builder}. + *

+ * 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 Spotify: Using Scopes + */ + 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 { + + /** + * 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 { + + /** + * Create a new {@link GetUsersCurrentlyPlayingTrackRequest.Builder}. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: Track Relinking Guide + * @see Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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 { + + /** + * 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. Note: 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 { + + /** + * Create a new {@link PauseUsersPlaybackRequest.Builder}. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + 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 { + + /** + * 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. Note: 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 { + + /** + * Create a new {@link SeekToPositionInCurrentlyPlayingTrackRequest.Builder}. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + 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 { + + /** + * 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. Note: 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 { + + /** + * Create a new {@link SetRepeatModeOnUsersPlaybackRequest.Builder}. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + 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 { + + /** + * 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. Note: 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 { + + /** + * Create a new {@link SetVolumeForUsersPlaybackRequest.Builder}. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + 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 { + + /** + * 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. Note: 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 { + + /** + * Create a new {@link SkipUsersPlaybackToNextTrackRequest.Builder}. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + 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. + *

+ * Note: 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 { + + /** + * 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. Note: 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 { + + /** + * Create a new {@link SkipUsersPlaybackToPreviousTrackRequest.Builder}. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + 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 { + + /** + * 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. Note: 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 { + + /** + * Create a new {@link StartResumeUsersPlaybackRequest.Builder}. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + 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 Spotify: URIs & IDs + */ + 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 Spotify: URIs & IDs + */ + public Builder uris(final JsonArray uris) { + assert (uris != null); + assert (!uris.isJsonNull()); + return setBodyParameter("uris", uris); + } + + /** + * The offset setter. + *

+ * Note: 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.
The {@code position} parameter in the + * {@code offset} object is zero based and can’t be negative.
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 { + + /** + * 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. Note: 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 { + + /** + * Create a new {@link ToggleShuffleForUsersPlaybackRequest.Builder}. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + 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 { + + /** + * 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. Note: 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 { + + /** + * Create a new {@link TransferUsersPlaybackRequest.Builder}. + *

+ * 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 Spotify: Using Scopes + */ + 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. Note: Although an array is accepted, only a single + * {@code device_id} is currently supported. + * @return A {@link TransferUsersPlaybackRequest.Builder}. + * @see Spotify: URIs & IDs + */ + 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. + *

+ * 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 { + + /** + * 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 + * Spotify: Version Control and Snapshots + */ + 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 { + + /** + * Create a new {@link AddItemsToPlaylistRequest.Builder}. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + * @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 Spotify: URIs & IDs + */ + 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. + *

+ * Note: 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 Spotify: URIs & IDs + */ + 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. + *

+ * Note: 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 Spotify: URIs & IDs + */ + 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 { + + /** + * 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. Note: 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 { + + /** + * Create a new {@link ChangePlaylistsDetailsRequest.Builder}. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + * @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 Spotify: URIs & IDs + */ + 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. Note: 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 { + + /** + * 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 { + + /** + * Create a new {@link CreatePlaylistRequest.Builder}. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + 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. + *

+ * Note: 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. + * Note: 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> { + + /** + * 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 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 { + + /** + * Create a new {@link GetListOfCurrentUsersPlaylistsRequest.Builder}. + *

+ * Private playlists are only retrievable for the current user and requires the {@code playlist-read-private} + * scope to have been authorized by the user. Note: This scope alone will not return collaborative playlists, + * even though they are always private. + *

+ * 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 Spotify: Using Scopes + */ + 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> { + + /** + * 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 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 { + + /** + * Create a new {@link GetListOfUsersPlaylistsRequest.Builder}. + *

+ * Private playlists are only retrievable for the current user and requires the {@code playlist-read-private} + * scope to have been authorized by the user. Note: This scope alone will not return collaborative playlists, + * even though they are always private. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + 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 { + + /** + * 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 { + + /** + * 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 Spotify: URIs & IDs + * @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 Spotify: URIs & IDs + */ + 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 { + + /** + * 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 { + + /** + * Create a new {@link GetPlaylistRequest.Builder}. + *

+ * 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 Spotify: URIs & IDs + * @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 Spotify: URIs & IDs + */ + 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 + * Spotify: More Details on Playlist Fields + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + * @see Spotify: Track Relinking Guide + */ + 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> { + + /** + * 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 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 { + + /** + * Create a new {@link GetPlaylistsItemsRequest.Builder}. + *

+ * 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 Spotify: URIs & IDs + * @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 Spotify: URIs & IDs + */ + 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 + * Spotify: More Details on Playlist Fields + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + * @see Spotify: Track Relinking Guide + */ + 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 { + + /** + * 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 + * Spotify: Version Control and Snapshots + */ + 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 { + + /** + * Create a new {@link RemoveItemsFromPlaylistRequest.Builder}. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + * @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 Spotify: URIs & IDs + */ + 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. + *

+ * There are several ways to specify which tracks or episodes to remove, determined by the request parameters. + * Removing all occurrences of specific items:
+ * {@code [{ "uri": "spotify:track:4iV5W9uYEdYUVa79Axb7Rh" }, + * {"uri": "spotify:episode:512ojhOuo1ktJprKbVcKyQ" }] }
+ * Removing a specific occurrence of an item:
+ * {@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 Spotify: URIs & IDs + */ + 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. + *

+ * 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 + * Spotify: Version Control and Snapshots + */ + 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. + *

+ * 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 { + + /** + * 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 + * Spotify: Version Control and Snapshots + */ + 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 { + + /** + * Create a new {@link ReorderPlaylistsItemsRequest.Builder}. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + * @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 Spotify: URIs & IDs + */ + 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 + * Spotify: Version Control and Snapshots + */ + 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 { + + /** + * 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. Note: 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 { + + /** + * Create a new {@link ReplacePlaylistsItemsRequest.Builder}. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + * @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 Spotify: URIs & IDs + */ + 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 Spotify: URIs & IDs + */ + 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. + *

+ * Note: 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 Spotify: URIs & IDs + */ + 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 { + + /** + * 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. Note: 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 { + + /** + * Create a new {@link UploadCustomPlaylistCoverImageRequest.Builder}. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + * @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 Spotify: URIs & IDs + */ + 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 { + + /** + * 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 { + + /** + * 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 Spotify: Search Query Options + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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> { + + /** + * 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 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 { + + /** + * 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 Spotify: Search Query Options + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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> { + + /** + * 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 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 { + + /** + * 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 Spotify: Search Query Options + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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> { + + /** + * 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 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 { + + /** + * 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 Spotify: Search Query Options + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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> { + + /** + * 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 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 { + + /** + * 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 Spotify: Search Query Options + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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> { + + /** + * 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 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 { + + /** + * 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 Spotify: Search Query Options + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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> { + + /** + * 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 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 { + + /** + * 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 Spotify: Search Query Options + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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. + *

+ * 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> { + + /** + * 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 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 { + + /** + * 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 Spotify: Search Query Options + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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 { + + /** + * 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 { + + /** + * Create a new {@link GetSeveralShowsRequest.Builder}. + *

+ * 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 Spotify: URIs & IDs + */ + public GetSeveralShowsRequest.Builder ids(final String ids) { + assert (ids != null); + assert (ids.split(",").length <= 50); + return setQueryParameter("ids", ids); + } + + /** + * The market country code setter.

+ * 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. + * Note: If neither market or user country are provided, the content is considered unavailable for the client.

+ * 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 Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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 { + + /** + * 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 { + + /** + * Create a new {@link GetShowRequest.Builder}. + *

+ * 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 Spotify: URIs & IDs + */ + public Builder id(final String id) { + assert (id != null); + assert (!id.equals("")); + return setPathParameter("id", id); + } + + /** + * The market country code setter.

+ * 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. + * Note: If neither market or user country are provided, the content is considered unavailable for the client.

+ * 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 Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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> { + + /** + * 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 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 { + + /** + * Create a new {@link GetShowsEpisodesRequest.Builder} instance. + *

+ * 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 Spotify: Using Scopes + */ + 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 Spotify: URIs & IDs + */ + 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.

+ * 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. + * Note: If neither market or user country are provided, the content is considered unavailable for the client.

+ * 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 Wikipedia: ISO 3166-1 alpha-2 country codes + */ + 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 { + + /** + * 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 { + + /** + * 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 Spotify: URIs & IDs + */ + 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 { + + /** + * 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 { + + /** + * 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 Spotify: URIs & IDs + */ + 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 { + + /** + * 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 { + + /** + * 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 Spotify: URIs & IDs + */ + 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 { + + /** + * 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 { + + /** + * 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 Spotify: URIs & IDs + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + * @see Spotify: Track Relinking Guide + */ + 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 { + + /** + * 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 { + + /** + * 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 Spotify: URIs & IDs + */ + 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 Wikipedia: ISO 3166-1 alpha-2 country codes + * @see Spotify: Track Relinking Guide + */ + 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 { + + /** + * 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 { + + /** + * Create a new {@link GetCurrentUsersProfileRequest.Builder}. + *

+ * 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 Spotify: Using Scopes + */ + 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 { + + /** + * 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 { + + /** + * 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 Spotify: URIs & IDs + */ + 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