From 367464d0f8da54fcf983af3e74d20e69e1db60e8 Mon Sep 17 00:00:00 2001 From: "Skylar \"The Cobra\" Widulski" Date: Fri, 20 Jan 2023 19:54:10 -0500 Subject: [PATCH] Init Signed-off-by: Skylar "The Cobra" Widulski --- LICENSE | 661 ++++++++++++++++++++++++++++ README.md | 19 + main.py | 585 ++++++++++++++++++++++++ static/img/favicon.png | Bin 0 -> 4245 bytes static/img/favicon.xcf | Bin 0 -> 10043 bytes static/img/logo.png | Bin 0 -> 23988 bytes static/img/logo.xcf | Bin 0 -> 77613 bytes templates/400.html | 18 + templates/404.html | 18 + templates/429.html | 18 + templates/archives.html | 41 ++ templates/article-review.html | 18 + templates/article.html | 58 +++ templates/category.html | 52 +++ templates/collection.html | 36 ++ templates/contest.html | 32 ++ templates/contests.html | 48 ++ templates/footer.html | 4 + templates/header.html | 16 + templates/index.html | 142 ++++++ templates/member-instructables.html | 42 ++ templates/member.html | 49 +++ templates/projects.html | 36 ++ templates/sitemap.html | 29 ++ templates/style.html | 166 +++++++ 25 files changed, 2088 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 main.py create mode 100644 static/img/favicon.png create mode 100644 static/img/favicon.xcf create mode 100644 static/img/logo.png create mode 100644 static/img/logo.xcf create mode 100644 templates/400.html create mode 100644 templates/404.html create mode 100644 templates/429.html create mode 100644 templates/archives.html create mode 100644 templates/article-review.html create mode 100644 templates/article.html create mode 100644 templates/category.html create mode 100644 templates/collection.html create mode 100644 templates/contest.html create mode 100644 templates/contests.html create mode 100644 templates/footer.html create mode 100644 templates/header.html create mode 100644 templates/index.html create mode 100644 templates/member-instructables.html create mode 100644 templates/member.html create mode 100644 templates/projects.html create mode 100644 templates/sitemap.html create mode 100644 templates/style.html diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0ad25db --- /dev/null +++ b/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 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 Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are 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. + + 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. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + 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 Affero 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. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + 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 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 work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero 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 Affero 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 Affero 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 Affero 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 Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + 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 AGPL, see +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..10963c9 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# Destructables +Destructables is a privacy-respecting frontend to Instructables + +# Instances +See instances.json + +# Run your own instance +## Dependencies +This program depends on `bs4`, `requests`, and `flask`. Install them by running `pip3 install bs4 requests flask`. + +For the production environment, you also need the uWSGI Python3 plugin. On Debian, it can be installed via `apt install uwsgi-plugin-python3` +## Production +1. Clone the repository +2. Run `uwsgi --plugin python3 --http-socket 0.0.0.0:8002 --wsgi-file main.py --callable app --processes 4 --threads 2` +3. Point your reverse proxy to http://localhost:8002 +## Development +1. Clone the repository +2. Run `python3 main.py` +3. Connect to http://localhost:8002 diff --git a/main.py b/main.py new file mode 100644 index 0000000..313fa64 --- /dev/null +++ b/main.py @@ -0,0 +1,585 @@ +#!/usr/bin/env python + +from flask import Flask, render_template, request, redirect, Response, stream_with_context +import requests +import re +from bs4 import BeautifulSoup +from urllib.parse import quote, unquote +from traceback import print_exc + +def proxy(src): + return "/proxy/?url=" + quote(str(src)) + +def explore_lists(soup): + list_ = [] + for ible in soup.select(".home-content-explore-ible"): + link = ible.a["href"] + img = proxy(ible.select("a img")[0].get("data-src")) + alt = ible.select("a img")[0].get("alt") + title = ible.select("div strong a")[0].text + author = ible.select("div span.ible-author a")[0].text + author_link = ible.select("div span.ible-author a")[0].get("href") + channel = ible.select("div span.ible-channel a")[0].text + channel_link = ible.select("div span.ible-channel a")[0].get("href") + views = 0 + if ible.select("span.ible-views") != []: + views = ible.select("span.ible-views")[0].text + favorites = 0 + if ible.select("span.ible-favorites") != []: + favorites = ible.select("span.ible-favorites")[0].text + list_.append([link, img, alt, title, author, author_link, channel, channel_link, favorites, views]) + return list_ + +def member_header(header): + avatar = proxy(header.select("div.profile-avatar-container img.profile-avatar")[0].get("src")) + title = header.select("div.profile-top div.profile-headline h1.profile-title")[0].text + + stats_text = header.select("div.profile-top div.profile-header-stats")[0] + stats_num = header.select("div.profile-top div.profile-header-stats")[1] + + + location = stats_text.select("span.member-location") + if location != []: + location = location[0].text + else: + location = 0 + + signup = stats_text.select("span.member-signup-date") + if signup != []: + signup = signup[0].text + else: + signup = 0 + + + instructables = stats_num.select("span.ible-count") + if instructables != []: + instructables = instructables[0].text + else: + instructables = 0 + + views = stats_num.select("span.total-views") + if views != []: + views = views[0].text + else: + views = 0 + + comments = stats_num.select("span.total-comments") + if comments != []: + comments = comments[0].text + else: + comments = 0 + + followers = stats_num.select("span.follower-count") + if followers != []: + followers = followers[0].text + else: + followers = 0 + + bio = header.select("span.member-bio")[0].text + + return [avatar, title, location, signup, instructables, views, comments, followers, bio] + +def category_page(path, name, teachers=False): + data = requests.get("https://www.instructables.com" + path) + if data.status_code != 200: + return Response(render_template(str(data.status_code) + ".html"), status=data.status_code) + + soup = BeautifulSoup(data.text, "html.parser") + + channels = [] + for card in soup.select("div.scrollable-cards-inner div.scrollable-card"): + link = card.a["href"] + img = proxy(card.select(f"a{' noscript' if teachers else ''} img")[0].get("src")) + title = card.select("a img")[0].get("alt") + + channels.append([link, title, img]) + + ibles = [] + for ible in soup.select("div.category-landing-projects-list div.category-landing-projects-ible"): + link = ible.a["href"] + img = proxy(ible.select("a noscript img")[0].get("src")) + + info = ible.select("div.category-landing-projects-ible-info")[0] + title = info.select("a.ible-title")[0].text + author = info.select("span.ible-author a")[0].text + author_link = info.select("span.ible-author a")[0].get("href") + channel = info.select("span.ible-channel a")[0].text + channel_link = info.select("span.ible-channel a")[0].get("href") + + stats = ible.select("span.ible-stats-right-col")[0] + views = 0 + if stats.select("span.ible-views") != []: + views = stats.select("span.ible-views")[0].text + favorites = 0 + if stats.select("span.ible-favorites") != []: + favorites = stats.select("span.ible-favorites")[0].text + + ibles.append([link, img, title, author, author_link, channel, channel_link, views, favorites]) + + contests = [] + for contest in soup.select("div.category-landing-contests-list div.category-landing-contests-item"): + link = contest.a["href"] + img = proxy(contest.select("a noscript img")[0].get("src")) + title = contest.select("a img")[0].get("alt") + + contests.append([link, img, title]) + + return render_template("category.html", data=[name, channels, ibles, contests, path]) + +def project_list(path, head, sort=''): + data = requests.get("https://www.instructables.com" + path) + if data.status_code != 200: + return Response(render_template(str(data.status_code) + ".html"), status=data.status_code) + head = f"{head + ' ' if head != '' else ''}Projects" + sort + path_ = path.rsplit('/', 1)[0] + soup = BeautifulSoup(data.text, "html.parser") + + ibles = [] + for ible in soup.select("div.category-projects-list div.category-projects-ible"): + link = ible.a["href"] + img = proxy(ible.select("a noscript img")[0].get("src")) + + info = ible.select("div.category-projects-ible-info")[0] + title = info.select("strong a.ible-title")[0].text + author = info.select("span.ible-author a")[0].get("href") + author_link = info.select("span.ible-author a")[0].text + channel = info.select("span.ible-channel a")[0].get("href") + channel_link = info.select("span.ible-channel a")[0].text + + stats = ible.select("div.ible-stats")[0] + views = 0 + if stats.select("span.ible-views") != []: + views = stats.select("span.ible-views")[0].text + favorites = 0 + if stats.select("span.ible-favorites") != []: + favorites = stats.select("span.ible-favorites")[0].text + + ibles.append([link, img, title, author, author_link, channel, channel_link, views, favorites]) + + return render_template("projects.html", data=[head, ibles, path_]) + +app = Flask(__name__, template_folder="templates", static_folder="static") + +@app.route('/sitemap/') +def route_sitemap(): + data = requests.get(f"https://www.instructables.com/sitemap/") + if data.status_code != 200: + return Response(render_template(str(data.status_code) + ".html"), status=data.status_code) + + soup = BeautifulSoup(data.text, "html.parser") + + main = soup.select("div.sitemap-content")[0] + + groups = [] + for group in main.select("div.group-section"): + category = group.select("h2 a")[0].text + category_link = group.select("h2 a")[0].get("href") + channels = [] + for li in group.select("ul.sitemap-listing li"): + channel = li.a.text + channel_link = li.a["href"] + channels.append([channel, channel_link]) + groups.append([category, category_link, channels]) + + return render_template("sitemap.html", data=groups) + +@app.route('/contest/archive/') +def route_contest_archive(): + page = 1 + if request.args.get("page") != None: + page = request.args.get("page") + data = requests.get(f"https://www.instructables.com/contest/archive/?page={page}") + if data.status_code != 200: + return Response(render_template(str(data.status_code) + ".html"), status=data.status_code) + + soup = BeautifulSoup(data.text, "html.parser") + + main = soup.select("div#contest-archive-wrapper")[0] + + contest_count = main.select("p.contest-count")[0].text + + contest_list = [] + for index, year in enumerate(main.select("div.contest-archive-list h2")): + year_list = main.select("div.contest-archive-list div.contest-archive-list-year")[index] + year_name = year.text + month_list = [] + for month in year_list.select("div.contest-archive-list-month"): + month_name = month.select("h3")[0].text + month_contest_list = [] + for p in month.select("p"): + date = p.select("span")[0].text + link = p.select("a")[0].get("href") + title = p.select("a")[0].text + month_contest_list.append([date, link, title]) + month_list.append([month_name, month_contest_list]) + contest_list.append([year_name, month_list]) + + pagination = main.select("nav.pagination ul.pagination")[0] + + return render_template("archives.html", data=[page, contest_count, pagination, contest_list]) + + +@app.route('/contest//') +def route_contest(contest): + data = requests.get(f"https://www.instructables.com/contest/{contest}/") + if data.status_code != 200: + return Response(render_template(str(data.status_code) + ".html"), status=data.status_code) + + soup = BeautifulSoup(data.text, "html.parser") + + title = soup.select("meta[property=\"og:title\"]")[0].get("content") + + body = soup.select("div#contest-wrapper")[0] + + img = proxy(body.select("div#contest-masthead img")[0].get("src")) + + entry_count = body.select("li.entries-nav-btn")[0].text.split(' ')[0] + prizes = body.select("li.prizes-nav-btn")[0].text.split(' ')[0] + + info = body.select("div.contest-body-column-left")[0] + info.select("div#site-announcements-page")[0].decompose() + info.select("h3")[0].decompose() + info.select("div#contest-body-nav")[0].decompose() + info = str(info).replace("https://www.instructables.com", '') + + entries = body.select("span.contest-entity-count")[0].text + + entry_list = [] + for entry in body.select("div.contest-entries-list div.contest-entries-list-ible"): + link = entry.a["href"] + entry_img = proxy(entry.select("a noscript img")[0].get("src")) + entry_title = entry.select("a.ible-title")[0].text + author = entry.select("div span.ible-author a")[0].text + author_link = entry.select("div span.ible-author a")[0].get("href") + channel = entry.select("div span.ible-channel a")[0].text + channel_link = entry.select("div span.ible-channel a")[0].get("href") + views = entry.select(".ible-views")[0].text + + entry_list.append([link, entry_img, entry_title, author, author_link, channel, channel_link, views]) + + return render_template("contest.html", data=[title, img, entry_count, prizes, info, entry_list]) + + +@app.route('/contest/') +def route_contests(): + data = requests.get("https://www.instructables.com/contest/") + if data.status_code != 200: + return Response(render_template(str(data.status_code) + ".html"), status=data.status_code) + + soup = BeautifulSoup(data.text, "html.parser") + + contest_count = str(soup.select("p.contest-count")[0]) + + contests = [] + for contest in soup.select("div#cur-contests div.row-fluid div.contest-banner"): + link = contest.select("div.contest-banner-inner a")[0].get("href") + img = proxy(contest.select("div.contest-banner-inner a img")[0].get("src")) + alt = contest.select("div.contest-banner-inner a img")[0].get("alt") + deadline = contest.select("span.contest-meta-deadline")[0].get("data-deadline") + prizes = contest.select("span.contest-meta-count")[0].text + entries = contest.select("span.contest-meta-count")[1].text + + contests.append([link, img, alt, deadline, prizes, entries]) + + closed = [] + for display in soup.select("div.contest-winner-display"): + link = display.select("div.contest-banner-inner a")[0].get("href") + img = proxy(display.select("div.contest-banner-inner a img")[0].get("src")) + alt = display.select("div.contest-banner-inner a img")[0].get("alt") + featured_items = [] + for featured_item in display.select("ul.featured-items li"): + item_link = featured_item.select("div.ible-thumb a")[0].get("href") + item_img = proxy(featured_item.select("div.ible-thumb a img")[0].get("src")) + item_title = featured_item.select("a.title")[0].text + item_author = featured_item.select("a.author")[0].text + item_author_link = featured_item.select("a.author")[0].get("href") + + featured_items.append([item_link, item_img, item_title, item_author, item_author_link]) + closed.append([link, img, alt, featured_items]) + + return render_template("contests.html", data=[contest_count, contests, closed]) + +@app.route('///projects/') +def route_channel_projects(category, channel): + return project_list(f"/{category}/{channel}/projects/", channel.title()) + +@app.route('///projects//') +def route_channel_projects_sort(category, channel, sort): + return project_list(f"/{category}/{channel}/projects/{sort}", channel.title(), " Sorted by " + sort.title()) + +@app.route('//projects/') +def route_category_projects(category): + return project_list(f"/{category}/projects/", category.title()) + +@app.route('//projects//') +def route_category_projects_sort(category, sort): + return project_list(f"/{category}/projects/{sort}", category.title(), " Sorted by " + sort.title()) + +@app.route('/projects/') +def route_projects(): + return project_list("/projects/", '') + +@app.route('/projects//') +def route_projects_sort(sort): + return project_list(f"/projects/{sort}", '', " Sorted by " + sort.title()) + +@app.route('/circuits/') +def route_circuits(): + return category_page("/circuits/", "Circuits") + +@app.route('/workshop/') +def route_workshop(): + return category_page("/workshop/", "Workshop") + +@app.route('/craft/') +def route_craft(): + return category_page("/craft/", "Craft") + +@app.route('/cooking/') +def route_cooking(): + return category_page("/cooking/", "Cooking") + +@app.route('/living/') +def route_living(): + return category_page("/living/", "Living") + +@app.route('/outside/') +def route_outside(): + return category_page("/outside/", "Outside") + +@app.route('/teachers/') +def route_teachers(): + return category_page("/teachers/", "Teachers", True) + +@app.route('/member//instructables/') +def route_member_instructables(member): + data = requests.get(f"https://www.instructables.com/member/{member}/instructables") + if data.status_code != 200: + return Response(render_template(str(data.status_code) + ".html"), status=data.status_code) + + soup = BeautifulSoup(data.text, "html.parser") + + header = soup.select(".profile-header.profile-header-social")[0] + header_content = member_header(header) + + ibles = soup.select("ul.ible-list-items")[0] + ible_list = [] + for ible in ibles.select("li"): + link = ible.select("div.thumbnail-image")[0].a.get("href") + img = proxy(ible.select("div.thumbnail-image a noscript img")[0].get("src")) + title = ible.select("div.caption-inner a.title")[0].text + + stats = ible.select("div.ible-stats-right-col")[0] + views = 0 + if stats.select("span.ible-views") != []: + views = stats.select("span.ible-views")[0].text + favorites = 0 + if stats.select("span.ible-favorites") != []: + favorites = stats.select("span.ible-favorites")[0].text + + ible_list.append([link, img, title, views, favorites]) + + return render_template("member-instructables.html", data=header_content + [ible_list]) + + +@app.route('/member//') +def route_member(member): + data = requests.get(f"https://www.instructables.com/member/{member}/") + if data.status_code != 200: + return Response(render_template(str(data.status_code) + ".html"), status=data.status_code) + + soup = BeautifulSoup(data.text, "html.parser") + + header = soup.select(".profile-header.profile-header-social")[0] + header_content = member_header(header) + + body = soup.select("div.member-profile-body")[0] + + ible_list = body.select(".boxed-content.promoted-content") + + ible_list_title = "" + ibles = [] + + if ible_list != []: + ible_list = ible_list[0] + ible_list_title = ible_list.select("h2.module-title")[0].text + for ible in ible_list.select("ul.promoted-items li"): + ible_title = ible.get("data-title") + ible_link = ible.select("div.image-wrapper")[0].a.get("href") + ible_img = proxy(ible.select("div.image-wrapper a img")[0].get("src")) + + ibles.append([ible_title, ible_link, ible_img]) + + + ach_list = body.select("div.two-col-section div.right-col-section.centered-sidebar div.boxed-content.about-me") + + ach_list_title = "" + achs = [] + + if len(ach_list) > 1: + ach_list = ach_list[1] + ach_list_title = ach_list.select("h2.module-title")[0].text + for ach in ach_list.select("div.achievements-section.main-achievements.contest-achievements div.achievement-item:not(.two-column-filler)"): + ach_title = ach.select("div.achievement-info span.achievement-title")[0].text + ach_desc = ach.select("div.achievement-info span.achievement-description")[0].text + achs.append([ach_title, ach_desc]) + + return render_template("member.html", data=header_content + [ible_list_title, ibles, ach_list_title, achs]) + + +@app.route('/
/') +def route_article(article): + data = requests.get(f"https://www.instructables.com/{article}/") + if data.status_code != 200: + return Response(render_template(str(data.status_code) + ".html"), status=data.status_code) + + soup = BeautifulSoup(data.text, "html.parser") + + try: + header = soup.select("header") + if len(header) < 2 and soup.select("title")[0].text.contains("Pending Review"): + return render_template("article-review.html") + else: + header = header[1] + title = header.find("h1").text + + byline = header.select("div.sub-header div.header-byline")[0] + author = byline.select("a")[0].text + author_link = byline.select("a")[0].get("href") + category = byline.select("a")[1].text + category_link = byline.select("a")[1].get("href") + channel = byline.select("a")[2].text + channel_link = byline.select("a")[2].get("href") + + stats = header.select("div.sub-header div.header-stats")[0] + views = stats.select(".view-count")[0].text + favorites = 0 + if stats.select(".favorite-count") != []: + favorites = stats.select(".favorite-count")[0].text + + if soup.select("div.main-content") != []: + ## Instructables + body = soup.select("div.main-content")[0] + + steps = [] + for step in body.select("section.step"): + step_title = step.select("h2")[0].text + step_imgs = [] + for img in step.select("div.no-js-photoset img"): + step_imgs.append([proxy(img.get("src")), img.get("alt")]) + + step_text = str(step.select("div.step-body")[0]) + step_text = step_text.replace("https://content.instructables.com", "/proxy/?url=https://content.instructables.com") + steps.append([step_title, step_imgs, step_text]) + + comments = body.select("section.discussion")[0] + + comment_count = comments.select("h2")[0].text + comment_list = comments.select("div.posts") + + comments_list = [] + if comment_list != []: + comment_list = comment_list[0] + comments_list = [] + replies_used = 0 + for comment in comment_list.select(".post.js-comment:not(.reply)"): + comment_votes = comment.select(".votes")[0].text + comment_author_img_src = proxy(comment.select(".avatar a noscript img")[0].get("src")) + comment_author_img_alt = comment.select(".avatar a noscript img")[0].get("alt") + comment_author = comment.select(".posted-by a")[0].text + comment_author_link = comment.select(".posted-by a")[0].get("href") + comment_date = comment.select(".posted-by p.posted-date")[0].text + comment_text = comment.select("div.text p")[0] + comment_reply_count = comment.select("button.js-show-replies") + if comment_reply_count != []: + comment_reply_count = comment_reply_count[0].get("data-num-hidden") + else: + comment_reply_count = 0 + reply_list = [] + for index, reply in enumerate(comment_list.select(".post.js-comment:not(.reply) ~ .post.js-comment.reply.hide:has(~.post.js-comment:not(.reply))")[replies_used:int(comment_reply_count) + replies_used]): + reply_votes = reply.select(".votes")[0].text + reply_author_img_src = proxy(reply.select(".avatar a noscript img")[0].get("src")) + reply_author_img_alt = reply.select(".avatar a noscript img")[0].get("alt") + reply_author = reply.select(".posted-by a")[0].text + reply_author_link = reply.select(".posted-by a")[0].get("href") + reply_date = reply.select(".posted-by p.posted-date")[0].text + reply_text = reply.select("div.text p")[0] + + reply_list.append([reply_votes, reply_author_img_src, reply_author_img_alt, reply_author, reply_author_link, reply_date, reply_text]) + replies_used += 1 + + comments_list.append([comment_votes, comment_author_img_src, comment_author_img_alt, comment_author, comment_author_link, comment_date, comment_text, comment_reply_count, reply_list]) + return render_template("article.html", data=[title, author, author_link, category, category_link, channel, channel_link, views, favorites, steps, comment_count, comments_list], enumerate=enumerate) + else: + ## Collections + thumbnails = [] + for thumbnail in soup.select("ul#thumbnails-list li"): + text = link = img = thumbnail_title = thumbnail_author = thumbnail_author_link = thumbnail_channel = thumbnail_channel_link = '' + + if thumbnail.select("div.thumbnail > p") != []: + text = thumbnail.select("div.thumbnail > p")[0] + if thumbnail.select("div.thumbnail div.thumbnail-image"): + link = thumbnail.select("div.thumbnail div.thumbnail-image a")[0].get("href") + img = proxy(thumbnail.select("div.thumbnail div.thumbnail-image a img")[0].get("src")) + thumbnail_title = thumbnail.select("div.thumbnail div.thumbnail-info h3.title a")[0].text + thumbnail_author = thumbnail.select("div.thumbnail div.thumbnail-info span.author a")[0].text + thumbnail_author_link = thumbnail.select("div.thumbnail div.thumbnail-info span.author a")[0].get("href") + thumbnail_channel = thumbnail.select("div.thumbnail div.thumbnail-info span.origin a")[0].text + thumbnail_channel_link = thumbnail.select("div.thumbnail div.thumbnail-info span.origin a")[0].get("href") + thumbnails.append([text, link, img, thumbnail_title, thumbnail_author, thumbnail_author_link, thumbnail_channel, thumbnail_channel_link]) + + + return render_template("collection.html", data=[title, author, author_link, category, category_link, channel, channel_link, views, favorites, thumbnails]) + + except Exception: + print_exc() + return Response(render_template("404.html"), status=404) + +@app.route('///') +def route_channel_redirect(category, channel): + if category == "circuits" or category == "workshop" or category == "craft" or category == "cooking" or category == "living" or category == "outside" or category == "teachers": + return redirect(f"/{category}/{channel}/projects/", 307) + else: + return Response(render_template("404.html"), status=404) + +@app.route('/') +def route_explore(): + data = requests.get("https://www.instructables.com/") + if data.status_code != 200: + return Response(render_template(str(data.status_code) + ".html"), status=data.status_code) + + soup = BeautifulSoup(data.text, "html.parser") + + explore = soup.select(".home-content-explore-wrap")[0] + + title = explore.select("h2")[0].text + + circuits = explore_lists(explore.select(".home-content-explore-category-circuits")[0]) + workshop = explore_lists(explore.select(".home-content-explore-category-workshop")[0]) + craft = explore_lists(explore.select(".home-content-explore-category-craft")[0]) + cooking = explore_lists(explore.select(".home-content-explore-category-cooking")[0]) + living = explore_lists(explore.select(".home-content-explore-category-living")[0]) + outside = explore_lists(explore.select(".home-content-explore-category-outside")[0]) + teachers = explore_lists(explore.select(".home-content-explore-category-teachers")[0]) + + return render_template("index.html", data=[title, circuits, workshop, craft, cooking, living, outside, teachers]) + + +@app.route('/proxy/') +def route_proxy(): + url = request.args.get("url") + if url != None: + if url.startswith("https://cdn.instructables.com/") or url.startswith("https://content.instructables.com/"): + data = requests.get(unquote(url)) + return Response(data.content, content_type=data.headers["content-type"]) + else: + return Response(render_template("400.html"), status=400) + else: + return Response(render_template("400.html"), status=400) + +@app.errorhandler(404) +def not_found(e): + return render_template("404.html") + +if __name__ == '__main__': + app.run(port=8002) diff --git a/static/img/favicon.png b/static/img/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..a1beeed10fc7cf266f955e512a4081699ea51568 GIT binary patch literal 4245 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 literal 0 HcmV?d00001 diff --git a/static/img/favicon.xcf b/static/img/favicon.xcf new file mode 100644 index 0000000000000000000000000000000000000000..8f941adb13239eb72235237d6105248e35c26844 GIT binary patch literal 10043 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≻eiN|0%%qV!K!ZQ_~spvDBrVZnh zcWX$9awj{rz6q|^$|mYqRL6+g@6a-)mY7)3qGiQzpS}0REFZa;Xc@VB|9y|0d3i}k z>ce%_aeMGr-7~dd&>}%!CZF@)B8Kw>F}$b36U?l$!V{QnONA$x&098`9*OSu)+1hY zGPi+|qU(bp28$LuMl@%=qkW7aYYP)Bb3s;Kymz+UpLB06%kt98XCAw6|J5Tc2_1@S z7W!>jU*g6&%U=FJrF@OYu&5yX2b#5OkxoO(7rq_e7}cX#uF=ggTDwSFPx-pG$G`V% zi?R1`jOFndhd&tO$d(wLTQC=oQ#!AHWcsT!oVB!0D~TCWwMJsQAf~2?n;zSZqEF0i z#DEPd!|w@Vvtm+O>)3+n_cLCzVVaZ?SJ0V+%*2QkDkP$YL{uYY67F=#keP(c;yuJn zLS_;&laQH!e#lH3FbJ6md?I8fU@~MThRg&^h0Fv@hs-3j*vM?6Y?@Gb#SqPdpC)DT zE`FbrlTak!$mC{`Zs3f_N61$=*pRP-lNuGh9ZnXBf6X&^EN30EW5!7mU4zxolfk#;9ds^gWdB1@SzCm&Q! z*7M!Ry;diW8QacBZ)jm#t{I!-n_bNu=;$g)f|TdwfW&H!!{g#?eVb-OXImsVvz&@$ zma~Wc0Fhi#mr=2gWmN2C`FeP!izQVISwh85vhf~g)RO7c#CP`tdB(RaOH!}7ewP68KFOd%w_`4CL{DmFuKeL{Sl}pjHt;7Vq-?sW%$ELNKIy> zUVu$|nCcht;}66=9yQTJ#vVEC+!90eneKV#1^>MxUUsu^>NQVZOq9giEc=$*~={?aQ9em zR;v-&Xl<{xC2P~x`fBVFmnzR&!SYRO7p>iI?KReJ>Bj}$9vB`7OSx3SBZ3a{g$VN4L>ZeqDis z%cQ!F+_#lB3SOEf2FQSVkj!6B8~M*o5hkMM+7>)X_Gue=Pj2R*B+6adgjdNf^(lFH zk~T`cd6~3OiK=VKy&Gv6a{?chUOmr{gX32J{m|;aQqAhWz1mXdSpk^2Um?w!<%#rBzupMMb zY!%bwe~KwiqZJcmhdpc)KFOw{N8+g{B%yH?wpNY(ZrIND;*+vB+n7TZa+S|8{{T!d zPeox~YauQ{h|@5aB+O~3izyCwX~La`yqMy!mnH0J=!+>1e|f^6hQOHOFjyoEYAB3p zvxUSG+ZMWIn+gaO1w(Xeh;9whts%Mv6NH2oLv(A1ZVk~bm?Z3V7@}K4bZdxi!4zSx z#}M5bqFZq2_a=19SVM#)9dpRSu_C%9~*| zYyYjCWB&dfzK-*=X7`TYedG5;t}y;(%6UJ${&5n-i-G;}uNk-Em3E(6X2UoV=sV9R z?3V{Fpyn3+<(=o8SJ=F89%Vk0fUo__7tf=hsRZ5@U0^quq54+$lcpqjhCGFdh)kw% zyy(2IAfHxGltP-+L!QWJJC?x*`BRUAd_tX)eT{5HzLC>*D2F>nuXze`qxvTs5($d@ zRYBWw0ndzn?kULi>Z-cJn?=6dq-}o_xl8o2ry$psv`v+qMkGA~UONU}I|A0EN{@c^ z$X82w{TrK}J!uJV|Np|9)ptYQGvc6>N@5+!NZFDJ-VJ#-%qBtW|ui`Np$) z7*Olj_lzEkt!BpOPwP3ra-w6~{5O~Mzlyw_DE3cXcW~jX%~95q&zDg_ANhY5 zqPHUI5fha^H4|hm6FJnvH>0m*Tlj4BXj?0%^U?jKHYO0=lxXM7CVHg5lQpS5waE*a zO6}oMubb1C+SwbM`?`LkiCjAO%Ck?N VS~z&!RDUs{pXZu|{@Yo@e*vQFz zSLg;YAvko16u5(v=eDx4n#!`WS6$qkt!y1EArQ{kz*tG8W=*Q+8af{k)UmpkT)%0i z@t|m22X9cl{pTaKzUNKfB^E(jC2d1Sf{`arr6V2?(OMfZ84}#xBx)J@s@x}kufB+@ z9JTdjE-=-*_eaAqYe)G4*x9rJ;lk#T) z%s$fZojdT}8f1ERrx~m;=1|C_SiP!;l9Pv&;8_;=*VpB^J7M?A7xrP#)ZGeS%T3LK zeIb0Tq9_NB;Lo9KT`)M1xG3p?t>RBGb3xQmPD9hcw?=!wSStQW5vD`*t>sZd{sG)?f?D#e-ik=T>^q3YNhz}WN_CnH1E^2ZKHA%BbkiabBwhP*sK5<|v~VJTA_ZyEhWfkWS|tYuB~=b971e73?i#QT zjMKj!hRmbs=&&Q@KM~7zG{uh70qN;lR<759tbpL&=Vr}ap%q@WYLB)~-Whz_Tsgh% z7^B&^2Ja!*%LwF}ph;_g|LQ?G^+8+$7zj*0D!Jdht-Acow`Y1Ae_$o()j;Kf#O_u1 zbibE7cE+D|I(rsBFAvzJ&8q19nOcGlH-ne^bYg~p`*Akh(c5pTs5*1~VQP&JfwYMi zGXr;?#1-6?q>!v1y2rB-hNXcF7g^4xKRUH_t-YxZIA&LGI`HG_TD}>P#Dq1U zNN##Bt;%iB$+FDhr#KzTIuye($APc?iqokL(lb9bOu@MSJh{VTEZUlL=E!Aw zRpW3tw8o)Lfceqin{zJiuUWRkX4>mc^|#U9)h;u}6vaS}z+^};Cj}a{&VN@I;5F~O z{JPH4e0qIml9!=oTV$8n=FdY?MtjEgvX1|(1AUn@!>HbyVt#_T{rHq;W{;S5V`>hX zbiOy4VN3aoUNuFyXIelq19aUFZVpKwZ#^5ESfb5*FvfB2Bw!FV(g(b}>vex0G1KiN`m0d|Hy#wZJr%j|^gmxwYhau|M=(ME^+t~X2n`RXF&fx#v%2nci4luAZcL^-K`mF>VF%UNl?HM zUQo#qMeEug_VF&u=G*|S&(*(fGH1K_dBm98(70>WltH0>Cj~9v-oMS=)neOXT`z#W z$#8eUwX7Q)tZ=7_?8l`zX#t;UxwiQeuhD*)j)f*SrX+LOlumg39|Iupb7zwnsFy+zx7cO*6cF6HgLo;SVg zE%S!Ai{hSB1Usub4D>_1Sn^}N#73wvts zOPPphi!h6wJ?7??uZZMvPlO^9xPdG7sejCulwOliRweQIcdbd3QXT(r{ga%;wBDyf zIRj!}ld^I1gh99#^>b8zq!V29&0O%{bErmM7f(0q(V8O;q-nOx%b|h$I z`zt}#!UEF^44*WtWp|fSJFwWp%)br@bZDpT3MAbW zaTTrL?e226wbI?Wd+!a!?Sh)HqzYjz7aQr9wE;6KALbePxN|BT*oiu>Z}q< zs*1lq`KuYNwIEim79?M!cISNoR*>atR(kzBMgVogEgu&P&yRmW#D9y@Ks>mMaAt6R z{EI;TXE)J%>F|WG*-EIe$8(2N!HS^ju<(#4k@8|lS+p|}OW2sA#3rmS`Um#(k8b>? zg<|mxi4rm%Z{cN9JxXbuic@8OgRBeLXS*Ec#eG9LjvY?}`jRcGst22fhGGizJkE}_ z(}PYzejkC^;m`;@D-+iHdVD*fDX*rWY924%FnfIa3ya%_e0e;7ubAq=CWY|h?YIxN z1YjbTyuh8?aqd70t$vGGEKv|e^5DqrwY4=tAt6@`8mez#AnNh!5)%^>6&01Hp`og+ zEmxUU_q+P~)OYWsh$vJErejsvJ1N6jHVl0KF6Eo&*~V(o&dlUw*ZNdlZc)+3?iP4J zd1Yn)_;?5S2>P+GF#FS|@Y2%KY3AD`2MrdteXS9(K4pqvQ|2kp7dg~}bwt1MTRCBo z^h-z+_b#;^$d(ev27<>~4`i#YPS(V~SB%-C#;fO=YGhN^o_4`Uvf1)@oh9M1WF}?S zR#dz)s`Kg_9UWae>*%<*(Dk}Gl1_k=hbJpD6B!*%9?dBFkBZ90-5Ef!Hk;EBErKT)p~HfdiCn5U1n)%+y|8;uji1c zs3@cNcz($V)6C}vtQ_}ZY&+dsP(-B6ye+zLhL+!0RbKw2By}_p6;O5TF&0VTUBPCc``)eV#$@P_s zOh9+0_;w!}8A;}oSrryFHm2|G?d4bgarkjrpT)}-nbg$OjHDmx>RL(uFsTG0tc~LQ z=1|o1?+0yK{jQ%whro=adtVE1a70-Zij*~+{pT5P`gfv-hlk@EHtA}XLaqPnw`Lx{ zzP~NZxOnlRd%wbhpTI=?9|N|1<^lek8ft33j2FH=@@6a@=swQCYg`=!#TNCRk+w+) z{$2Br0WiYs>}+9145MgVyn+C@+&FjwI4GQl=*LJ7>_qqX_k#%}PX2EG@86ylFERlm zSw8Z`o}G%?3c5GEUfZoQ`q!>$BVwz`etEqM_Vx80=_Mm0E3c>^vw!30=qM;C_;A0=F<64sZ}sm#efqSr z2(EOR8Ksbym4yMtXsC?$Li|-2dS7^nFk;&7I@|nvIm8wW)7N#}(b4g!+(v6;W1`C0 zdZsZ@)(-ct!%Snt%D#LAjh3$NuzQK(zyvF9<(cq}lU@R;izhmAJQawz?Ll$maF@89 zon5ksGcyEiyu(zj=XX{pJw5$sttU750MhkFBqk*#B|V}dyGr(4xbJ5(LTb<+7-`nW zDc55YoG*v9=weuqb7}i(X=1O&$MtXAxKaBLlGmWJ*mGq#pM%iB-rjs=xWHko^h4f` ziI&z~V52%Y>fMIF?`%>mtpU&6aZ!io^8p;b0Wr>S9pOf5z&6Un;%gf6j^YblwQboIxZ$16;<(~@|F8uoS3-c|O zwYDmg-!=7rg<0MSt}o)p`oJsPKTH3IjonEkWh>>IZFn5dP0wT=y&oFAAtoOc4J8} zSm5DB3<8zo5}YLnOana5IG`p_tKE`sYj7?54{Y;4j%R*5PEF82U>=PlNFj;TJQ%C9)MFakJGg~KTYD;)Gnpt-K-SH#)lL^ zuj@(ug%j3InpM7?t@ zD;H~Dgt0JXn;-9@k_8C&awQPJ2&o<|XeB0mJ#H06+sZzY_fPChOO%=i$Om8>)}xTB zmxJH2xy|Jr%O1oxZ4>sawdLxmM+r~DdPS6BHwT>;SXe$+p1T=AQx+oQ*!j{Km~qGB z8X^goMO!>h_Sz9-MUeI+O(D~iQl*<6F${qf>#gKmvMP{UzRDZ>8$`UWIBG{ zTsL0K5yU?(XHh)U1o@U5m|-5;VA2D*m^PN{o?;Ea`o<4sv2%0&ge%UFPijU zA%WV7}-b?$WAB2rE>5B}sCL~~$Avm(xLunMX2c9W}rHRyt^dO7v+dHOnGPdIA- znDpgf=v(ryCc)t|?qj(9ha!XW1SA=%Sv$WPAgknBws?}T=BDm6oecsArw%v&ULt0~ zM)EG^7Cve6{7BRa>*JhWi1O(p$!Bfr?4H--R2dXL;$#bh>@H^@55dOdz_$!5xua2D z7&7$5F$YCCo^@*@OZXOnc05I+)urPxNIk6jDuQ{lSY=LDjwc4y{{Fj@(B}5F{KtWE zslQxjWYH0NQbD$Ig9oI*y93WsNKtLyex@BT6N4%ue-~vP!VqGf^T>nH<)IwaUTO4- zGTDAGzvfM}+AoRTU|YEWPsn>(wkR@b*q&TVutG~6>*LSF75=-ISeo7@?UyF?Uz4dU z+}>1)%L)+sdO6Z&vFY-`KZy|=MAMSbD%T@WP54rt;qpna;EMrQPpiK!Io4afRGT5I zpLu)MU;PkTe=CsnG-dj`uZl*o$m9z*@-q_cva@|^QbPfx<3UO&a7lEKk9Gd!x=@kW z6j48tT(i@(vK1D*J;($y3N14BJI3VdvHLIV9mF1~>qY6bbxej`%jaX(S-2@(E;k7k z`MZK{G!bI~MC;c)3*VtnRE6sxSBube7g;gv5p(j!qXq|TgWD4_~1 zS;p$P%;q*EIg<&S7qTBqUpW5Ix(5GIYGZ@b4GyWpJoyn?p3!6Odj=Vj4=!Sv;WNz3 zRtha)r~_Frd;S&cK4o+j;g~Ay68a(HT{B0h;5Dk37ig1UEO_J}_~pmxh$W;@HzCKgjK@Gc5UFqqv~UL zcH?0G#JeWwi?!Rn@ID%N6NLkw6}h>PG2R*7!3xS|tNogq&DX3KJ>KL$W*FcM17>In z)^~gno}`|HXJn`K6zM2b2E@6?%_xryIL|mJx@G;O33|Y-J)=#g-zYDP2BkVmRP*X_aBo{}vX_s=8b;5NPHv(CO`*{Wy4p zR?P&3!p7!Jp-?fXY>3$?O>a>*W!^*orQdz!L8nJsmF-M23nx&9f#8S~ zi3ANs==Ua# z+b!;AWv(XelgcJ}enXzDC9+HI&cU=cQeWSB!xrUQ6xU8v= z&42Tc-5F9^344a;!h@J*f>-F(Z&bvc+JxX?E~o(7Z+opd!LN4Fl-gyNvphg(pE3V& zch?l_;B*_ZEF@=3B3OB@*Wsvf5)%mT8|*Y6jz7f@&bu<%l6#Kl=f0`QnDH_En%hP` zU#gUryev)!XF{(?PR4D^EiU105JoGuP|}51U{{Ogm>KuQI*3*doO*2_}pPyd-4s=aYT;7G{-mRM1)^iJsMRFo+ZMN+X<% zYlOqaMMcFh(XDx-@z*Ks=BYLF2Z~u#p#XJ8!V4>ku~1I{P+K1`~$5 z;{;aN@8UXY6}&EbK!(YN+pWFHf9<=2zbxoOwkA>Hw`&)1GkmznBqL&EG0RL}qxTTT z2QyyK>KqTka4g+44U}6gWdj8XPMkf(Ksu4`?vp+I{f>as_+@#996~qfvfI1=I`#-T zM!ajkj0?5MAEj1SO#6%{4E?eO?mtB_&W{AjdY-6sxHs98ykOh$2#H1=_$pl8G?im4 z`LkO#*U-VPy`K%e&ZIx5yvAC-y{-nSdi2ChM`pj145TihK%{MwLjXR~K}dTtAQ=n` zo@Y-6MX+9H*XJ9458>T=Q5g@?ONBsP85 zMIXTeEWi(w;PQ69gM)j6F%Q-%QM!~ma z#7L_-5X(40;Yb@x^C5sk+^=3#Y%x<0dZQ+HACi<)HAjyjrBH5K=mUXCIvPcTvq2m; z(cn9BiP>L6&V%&B*6$POjQWgHFH7-_IpI#(!J_mXq7ww`rO_*J#lKI_BH=?^P&Ud7 zDsD`C>Nrj^37v$)3DCYvS26q{+A-r?(jq+Pre>+iS$z#5Y=HQ~Gf)m_@{jDB@`>#>KnoQv4tz-Ro5`&V7ABqz>q^P)*9Tr=1QPCY`J;jvxB_~}X} zpU-n?z(AIvIAQ7xUv8|^|5$U3L4}6ol?Hq}4l!|cBR;uj`bn`-W8ha%&F542R=md* z7iT#MdtiGwZZ=Az6R235qS+e}(yNea1fo&Fjyyy|UXH}YOo79@Mk6Gs`|U<2nG*^H zO9X^3V0-%yioJ+q9It#>=`JFYdOcs7hj^^LXnO(Pm)I9_9VCuP+0Dq2TjhP1DDN)J z6N)2d7wL8bZX%)|{S}$HDnMaQ^j~hA`6dXTl9;k+#(ph*kX7imX~7~i3j6Cb+&s_~ zKJA~+sJ;iDA!U~a1jJw=ZHhiY#D4_2+#HbULmY#hwsG)~W7Nn?}lb_r6r>O!LVQ=mXYa*@L47J~WnA?(jak=4n|2Al+%o3ad3 zm`dxCc|*Rlk^;jSAjpP>0e|`c&6eR6n4-Rah=cA=!77R1TMR0oLh=u0ekr*3s^^FJ z!z^~KG-bPY1GeZ+rEXU@Mjw(pq~&--%4)T>LjK!1(o<}A)W6N?i<(xTV(Fi7H zbYwtEchtWJBnm;B z)tWdJ<5f$5aKV0h*D&yn#tbkyp9DT>t|(6xr6?~yp*uSx9rFFDsMP6WyjX#xu5^HNTRy033j&JS8Vp{0o% zm?CIg`%mwXUiQmjQ8nY=QKO1rw0!nmwbY|T0Q5>w2l5J6i*F&K^$}_^I|Cam4K)Z6A_47QRk_eXVy zI>V-o80kTfoP?!vLYrO44AeMj{Uqj4Is{FjWF+jB6siF%5sD;*kU|tVA%HWMM~63u zQZ(&;NHRWZj{LEeddKw%pzPfaHqG?)^?iOkr?s)Od-wkR`>F@sYQgU%?ZE0KFwP-W z^QOBdFsW1}HhRUShN^_YCA_?W5VT%nIH84g?LY~%S{jkU6V_Vu4@LoGn7ea6(v1`+ zw41ybROo|$&%x~yge|`P75X@2grYnxJDWW{_{y*%W2`XQ68=*ouo?VD zsKeGx6++1Qa8UG3d`P>6o6x4Rc9K3r9IZ(}EUug|I(krp^1*U?ofW~((t>(kJ3Bk) zqavu`WYjk5)|2xMKA_;NN(eD zX(h!07e^7_X6p4m&f3d96r0&3)f-rnlZ!4*8nAzl_w6?1+sKctc8l%#rD%Q2UaYv^ zZ%)8Gua1_Oe_t^vw}A@@38|)y+t1WMt0e$0>Bm^_oCKi-!*m7`kShV_Dk9ecRe1+@ zfda%vqw;wK)Td(fE&y-yXGOm_{*F)zInb(=olvv4=dSZwdsk9&jC}V0UE7&3U+8|L z_@}Vb;Mhut8J9!!-RhZc<5o6$XHV_Iq&d%h+nx#C3lIzA-j!PEs<@P!kSo84|6 zqND-j2a5HPnSil_LFgb?ASYgsm%Yt%@$ElZpjG?l6(Pk1{O5y%gPKN0YRby(Tq6^p zko2brWVHX_{{3Q`zIToPAPqo~Ogd&~f4AolR;YnB_{lA3ABsH3U8mx?*L=h`t+UvV zbT%p>SeDHPD%W6;a~TstIK;pCic3hnR!xM|h4iN%@BSXp@DAKDxqrW9_G?6LemK>e`PhpO6Y-^ z+@RiNOEbDnTC@R={O}pmfq4C6B>PLQM47ljMWPzy;9*?T&<<1Kk1cVp)y#r|f}$T= zuh6vd?G}YE+L{`y7g;h33kwYvgF&6@{{8!;pF+THWUIdZGF)I_3v|7tBsMPYGT^C4 z-TV4~?*&L{;31n+PlVr~l3|9i~V!BHxR) zU{HbS?(Uva5acng_xYZNRi~z=W)$~|1Z4$}=gei+z3XegLn#(iAm3H47H16FvdySt z(|v0`=ONupoKE$L>%{}b0ZH+V7++YSMN37{>;WDN!L&6Oq2sMhON<1DGD{&t*|$>% zBB;+oC`oWt!{-9}SH}<+9<^t5_%D#@u7ytz(_&qr_}(9^nr%qJoKA?>JxGJ9laNE3qkovqI#(Fs|u z&P-PQPhyC&w)1wN&&iPFhK;lHhH$Kzr{``lO*yFha6>*873EY`R#xR*GbB?jhy1sU z=?5*%DOdYPz|#}?++-p?xrzuf58K|8JStL(BJbO*2_NLT7yOhGzXJy;LVg}^9?TrI z=7Tz)!JXJ{2P|y)!>a^MJ-sm@qJMOBC;@}t;5b6du}#U3Anyb4rUu>NLA0-r@i0k0 zO*xO0oCtm1zUqA~jQJgH_J=fawPLE*b9L}=5mEc>9z~4w`^D2b?~V1Nn(S;V&E_+I z9X&l|cX#)ZsyW=pypTJrK$fDB&2uJWJJG;zdYnpF_oSu{dVz|K5fqdti2N=>jF$v9 ztMEb^JTjy)4(jUcKBBqK=%HZiY~VTfP)c|!AbL>3T_-O`F<7F-Ft9|#;)M__Sr^oa z)zelVho2IX*uQKZ4ERm=jr(m!^w9}e{md$Uz0!@VN%lJvfU>&=G-CxMW1Yw(y$@2< zvGkVxqIe6>QPt8+g!wTjqePpoPwL)hGqzj`gG5ScFT5Buokek_!VX1BGoKUZ4;Mqp znU;@gif@Xr+W##Veh6wt0tBzh;i=5ONb#-5;Yi8|(}=Pul{5eH&SMeFoA~&Cef%H_ zIe}zKl*OT$00H=mwyOPA2k(CuG6TB|ovr*OD^;FS;2URwgkwk9=z%G9@r%K#kIM!N zOLzd4hei5HQNg986?1li8C(W@#xN^IP3fRkqzynwzFrw8FYgCX+>s3>?*m~8$aJ*I znMK@t{paxv_RMFjpb8SNOmOQ3F4(cnzY4o4aVD}vF+3%lc`D#e7%St>TTjSXRZ_ijg)iF7FVuI))q*jY`H#eFG=X$IsRW^XdFZ zjc!C8*a9TuDkotb_Ts_fvtV<(Q7rXC!_U`~48MGNei1m-tnzXXrIj~go;TbVdo0%{ zYd{q~(!Qu6(Ys;A2o!0H-51rF{eE2w+K$hN{BXIR;11r2s5&VpZffbcscpcFGkg_o zZEYVyzW4XX#X~YdSAl_lphJ=ZJLw$-0B`RhFA9j&bQWm;Z^x%t zpRL(x^=^7EaAQJZ;`Vk@Qz~u-Q+3{DfyZ8P?QGsNf%}g*+r>k4F}(pPIGatNz^Spn z|MV^U;rv1sruCVojKiHpieoA8Ta$tR7#N=ClFfUdCa=)f(Ysj-6v>XSj$Vld%P7j7 zNW4#{kZVy%KYhBvSGRiGsqxS%Jz!h8 zaVyHX*nZ@rarrC^9fYBjGO`3ZPrtDp9Tq;o;%9#*;=Vp(-5^o>h|gm?~SvMlRywww9Kb^ThiSIzq;r<0ZTSq#VG# zQn|`IgLO+_7MUQ2Q3{3b%RQs~^udt)ggRHHC#o8hr%N7xyv17;RP)ZJUpL@9d0T&Hk(2U%in@pecpTHhYuC?^yom(hOE=a zw{JsgYsGF)EWM>}lBd_eWgi)QH;JkdAp4+yWcunzD5i0d0_bhIDmvwUws`zS#o8YX z&h@cE_QN=BaeY|+TENr`c38wT?D_ zesB8kycFmW;-PDvo0ypJ-TX{cmss=bUBn9zWmsb*`ge~3gdg8w*j@yXwAlL-!07<` zSWj00EAU!XGFqqsohzW-1h|u1zl8VJCfd&9zecxNwM`#_hVP`H_n-6yf4qn1yh8%PVteTA_lnV^A3ktHwzbtP({335VV(nwE% z>zZ8dmZM|FUdIrsldENe9vo16rDtFOXu}#rR}kSHH2?G;GzR)VTnwJB_ia%7SaGd^ zsUrh&b;s=~;6muk&>tfEFKxK~9uou+q7vm(E^rBj>h`Vf4ZhQ#hUG4*kE^gBaqAIX z{HX?4QBl!r@Dl>TZqjqaXfhak$~ty(jLd+UY+cVU>48J&3n&z$A>SxL4cbVQ5vqx| z$_@~>ySu;}VaH{Cr?FDYW?_k-&O~0LrXocb!KoLg5C4G3^qF!YND{~8Fq314^qaxhzeMPvuY;*G-CvGo zRI+E^nr_p`w54@s#&4y-&~xZP^Y)pfRR$Xw*(C58lR?L8tYfQQ+B=I5Tb4I)C=(5l znY#f|@{Z)_+B~qhU31hDM(M4VBA6>7fInJcPYarerVJbB@x~N1@&HGIL)$hIgb)IB%(pAd!M}fLr3W1? z6{@>}_JIgme&)&_Th?iwH0Qk_4lox-MC`9`p1d3NJ3UotUgNgdN1B?Ni8W2Q_NcdQ zrab!PC|oDXvF9Zp70`h*VO`=^e+DL1P0v|&LrAT%hWBPS#u5GN`mXhTJL!?+b=8efJ1o04;?FvrgAwh}#EkK>Sc_x7qbk?a` zBcQWj>cur&o0b3mx|2I~>OD_SoDxZDSVInkHlyVbvA^l^b?RyS0qmvQyv-e7af;UH zH$xwxMulEfI!YC%4{||7D*_JPrVo3(w>us-x_yy?2OV~rY|rxtzXlnN#f30lw4G@T z|K0OU36x16LS@A8lQo;?q_Ky+Cgs4{QoKq^!aDV0?f)m4XM>sr@um=_8IT`+)4@$7(dp0?aAjxqK+IhnY$75s{s%kUW zH3F^`ii9FtjUW(8cb^l-swv_5NP!-oiOCvwXC7{D10mFd0NMiwMC|CVvInf2RYJIo zy1hC)RDJ2vrVC)neaNNr0?4YbCyAHgi zgG6;gAnbda2Qk4Xa~ZsRd}r}`Wmce3N&$2ifzCv8&|UOjsg64?7__tTVaG#0b+*fE zQYu3Cf~|AT^Xw#;PQL2!UffY)-K`GX|c;{ zMg)`2Ux8IfCttzYg&`p%?S-$;$=p&jHkp~31rE&zT>zjhA-h*I>h0pjs>h-`buLMV zT|oyDq*ldJ2y${a$r-%qgif;4E^Ddg=d{MZ;Ns7LkM&?HB;U+HN9h`vhXK0X7VT6s zpE%)Mw^qqdX6X|Epnk73RsuA{9G9V7Q1uBD5DWokV#%O)fS?es10Wb0ybbjkF$4n0 zl?XBWfoHhn_DCA`Z+MK+rP0lQ^ciRDYn2mjV_k2M!3d!gIUJgdG67CooG^gyV0#98 zI>VnT>Ay#9X=z#XTEcgmAtEBedQV8=49qkW&?W$C_B<%qZjKe4{WrEFP0fH48{$#n zK>5pByk<9Tfy1UN@EDinL)dg|XH@O(ut5#*G2nTnikuhYIW!h#Bls7F4V>rqCY*aM zbv8DgQ*q5WzVp4WNCnxwB(; zuTN(NAsR4)b45}8Mgfcxsm>*QeTIRN%u^IJbs-Fr&D%esIdcL|=g;MX1YMoA6f-(qK9E}E(m zCpgKXpmB4vli634#P+1V)mx#33BJXiiRUAf>ntJy;n#b+4#KYQb~}fA={f)*eRSB+ zcCb<3qC^%PzMR?B(<57n6+zbSW(M0Nf;|d)bv?e-^2+zS0QzRbXk7s>o|lYCN(%xw z#jSE3Kp0JZ{bfqmD*)@3T6NRopv(NHFcApW>H`i_b|~(XyBrFzz7s}x>517I65;Xp zgOt-3Z3#Hy3Yl)XPm2Ej;?hTbF>a;nIL-6fdYs|WYy(y(^Y_EK3r)XY)OnU5RZ*Q+xY7wT9$*5myK?3^dR>y@_-|s$*ul=WoIgbaIbTl54SFvo)R z;nQuRKk&}W?*nz|xGclKWM=|q228UAnT+9l)~Xd6cx@i()yqT*V5wP;*|*zbp#g%U!7t;g7QiDA+4^h}^n(;I&4IpA?eKFo;p zSVsnyyn6a$&d__x6Hp@u&#piLLx?+@4BVe;ZdmTjFT|PTp&hK)3DRjo4QVN3b=-V<}Bcf+?5*dARimb}t=?Gg{8#mbY|E7#cGcM{t^ z6P!J{#qiRCMBt{s)kfb&St3opP4DsgX}$mV`rdllntT74Qu$0DeYqkK^Nl0V9z|MG zsPyv?&~L84fAu9Nt?V2Q9=QJ8Ib2nO@DL{AMTHl0La!na?F*N_Q~WfZ+oD6p{PMnZ zDfV^RrC;o&6`0=q4DOoJWDHCuNTN5Vs%j+BE$`E(!BJNW^ZJQEM$_yCdjPddT)OA69XQZt0aGCb*$`GQ}ly8-P~K0 zrbC||3=R!_tsR}9*3-h1k&}xPgE%ZE>&KD{NKv%4}9A0Pj|RT-I@$_UEr&We2^+_iDU^f<9^X#h1L{5&)RuX3TbDVZu z-y$;icB>$l2Vyq!nT+&i`R?;u*~HZ&_lPBk6Vj!Fr51J*6BBP7kXY!P&~lZ&!$+b} zY%6aT)Xi04eY^qXG@3wkjHkg2@xv50j*h1!v4PX(*~P`0pVhCvnQfAZr3S+pvYGoN zr=Ce!?eE6+ji~sU@R#}Z{#X)MT$T}mn2+rc`|r@D#rGC_lV96l?D22FcW-#sKR_+_ z+Scy9DkL8&%;kWXefLb};>}AdZadrCRFn9nTxUDy`_}Sf;rD**i0_L@9gcPfpC7z2 z8fzPD6;1Kw!rxMV*S)3o9%w2~O@DXzd%=&|@HH0+QbW=8gyo94#4w{F9A3qVvNQt$ znw6JVO-n7ayj&3Eb!|Oq;!OS-D4^As^JtZQ65h!mYr2bKck31(>pEFAU$hXemx13r z*xTQ1oxSdnm~eBsuTwj27;1W@diV#wJ5n6!2Q(eW?&eOf|DHYW2fUyNpghC$x9 zFjQxYM^obyygv;@F575p*pW7F-B`i>j{B^=(oFnXEWRIm$K1xPRts*hCkTuLzJd;n#r^S7pL0<(jw)R;O8I1(zm(%Zy!@j8o z2B*Dx^#LV@BwNc2O2*yFc~PTMyK!aFBGsqeburO(_Iii+Y3g$hgCWgXzv1w+fXnnVFAv3 zS`!3m9_B@DA5ga@KYp$zXZV;i@%irW-<;<|ll~c+k=McL-R8pyBk$?wP2)XZ7TAR{ zs}pZ62whDP<>f-NaJnI8FW)--Se=)r0{4V4jNg?Nmm`71`f&N5vdPy4&yMf#kDpR@ z7)$5Apc0v2nBUSTl98KyCzANQqP+aiP`_`Suac3w6B~tZ`o> zaRoUNRV}ZIy|V#c8j6jD%L|1lC@759xaYjzpoD|*hJG?<8?2Z z>6!qQc5}D^C8UqXssw$-J>m;Hg74B!gMS6N%;Pbt;I%Ct;NM0I%9n(0Gmx`)?^*~m zW`Pr-{Oagi3uG9Z1dRbf2;BAQKndR_aIWEYcw zdv4@sezh>m7J`V2G>-8M0q;%xxq9S}6)VU$Nf+8vx_Se5Yp$9CCPGfTGc;@LJ5LRk z;t<}X>rDk#v8wG>n>MCM&bGGOE#-!c8#wZiF^`>$z;W-wMdTIq7|33;c^%gJ3xxh4qV#pgsgZMx-OEH?vYZ8&M7Yavep zfxqdJ9Q$NX?OJI4PU38;^-2+L{@!fE1X9?O9k5RDWY#}Yd7A(Bnj6KbU#p?>`dgED zA;GK{v?8JxJmg(Zcra2xzZlm=O8ANk3!`POW`1kcx_SwepL(eGLs6F@2$?g|NoIZ% z1HABvE9R|8n;9cpdL-)3RD>7R_~2mHx7OIGtst#FoH{#TB&V|NXkMtx?X6x>C)tgkn8{&~F>JoFBX()+PA3tS zDBSgq0ecLKbfIJL+~?LiBV4t(*glJS`Y`8r^2R{t33Le)kKbv|Fu*+BF^_I7r^)`n*M#DDMY zY3u0BeLB7i283UAB=mkV7q~`Ga4EQ*#@$?%zi%TVA1LgshsK1Kjgb)(;Hkn3$(C1E za`W=shfc481rguO+fOYtYB743PMG;J4AoqLC%(p6U0XZe6S!=h1xoUO{9n5fl;bIl zizbgdyO~ypE81cepq#@bN{~tGNaYcxYY6gv_qq$y;EjyWrke2R5YwCAz#D`{zAU(V z@UloVMk+Wm%|FQ@XZ>O1crY!-41&o5aalP=C!+68Dxbx@T)-PF@^=WynZ#P6nZ#Fb zG)~o0;P$Ab^Xu=iS})K0LtEZ(^DY8R4QEF7`I4H^l)|8tvzWds@CC3ww^~Q_zETbc zUX61pqry|xH5c94?E0<+0Y}CGSL!fr-pw=4Xp@U4X*kWUB0GpQdhQHp3XQ;J9q?8l zcfZ#1uV1DhecrMOhAVyt1w8%t?%SSgh;>TG;a}wDkxo8v*IGi1q8D|e^NiHq`cx+x zjYKd#4+(+dP8tJ`SMwJXb&n%(Qp$mEFdEgN#An#Nn3lC#&f5=;#$93T6wyM-)1FPx^%_35pL(}$htA=n=dazW}W(+h(6*X1n=3<`UPR9$!{g* zZB$Encf3}wFo-y*hVRkf9EqEh$ior7S@pg`n5Jc9t|qV)f<36gTA>9NiH^y~r z92{^bTOaMX`(kV;!CgQ7b{G9>l5g#ZeW4X_Tk73iCcHFqyETbgU>t&2?!Lv%TKsnd0#9uq#bGX>!u=wt#@LiVExX>(2ot738xvUf~UkLSG!-SqYRf zQF==SCpx0pgql8|QbvkQ`#jYEMWTGe8n;%$W6%-dFkLUooN*#+A@as~iG~(z%w>$> z-D@8NHdEx|&_Sm7O5B{Bra!(s|GBuR;qL<8jl7^~6<7e3qQLAQV_^r@8NE3VlI2^J zdmuAc$qibVfJ8u-&CYI7aGLlAC>PQR-g9wqhyyC%;z4 z8U^1mFRa2AN?Y6%&>`b5w2ma;Q+H6AyHO*idfInQr10z*J zq@y2Nf-TOftV|3E388~e0?h<(iOo$NVoQb!5kR(V9Srl-wYqTHt>aVqpr+u??fFjU zG)aH2{O|_Y=-mEs)xqf*LS9dfaW+q8Qg=D8uy7$aCmnSEu;xA;@9X=lqai5|h+2s} z$I@{fT=-|HCye?tkLd!UH|YqDZ`MLbZ_m)3V0lRt}PX5walks zLs6s|sRC2CK>7iye0>80vNx|-xVXG@xf9toca57n0^}3h+uPrIdpTHH&1)Y!5qce- zp2ZvoG#Z81qu53%LMD(+;SZk!%Qgq^OkOdsUMZ~m*6xapYyWKe5>!*oB9-NKXDY_E zaE8P|%)@E9zP5ksSjnvr=5@Ar3_f@u8}UXQYail>^!_xnzq<=3#s@Y1jP1i8F3F}h zm2O^PyF?5D#K=96(iv|g$oD`i!4xRK6s!mF5OJm^gicUql=X+!D^lCBF`JmD6xt6S z%#UM0K3(F!@8q>Q8jD)q*I(oKUwvHpBb06TpT&@}gkltth>}O6WM9%`i7bVTT|+88 zS+XU=jHro`j6}p(ikK{gQjcW{LlLq>mS#dtwoJCM4D(*o`+dLfZ|@&)&)oO9&UMb` ze9q^bv8`D!#Uth{jaO4c=to~x8)XOeomjEHzI+BBMD5~Pu3d3`eGUi$K%0e>m3=t1 zU9+;k)C;Ygs*l%%Y5XpaY`rUgWw4R}D7x<9;px)6rRC)p{#|>HT^YRhv@SZDwa@Pfks};ktm<9O%CK z`ujJQ+_MgV`1<*wz=$`PmE?wB(g-sm&7^ zk`C8FUIac3lrik*DL9f_V0}6muWS_nk0t|8t@?ZT80h*);q-a@L=B`*xyb98NY$B&fH~FX4R;s;0_dWZjhOJ+kd8NH4D`Y#AIwGkJgBBP{#E-> zlrK24Fi2X9933XUA-6r%L7p$F>$Nb*ScHu8&Kes%?Gjw zjY9OGZc98h7*o9I>ajM_h&` zIP-2#$}U^-TX>p+Zp5#V?{na)7rW2$%)y1SLpmI8XrSg~06{hxLfNOqaUTn;+8G_W$hV3hL>K*}AJ<~~Hx8*C+&@R_y{lWCyS_>6wSJ&=_{Pg! zmgJ9d@5KdfzHMi)e*73ZGr1DdIuKE_ASn?{G-d|kuQGWO7)ARTln56-G-U^jYIPM} z<=-1QY@qhnBRhvC2dx+{%DsgxqyJ_V_CeQY6Pw>>?`MdMiFs}s8;$02YR22;y%_zS z*63!R29Z!mtdQeDIn)3wT4J!kp6|v-&=yw09tjEe4`JwaSc}@piT2!!{swYlUYlRBy{1x|IzXD8Rk*xQ{~E_y(b9~s8_4nFJd^4G{Qt1HjL_?M+)btsF0!- zU)-1dcC<1wgvC8mY$zu-OcscwDmU?!LU?gEFC^CWnXjqFC(&0Lj#5d)kIV36wDi+; z^@QyT}R#zg2BvmbTiITK;IdpUhppkS=c58-EW-n)>f#2>xc z(@uGQMS92dp!d?#!>)U7ve}0*7!0Z+G%_;M43~BqrI9DOqVRd(;tnUj(mN-W#q{xw zf_GsQMClIlQ*CGp%*0WRpI9H}=?v_ie5Q#xMK4`{KRJgD_fn9>)j#GeX;C?HxeeDI zi-p1uKQKzE)S-ORTNSy%+CJa;t!}{j@PM_NM*ydd^ipPJ{I~eT#Du#oWRb%eCPRMC z%~9+bzP&RdK}NAhYTEA-n|s9xG6deXX!mcm7fb{jwjKo`78GsnPHILu_Q&^nAa+fv zhsZD6#3*WNrkE;1Od}$6fIu z`(aH{%g8&c3|iqd^th2~kc>*%h5d}gR{w)1Hn&TAYp{2ETYk&*ZcA);4cqi9>e&LQ zRIB?Ha7M%833GEnaOJCCSxA2cnq+^VQ9w~=&b)^3vz@Q6&H6g>y?`+mCn}5Ox?}9w zMAQYFS9s#!gh;A3^fI1@xMFWPxHVcl(klNCV~QRN+{RX6&iiu7WOBRwZ=lJj^V9?Z zPRHHB!a@ivwjfpb*9GZzl$oxFtT{+m=%MTXuWp;W^Rh(u9O*dDpJSG4%>I2%6!0I=F;-o!?S5Tk? za@c;kBS(*hf^Kgb3qNe-em)vd@;=y(Pr>_ZVLhw-={ErG0T!v@dSOLHh>|PP3&S#{jI{@4l9=`1MK=1Hj z(;GL|<9&Dt1cI+;onMZXr)?Nh=A5uL5+ff>mKXJ=w83=qK| zV21tQz^s3B@BI5%usLR;Ggg7^16;M^Xz#Y!3w#ld z`F!F84aLl+XUa#Nyi;qAU|Z0Hh|kD#p}31(N3j>gs@#{+ruWPiw^)Gs**q7I^hSDNjNDI~|3muUV!g zdzpBrpo%m*Io9g-#vdy*C9SGg2#q>)xzFl0%{za%S>gN9=H?pVG(By27c9(~#Uo`| zfoKA1QM7v&1Ty%-ue}MZ3R#Peidu_z0@c*o76F*U-~H$Q4~$nt%vX3KjK3|catDwp zd%W*1Veg=dnviLtBQ5Fa8*@RH3km#Yg<$~eUNqK5ly@~JNQz;ZXb91_53vV|YL+KijJ!!ZsvARPf zZwQGk(k()|_^H76z)s2aJYo@KDt$H}*p6aC;D|R$20wrgL}K?0Q0o z;BJB9LAh03tbbB*d9PsE&pLn22k06{j4%ey5r!cO=ZdzC*iQ6TqFpR~#o2Z)@~hxj zxI@U^SkWBb6=wI*9Nwqt=)mXJ5p|$ZwgDo6q}e`z#W&VMCm6RXaI*pF04sXTE=4f_ zB_9?5K;U&(*P#)BlS7p#wG%UgK?v@kpsD20VGTS=>W9!&Xp&+lmP4S zLzU8QJHR0G?o0f(Qtdmw5YNol^QX9!{l+!g?Xo!&E8ms%P_PwpQ>ZsIFZKd1nQ@if zbFSc}+@q7es3rq^7vwm$Bgt9IyBPIjC$@udNuRew&#d{pB!D+jh^U_vG`92I5uc$G zyK=aovk`ndz#>XYw!c?u1!!)kPr6*%Fz_YmEjuIJn%Irv(G27`^s8V0^9OCVa2=JO zS=VaG_apmUpEr6Zj(a}WU+wi^B%!6v>lPO*2s~`d0jY~UvnonT{3J;v?n7chFc60C z$s0kYT_(w9kJYM}K3Kvwh4o48*J|9{&OV8?kq+vry}M#yIkH$^qR&gFXS!8QI>`xA%3>T^h`6R zF~2jZl(eSuArE!oi#C{h4cHj;CSiO?dOjOhA;|8s|9`tQ?$9X+#L)0GjJYx1z>u4k zs(d+{eRTt0gI@l6H{+&5_~u@%xUpK)4>4?q!{`}cCVtsK)cv&I8y*zj_8EV6^%pJH)TaGCd`*02i$lbv4O8ZgIZfTy-f`I=Yi&jFQND2yN}_O?#AGz0#Afgk6&S4 zoxzwe3sC!)^!0fV-bK>tBUek%h1~p;la%$OAgqVAF|=r=k+T*PPK(>EwkZl=GMXPQ zfhqj=3v_yyDR?K(yIA>-;vEP# z8byy(eDsF~vOa*BVth5#@H>Jz^0;-Lcm<>D!Qut(yd)u!+WZGG08^V2nm!MlBcZ;~ zJV1U4q%7us`=ypRP#OE5gJ{kdCP4R>E7-#fg&fT>_w{+eKw{)lAgUy`xhV_4(6=}i wM{w3T|9v9C3UAB&$CSCNp&{>&?=5g+sRimM5_Ud+0CsmntUx2H+{`=fUtKaH=Kufz literal 0 HcmV?d00001 diff --git a/static/img/logo.xcf b/static/img/logo.xcf new file mode 100644 index 0000000000000000000000000000000000000000..725ee48a85d9da9ad2d9448a307d81e20fa46b03 GIT binary patch literal 77613 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 literal 0 HcmV?d00001 diff --git a/templates/400.html b/templates/400.html new file mode 100644 index 0000000..c429e94 --- /dev/null +++ b/templates/400.html @@ -0,0 +1,18 @@ + + + + 400 - Destructables + + + {% include "style.html" %} + + + + {% include "header.html" %} +
+

400

+

The request was malformed

+
+ {% include "footer.html" %} + + diff --git a/templates/404.html b/templates/404.html new file mode 100644 index 0000000..1bfd262 --- /dev/null +++ b/templates/404.html @@ -0,0 +1,18 @@ + + + + 404 - Destructables + + + {% include "style.html" %} + + + + {% include "header.html" %} +
+

404

+

This resource cannot be found

+
+ {% include "footer.html" %} + + diff --git a/templates/429.html b/templates/429.html new file mode 100644 index 0000000..2b6f282 --- /dev/null +++ b/templates/429.html @@ -0,0 +1,18 @@ + + + + 429 - Destructables + + + {% include "style.html" %} + + + + {% include "header.html" %} +
+

429

+

This instance is being rate-limited

+
+ {% include "footer.html" %} + + diff --git a/templates/archives.html b/templates/archives.html new file mode 100644 index 0000000..47aa390 --- /dev/null +++ b/templates/archives.html @@ -0,0 +1,41 @@ + + + + Contests Archive, Page {{ data[0] }} - Destructables + + + {% include "style.html" %} + + + + {% include "header.html" %} +
+

Past Contests

+

{{ data[1] }}

+

See running contests

+
+ {% for year in data[3] %} +
+
+

{{ year[0] }}

+
+ {% for month in year[1] %} +
+
+

{{ month[0] }}

+ {% for contest in month[1] %} +
+

{{ contest[0] }}{{ contest[2] }}

+
+ {% endfor %} +
+
+ {% endfor %} +
+
+ {% endfor %} +
+ {{ data[2]|safe }} + {% include "footer.html" %} + + diff --git a/templates/article-review.html b/templates/article-review.html new file mode 100644 index 0000000..051a550 --- /dev/null +++ b/templates/article-review.html @@ -0,0 +1,18 @@ + + + + Pending Review - Destructables + + + {% include "style.html" %} + + + + {% include "header.html" %} +
+

This content is being reviewed

+

This Instructable was just published and is still pending review.

+
+ {% include "footer.html" %} + + diff --git a/templates/article.html b/templates/article.html new file mode 100644 index 0000000..2d8c51b --- /dev/null +++ b/templates/article.html @@ -0,0 +1,58 @@ + + + + {{ data[0] }} - Destructables + + + {% include "style.html" %} + + + + {% include "header.html" %} +
+

{{ data[0] }}

+ +

by {{ data[1] }} in {{ data[3] }} > {{ data[5] }}

+

{{ data[7] }} Views, {{ data[8] }} Favorites, {{ data[10] }}

+
+ + {% for step in data[9] %} +

{{ step[0] }}

+
+ {% for step_img in step[1] %} + {{ step_img[1] }} + {% endfor %} +
+ {{ step[2]|safe }} + {% endfor %} + +
+

{{ data[10] }}

+ {% for index, comment in enumerate(data[11]) %} + + {{ comment[2] }} + {{ comment[3] }} + + {{ comment[5] }} + {{ comment[0] }} votes + {{ comment[6]|safe }} + + +
+ {% for reply in comment[8] %} +
+ + {{ comment[2] }} + {{ reply[3] }} + + {{ reply[5] }} + {{ reply[0] }} votes + {{ reply[6]|safe }} +
+ {% endfor %} +
+
+ {% endfor %} + {% include "footer.html" %} + + diff --git a/templates/category.html b/templates/category.html new file mode 100644 index 0000000..994937c --- /dev/null +++ b/templates/category.html @@ -0,0 +1,52 @@ + + + + {{ data[0] }} - Destructables + + + {% include "style.html" %} + + + + {% include "header.html" %} +
+

{{ data[0] }}

+
+ {% for channel in data[1] %} + + {% endfor %} +
+

Featured Projects

+
+ {% for ible in data[2] %} +
+ + {{ ible[2] }} +
+ {{ ible[2] }} +
+ by {{ ible[3] }} + in {{ ible[5] }} +
+ {{ ible[7] }} Views   + {{ ible[8] }} Favorites +
+ {% endfor %} +
+

Contests

+
+ {% for contest in data[3] %} + + {{ contest[2] }} + + {% endfor %} +
+
+ {% include "footer.html" %} + + diff --git a/templates/collection.html b/templates/collection.html new file mode 100644 index 0000000..c5dbeb2 --- /dev/null +++ b/templates/collection.html @@ -0,0 +1,36 @@ + + + + {{ data[0] }} - Destructables + + + {% include "style.html" %} + + + + {% include "header.html" %} +
+

{{ data[0] }}

+ +

by {{ data[1] }} in {{ data[3] }} > {{ data[5] }}

+

{{ data[7] }} Views, {{ data[8] }} Favorites, {{ data[10] }}

+ +
+ {% for thumbnail in data[9] %} +
+ {% if thumbnail[0] == '' %} + + {{ thumbnail[3] }} +

{{ thumbnail[3] }}

+
+

by {{ thumbnail[4] }} in {{ thumbnail[6] }}

+ {% else %} + {{ thumbnail[0]|safe }} + {% endif %} +
+ {% endfor %} +
+
+ {% include "footer.html" %} + + diff --git a/templates/contest.html b/templates/contest.html new file mode 100644 index 0000000..1b34357 --- /dev/null +++ b/templates/contest.html @@ -0,0 +1,32 @@ + + + + {{ data[0] }} - Destructables + + + {% include "style.html" %} + + + + {% include "header.html" %} +
+ {{ data[0] }} +

{{ data[2] }} Entries, {{ data[3] }} Prizes

+
+ {{ data[4]|safe }} +
+ {% for ible in data[5] %} +
+ + {{ ible[2] }} +

{{ ible[2] }}

+
+

by {{ ible[3] }} in {{ ible[5] }}

+

{{ ible[7] }} Views

+
+ {% endfor %} +
+
+ {% include "footer.html" %} + + diff --git a/templates/contests.html b/templates/contests.html new file mode 100644 index 0000000..81098a8 --- /dev/null +++ b/templates/contests.html @@ -0,0 +1,48 @@ + + + + Contests - Destructables + + + {% include "style.html" %} + + + + {% include "header.html" %} +
+

Contests

+ {{ data[0]|safe }} +
+
+ {% for contest in data[1] %} +
+ + {{ contest[2] }} + +

Closes {{ contest[3] }}

+

{{ contest[4] }} Prizes, {{ contest[5] }} Entries

+
+ {% endfor %} +
+
+

Winner's Circle

+ {% for closed in data[2] %} +
+ {{ closed[2] }} + {% for featured_items in closed[3] %} + + {% endfor %} +
+ {% endfor %} +
+
+ {% include "footer.html" %} + + diff --git a/templates/footer.html b/templates/footer.html new file mode 100644 index 0000000..d05128d --- /dev/null +++ b/templates/footer.html @@ -0,0 +1,4 @@ + diff --git a/templates/header.html b/templates/header.html new file mode 100644 index 0000000..327f919 --- /dev/null +++ b/templates/header.html @@ -0,0 +1,16 @@ +
+ +
+
diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..7283062 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,142 @@ + + + + Destructables + + + {% include "style.html" %} + + + + {% include "header.html" %} +
+

{{ data[0] }}

+ + Circuits +
+ {% for ible in data[1] %} +
+ + {{ ible[2] }} +

{{ ible[3] }}

+
+
+

by {{ ible[4] }}

+

in {{ ible[6] }}

+

{{ ible[8] }} Favorites, {{ ible[9] }} Views

+
+
+ {% endfor %} +
+
+ + Workshop +
+ {% for ible in data[2] %} +
+ + {{ ible[2] }} +

{{ ible[3] }}

+
+
+

by {{ ible[4] }}

+

in {{ ible[6] }}

+

{{ ible[8] }} Favorites, {{ ible[9] }} Views

+
+
+ {% endfor %} +
+
+ + Craft +
+ {% for ible in data[3] %} +
+ + {{ ible[2] }} +

{{ ible[3] }}

+
+
+

by {{ ible[4] }}

+

in {{ ible[6] }}

+

{{ ible[8] }} Favorites, {{ ible[9] }} Views

+
+
+ {% endfor %} +
+
+ + Cooking +
+ {% for ible in data[4] %} +
+ + {{ ible[2] }} +

{{ ible[3] }}

+
+
+

by {{ ible[4] }}

+

in {{ ible[6] }}

+

{{ ible[8] }} Favorites, {{ ible[9] }} Views

+
+
+ {% endfor %} +
+
+ + Living +
+ {% for ible in data[5] %} +
+ + {{ ible[2] }} +

{{ ible[3] }}

+
+
+

by {{ ible[4] }}

+

in {{ ible[6] }}

+

{{ ible[8] }} Favorites, {{ ible[9] }} Views

+
+
+ {% endfor %} +
+
+ + Outside +
+ {% for ible in data[6] %} +
+ + {{ ible[2] }} +

{{ ible[3] }}

+
+
+

by {{ ible[4] }}

+

in {{ ible[6] }}

+

{{ ible[8] }} Favorites, {{ ible[9] }} Views

+
+
+ {% endfor %} +
+
+ + Teachers +
+ {% for ible in data[7] %} +
+ + {{ ible[2] }} +

{{ ible[3] }}

+
+
+

by {{ ible[4] }}

+

in {{ ible[6] }}

+

{{ ible[8] }} Favorites, {{ ible[9] }} Views

+
+
+ {% endfor %} + +
+ {% include "footer.html" %} + + diff --git a/templates/member-instructables.html b/templates/member-instructables.html new file mode 100644 index 0000000..f7401ec --- /dev/null +++ b/templates/member-instructables.html @@ -0,0 +1,42 @@ + + + + {{ data[1] }}'s Instructables - Destructables + + + {% include "style.html" %} + + + + {% include "header.html" %} +
+ {{ data[1] }} +

{{ data[1] }}

+ {{ data[2] }}   + {{ data[3] }} +
+ {{ data[4] }} Instructables   + {{ data[5] }} Views   + {{ data[6] }} Comments   + {{ data[7] }} Followers +
+

{{ data[8] }}

+
+

Instructables

+ +
+ {% for ible in data[9] %} + + {% endfor %} +
+ +
+ {% include "footer.html" %} + + diff --git a/templates/member.html b/templates/member.html new file mode 100644 index 0000000..d11a6a5 --- /dev/null +++ b/templates/member.html @@ -0,0 +1,49 @@ + + + + {{ data[1] }}'s Profile - Destructables + + + {% include "style.html" %} + + + + {% include "header.html" %} +
+ {{ data[1] }} +

{{ data[1] }}

+ {{ data[2] }}   + {{ data[3] }} +
+ {{ data[4] }} Instructables   + {{ data[5] }} Views   + {{ data[6] }} Comments   + {{ data[7] }} Followers +
+

{{ data[8] }}

+ {% if data[9] != "" %} +
+ {% endif %} +

{{ data[9] }}

+ {% for ible in data[10] %} + + {% endfor %} +

View all Instructables

+
+

{{ data[11] }}

+ {% for ach in data[12] %} +
+

{{ ach[0] }}

+

{{ ach[1] }}

+
+ {% endfor %} + +
+ {% include "footer.html" %} + + diff --git a/templates/projects.html b/templates/projects.html new file mode 100644 index 0000000..b4ebc11 --- /dev/null +++ b/templates/projects.html @@ -0,0 +1,36 @@ + + + + {{ data[0] }} - Destructables + + + {% include "style.html" %} + + + + {% include "header.html" %} +
+

{{ data[0] }}

+ Featured + Recent + Popular + Views + Winners +
+ +
+ {% for ible in data[1] %} +
+ + {{ ible[2] }} +

{{ ible[2] }}

+
+

by {{ ible[4] }} in {{ ible[6] }}

+

{{ ible[7] }} Views, {{ ible[8] }} Favorites

+
+ {% endfor %} +
+
+ {% include "footer.html" %} + + diff --git a/templates/sitemap.html b/templates/sitemap.html new file mode 100644 index 0000000..fe664a1 --- /dev/null +++ b/templates/sitemap.html @@ -0,0 +1,29 @@ + + + + Sitemap - Destructables + + + {% include "style.html" %} + + + + {% include "header.html" %} +
+

Sitemap

+
+ {% for group in data %} +
+

{{ group[0] }}

+ +
+ {% endfor %} +
+
+ {% include "footer.html" %} + + diff --git a/templates/style.html b/templates/style.html new file mode 100644 index 0000000..6c52400 --- /dev/null +++ b/templates/style.html @@ -0,0 +1,166 @@ +