diff --git a/FyneApp.toml b/FyneApp.toml index 1fe1f80..80050b2 100644 --- a/FyneApp.toml +++ b/FyneApp.toml @@ -1,9 +1,14 @@ +Website = "https://gui-for-ffmpeg.projects.kor-elf.net/language/en" + [Details] - Icon = "icon.png" + Icon = "assets/icon.png" Name = "GUI for FFmpeg" ID = "net.kor-elf.projects.gui-for-ffmpeg" - Version = "0.9.0" - Build = 4 + Version = "1.0.0" + Build = 75 -[Migrations] - fyneDo = true \ No newline at end of file +[LinuxAndBSD] + GenericName = "GUI for FFmpeg" + Categories = ["AudioVideo", "Utility"] + Comment = "A simple interface for the FFmpeg console utility." + Keywords = ["ffmpeg", "media", "convert", "transcode", "audio", "video", "конвертер", "видео", "аудио", "кодек"] diff --git a/LICENSE-3RD-PARTY.txt b/LICENSE-3RD-PARTY.txt index 548dda4..479031f 100644 --- a/LICENSE-3RD-PARTY.txt +++ b/LICENSE-3RD-PARTY.txt @@ -1566,31 +1566,6 @@ SOFTWARE. -------------------------------------------------------------------------------- -go.etcd.io/bbolt - -The MIT License (MIT) - -Copyright (c) 2013 Ben Johnson - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - --------------------------------------------------------------------------------- - golang.org/x/image Copyright 2009 The Go Authors. @@ -1721,43 +1696,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. gopkg.in/yaml.v3 -This project is covered by two different licenses: MIT and Apache. - -#### MIT License #### - -The following files were ported to Go from C files of libyaml, and thus -are still covered by their original MIT license, with the additional -copyright staring in 2011 when the project was ported over: - - apic.go emitterc.go parserc.go readerc.go scannerc.go - writerc.go yamlh.go yamlprivateh.go - -Copyright (c) 2006-2010 Kirill Simonov -Copyright (c) 2006-2011 Kirill Simonov - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -### Apache License ### - -All the remaining project files are covered by the Apache license: - -Copyright (c) 2011-2019 Canonical Ltd +Copyright 2011-2016 Canonical Ltd. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -1805,3 +1744,683 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- +FFmpeg + +FFmpeg is a trademark of Fabrice Bellard, originator of the FFmpeg project. +https://ffmpeg.org/legal.html + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..564205c --- /dev/null +++ b/Makefile @@ -0,0 +1,88 @@ +VERSION ?= $(shell grep '^ *Version *= *' FyneApp.toml | sed -E "s/.*=[[:space:]]*\"([0-9\.]+)\".*/\1/") + +BUILD_TMP := fyne-cross/tmp + +WINDOWS_AMD64 := gui-for-ffmpeg-$(VERSION)-windows-amd64 +BUILD_TMP_WINDOWS_AMD64 := $(BUILD_TMP)/$(WINDOWS_AMD64) + +LINUX_AMD64 := gui-for-ffmpeg-$(VERSION)-linux-amd64 +BUILD_TMP_LINUX_AMD64 := $(BUILD_TMP)/$(LINUX_AMD64) + +RELEASES := fyne-cross/releases/$(VERSION) + +default: + # Run "make build-for-linux_amd64" + # Run "make build-for-windows_amd64" + # Build for all + # Run "make build" + +build: + make build-for-linux_amd64 + make build-for-windows_amd64 + # $(RELEASES)/$(LINUX_AMD64).tar.gz + # $(RELEASES)/$(LINUX_AMD64).tar.gz.sha256 + # $(RELEASES)/$(WINDOWS_AMD64).zip + # $(RELEASES)/$(WINDOWS_AMD64).zip.sha256 + +build-for-windows_amd64: + fyne-cross windows + + @if [ -d $(BUILD_TMP_WINDOWS_AMD64) ]; then \ + rm -rf $(BUILD_TMP_WINDOWS_AMD64)/*; \ + else \ + mkdir -p $(BUILD_TMP_WINDOWS_AMD64); \ + fi + cp LICENSE $(BUILD_TMP_WINDOWS_AMD64)/LICENSE + cp LICENSE-3RD-PARTY.txt $(BUILD_TMP_WINDOWS_AMD64)/LICENSE-3RD-PARTY.txt + cp "fyne-cross/bin/windows-amd64/GUI for FFmpeg.exe" $(BUILD_TMP_WINDOWS_AMD64)/gui-for-ffmpeg.exe + cd $(BUILD_TMP) && 7z a -tzip $(WINDOWS_AMD64).zip $(WINDOWS_AMD64) + + @if [ ! -d $(RELEASES) ]; then \ + mkdir -p $(RELEASES); \ + fi + + @if [ -f $(RELEASES)/$(WINDOWS_AMD64).zip ]; then \ + rm $(RELEASES)/$(WINDOWS_AMD64).zip; \ + fi + + @if [ -f $(RELEASES)/$(WINDOWS_AMD64).zip.sha256 ]; then \ + rm $(RELEASES)/$(WINDOWS_AMD64).zip.sha256; \ + fi + + mv $(BUILD_TMP)/$(WINDOWS_AMD64).zip $(RELEASES)/$(WINDOWS_AMD64).zip + cd $(RELEASES) && sha256sum $(WINDOWS_AMD64).zip > $(WINDOWS_AMD64).zip.sha256 + # $(RELEASES)/$(WINDOWS_AMD64).zip + # $(RELEASES)/$(WINDOWS_AMD64).zip.sha256 + +build-for-linux_amd64: + fyne-cross linux + + @if [ -d $(BUILD_TMP_LINUX_AMD64) ]; then \ + rm -rf $(BUILD_TMP_LINUX_AMD64)/*; \ + else \ + mkdir -p $(BUILD_TMP_LINUX_AMD64); \ + fi + cp -r dist/linux/* $(BUILD_TMP_LINUX_AMD64)/ + cp LICENSE $(BUILD_TMP_LINUX_AMD64)/LICENSE + cp LICENSE-3RD-PARTY.txt $(BUILD_TMP_LINUX_AMD64)/LICENSE-3RD-PARTY.txt + cp fyne-cross/bin/linux-amd64/gui-for-ffmpeg $(BUILD_TMP_LINUX_AMD64)/usr/local/bin/gui-for-ffmpeg + cp assets/icon.png $(BUILD_TMP_LINUX_AMD64)/usr/local/share/pixmaps/gui-for-ffmpeg.png + + cd $(BUILD_TMP) && tar -czvf $(LINUX_AMD64).tar.gz $(LINUX_AMD64) + + @if [ ! -d $(RELEASES) ]; then \ + mkdir -p $(RELEASES); \ + fi + + @if [ -f $(RELEASES)/$(LINUX_AMD64).tar.gz ]; then \ + rm $(RELEASES)/$(LINUX_AMD64).tar.gz; \ + fi + + @if [ -f $(RELEASES)/$(LINUX_AMD64).tar.gz.sha256 ]; then \ + rm $(RELEASES)/$(LINUX_AMD64).tar.gz.sha256; \ + fi + + mv $(BUILD_TMP)/$(LINUX_AMD64).tar.gz $(RELEASES)/$(LINUX_AMD64).tar.gz + cd $(RELEASES) && sha256sum $(LINUX_AMD64).tar.gz > $(LINUX_AMD64).tar.gz.sha256 + # $(RELEASES)/$(LINUX_AMD64).tar.gz + # $(RELEASES)/$(LINUX_AMD64).tar.gz.sha256 diff --git a/README.md b/README.md index 8f59d1f..cd7e398 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # GUI for FFmpeg

Простенький интерфейс для консольной утилиты FFmpeg. Но я не являюсь автором самой утилиты FFmpeg.

-

FFmpeg — торговая марка Fabrice Bellard, создателя проекта FFmpeg.

+

FFmpeg — торговая марка Fabrice Bellard, создателя проекта FFmpeg.

Программное обеспечение является MIT (см. LICENSE) и использует сторонние библиотеки, которые распространяются на их собственных условиях (см. LICENSE-3RD-PARTY.txt).

- +Скриншот программы

Скачать скомпилированные готовые версии можно тут: https://git.kor-elf.net/kor-elf/gui-for-ffmpeg/releases.

@@ -13,6 +13,18 @@ 1. go install fyne.io/fyne/v2/cmd/fyne@latest 2. fyne get git.kor-elf.net/kor-elf/gui-for-ffmpeg +## Скомпилировать через Makefile: +1. git clone https://git.kor-elf.net/kor-elf/gui-for-ffmpeg.git +2. Переходим в папку проекта и там переходим в папку src: **cd gui-for-ffmpeg** +3. Ознакамливаемся, что нужно ещё установить для Вашей ОС для простого запуска (через go run) тут: https://docs.fyne.io/started/ +4. go install github.com/fyne-io/fyne-cross@latest + * У Вас так же должен быть установлен docker + * О fyne-cross можно по подробней почитать тут: https://github.com/fyne-io/fyne-cross +5. * make build-for-linux_amd64 + * make build-for-windows_amd64 + * Или просто **make build** +6. Создаться папка с архивом в **fyne-cross/releases** + ## Скомпилировать через исходники: 1. git clone https://git.kor-elf.net/kor-elf/gui-for-ffmpeg.git 2. Переходим в папку проекта и там переходим в папку src: **cd gui-for-ffmpeg** @@ -21,25 +33,7 @@ 5. go install github.com/fyne-io/fyne-cross@latest * У Вас так же должен быть установлен docker * О fyne-cross можно по подробней почитать тут: https://github.com/fyne-io/fyne-cross -6. * fyne-cross windows --icon icon.png --app-id "." -name "gui-for-ffmpeg" - * fyne-cross linux --icon icon.png --app-id "." -name "gui-for-ffmpeg" -7. Создаться папка **fyne-cross/bin** и там будет созданна папка с тем названием под которую Вы компилировали приложения (linux-amd64 или windows-amd64). -8. В папку **fyne-cross/bin/linux-amd64** или **fyne-cross/bin/windows-amd64** копируете: - * icon.png - * data - * languages - * LICENSE - * LICENSE-3RD-PARTY.txt -

Структура должна получиться такая:

- - -## Работа с переводами: -1. go install -v github.com/nicksnyder/go-i18n/v2/goi18n@latest -3. Создаём файл languages/translate.\*.toml -4. goi18n merge -sourceLanguage ru -outdir languages languages/active.\*.toml languages/translate.\*.toml -5. В файлах **languages/translate.\*.toml** переводим текст на нужный язык -6. goi18n merge -sourceLanguage ru -outdir languages languages/active.\*.toml languages/translate.\*.toml - -___где * подставляем нужный язык___ - -Более подробно можно почитать тут: https://github.com/nicksnyder/go-i18n \ No newline at end of file +6. * fyne-cross windows + * fyne-cross linux +7. Создаться папка **fyne-cross/dist** и там будет созданна папка с тем названием под которую Вы компилировали приложения (linux-amd64 или windows-amd64). +8. В папке **fyne-cross/bin/linux-amd64** или **fyne-cross/bin/windows-amd64** будут архивы, которые надо распаковать и пользоваться программой. diff --git a/assets/icon.png b/assets/icon.png new file mode 100644 index 0000000..6eaf8f6 Binary files /dev/null and b/assets/icon.png differ diff --git a/assets/screenshot-gui-for-ffmpeg.png b/assets/screenshot-gui-for-ffmpeg.png new file mode 100644 index 0000000..0fc23fd Binary files /dev/null and b/assets/screenshot-gui-for-ffmpeg.png differ diff --git a/convertor/repository.go b/convertor/repository.go deleted file mode 100644 index f751607..0000000 --- a/convertor/repository.go +++ /dev/null @@ -1,46 +0,0 @@ -package convertor - -import ( - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/setting" -) - -type RepositoryContract interface { - GetPathFfmpeg() (string, error) - SavePathFfmpeg(code string) (setting.Setting, error) - GetPathFfprobe() (string, error) - SavePathFfprobe(code string) (setting.Setting, error) - GetPathFfplay() (string, error) - SavePathFfplay(code string) (setting.Setting, error) -} - -type Repository struct { - settingRepository setting.RepositoryContract -} - -func NewRepository(settingRepository setting.RepositoryContract) *Repository { - return &Repository{settingRepository: settingRepository} -} - -func (r Repository) GetPathFfmpeg() (string, error) { - return r.settingRepository.GetValue("ffmpeg") -} - -func (r Repository) SavePathFfmpeg(path string) (setting.Setting, error) { - return r.settingRepository.CreateOrUpdate("ffmpeg", path) -} - -func (r Repository) GetPathFfprobe() (string, error) { - return r.settingRepository.GetValue("ffprobe") -} - -func (r Repository) SavePathFfprobe(path string) (setting.Setting, error) { - return r.settingRepository.CreateOrUpdate("ffprobe", path) -} - -func (r Repository) GetPathFfplay() (string, error) { - return r.settingRepository.GetValue("ffplay") -} - -func (r Repository) SavePathFfplay(path string) (setting.Setting, error) { - return r.settingRepository.CreateOrUpdate("ffplay", path) -} diff --git a/convertor/view.go b/convertor/view.go deleted file mode 100644 index 58e6a3f..0000000 --- a/convertor/view.go +++ /dev/null @@ -1,63 +0,0 @@ -package convertor - -import ( - "fyne.io/fyne/v2" - "fyne.io/fyne/v2/canvas" - "fyne.io/fyne/v2/container" - "fyne.io/fyne/v2/widget" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/convertor/view" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" - "github.com/nicksnyder/go-i18n/v2/i18n" - "image/color" -) - -type ViewContract interface { - Main( - formConversion view.ConversionContract, - ) - SelectFFPath( - ffmpegPath string, - ffprobePath string, - ffplayPath string, - save func(ffmpegPath string, ffprobePath string, ffplayPath string) error, - cancel func(), - donwloadFFmpeg func(progressBar *widget.ProgressBar, progressMessage *canvas.Text) error, - ) -} - -type View struct { - app kernel.AppContract - downloadFFmpeg *downloadFFmpeg -} - -type downloadFFmpeg struct { - isDownloadFFmpeg bool - blockDownloadFFmpegContainer *fyne.Container -} - -func NewView(app kernel.AppContract) *View { - return &View{ - app: app, - downloadFFmpeg: &downloadFFmpeg{ - blockDownloadFFmpegContainer: nil, - }, - } -} - -func (v View) Main(formConversion view.ConversionContract) { - converterVideoFilesTitle := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "converterVideoFilesTitle", - }) - v.app.GetWindow().SetContent(widget.NewCard(converterVideoFilesTitle, "", container.NewVScroll(formConversion.GetContent()))) - formConversion.AfterViewContent() -} - -func setStringErrorStyle(text *canvas.Text) { - text.Color = color.RGBA{R: 255, G: 0, B: 0, A: 255} - text.Refresh() -} - -func setStringSuccessStyle(text *canvas.Text) { - text.Color = color.RGBA{R: 49, G: 127, B: 114, A: 255} - text.Refresh() -} diff --git a/convertor/view/conversion.go b/convertor/view/conversion.go deleted file mode 100644 index 947a37c..0000000 --- a/convertor/view/conversion.go +++ /dev/null @@ -1,539 +0,0 @@ -package view - -import ( - "errors" - "fyne.io/fyne/v2" - "fyne.io/fyne/v2/canvas" - "fyne.io/fyne/v2/container" - "fyne.io/fyne/v2/storage" - "fyne.io/fyne/v2/widget" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/convertor/view/form_items" - encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel/encoder" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/setting" - "github.com/nicksnyder/go-i18n/v2/i18n" - "image/color" - "os" - "path/filepath" -) - -type ConversionContract interface { - GetContent() fyne.CanvasObject - AfterViewContent() -} - -type Conversion struct { - app kernel.AppContract - form *form - conversionMessage *canvas.Text - fileForConversion *fileForConversion - directoryForSaving *directoryForSaving - overwriteOutputFiles *overwriteOutputFiles - selectEncoder *selectEncoder - runConvert func(setting HandleConvertSetting) - itemsToConvertService kernel.ItemsToConvertContract -} - -type HandleConvertSetting struct { - DirectoryForSave string - OverwriteOutputFiles bool - Format string - Encoder encoder2.EncoderContract -} - -func NewConversion(app kernel.AppContract, formats encoder.ConvertorFormatsContract, runConvert func(setting HandleConvertSetting), settingDirectoryForSaving setting.DirectoryForSavingContract, itemsToConvertService kernel.ItemsToConvertContract) *Conversion { - conversionMessage := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255}) - conversionMessage.TextSize = 16 - conversionMessage.TextStyle = fyne.TextStyle{Bold: true} - - fileForConversion := newFileForConversion(app, itemsToConvertService) - directoryForSaving := newDirectoryForSaving(app, settingDirectoryForSaving) - overwriteOutputFiles := newOverwriteOutputFiles(app) - selectEncoder := newSelectEncoder(app, formats) - - items := []*widget.FormItem{ - { - Text: app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "fileForConversionTitle"}), - Widget: fileForConversion.button, - }, - { - Widget: container.NewHScroll(fileForConversion.message), - }, - { - Text: app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "buttonForSelectedDirTitle"}), - Widget: directoryForSaving.button, - }, - { - Widget: container.NewHScroll(directoryForSaving.message), - }, - { - Widget: overwriteOutputFiles.checkbox, - }, - { - Widget: selectEncoder.SelectFileType, - }, - { - Text: app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "selectFormat"}), - Widget: selectEncoder.SelectFormat, - }, - { - Text: app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "selectEncoder"}), - Widget: selectEncoder.SelectEncoder, - }, - } - form := newForm(app, items) - - return &Conversion{ - app: app, - form: form, - conversionMessage: conversionMessage, - fileForConversion: fileForConversion, - directoryForSaving: directoryForSaving, - overwriteOutputFiles: overwriteOutputFiles, - selectEncoder: selectEncoder, - runConvert: runConvert, - itemsToConvertService: itemsToConvertService, - } -} - -func (c Conversion) GetContent() fyne.CanvasObject { - c.form.form.OnSubmit = c.submit - c.fileForConversion.AddChangeCallback(c.selectFileForConversion) - c.selectEncoder.AddChangeCallback(c.changeEncoder) - if c.selectEncoder.Encoder != nil { - c.selectEncoder.SelectEncoder.SetSelectedIndex(c.selectEncoder.SelectEncoder.SelectedIndex()) - } - - return container.NewVBox( - c.form.form, - c.conversionMessage, - ) -} - -func (c Conversion) changeEncoder(encoder encoder2.EncoderContract) { - items := []*widget.FormItem{} - - if form_items.Views[encoder.GetName()] != nil { - items = form_items.Views[encoder.GetName()](encoder, c.app) - } - - c.form.ChangeItems(items) -} - -func (c Conversion) AfterViewContent() { - if len(c.itemsToConvertService.GetItems()) == 0 { - c.form.form.Disable() - } -} - -func (c Conversion) selectFileForConversion(err error) { - c.conversionMessage.Text = "" - if len(c.itemsToConvertService.GetItems()) == 0 { - if err != nil { - c.form.form.Disable() - return - } - } - - c.form.form.Enable() -} - -func (c Conversion) submit() { - if len(c.itemsToConvertService.GetItems()) == 0 { - showConversionMessage(c.conversionMessage, errors.New(c.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "errorNoFilesAddedForConversion", - }))) - c.enableFormConversion() - return - } - - if len(c.directoryForSaving.path) == 0 { - showConversionMessage(c.conversionMessage, errors.New(c.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "errorSelectedFolderSave", - }))) - c.enableFormConversion() - return - } - if len(c.selectEncoder.SelectFormat.Selected) == 0 { - showConversionMessage(c.conversionMessage, errors.New(c.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "errorSelectedFormat", - }))) - return - } - if c.selectEncoder.Encoder == nil { - showConversionMessage(c.conversionMessage, errors.New(c.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "errorSelectedEncoder", - }))) - return - } - c.conversionMessage.Text = "" - - c.fileForConversion.button.Disable() - c.directoryForSaving.button.Disable() - c.form.form.Disable() - - c.runConvert(HandleConvertSetting{ - DirectoryForSave: c.directoryForSaving.path, - OverwriteOutputFiles: c.overwriteOutputFiles.IsChecked(), - Format: c.selectEncoder.SelectFormat.Selected, - Encoder: c.selectEncoder.Encoder, - }) - c.enableFormConversion() - - if len(c.itemsToConvertService.GetItems()) == 0 { - c.form.form.Disable() - } -} - -func (c Conversion) enableFormConversion() { - c.fileForConversion.button.Enable() - c.directoryForSaving.button.Enable() - c.form.form.Enable() -} - -type fileForConversion struct { - button *widget.Button - message *canvas.Text - file *kernel.File - - changeCallbacks map[int]func(err error) -} - -func newFileForConversion(app kernel.AppContract, itemsToConvertService kernel.ItemsToConvertContract) *fileForConversion { - message := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255}) - fileForConversion := &fileForConversion{ - message: message, - - changeCallbacks: map[int]func(err error){}, - } - - buttonTitle := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "choose", - }) + "\n" + app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "or", - }) + "\n" + app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "dragAndDropFiles", - }) - - var locationURI fyne.ListableURI - - fileForConversion.button = widget.NewButton(buttonTitle, func() { - app.GetWindow().NewFileOpen(func(r fyne.URIReadCloser, err error) { - fyne.Do(func() { - fileForConversion.message.Text = "" - fileForConversion.message.Refresh() - }) - - if err != nil { - fyne.Do(func() { - fileForConversion.message.Text = err.Error() - fileForConversion.message.Refresh() - }) - fileForConversion.eventSelectFile(err) - return - } - if r == nil { - return - } - app.GetWindow().GetLayout().GetRightTabs().SelectAddedFilesTab() - - itemsToConvertService.Add(&kernel.File{ - Path: r.URI().Path(), - Name: r.URI().Name(), - Ext: r.URI().Extension(), - }) - - fileForConversion.eventSelectFile(nil) - - listableURI := storage.NewFileURI(filepath.Dir(r.URI().Path())) - locationURI, _ = storage.ListerForURI(listableURI) - }, locationURI) - }) - - app.GetWindow().SetOnDropped(func(position fyne.Position, uris []fyne.URI) { - if len(uris) == 0 { - return - } - - isError := false - for _, uri := range uris { - info, err := os.Stat(uri.Path()) - if err != nil { - isError = true - continue - } - if info.IsDir() { - isError = true - continue - } - - itemsToConvertService.Add(&kernel.File{ - Path: uri.Path(), - Name: uri.Name(), - Ext: uri.Extension(), - }) - - fileForConversion.eventSelectFile(nil) - - listableURI := storage.NewFileURI(filepath.Dir(uri.Path())) - locationURI, _ = storage.ListerForURI(listableURI) - } - app.GetWindow().GetLayout().GetRightTabs().SelectAddedFilesTab() - if isError { - fileForConversion.message.Text = app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "errorDragAndDropFile", - }) - setStringErrorStyle(fileForConversion.message) - fileForConversion.eventSelectFile(errors.New(fileForConversion.message.Text)) - } else { - fyne.Do(func() { - fileForConversion.message.Text = "" - fileForConversion.message.Refresh() - }) - } - }) - - return fileForConversion -} - -func (c fileForConversion) AddChangeCallback(callback func(err error)) { - c.changeCallbacks[len(c.changeCallbacks)] = callback -} - -func (c fileForConversion) eventSelectFile(err error) { - for _, changeCallback := range c.changeCallbacks { - changeCallback(err) - } -} - -type directoryForSaving struct { - button *widget.Button - message *canvas.Text - path string -} - -func newDirectoryForSaving(app kernel.AppContract, settingDirectoryForSaving setting.DirectoryForSavingContract) *directoryForSaving { - directoryForSaving := &directoryForSaving{ - path: "", - } - - directoryForSaving.message = canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255}) - directoryForSaving.message.TextSize = 16 - directoryForSaving.message.TextStyle = fyne.TextStyle{Bold: true} - - buttonTitle := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "choose", - }) - - var locationURI fyne.ListableURI - - location, err := getDirectoryForSaving(settingDirectoryForSaving) - if err == nil { - directoryForSaving.path = location.Path() - directoryForSaving.message.Text = location.Path() - setStringSuccessStyle(directoryForSaving.message) - } - - directoryForSaving.button = widget.NewButton(buttonTitle, func() { - app.GetWindow().NewFolderOpen(func(r fyne.ListableURI, err error) { - if err != nil { - directoryForSaving.message.Text = err.Error() - setStringErrorStyle(directoryForSaving.message) - return - } - if r == nil { - return - } - - directoryForSaving.path = r.Path() - - directoryForSaving.message.Text = r.Path() - setStringSuccessStyle(directoryForSaving.message) - locationURI, err = storage.ListerForURI(r) - - if err == nil { - _, _ = settingDirectoryForSaving.SaveDirectoryForSaving(locationURI.Path()) - } - - }, locationURI) - }) - - return directoryForSaving -} - -func getDirectoryForSaving(settingDirectoryForSaving setting.DirectoryForSavingContract) (fyne.ListableURI, error) { - path, err := settingDirectoryForSaving.GetDirectoryForSaving() - if err != nil { - return nil, err - } - - if len(path) > 0 { - path = "file://" + path - } - - uri, err := storage.ParseURI(path) - if err != nil { - return nil, err - } - - return storage.ListerForURI(uri) -} - -type overwriteOutputFiles struct { - checkbox *widget.Check - isChecked bool -} - -func newOverwriteOutputFiles(app kernel.AppContract) *overwriteOutputFiles { - overwriteOutputFiles := &overwriteOutputFiles{ - isChecked: false, - } - checkboxOverwriteOutputFilesTitle := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "checkboxOverwriteOutputFilesTitle", - }) - overwriteOutputFiles.checkbox = widget.NewCheck(checkboxOverwriteOutputFilesTitle, func(b bool) { - overwriteOutputFiles.isChecked = b - }) - - return overwriteOutputFiles -} - -func (receiver overwriteOutputFiles) IsChecked() bool { - return receiver.isChecked -} - -type selectEncoder struct { - SelectFileType *widget.RadioGroup - SelectFormat *widget.Select - SelectEncoder *widget.Select - Encoder encoder2.EncoderContract - - changeCallbacks map[int]func(encoder encoder2.EncoderContract) -} - -func newSelectEncoder(app kernel.AppContract, formats encoder.ConvertorFormatsContract) *selectEncoder { - selectEncoder := &selectEncoder{ - changeCallbacks: map[int]func(encoder encoder2.EncoderContract){}, - } - - encoders := map[int]encoder2.EncoderDataContract{} - selectEncoder.SelectEncoder = widget.NewSelect([]string{}, func(s string) { - if encoders[selectEncoder.SelectEncoder.SelectedIndex()] == nil { - return - } - selectEncoderData := encoders[selectEncoder.SelectEncoder.SelectedIndex()] - selectEncoder.ChangeEncoder(selectEncoderData.NewEncoder()) - }) - - formatSelected := "" - selectEncoder.SelectFormat = widget.NewSelect([]string{}, func(s string) { - if formatSelected == s { - return - } - formatSelected = s - format, err := formats.GetFormat(s) - if err != nil { - return - } - encoderOptions := []string{} - encoders = map[int]encoder2.EncoderDataContract{} - for _, e := range format.GetEncoders() { - encoders[len(encoders)] = e - encoderOptions = append(encoderOptions, app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "encoder_" + e.GetTitle()})) - } - selectEncoder.SelectEncoder.SetOptions(encoderOptions) - selectEncoder.SelectEncoder.SetSelectedIndex(0) - }) - - fileTypeOptions := []string{} - for _, fileType := range encoder2.GetListFileType() { - fileTypeOptions = append(fileTypeOptions, fileType.Name()) - } - - encoderGroupVideo := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "encoderGroupVideo"}) - encoderGroupAudio := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "encoderGroupAudio"}) - encoderGroupImage := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "encoderGroupImage"}) - encoderGroup := map[string]string{ - encoderGroupVideo: "video", - encoderGroupAudio: "audio", - encoderGroupImage: "image", - } - selectEncoder.SelectFileType = widget.NewRadioGroup([]string{encoderGroupVideo, encoderGroupAudio, encoderGroupImage}, func(s string) { - groupCode := encoderGroup[s] - - formatOptions := []string{} - for _, f := range formats.GetFormats() { - if groupCode != f.GetFileType().Name() { - continue - } - formatOptions = append(formatOptions, f.GetTitle()) - } - selectEncoder.SelectFormat.SetOptions(formatOptions) - if groupCode == encoder2.FileType(encoder2.Video).Name() { - selectEncoder.SelectFormat.SetSelected("mp4") - } else { - selectEncoder.SelectFormat.SetSelectedIndex(0) - } - }) - selectEncoder.SelectFileType.Horizontal = true - selectEncoder.SelectFileType.Required = true - selectEncoder.SelectFileType.SetSelected(encoderGroupVideo) - - return selectEncoder -} - -func (e *selectEncoder) ChangeEncoder(encoder encoder2.EncoderContract) { - e.Encoder = encoder - e.eventSelectEncoder(e.Encoder) -} - -func (e *selectEncoder) AddChangeCallback(callback func(encoder encoder2.EncoderContract)) { - e.changeCallbacks[len(e.changeCallbacks)] = callback -} - -func (e *selectEncoder) eventSelectEncoder(encoder encoder2.EncoderContract) { - for _, changeCallback := range e.changeCallbacks { - changeCallback(encoder) - } -} - -func setStringErrorStyle(text *canvas.Text) { - text.Color = color.RGBA{R: 255, G: 0, B: 0, A: 255} - text.Refresh() -} - -func setStringSuccessStyle(text *canvas.Text) { - text.Color = color.RGBA{R: 49, G: 127, B: 114, A: 255} - text.Refresh() -} - -func showConversionMessage(conversionMessage *canvas.Text, err error) { - conversionMessage.Text = err.Error() - setStringErrorStyle(conversionMessage) -} - -type form struct { - form *widget.Form - items []*widget.FormItem -} - -func newForm(app kernel.AppContract, items []*widget.FormItem) *form { - f := widget.NewForm() - f.SubmitText = app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "converterVideoFilesSubmitTitle", - }) - f.Items = items - - return &form{ - form: f, - items: items, - } -} - -func (f form) ChangeItems(items []*widget.FormItem) { - f.form.Items = f.items - f.form.Refresh() - f.form.Items = append(f.form.Items, items...) - f.form.Refresh() -} diff --git a/convertor/view/form_items/form.go b/convertor/view/form_items/form.go deleted file mode 100644 index c0d1a7d..0000000 --- a/convertor/view/form_items/form.go +++ /dev/null @@ -1,16 +0,0 @@ -package form_items - -import ( - "fyne.io/fyne/v2/widget" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/convertor/view/form_items/h264_nvenc" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/convertor/view/form_items/libx264" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/convertor/view/form_items/libx265" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" -) - -var Views = map[string]func(encoder encoder.EncoderContract, app kernel.AppContract) []*widget.FormItem{ - "libx264": libx264.View, - "h264_nvenc": h264_nvenc.View, - "libx265": libx265.View, -} diff --git a/convertor/view_setting.go b/convertor/view_setting.go deleted file mode 100644 index ca5e3f4..0000000 --- a/convertor/view_setting.go +++ /dev/null @@ -1,145 +0,0 @@ -package convertor - -import ( - "fyne.io/fyne/v2" - "fyne.io/fyne/v2/canvas" - "fyne.io/fyne/v2/container" - "fyne.io/fyne/v2/storage" - "fyne.io/fyne/v2/widget" - "github.com/nicksnyder/go-i18n/v2/i18n" - "image/color" - "net/url" - "path/filepath" -) - -func (v View) SelectFFPath( - currentPathFfmpeg string, - currentPathFfprobe string, - currentPathFfplay string, - save func(ffmpegPath string, ffprobePath string, ffplayPath string) error, - cancel func(), - donwloadFFmpeg func(progressBar *widget.ProgressBar, progressMessage *canvas.Text) error, -) { - errorMessage := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255}) - errorMessage.TextSize = 16 - errorMessage.TextStyle = fyne.TextStyle{Bold: true} - - ffmpegPath, buttonFFmpeg, buttonFFmpegMessage := v.getButtonSelectFile(currentPathFfmpeg) - ffprobePath, buttonFFprobe, buttonFFprobeMessage := v.getButtonSelectFile(currentPathFfprobe) - ffplayPath, buttonFFplay, buttonFFplayMessage := v.getButtonSelectFile(currentPathFfplay) - - link := widget.NewHyperlink("https://ffmpeg.org/download.html", &url.URL{ - Scheme: "https", - Host: "ffmpeg.org", - Path: "download.html", - }) - - form := &widget.Form{ - Items: []*widget.FormItem{ - { - Text: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "titleDownloadLink", - }), - Widget: link, - }, - { - Text: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "pathToFfmpeg", - }), - Widget: buttonFFmpeg, - }, - { - Widget: container.NewHScroll(buttonFFmpegMessage), - }, - { - Text: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "pathToFfprobe", - }), - Widget: buttonFFprobe, - }, - { - Widget: container.NewHScroll(buttonFFprobeMessage), - }, - { - Text: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "pathToFfplay", - }), - Widget: buttonFFplay, - }, - { - Widget: container.NewHScroll(buttonFFplayMessage), - }, - { - Widget: errorMessage, - }, - }, - SubmitText: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "save", - }), - OnSubmit: func() { - err := save(*ffmpegPath, *ffprobePath, *ffplayPath) - if err != nil { - errorMessage.Text = err.Error() - } - }, - } - if cancel != nil { - form.OnCancel = cancel - form.CancelText = v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "cancel", - }) - } - selectFFPathTitle := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "selectFFPathTitle", - }) - - if v.downloadFFmpeg.blockDownloadFFmpegContainer == nil { - v.downloadFFmpeg.blockDownloadFFmpegContainer = v.blockDownloadFFmpeg(donwloadFFmpeg) - } - - v.app.GetWindow().SetContent(widget.NewCard(selectFFPathTitle, "", container.NewVBox( - form, - v.downloadFFmpeg.blockDownloadFFmpegContainer, - ))) -} - -func (v View) getButtonSelectFile(path string) (filePath *string, button *widget.Button, buttonMessage *canvas.Text) { - filePath = &path - - buttonMessage = canvas.NewText(path, color.RGBA{R: 49, G: 127, B: 114, A: 255}) - buttonMessage.TextSize = 16 - buttonMessage.TextStyle = fyne.TextStyle{Bold: true} - - buttonTitle := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "choose", - }) - - var locationURI fyne.ListableURI - if len(path) > 0 { - listableURI := storage.NewFileURI(filepath.Dir(path)) - locationURI, _ = storage.ListerForURI(listableURI) - } - - button = widget.NewButton(buttonTitle, func() { - v.app.GetWindow().NewFileOpen(func(r fyne.URIReadCloser, err error) { - if err != nil { - buttonMessage.Text = err.Error() - setStringErrorStyle(buttonMessage) - return - } - if r == nil { - return - } - - path = r.URI().Path() - - buttonMessage.Text = r.URI().Path() - setStringSuccessStyle(buttonMessage) - - listableURI := storage.NewFileURI(filepath.Dir(r.URI().Path())) - locationURI, _ = storage.ListerForURI(listableURI) - }, locationURI) - }) - - return -} diff --git a/convertor/view_setting_button_download_ffmpeg_anyos.go b/convertor/view_setting_button_download_ffmpeg_anyos.go deleted file mode 100644 index 794fd91..0000000 --- a/convertor/view_setting_button_download_ffmpeg_anyos.go +++ /dev/null @@ -1,17 +0,0 @@ -//go:build !windows && !linux -// +build !windows,!linux - -package convertor - -import ( - "fyne.io/fyne/v2" - "fyne.io/fyne/v2/canvas" - "fyne.io/fyne/v2/container" - "fyne.io/fyne/v2/widget" -) - -func (v View) blockDownloadFFmpeg( - donwloadFFmpeg func(progressBar *widget.ProgressBar, progressMessage *canvas.Text) error, -) *fyne.Container { - return container.NewVBox() -} diff --git a/data/.gitignore b/data/.gitignore deleted file mode 100644 index c96a04f..0000000 --- a/data/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore \ No newline at end of file diff --git a/db/db.go b/db/db.go deleted file mode 100644 index ce2ddf9..0000000 --- a/db/db.go +++ /dev/null @@ -1,7 +0,0 @@ -package db - -import "errors" - -var ( - ErrRecordNotFound = errors.New("record not found") -) diff --git a/dist/linux/Makefile b/dist/linux/Makefile new file mode 100644 index 0000000..f8ba5e5 --- /dev/null +++ b/dist/linux/Makefile @@ -0,0 +1,39 @@ +# If PREFIX isn't provided, we check for $(DESTDIR)/usr/local and use that if it exists. +# Otherwice we fall back to using /usr. + +LOCAL != test -d $(DESTDIR)/usr/local && echo -n "/local" || echo -n "" +LOCAL ?= $(shell test -d $(DESTDIR)/usr/local && echo "/local" || echo "") +PREFIX ?= /usr$(LOCAL) + +Name := "gui-for-ffmpeg" +Exec := "gui-for-ffmpeg" +Icon := "gui-for-ffmpeg.png" + +default: + # User install + # Run "make user-install" to install in ~/.local/ + # Run "make user-uninstall" to uninstall from ~/.local/ + # + # System install + # Run "sudo make install" to install the application. + # Run "sudo make uninstall" to uninstall the application. + +install: + install -Dm00644 usr/local/share/applications/$(Name).desktop $(DESTDIR)$(PREFIX)/share/applications/$(Name).desktop + install -Dm00755 usr/local/bin/$(Exec) $(DESTDIR)$(PREFIX)/bin/$(Exec) + install -Dm00644 usr/local/share/pixmaps/$(Icon) $(DESTDIR)$(PREFIX)/share/pixmaps/$(Icon) +uninstall: + -rm $(DESTDIR)$(PREFIX)/share/applications/$(Name).desktop + -rm $(DESTDIR)$(PREFIX)/bin/$(Exec) + -rm $(DESTDIR)$(PREFIX)/share/pixmaps/$(Icon) + +user-install: + install -Dm00644 usr/local/share/applications/$(Name).desktop $(DESTDIR)$(HOME)/.local/share/applications/$(Name).desktop + install -Dm00755 usr/local/bin/$(Exec) $(DESTDIR)$(HOME)/.local/bin/$(Exec) + install -Dm00644 usr/local/share/pixmaps/$(Icon) $(DESTDIR)$(HOME)/.local/share/icons/$(Icon) + sed -i -e "s,Exec=$(Exec),Exec=$(DESTDIR)$(HOME)/.local/bin/$(Exec),g" $(DESTDIR)$(HOME)/.local/share/applications/$(Name).desktop + +user-uninstall: + -rm $(DESTDIR)$(HOME)/.local/share/applications/$(Name).desktop + -rm $(DESTDIR)$(HOME)/.local/bin/$(Exec) + -rm $(DESTDIR)$(HOME)/.local/share/icons/$(Icon) diff --git a/dist/linux/Readme-eng.txt b/dist/linux/Readme-eng.txt new file mode 100644 index 0000000..0548615 --- /dev/null +++ b/dist/linux/Readme-eng.txt @@ -0,0 +1,7 @@ +User install +Run "make user-install" to install in ~/.local/ +Run "make user-uninstall" to uninstall from ~/.local/ + +System install +Run "sudo make install" to install the application. +Run "sudo make uninstall" to uninstall the application. diff --git a/dist/linux/Readme-rus.txt b/dist/linux/Readme-rus.txt new file mode 100644 index 0000000..9331b2b --- /dev/null +++ b/dist/linux/Readme-rus.txt @@ -0,0 +1,7 @@ +Установить для пользователя (рекомендуется) +Запустите "make user-install" для установки в домашнюю папку ~/.local/ +Запустите "make user-uninstall" для удаления из домашней папки ~/.local/ + +Установить для всей системы +Запустить "sudo make install" Для установки в систему. +Запустить "sudo make uninstall" Для удаления из системы. diff --git a/dist/linux/usr/local/share/applications/gui-for-ffmpeg.desktop b/dist/linux/usr/local/share/applications/gui-for-ffmpeg.desktop new file mode 100644 index 0000000..cda0b2b --- /dev/null +++ b/dist/linux/usr/local/share/applications/gui-for-ffmpeg.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Application +Name=GUI for FFmpeg +GenericName=GUI for FFmpeg +Exec=gui-for-ffmpeg +Icon=gui-for-ffmpeg +Comment=A simple interface for the FFmpeg console utility. +Categories=AudioVideo;Utility; +Keywords=ffmpeg;media;convert;transcode;audio;video;конвертер;видео;аудио;кодек; diff --git a/encoder/apng/encoder.go b/encoder/apng/encoder.go deleted file mode 100644 index c43b48a..0000000 --- a/encoder/apng/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package apng - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:v", "apng"} - } - - return encoder2.NewEncoder("apng", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "apng" - formats := []string{"apng"} - fileType := encoder2.FileType(encoder2.Image) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/bmp/encoder.go b/encoder/bmp/encoder.go deleted file mode 100644 index f72a6d9..0000000 --- a/encoder/bmp/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package bmp - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:v", "bmp"} - } - - return encoder2.NewEncoder("bmp", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "bmp" - formats := []string{"bmp"} - fileType := encoder2.FileType(encoder2.Image) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/flv/encoder.go b/encoder/flv/encoder.go deleted file mode 100644 index 7c2c8e4..0000000 --- a/encoder/flv/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package flv - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:v", "flv"} - } - - return encoder2.NewEncoder("flv", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "flv" - formats := []string{"flv"} - fileType := encoder2.FileType(encoder2.Video) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/gif/encoder.go b/encoder/gif/encoder.go deleted file mode 100644 index d138ae1..0000000 --- a/encoder/gif/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package gif - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:v", "gif"} - } - - return encoder2.NewEncoder("gif", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "gif" - formats := []string{"gif"} - fileType := encoder2.FileType(encoder2.Image) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/libmp3lame/encoder.go b/encoder/libmp3lame/encoder.go deleted file mode 100644 index 33c2f1a..0000000 --- a/encoder/libmp3lame/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package libmp3lame - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:a", "libmp3lame"} - } - - return encoder2.NewEncoder("libmp3lame", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "libmp3lame" - formats := []string{"mp3"} - fileType := encoder2.FileType(encoder2.Audio) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/libshine/encoder.go b/encoder/libshine/encoder.go deleted file mode 100644 index d7859b4..0000000 --- a/encoder/libshine/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package libshine - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:a", "libshine"} - } - - return encoder2.NewEncoder("libshine", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "libshine" - formats := []string{"mp3"} - fileType := encoder2.FileType(encoder2.Audio) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/libtwolame/encoder.go b/encoder/libtwolame/encoder.go deleted file mode 100644 index 87f5ba4..0000000 --- a/encoder/libtwolame/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package libtwolame - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:a", "libtwolame"} - } - - return encoder2.NewEncoder("libtwolame", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "libtwolame" - formats := []string{"mp2"} - fileType := encoder2.FileType(encoder2.Audio) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/libvpx/encoder.go b/encoder/libvpx/encoder.go deleted file mode 100644 index 86ab780..0000000 --- a/encoder/libvpx/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package libvpx - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:v", "libvpx"} - } - - return encoder2.NewEncoder("libvpx", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "libvpx" - formats := []string{"webm", "mkv"} - fileType := encoder2.FileType(encoder2.Video) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/libvpx_vp9/encoder.go b/encoder/libvpx_vp9/encoder.go deleted file mode 100644 index 00cecc3..0000000 --- a/encoder/libvpx_vp9/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package libvpx_vp9 - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:v", "libvpx-vp9"} - } - - return encoder2.NewEncoder("libvpx_vp9", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "libvpx-vp9" - formats := []string{"webm", "mkv"} - fileType := encoder2.FileType(encoder2.Video) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/libwebp/encoder.go b/encoder/libwebp/encoder.go deleted file mode 100644 index b324503..0000000 --- a/encoder/libwebp/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package libwebp - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:v", "libwebp"} - } - - return encoder2.NewEncoder("libwebp", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "libwebp" - formats := []string{"webp"} - fileType := encoder2.FileType(encoder2.Image) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/libwebp_anim/encoder.go b/encoder/libwebp_anim/encoder.go deleted file mode 100644 index 0a6fcc5..0000000 --- a/encoder/libwebp_anim/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package libwebp_anim - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:v", "libwebp_anim"} - } - - return encoder2.NewEncoder("libwebp_anim", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "libwebp_anim" - formats := []string{"webp"} - fileType := encoder2.FileType(encoder2.Image) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/libxvid/encoder.go b/encoder/libxvid/encoder.go deleted file mode 100644 index fca496a..0000000 --- a/encoder/libxvid/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package libxvid - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:v", "libxvid"} - } - - return encoder2.NewEncoder("libxvid", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "libxvid" - formats := []string{"avi"} - fileType := encoder2.FileType(encoder2.Video) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/mjpeg/encoder.go b/encoder/mjpeg/encoder.go deleted file mode 100644 index be934e7..0000000 --- a/encoder/mjpeg/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package mjpeg - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:v", "mjpeg"} - } - - return encoder2.NewEncoder("mjpeg", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "mjpeg" - formats := []string{"jpg"} - fileType := encoder2.FileType(encoder2.Image) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/mp2/encoder.go b/encoder/mp2/encoder.go deleted file mode 100644 index 3d1a98e..0000000 --- a/encoder/mp2/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package mp2 - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:a", "mp2"} - } - - return encoder2.NewEncoder("mp2", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "mp2" - formats := []string{"mp2"} - fileType := encoder2.FileType(encoder2.Audio) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/mp2fixed/encoder.go b/encoder/mp2fixed/encoder.go deleted file mode 100644 index c235bee..0000000 --- a/encoder/mp2fixed/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package mp2fixed - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:a", "mp2fixed"} - } - - return encoder2.NewEncoder("mp2fixed", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "mp2fixed" - formats := []string{"mp2"} - fileType := encoder2.FileType(encoder2.Audio) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/mpeg1video/encoder.go b/encoder/mpeg1video/encoder.go deleted file mode 100644 index 11c43e3..0000000 --- a/encoder/mpeg1video/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package mpeg1video - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:v", "mpeg1video"} - } - - return encoder2.NewEncoder("mpeg1video", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "mpeg1video" - formats := []string{"mpg", "mpeg"} - fileType := encoder2.FileType(encoder2.Video) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/mpeg2video/encoder.go b/encoder/mpeg2video/encoder.go deleted file mode 100644 index 05192ef..0000000 --- a/encoder/mpeg2video/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package mpeg2video - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:v", "mpeg2video"} - } - - return encoder2.NewEncoder("mpeg2video", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "mpeg2video" - formats := []string{"mpg", "mpeg"} - fileType := encoder2.FileType(encoder2.Video) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/mpeg4/encoder.go b/encoder/mpeg4/encoder.go deleted file mode 100644 index 5943f35..0000000 --- a/encoder/mpeg4/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package mpeg4 - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:v", "mpeg4"} - } - - return encoder2.NewEncoder("mpeg4", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "mpeg4" - formats := []string{"avi"} - fileType := encoder2.FileType(encoder2.Video) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/msmpeg4/encoder.go b/encoder/msmpeg4/encoder.go deleted file mode 100644 index 246683d..0000000 --- a/encoder/msmpeg4/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package msmpeg4 - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:v", "msmpeg4"} - } - - return encoder2.NewEncoder("msmpeg4", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "msmpeg4" - formats := []string{"avi"} - fileType := encoder2.FileType(encoder2.Video) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/msmpeg4v2/encoder.go b/encoder/msmpeg4v2/encoder.go deleted file mode 100644 index cc2cd5f..0000000 --- a/encoder/msmpeg4v2/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package msmpeg4v2 - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:v", "msmpeg4v2"} - } - - return encoder2.NewEncoder("msmpeg4v2", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "msmpeg4v2" - formats := []string{"avi"} - fileType := encoder2.FileType(encoder2.Video) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/msvideo1/encoder.go b/encoder/msvideo1/encoder.go deleted file mode 100644 index e9bd448..0000000 --- a/encoder/msvideo1/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package msvideo1 - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:v", "msvideo1"} - } - - return encoder2.NewEncoder("msvideo1", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "msvideo1" - formats := []string{"avi"} - fileType := encoder2.FileType(encoder2.Video) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/png/encoder.go b/encoder/png/encoder.go deleted file mode 100644 index 3166e58..0000000 --- a/encoder/png/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package png - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:v", "png"} - } - - return encoder2.NewEncoder("png", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "png" - formats := []string{"png"} - fileType := encoder2.FileType(encoder2.Image) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/qtrle/encoder.go b/encoder/qtrle/encoder.go deleted file mode 100644 index befac18..0000000 --- a/encoder/qtrle/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package qtrle - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:v", "qtrle"} - } - - return encoder2.NewEncoder("qtrle", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "qtrle" - formats := []string{"mov"} - fileType := encoder2.FileType(encoder2.Video) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/sgi/encoder.go b/encoder/sgi/encoder.go deleted file mode 100644 index b666b9b..0000000 --- a/encoder/sgi/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package sgi - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:v", "sgi"} - } - - return encoder2.NewEncoder("sgi", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "sgi" - formats := []string{"sgi"} - fileType := encoder2.FileType(encoder2.Image) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/tiff/encoder.go b/encoder/tiff/encoder.go deleted file mode 100644 index 788d743..0000000 --- a/encoder/tiff/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package tiff - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:v", "tiff"} - } - - return encoder2.NewEncoder("tiff", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "tiff" - formats := []string{"tiff"} - fileType := encoder2.FileType(encoder2.Image) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/wmav1/encoder.go b/encoder/wmav1/encoder.go deleted file mode 100644 index a2b9239..0000000 --- a/encoder/wmav1/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package wmav1 - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:a", "wmav1"} - } - - return encoder2.NewEncoder("wmav1", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "wmav1" - formats := []string{"wma"} - fileType := encoder2.FileType(encoder2.Audio) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/wmav2/encoder.go b/encoder/wmav2/encoder.go deleted file mode 100644 index 66f490c..0000000 --- a/encoder/wmav2/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package wmav2 - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:a", "wmav2"} - } - - return encoder2.NewEncoder("wmav2", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "wmav2" - formats := []string{"wma"} - fileType := encoder2.FileType(encoder2.Audio) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/wmv1/encoder.go b/encoder/wmv1/encoder.go deleted file mode 100644 index e5cf873..0000000 --- a/encoder/wmv1/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package wmv1 - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:v", "wmv1"} - } - - return encoder2.NewEncoder("wmv1", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "wmv1" - formats := []string{"wmv"} - fileType := encoder2.FileType(encoder2.Video) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/wmv2/encoder.go b/encoder/wmv2/encoder.go deleted file mode 100644 index 4ca9797..0000000 --- a/encoder/wmv2/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package wmv2 - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:v", "wmv2"} - } - - return encoder2.NewEncoder("wmv2", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "wmv2" - formats := []string{"wmv"} - fileType := encoder2.FileType(encoder2.Video) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/encoder/xbm/encoder.go b/encoder/xbm/encoder.go deleted file mode 100644 index d23f4a8..0000000 --- a/encoder/xbm/encoder.go +++ /dev/null @@ -1,19 +0,0 @@ -package xbm - -import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{} - getParams := func(parameters map[string]encoder2.ParameterContract) []string { - return []string{"-c:v", "xbm"} - } - - return encoder2.NewEncoder("xbm", parameters, getParams) -} - -func NewData() encoder2.EncoderDataContract { - title := "xbm" - formats := []string{"xbm"} - fileType := encoder2.FileType(encoder2.Image) - return encoder2.NewData(title, formats, fileType, NewEncoder) -} diff --git a/error/view.go b/error/view.go deleted file mode 100644 index 2d68856..0000000 --- a/error/view.go +++ /dev/null @@ -1,86 +0,0 @@ -package error - -import ( - "errors" - "fyne.io/fyne/v2/container" - "fyne.io/fyne/v2/lang" - "fyne.io/fyne/v2/widget" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/localizer" - "github.com/nicksnyder/go-i18n/v2/i18n" - "go.etcd.io/bbolt" -) - -type ViewContract interface { - PanicError(err error) -} - -type View struct { - app kernel.AppContract - isSetLanguage bool -} - -func NewView(app kernel.AppContract) *View { - return &View{ - app: app, - isSetLanguage: true, - } -} - -func (v View) PanicError(err error) { - if v.isSetLanguage { - v.isSetLanguage = false - _ = v.app.GetLocalizerService().SetCurrentLanguageByCode(lang.SystemLocale().LanguageString()) - } - - messageHead := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "error", - }) - - messagetText := err.Error() - if errors.Is(err, bbolt.ErrTimeout) { - messagetText = v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "errorDatabaseTimeout", - }) - } - - v.app.GetWindow().SetContent(container.NewBorder( - container.NewVBox( - widget.NewLabel(messageHead), - widget.NewLabel(messagetText), - ), - nil, - nil, - nil, - localizer.LanguageSelectionForm(v.app.GetLocalizerService(), func(lang kernel.Lang) { - v.PanicError(err) - }), - )) -} - -func (v View) PanicErrorWriteDirectoryData() { - if v.isSetLanguage { - v.isSetLanguage = false - _ = v.app.GetLocalizerService().SetCurrentLanguageByCode(lang.SystemLocale().LanguageString()) - } - - message := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "errorDatabase", - }) - messageHead := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "error", - }) - - v.app.GetWindow().SetContent(container.NewBorder( - container.NewVBox( - widget.NewLabel(messageHead), - widget.NewLabel(message), - ), - nil, - nil, - nil, - localizer.LanguageSelectionForm(v.app.GetLocalizerService(), func(lang kernel.Lang) { - v.PanicErrorWriteDirectoryData() - }), - )) -} diff --git a/go.mod b/go.mod index 721120e..2c96e56 100644 --- a/go.mod +++ b/go.mod @@ -6,16 +6,13 @@ toolchain go1.23.9 require ( fyne.io/fyne/v2 v2.6.1 - github.com/BurntSushi/toml v1.5.0 - github.com/nicksnyder/go-i18n/v2 v2.6.0 github.com/ulikunitz/xz v0.5.12 - go.etcd.io/bbolt v1.4.0 golang.org/x/image v0.27.0 - golang.org/x/text v0.25.0 ) require ( fyne.io/systray v1.11.0 // indirect + github.com/BurntSushi/toml v1.5.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fredbi/uri v1.1.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect @@ -34,6 +31,7 @@ require ( github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect github.com/kr/text v0.2.0 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect + github.com/nicksnyder/go-i18n/v2 v2.6.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rymdport/portal v0.4.1 // indirect github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect @@ -42,5 +40,6 @@ require ( github.com/yuin/goldmark v1.7.11 // indirect golang.org/x/net v0.40.0 // indirect golang.org/x/sys v0.33.0 // indirect + golang.org/x/text v0.25.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 9db999c..2efe54d 100644 --- a/go.sum +++ b/go.sum @@ -67,14 +67,10 @@ github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/yuin/goldmark v1.7.11 h1:ZCxLyDMtz0nT2HFfsYG8WZ47Trip2+JyLysKcMYE5bo= github.com/yuin/goldmark v1.7.11/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= -go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= -go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w= golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g= golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= diff --git a/handler/convertor.go b/handler/convertor.go deleted file mode 100644 index a0cd29b..0000000 --- a/handler/convertor.go +++ /dev/null @@ -1,185 +0,0 @@ -package handler - -import ( - "errors" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/convertor" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/convertor/view" - error2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/error" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/helper" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/setting" - "github.com/nicksnyder/go-i18n/v2/i18n" -) - -type ConvertorHandlerContract interface { - MainConvertor() - FfPathSelection() - GetFfmpegVersion() (string, error) - GetFfprobeVersion() (string, error) - GetFfplayVersion() (string, error) -} - -type ConvertorHandler struct { - app kernel.AppContract - convertorView convertor.ViewContract - errorView error2.ViewContract - convertorRepository convertor.RepositoryContract - settingDirectoryForSaving setting.DirectoryForSavingContract - itemsToConvertService kernel.ItemsToConvertContract -} - -func NewConvertorHandler( - app kernel.AppContract, - convertorView convertor.ViewContract, - errorView error2.ViewContract, - convertorRepository convertor.RepositoryContract, - settingDirectoryForSaving setting.DirectoryForSavingContract, - itemsToConvertService kernel.ItemsToConvertContract, -) *ConvertorHandler { - return &ConvertorHandler{ - app: app, - convertorView: convertorView, - errorView: errorView, - convertorRepository: convertorRepository, - settingDirectoryForSaving: settingDirectoryForSaving, - itemsToConvertService: itemsToConvertService, - } -} - -func (h ConvertorHandler) MainConvertor() { - if h.checkingFFPathUtilities() == true { - formats, err := h.app.GetConvertorService().GetSupportFormats() - if err != nil { - h.errorView.PanicError(err) - return - } - conversion := view.NewConversion(h.app, formats, h.runConvert, h.settingDirectoryForSaving, h.itemsToConvertService) - h.convertorView.Main(conversion) - return - } - h.convertorView.SelectFFPath("", "", "", h.saveSettingFFPath, nil, h.downloadFFmpeg) -} - -func (h ConvertorHandler) FfPathSelection() { - ffmpeg, _ := h.convertorRepository.GetPathFfmpeg() - ffprobe, _ := h.convertorRepository.GetPathFfprobe() - ffplay, _ := h.convertorRepository.GetPathFfplay() - h.convertorView.SelectFFPath(ffmpeg, ffprobe, ffplay, h.saveSettingFFPath, h.MainConvertor, h.downloadFFmpeg) -} - -func (h ConvertorHandler) GetFfmpegVersion() (string, error) { - return h.app.GetConvertorService().GetFFmpegVesrion() -} - -func (h ConvertorHandler) GetFfprobeVersion() (string, error) { - return h.app.GetConvertorService().GetFFprobeVersion() -} - -func (h ConvertorHandler) GetFfplayVersion() (string, error) { - return h.app.GetConvertorService().GetFFplayVersion() -} - -func (h ConvertorHandler) runConvert(setting view.HandleConvertSetting) { - h.app.GetWindow().GetLayout().GetRightTabs().SelectFileQueueTab() - - for _, item := range h.itemsToConvertService.GetItems() { - file := item.GetFile() - if file == nil { - continue - } - - h.app.GetQueue().Add(&kernel.ConvertSetting{ - VideoFileInput: *file, - VideoFileOut: kernel.File{ - Path: setting.DirectoryForSave + helper.PathSeparator() + file.Name + "." + setting.Format, - Name: file.Name, - Ext: "." + setting.Format, - }, - OverwriteOutputFiles: setting.OverwriteOutputFiles, - Encoder: setting.Encoder, - }) - } - h.itemsToConvertService.AfterAddingQueue() -} - -func (h ConvertorHandler) checkingFFPathUtilities() bool { - if h.checkingFFPath() == true { - return true - } - - pathsToFF := getPathsToFF() - for _, item := range pathsToFF { - ffmpegChecking, _ := h.app.GetConvertorService().ChangeFFmpegPath(item.FFmpeg) - if ffmpegChecking == false { - continue - } - ffprobeChecking, _ := h.app.GetConvertorService().ChangeFFprobePath(item.FFprobe) - if ffprobeChecking == false { - continue - } - - ffplayChecking, _ := h.app.GetConvertorService().ChangeFFplayPath(item.FFplay) - if ffplayChecking == false { - continue - } - _, _ = h.convertorRepository.SavePathFfmpeg(item.FFmpeg) - _, _ = h.convertorRepository.SavePathFfprobe(item.FFprobe) - _, _ = h.convertorRepository.SavePathFfplay(item.FFplay) - return true - } - - return false -} - -func (h ConvertorHandler) saveSettingFFPath(ffmpegPath string, ffprobePath string, ffplayPath string) error { - ffmpegChecking, _ := h.app.GetConvertorService().ChangeFFmpegPath(ffmpegPath) - if ffmpegChecking == false { - errorText := h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "errorFFmpeg", - }) - return errors.New(errorText) - } - - ffprobeChecking, _ := h.app.GetConvertorService().ChangeFFprobePath(ffprobePath) - if ffprobeChecking == false { - errorText := h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "errorFFprobe", - }) - return errors.New(errorText) - } - - ffplayChecking, _ := h.app.GetConvertorService().ChangeFFplayPath(ffplayPath) - if ffplayChecking == false { - errorText := h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "errorFFplay", - }) - return errors.New(errorText) - } - - _, _ = h.convertorRepository.SavePathFfmpeg(ffmpegPath) - _, _ = h.convertorRepository.SavePathFfprobe(ffprobePath) - _, _ = h.convertorRepository.SavePathFfplay(ffplayPath) - - h.MainConvertor() - - return nil -} - -func (h ConvertorHandler) checkingFFPath() bool { - _, err := h.app.GetConvertorService().GetFFmpegVesrion() - if err != nil { - return false - } - - _, err = h.app.GetConvertorService().GetFFprobeVersion() - if err != nil { - return false - } - - _, err = h.app.GetConvertorService().GetFFplayVersion() - if err != nil { - return false - } - - return true -} diff --git a/handler/convertor_anyos.go b/handler/convertor_anyos.go deleted file mode 100644 index 76daa45..0000000 --- a/handler/convertor_anyos.go +++ /dev/null @@ -1,18 +0,0 @@ -//go:build !windows && !linux -// +build !windows,!linux - -package handler - -import ( - "fyne.io/fyne/v2/canvas" - "fyne.io/fyne/v2/widget" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" -) - -func getPathsToFF() []kernel.FFPathUtilities { - return []kernel.FFPathUtilities{{FFmpeg: "ffmpeg/bin/ffmpeg", FFprobe: "ffmpeg/bin/ffprobe", FFplay: "ffmpeg/bin/ffplay"}, {FFmpeg: "ffmpeg", FFprobe: "ffprobe", FFplay: "ffplay"}} -} - -func (h ConvertorHandler) downloadFFmpeg(progressBar *widget.ProgressBar, progressMessage *canvas.Text) (err error) { - return nil -} diff --git a/handler/main.go b/handler/main.go deleted file mode 100644 index 0e97c6f..0000000 --- a/handler/main.go +++ /dev/null @@ -1,42 +0,0 @@ -package handler - -import ( - "fyne.io/fyne/v2/lang" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/localizer" -) - -type MainHandler struct { - app kernel.AppContract - convertorHandler ConvertorHandlerContract - menuHandler MenuHandlerContract - localizerRepository localizer.RepositoryContract -} - -func NewMainHandler( - app kernel.AppContract, - convertorHandler ConvertorHandlerContract, - menuHandler MenuHandlerContract, - localizerRepository localizer.RepositoryContract, -) *MainHandler { - return &MainHandler{ - app: app, - convertorHandler: convertorHandler, - menuHandler: menuHandler, - localizerRepository: localizerRepository, - } -} - -func (h MainHandler) Start() { - language, err := h.localizerRepository.GetCode() - if err != nil { - err = h.app.GetLocalizerService().SetCurrentLanguageByCode(lang.SystemLocale().LanguageString()) - if err != nil { - h.menuHandler.LanguageSelection() - return - } - } - _ = h.app.GetLocalizerService().SetCurrentLanguageByCode(language) - - h.convertorHandler.MainConvertor() -} diff --git a/handler/menu.go b/handler/menu.go deleted file mode 100644 index fa32d8b..0000000 --- a/handler/menu.go +++ /dev/null @@ -1,174 +0,0 @@ -package handler - -import ( - "fyne.io/fyne/v2" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/localizer" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/menu" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/theme" - "github.com/nicksnyder/go-i18n/v2/i18n" -) - -type MenuHandlerContract interface { - GetMainMenu() *fyne.MainMenu - LanguageSelection() -} - -type MenuHandler struct { - app kernel.AppContract - convertorHandler ConvertorHandlerContract - menuView menu.ViewContract - menuViewSetting menu.ViewSettingContract - localizerView localizer.ViewContract - localizerRepository localizer.RepositoryContract - themeService theme.ThemeContract -} - -func NewMenuHandler( - app kernel.AppContract, - convertorHandler ConvertorHandlerContract, - menuView menu.ViewContract, - menuViewSetting menu.ViewSettingContract, - localizerView localizer.ViewContract, - localizerRepository localizer.RepositoryContract, - themeService theme.ThemeContract, -) *MenuHandler { - return &MenuHandler{ - app: app, - convertorHandler: convertorHandler, - menuView: menuView, - menuViewSetting: menuViewSetting, - localizerView: localizerView, - localizerRepository: localizerRepository, - themeService: themeService, - } -} - -func (h MenuHandler) GetMainMenu() *fyne.MainMenu { - settings := h.getMenuSettings() - help := h.getMenuHelp() - - return fyne.NewMainMenu(settings, help) -} - -func (h MenuHandler) getMenuSettings() *fyne.Menu { - quit := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "exit", - }), nil) - quit.IsQuit = true - h.app.GetLocalizerService().AddChangeCallback("exit", func(text string) { - quit.Label = text - }) - - settingsSelection := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "settings", - }), h.settingsSelection) - h.app.GetLocalizerService().AddChangeCallback("settings", func(text string) { - settingsSelection.Label = text - }) - - ffPathSelection := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "changeFFPath", - }), h.convertorHandler.FfPathSelection) - h.app.GetLocalizerService().AddChangeCallback("changeFFPath", func(text string) { - ffPathSelection.Label = text - }) - - settings := fyne.NewMenu(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "settings", - }), settingsSelection, ffPathSelection, quit) - h.app.GetLocalizerService().AddChangeCallback("settings", func(text string) { - settings.Label = text - settings.Refresh() - }) - - return settings -} - -func (h MenuHandler) getMenuHelp() *fyne.Menu { - about := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "about", - }), h.openAbout) - h.app.GetLocalizerService().AddChangeCallback("about", func(text string) { - about.Label = text - }) - - gratitude := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "gratitude", - }), h.menuView.Gratitude) - h.app.GetLocalizerService().AddChangeCallback("gratitude", func(text string) { - gratitude.Label = text - }) - - helpFFplay := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplay", - }), h.menuView.HelpFFplay) - h.app.GetLocalizerService().AddChangeCallback("helpFFplay", func(text string) { - helpFFplay.Label = text - }) - - help := fyne.NewMenu(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "help", - }), helpFFplay, about, gratitude) - h.app.GetLocalizerService().AddChangeCallback("help", func(text string) { - help.Label = text - help.Refresh() - }) - - return help -} - -func (h MenuHandler) openAbout() { - ffmpeg, err := h.convertorHandler.GetFfmpegVersion() - if err != nil { - ffmpeg = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "errorFFmpegVersion", - }) - } - ffprobe, err := h.convertorHandler.GetFfprobeVersion() - if err != nil { - ffprobe = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "errorFFprobeVersion", - }) - } - ffplay, err := h.convertorHandler.GetFfplayVersion() - if err != nil { - ffplay = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "errorFFplayVersion", - }) - } - - h.menuView.About(ffmpeg, ffprobe, ffplay) -} - -func (h MenuHandler) LanguageSelection() { - h.localizerView.LanguageSelection(func(lang kernel.Lang) { - _, _ = h.localizerRepository.Save(lang.Code) - h.convertorHandler.MainConvertor() - }) -} - -func (h MenuHandler) settingsSelection() { - save := func(setting *menu.SettingForm) error { - err := h.app.GetLocalizerService().SetCurrentLanguage(setting.Language) - if err != nil { - return err - } - _, err = h.localizerRepository.Save(setting.Language.Code) - if err != nil { - return err - } - - err = h.themeService.SetCurrentTheme(setting.ThemeInfo) - if err != nil { - return err - } - - h.convertorHandler.MainConvertor() - return nil - } - cancel := func() { - h.convertorHandler.MainConvertor() - } - h.menuViewSetting.Main(save, cancel) -} diff --git a/images/screenshot-folder-structure.png b/images/screenshot-folder-structure.png deleted file mode 100644 index 19b2a3f..0000000 Binary files a/images/screenshot-folder-structure.png and /dev/null differ diff --git a/internal/application/app.go b/internal/application/app.go new file mode 100644 index 0000000..dc26bf3 --- /dev/null +++ b/internal/application/app.go @@ -0,0 +1,137 @@ +package application + +import ( + "fyne.io/fyne/v2" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application/convertor" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application/setting" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg" + "time" +) + +type AppContract interface { + FyneApp() fyne.App + GetSetting() setting.SettingContract + GetProgressBarService() convertor.ProgressBarContract + GetFFmpegService() ffmpeg.UtilitiesContract + GetConvertorService() convertor.ConvertorContract + GetItemsToConvert() convertor.ItemsToConvertContract + GetQueueService() convertor.QueueListContract + Run() + AfterClosing() + RunConvertor() +} + +type application struct { + fyneApp fyne.App + setting setting.SettingContract + progressBarService convertor.ProgressBarContract + ffmpegService ffmpeg.UtilitiesContract + itemsToConvert convertor.ItemsToConvertContract + queueService convertor.QueueListContract + convertorService convertor.ConvertorContract +} + +func NewApp( + fyneApp fyne.App, + setting setting.SettingContract, + progressBarService convertor.ProgressBarContract, + ffmpegService ffmpeg.UtilitiesContract, + itemsToConvert convertor.ItemsToConvertContract, + queueService convertor.QueueListContract, + convertorService convertor.ConvertorContract, +) AppContract { + return &application{ + fyneApp: fyneApp, + setting: setting, + progressBarService: progressBarService, + ffmpegService: ffmpegService, + itemsToConvert: itemsToConvert, + queueService: queueService, + convertorService: convertorService, + } +} + +func (a *application) FyneApp() fyne.App { + return a.fyneApp +} + +func (a *application) GetSetting() setting.SettingContract { + return a.setting +} + +func (a *application) GetProgressBarService() convertor.ProgressBarContract { + return a.progressBarService +} + +func (a *application) GetFFmpegService() ffmpeg.UtilitiesContract { + return a.ffmpegService +} + +func (a *application) GetItemsToConvert() convertor.ItemsToConvertContract { + return a.itemsToConvert +} + +func (a *application) GetQueueService() convertor.QueueListContract { + return a.queueService +} + +func (a *application) GetConvertorService() convertor.ConvertorContract { + return a.convertorService +} + +func (a *application) Run() { + a.fyneApp.Run() +} + +func (a *application) RunConvertor() { + go func() { + for { + time.Sleep(time.Millisecond * 3000) + queueId, queue := a.queueService.Next() + if queue == nil { + continue + } + + queue.Status = convertor.StatusType(convertor.InProgress) + a.queueService.EventChangeQueue(queueId, queue) + + if a.progressBarService.GetContainer().Hidden { + a.progressBarService.GetContainer().Show() + } + + totalDuration := float64(0) + ffprobe, err := a.ffmpegService.GetFFprobe() + if err == nil { + totalDuration, err = ffprobe.GetTotalDuration(&queue.Setting.FileInput) + if err != nil { + totalDuration = float64(0) + } + } + + progress := a.progressBarService.GetProgressbar( + totalDuration, + queue.Setting.FileInput.Path, + ) + + err = a.convertorService.RunConvert(*queue.Setting, progress) + if err != nil { + queue.Status = convertor.StatusType(convertor.Error) + queue.Error = err + a.queueService.EventChangeQueue(queueId, queue) + a.progressBarService.ProcessEndedWithError(err.Error()) + + continue + } + + queue.Status = convertor.StatusType(convertor.Completed) + a.queueService.EventChangeQueue(queueId, queue) + a.progressBarService.ProcessEndedWithSuccess(&queue.Setting.FileOut) + } + }() +} + +func (a *application) AfterClosing() { + for _, cmd := range a.convertorService.GetRunningProcesses() { + _ = cmd.Process.Kill() + } +} diff --git a/internal/application/convertor/convertor.go b/internal/application/convertor/convertor.go new file mode 100644 index 0000000..2cce78b --- /dev/null +++ b/internal/application/convertor/convertor.go @@ -0,0 +1,87 @@ +package convertor + +import ( + "bufio" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application/convertor/encoder" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg" + "io" + "os/exec" + "strings" +) + +type ConvertorContract interface { + RunConvert(setting ffmpeg.ConvertSetting, progress ffmpeg.ProgressContract) error + GetSupportFormats() (encoder.ConvertorFormatsContract, error) + GetRunningProcesses() map[int]*exec.Cmd +} + +type runningProcesses struct { + items map[int]*exec.Cmd + numberOfStarts int +} + +type convertor struct { + ffmpegService ffmpeg.UtilitiesContract + runningProcesses *runningProcesses +} + +func NewConvertor( + ffmpegService ffmpeg.UtilitiesContract, +) ConvertorContract { + return &convertor{ + ffmpegService: ffmpegService, + runningProcesses: &runningProcesses{items: map[int]*exec.Cmd{}, numberOfStarts: 0}, + } +} + +func (c *convertor) RunConvert(setting ffmpeg.ConvertSetting, progress ffmpeg.ProgressContract) error { + ffmpegService, err := c.ffmpegService.GetFFmpeg() + if err != nil { + return err + } + + index := c.runningProcesses.numberOfStarts + beforeWait := func(cmd *exec.Cmd) { + c.runningProcesses.numberOfStarts++ + c.runningProcesses.items[index] = cmd + } + + afterWait := func(cmd *exec.Cmd) { + delete(c.runningProcesses.items, index) + } + + return ffmpegService.RunConvert(setting, progress, beforeWait, afterWait) +} + +func (c *convertor) GetSupportFormats() (encoder.ConvertorFormatsContract, error) { + var err error + + formats := encoder.NewConvertorFormats() + ffmpeg, err := c.ffmpegService.GetFFmpeg() + if err != nil { + return formats, err + } + err = ffmpeg.GetEncoders(func(scanner *bufio.Reader) { + for { + line, _, err := scanner.ReadLine() + if err != nil { + if err == io.EOF { + break + } + continue + } + text := strings.Split(strings.TrimSpace(string(line)), " ") + encoderType := string(text[0][0]) + if len(text) < 2 || (encoderType != "V" && encoderType != "A") { + continue + } + formats.NewEncoder(text[1]) + } + }) + + return formats, err +} + +func (c *convertor) GetRunningProcesses() map[int]*exec.Cmd { + return c.runningProcesses.items +} diff --git a/kernel/encoder/encoder.go b/internal/application/convertor/encoder/encoder.go similarity index 66% rename from kernel/encoder/encoder.go rename to internal/application/convertor/encoder/encoder.go index db4e25f..0da7a01 100644 --- a/kernel/encoder/encoder.go +++ b/internal/application/convertor/encoder/encoder.go @@ -2,7 +2,7 @@ package encoder import ( "errors" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" ) type ConvertorFormatContract interface { @@ -18,7 +18,7 @@ type ConvertorFormat struct { encoders map[int]encoder.EncoderDataContract } -func NewConvertorFormat(title string, fileType encoder.FileTypeContract) *ConvertorFormat { +func NewConvertorFormat(title string, fileType encoder.FileTypeContract) ConvertorFormatContract { return &ConvertorFormat{ title: title, fileType: fileType, @@ -26,19 +26,19 @@ func NewConvertorFormat(title string, fileType encoder.FileTypeContract) *Conver } } -func (f ConvertorFormat) GetTitle() string { +func (f *ConvertorFormat) GetTitle() string { return f.title } -func (f ConvertorFormat) AddEncoder(encoder encoder.EncoderDataContract) { +func (f *ConvertorFormat) AddEncoder(encoder encoder.EncoderDataContract) { f.encoders[len(f.encoders)] = encoder } -func (f ConvertorFormat) GetEncoders() map[int]encoder.EncoderDataContract { +func (f *ConvertorFormat) GetEncoders() map[int]encoder.EncoderDataContract { return f.encoders } -func (f ConvertorFormat) GetFileType() encoder.FileTypeContract { +func (f *ConvertorFormat) GetFileType() encoder.FileTypeContract { return f.fileType } @@ -52,13 +52,13 @@ type ConvertorFormats struct { formats map[string]ConvertorFormatContract } -func NewConvertorFormats() *ConvertorFormats { +func NewConvertorFormats() ConvertorFormatsContract { return &ConvertorFormats{ formats: map[string]ConvertorFormatContract{}, } } -func (f ConvertorFormats) NewEncoder(encoderName string) bool { +func (f *ConvertorFormats) NewEncoder(encoderName string) bool { if supportEncoders[encoderName] == nil { return false } @@ -72,13 +72,13 @@ func (f ConvertorFormats) NewEncoder(encoderName string) bool { return true } -func (f ConvertorFormats) GetFormats() map[string]ConvertorFormatContract { +func (f *ConvertorFormats) GetFormats() map[string]ConvertorFormatContract { return f.formats } -func (f ConvertorFormats) GetFormat(format string) (ConvertorFormatContract, error) { +func (f *ConvertorFormats) GetFormat(format string) (ConvertorFormatContract, error) { if f.formats[format] == nil { - return ConvertorFormat{}, errors.New("not found ConvertorFormat") + return &ConvertorFormat{}, errors.New("not found ConvertorFormat") } return f.formats[format], nil } diff --git a/internal/application/convertor/encoder/encoders.go b/internal/application/convertor/encoder/encoders.go new file mode 100644 index 0000000..b2d2904 --- /dev/null +++ b/internal/application/convertor/encoder/encoders.go @@ -0,0 +1,74 @@ +package encoder + +import ( + encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/apng" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/bmp" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/flv" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/gif" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/h264_nvenc" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/libmp3lame" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/libshine" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/libtwolame" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/libvpx" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/libvpx_vp9" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/libwebp" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/libwebp_anim" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/libx264" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/libx265" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/libxvid" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/mjpeg" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/mp2" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/mp2fixed" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/mpeg1video" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/mpeg2video" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/mpeg4" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/msmpeg4" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/msmpeg4v2" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/msvideo1" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/png" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/qtrle" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/sgi" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/tiff" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/wmav1" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/wmav2" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/wmv1" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/wmv2" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/xbm" +) + +var supportEncoders = map[string]func() encoder2.EncoderDataContract{ + "libx264": libx264.NewData, + "h264_nvenc": h264_nvenc.NewData, + "libx265": libx265.NewData, + "png": png.NewData, + "gif": gif.NewData, + "flv": flv.NewData, + "apng": apng.NewData, + "bmp": bmp.NewData, + "mjpeg": mjpeg.NewData, + "mpeg1video": mpeg1video.NewData, + "mpeg2video": mpeg2video.NewData, + "mpeg4": mpeg4.NewData, + "libxvid": libxvid.NewData, + "msmpeg4v2": msmpeg4v2.NewData, + "msmpeg4": msmpeg4.NewData, + "msvideo1": msvideo1.NewData, + "qtrle": qtrle.NewData, + "tiff": tiff.NewData, + "sgi": sgi.NewData, + "libvpx": libvpx.NewData, + "libvpx-vp9": libvpx_vp9.NewData, + "libwebp_anim": libwebp_anim.NewData, + "libwebp": libwebp.NewData, + "wmv1": wmv1.NewData, + "wmv2": wmv2.NewData, + "xbm": xbm.NewData, + "mp2": mp2.NewData, + "mp2fixed": mp2fixed.NewData, + "libtwolame": libtwolame.NewData, + "libmp3lame": libmp3lame.NewData, + "libshine": libshine.NewData, + "wmav1": wmav1.NewData, + "wmav2": wmav2.NewData, +} diff --git a/internal/application/convertor/items_to_convert.go b/internal/application/convertor/items_to_convert.go new file mode 100644 index 0000000..32062c9 --- /dev/null +++ b/internal/application/convertor/items_to_convert.go @@ -0,0 +1,139 @@ +package convertor + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/widget" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg" +) + +type ItemsToConvertContract interface { + Add(file *ffmpeg.File) + Clear() + GetItems() map[int]ItemToConvertContract + GetItemsContainer() *fyne.Container + AfterAddingQueue() + GetIsAutoRemove() bool + SetIsAutoRemove(isAutoRemove bool) +} + +type itemsToConvert struct { + ffmpeg ffmpeg.UtilitiesContract + nextId int + items map[int]ItemToConvertContract + itemsContainer *fyne.Container + isAutoRemove bool +} + +func NewItemsToConvert(ffmpeg ffmpeg.UtilitiesContract) ItemsToConvertContract { + return &itemsToConvert{ + ffmpeg: ffmpeg, + nextId: 0, + items: map[int]ItemToConvertContract{}, + itemsContainer: container.NewVBox(), + isAutoRemove: true, + } +} + +func (items *itemsToConvert) GetItemsContainer() *fyne.Container { + return items.itemsContainer +} + +func (items *itemsToConvert) Add(file *ffmpeg.File) { + nextId := items.nextId + var content *fyne.Container + var buttonPlay *widget.Button + + buttonPlay = widget.NewButtonWithIcon("", theme.Icon(theme.IconNameMediaPlay), func() { + buttonPlay.Disable() + go func() { + ffplay, err := items.ffmpeg.GetFFplay() + if err != nil { + fyne.Do(func() { + buttonPlay.Enable() + }) + return + } + + _ = ffplay.Play(file) + fyne.Do(func() { + buttonPlay.Enable() + }) + }() + }) + + buttonRemove := widget.NewButtonWithIcon("", theme.Icon(theme.IconNameDelete), func() { + items.itemsContainer.Remove(content) + items.itemsContainer.Refresh() + delete(items.items, nextId) + }) + buttonRemove.Importance = widget.DangerImportance + + content = container.NewVBox( + container.NewBorder( + nil, + nil, + buttonPlay, + buttonRemove, + container.NewHScroll(widget.NewLabel(file.Name)), + ), + container.NewHScroll(widget.NewLabel(file.Path)), + container.NewPadded(), + canvas.NewLine(theme.Color(theme.ColorNameFocus)), + container.NewPadded(), + ) + + items.itemsContainer.Add(content) + items.items[nextId] = newItemToConvert(file, content) + items.nextId++ +} + +func (items *itemsToConvert) GetIsAutoRemove() bool { + return items.isAutoRemove +} + +func (items *itemsToConvert) SetIsAutoRemove(isAutoRemove bool) { + items.isAutoRemove = isAutoRemove +} + +func (items *itemsToConvert) GetItems() map[int]ItemToConvertContract { + return items.items +} + +func (items *itemsToConvert) AfterAddingQueue() { + if items.isAutoRemove { + items.Clear() + } +} + +func (items *itemsToConvert) Clear() { + items.itemsContainer.RemoveAll() + items.items = map[int]ItemToConvertContract{} +} + +type ItemToConvertContract interface { + GetFile() *ffmpeg.File + GetContent() *fyne.Container +} + +type itemToConvert struct { + file *ffmpeg.File + content *fyne.Container +} + +func newItemToConvert(file *ffmpeg.File, content *fyne.Container) ItemToConvertContract { + return &itemToConvert{ + file: file, + content: content, + } +} + +func (item *itemToConvert) GetFile() *ffmpeg.File { + return item.file +} + +func (item *itemToConvert) GetContent() *fyne.Container { + return item.content +} diff --git a/internal/application/convertor/progressbar.go b/internal/application/convertor/progressbar.go new file mode 100644 index 0000000..30c8155 --- /dev/null +++ b/internal/application/convertor/progressbar.go @@ -0,0 +1,217 @@ +package convertor + +import ( + "bufio" + "errors" + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/lang" + "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/widget" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg" + "image/color" + "io" + "regexp" + "strconv" + "strings" +) + +type ProgressBarContract interface { + GetContainer() *fyne.Container + GetProgressbar(totalDuration float64, filePath string) ffmpeg.ProgressContract + ProcessEndedWithError(errorText string) + ProcessEndedWithSuccess(file *ffmpeg.File) +} + +type progressBar struct { + container *fyne.Container + label *widget.Label + progressbar *widget.ProgressBar + errorBlock *container.Scroll + messageError *canvas.Text + statusMessage *canvas.Text + buttonPlay *widget.Button + ffmpegService ffmpeg.UtilitiesContract +} + +func NewProgressBar(ffmpegService ffmpeg.UtilitiesContract) ProgressBarContract { + label := widget.NewLabel("") + progressbar := widget.NewProgressBar() + + statusMessage := canvas.NewText("", theme.Color(theme.ColorNamePrimary)) + messageError := canvas.NewText("", theme.Color(theme.ColorNameError)) + buttonPlay := widget.NewButtonWithIcon("", theme.Icon(theme.IconNameMediaPlay), func() { + + }) + buttonPlay.Hide() + + errorBlock := container.NewHScroll(messageError) + errorBlock.Hide() + + content := container.NewVBox( + container.NewHScroll(label), + progressbar, + container.NewHScroll(container.NewHBox( + buttonPlay, + statusMessage, + )), + errorBlock, + ) + content.Hide() + + return &progressBar{ + container: content, + label: label, + progressbar: progressbar, + errorBlock: errorBlock, + messageError: messageError, + statusMessage: statusMessage, + buttonPlay: buttonPlay, + ffmpegService: ffmpegService, + } +} + +func (p *progressBar) GetContainer() *fyne.Container { + return p.container +} + +func (p *progressBar) GetProgressbar(totalDuration float64, filePath string) ffmpeg.ProgressContract { + p.label.Text = filePath + p.statusMessage.Color = theme.Color(theme.ColorNamePrimary) + p.statusMessage.Text = lang.L("inProgressQueue") + p.messageError.Text = "" + fyne.Do(func() { + p.buttonPlay.Hide() + if p.errorBlock.Visible() { + p.errorBlock.Hide() + } + p.statusMessage.Refresh() + p.container.Refresh() + p.errorBlock.Refresh() + }) + + p.progressbar.Value = 0 + return NewProgress(totalDuration, p.progressbar) +} + +func (p *progressBar) ProcessEndedWithError(errorText string) { + fyne.Do(func() { + p.statusMessage.Color = theme.Color(theme.ColorNameError) + p.statusMessage.Text = lang.L("errorQueue") + p.messageError.Text = errorText + p.errorBlock.Show() + }) +} + +func (p *progressBar) ProcessEndedWithSuccess(file *ffmpeg.File) { + fyne.Do(func() { + p.statusMessage.Color = color.RGBA{R: 49, G: 127, B: 114, A: 255} + p.statusMessage.Text = lang.L("completedQueue") + p.buttonPlay.Show() + p.buttonPlay.OnTapped = func() { + p.buttonPlay.Disable() + go func() { + ffplay, err := p.ffmpegService.GetFFplay() + if err == nil { + _ = ffplay.Play(file) + } + + fyne.Do(func() { + p.buttonPlay.Enable() + }) + }() + } + }) +} + +type Progress struct { + totalDuration float64 + progressbar *widget.ProgressBar + protocol string +} + +func NewProgress(totalDuration float64, progressbar *widget.ProgressBar) ffmpeg.ProgressContract { + return &Progress{ + totalDuration: totalDuration, + progressbar: progressbar, + protocol: "pipe:", + } +} + +func (p *Progress) GetProtocole() string { + return p.protocol +} + +func (p *Progress) Run(stdOut io.ReadCloser, stdErr io.ReadCloser) error { + isProcessCompleted := false + var errorText string + + p.progressbar.Value = 0 + p.progressbar.Max = p.totalDuration + fyne.Do(func() { + p.progressbar.Refresh() + }) + progress := 0.0 + + go func() { + scannerErr := bufio.NewReader(stdErr) + for { + line, _, err := scannerErr.ReadLine() + if err != nil { + if err == io.EOF { + break + } + continue + } + data := strings.TrimSpace(string(line)) + errorText = data + } + }() + + scannerOut := bufio.NewReader(stdOut) + for { + line, _, err := scannerOut.ReadLine() + if err != nil { + if err == io.EOF { + break + } + continue + } + data := strings.TrimSpace(string(line)) + if strings.Contains(data, "progress=end") { + p.progressbar.Value = p.totalDuration + fyne.Do(func() { + p.progressbar.Refresh() + }) + isProcessCompleted = true + break + } + + re := regexp.MustCompile(`frame=(\d+)`) + a := re.FindAllStringSubmatch(data, -1) + + if len(a) > 0 && len(a[len(a)-1]) > 0 { + c, err := strconv.Atoi(a[len(a)-1][len(a[len(a)-1])-1]) + if err != nil { + continue + } + progress = float64(c) + } + if p.progressbar.Value != progress { + p.progressbar.Value = progress + fyne.Do(func() { + p.progressbar.Refresh() + }) + } + } + + if isProcessCompleted == false { + if len(errorText) == 0 { + errorText = lang.L("errorConverter") + } + return errors.New(errorText) + } + + return nil +} diff --git a/internal/application/convertor/queue.go b/internal/application/convertor/queue.go new file mode 100644 index 0000000..455009c --- /dev/null +++ b/internal/application/convertor/queue.go @@ -0,0 +1,152 @@ +package convertor + +import ( + "errors" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg" +) + +type Queue struct { + Setting *ffmpeg.ConvertSetting + Status StatusContract + Error error +} + +type StatusContract interface { + Name() string + Ordinal() int +} + +const ( + Waiting = iota + InProgress + Completed + Error +) + +type StatusType uint + +var statusTypeStrings = []string{ + "waiting", + "inProgress", + "completed", + "error", +} + +func (status StatusType) Name() string { + return statusTypeStrings[status] +} + +func (status StatusType) Ordinal() int { + return int(status) +} + +type QueueListenerContract interface { + AddQueue(key int, queue *Queue) + ChangeQueue(key int, queue *Queue) + RemoveQueue(key int, status StatusContract) +} + +type QueueListContract interface { + AddListener(queueListener QueueListenerContract) + GetItems() map[int]*Queue + Add(setting *ffmpeg.ConvertSetting) + EventChangeQueue(key int, queue *Queue) + Remove(key int) + GetItem(key int) (*Queue, error) + Next() (key int, queue *Queue) +} + +type queueList struct { + currentKey int + items map[int]*Queue + queue map[int]int + queueListener map[int]QueueListenerContract +} + +func NewQueueList() QueueListContract { + return &queueList{ + currentKey: 0, + items: map[int]*Queue{}, + queue: map[int]int{}, + queueListener: map[int]QueueListenerContract{}, + } +} + +func (l *queueList) GetItems() map[int]*Queue { + return l.items +} + +func (l *queueList) Add(setting *ffmpeg.ConvertSetting) { + queue := Queue{ + Setting: setting, + Status: StatusType(Waiting), + } + + l.currentKey += 1 + l.items[l.currentKey] = &queue + l.queue[l.currentKey] = l.currentKey + + l.eventAdd(l.currentKey, &queue) +} + +func (l *queueList) EventChangeQueue(key int, queue *Queue) { + l.eventChange(key, queue) +} + +func (l *queueList) Remove(key int) { + if _, ok := l.queue[key]; ok { + delete(l.queue, key) + } + + if _, ok := l.items[key]; ok { + status := l.items[key].Status + l.eventRemove(key, status) + delete(l.items, key) + } +} + +func (l *queueList) GetItem(key int) (*Queue, error) { + if item, ok := l.items[key]; ok { + return item, nil + } + + return nil, errors.New("key not found") +} + +func (l *queueList) AddListener(queueListener QueueListenerContract) { + l.queueListener[len(l.queueListener)] = queueListener +} + +func (l *queueList) Next() (key int, queue *Queue) { + statusWaiting := StatusType(Waiting) + for key, queueId := range l.queue { + if queue, ok := l.items[queueId]; ok { + if queue.Status == statusWaiting { + return queueId, queue + } + } + + if _, ok := l.queue[key]; ok { + delete(l.queue, key) + } + } + return -1, nil +} + +func (l *queueList) eventAdd(key int, queue *Queue) { + for _, listener := range l.queueListener { + listener.AddQueue(key, queue) + } +} + +func (l *queueList) eventChange(key int, queue *Queue) { + for _, listener := range l.queueListener { + listener.ChangeQueue(key, queue) + } +} + +func (l *queueList) eventRemove(key int, status StatusContract) { + for _, listener := range l.queueListener { + listener.RemoveQueue(key, status) + } +} diff --git a/internal/application/setting/ffmpeg.go b/internal/application/setting/ffmpeg.go new file mode 100644 index 0000000..e20bb93 --- /dev/null +++ b/internal/application/setting/ffmpeg.go @@ -0,0 +1,40 @@ +package setting + +func (s *setting) GetFFmpegPath() string { + path := s.fyneApp.Preferences().String("ffmpegPath") + if path == "" { + return "ffmpeg" + } + + return path +} + +func (s *setting) SetFFmpegPath(path string) { + s.fyneApp.Preferences().SetString("ffmpegPath", path) +} + +func (s *setting) GetFFprobePath() string { + path := s.fyneApp.Preferences().String("ffprobePath") + if path == "" { + return "ffprobe" + } + + return path +} + +func (s *setting) SetFFprobePath(path string) { + s.fyneApp.Preferences().SetString("ffprobePath", path) +} + +func (s *setting) GetFFplayPath() string { + path := s.fyneApp.Preferences().String("ffplayPath") + if path == "" { + return "ffplay" + } + + return path +} + +func (s *setting) SetFFplayPath(path string) { + s.fyneApp.Preferences().SetString("ffplayPath", path) +} diff --git a/internal/application/setting/lang.go b/internal/application/setting/lang.go new file mode 100644 index 0000000..f7bf953 --- /dev/null +++ b/internal/application/setting/lang.go @@ -0,0 +1,66 @@ +package setting + +import ( + "encoding/json" + "fyne.io/fyne/v2" + fyneLang "fyne.io/fyne/v2/lang" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/resources" +) + +var supportedLanguages = map[string]Lang{ + "ru": {Code: "ru", Title: "Русский"}, + "kk": {Code: "kk", Title: "Қазақ Тілі"}, + "en": {Code: "en", Title: "English"}, +} + +type Lang struct { + Code string + Title string +} + +func ChangeLang(lang Lang) error { + translationsData, err := getTranslations(lang) + if err != nil { + return err + } + + name := fyneLang.SystemLocale().LanguageString() + return fyneLang.AddTranslations(fyne.NewStaticResource(name+".json", translationsData)) +} + +func defaultLang() Lang { + return supportedLanguages["ru"] +} + +func getTranslations(language Lang) ([]byte, error) { + translations := resources.GetTranslations() + + baseJson, err := translations.ReadFile("translations/base." + language.Code + ".json") + if err != nil { + return nil, err + } + appJson, err := translations.ReadFile("translations/app." + language.Code + ".json") + if err != nil { + return nil, err + } + + return mergeTranslations(baseJson, appJson) +} + +func mergeTranslations(baseJson []byte, appJson []byte) ([]byte, error) { + base := map[string]interface{}{} + custom := map[string]interface{}{} + err := json.Unmarshal(baseJson, &base) + if err != nil { + return nil, err + } + err = json.Unmarshal(appJson, &custom) + if err != nil { + return nil, err + } + + for k, v := range custom { + base[k] = v + } + return json.Marshal(base) +} diff --git a/internal/application/setting/setting.go b/internal/application/setting/setting.go new file mode 100644 index 0000000..b6afe5b --- /dev/null +++ b/internal/application/setting/setting.go @@ -0,0 +1,84 @@ +package setting + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/lang" + "golang.org/x/text/language" +) + +type SettingContract interface { + GetLanguages() []Lang + GetCurrentLangOrDefaultLang() (currentLang Lang, isDefault bool) + SetLang(language Lang) error + + GetDirectoryForSaving() string + SetDirectoryForSaving(path string) + + GetFFmpegPath() string + SetFFmpegPath(path string) + + GetFFprobePath() string + SetFFprobePath(path string) + + GetFFplayPath() string + SetFFplayPath(path string) + + ThemeInit() + GetThemes() map[string]ThemeInfoContract + GetTheme() ThemeInfoContract + SetTheme(themeInfo ThemeInfoContract) +} + +type setting struct { + fyneApp fyne.App +} + +func NewSetting(fyneApp fyne.App) SettingContract { + return &setting{ + fyneApp: fyneApp, + } +} + +func (s *setting) GetLanguages() []Lang { + items := []Lang{} + for _, item := range supportedLanguages { + items = append(items, item) + } + return items +} + +func (s *setting) GetCurrentLangOrDefaultLang() (currentLang Lang, isDefault bool) { + languageCode := s.fyneApp.Preferences().String("language") + + if languageCode == "" { + languageTag, err := language.Parse(lang.SystemLocale().LanguageString()) + if err != nil { + return currentLang, true + } + base, _ := languageTag.Base() + languageCode = base.String() + } + + if currentLang, ok := supportedLanguages[languageCode]; ok { + return currentLang, false + } + + return defaultLang(), true +} + +func (s *setting) SetLang(language Lang) error { + err := ChangeLang(language) + if err != nil { + return err + } + s.fyneApp.Preferences().SetString("language", language.Code) + return nil +} + +func (s *setting) GetDirectoryForSaving() string { + return s.fyneApp.Preferences().String("directoryForSaving") +} + +func (s *setting) SetDirectoryForSaving(path string) { + s.fyneApp.Preferences().SetString("directoryForSaving", path) +} diff --git a/internal/application/setting/theme.go b/internal/application/setting/theme.go new file mode 100644 index 0000000..b81547b --- /dev/null +++ b/internal/application/setting/theme.go @@ -0,0 +1,110 @@ +package setting + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/lang" + "fyne.io/fyne/v2/theme" + "image/color" +) + +func (s *setting) GetTheme() ThemeInfoContract { + name := s.fyneApp.Preferences().String("theme") + if name != "" { + if _, ok := s.GetThemes()[name]; ok { + return s.GetThemes()[name] + } + } + + return s.GetThemes()["default"] +} + +func (s *setting) SetTheme(themeInfo ThemeInfoContract) { + s.fyneApp.Preferences().SetString("theme", themeInfo.GetName()) + + if themeInfo.GetName() == "default" { + s.fyneApp.Settings().SetTheme(theme.DefaultTheme()) + return + } + s.fyneApp.Settings().SetTheme(&forcedVariant{theme: theme.DefaultTheme(), variant: themeInfo.GetVariant()}) +} + +func (s *setting) ThemeInit() { + themeInfo := s.GetTheme() + if themeInfo.GetName() == "default" { + s.fyneApp.Settings().SetTheme(theme.DefaultTheme()) + return + } + s.fyneApp.Settings().SetTheme(&forcedVariant{theme: theme.DefaultTheme(), variant: themeInfo.GetVariant()}) +} + +func (s *setting) GetThemes() map[string]ThemeInfoContract { + themesNameDefault := &themeInfo{ + name: "default", + title: lang.L("themesNameDefault"), + } + + themesNameLight := &themeInfo{ + name: "light", + title: lang.L("themesNameLight"), + variant: theme.VariantLight, + } + + themesNameDark := &themeInfo{ + name: "dark", + title: lang.L("themesNameDark"), + variant: theme.VariantDark, + } + + list := map[string]ThemeInfoContract{ + "default": themesNameDefault, + "light": themesNameLight, + "dark": themesNameDark, + } + + return list +} + +type ThemeInfoContract interface { + GetName() string + GetTitle() string + GetVariant() fyne.ThemeVariant +} + +type themeInfo struct { + name string + title string + variant fyne.ThemeVariant +} + +func (inf *themeInfo) GetName() string { + return inf.name +} + +func (inf *themeInfo) GetTitle() string { + return inf.title +} + +func (inf *themeInfo) GetVariant() fyne.ThemeVariant { + return inf.variant +} + +type forcedVariant struct { + theme fyne.Theme + variant fyne.ThemeVariant +} + +func (f *forcedVariant) Color(name fyne.ThemeColorName, _ fyne.ThemeVariant) color.Color { + return f.theme.Color(name, f.variant) +} + +func (f *forcedVariant) Font(style fyne.TextStyle) fyne.Resource { + return theme.DefaultTheme().Font(style) +} + +func (f *forcedVariant) Icon(name fyne.ThemeIconName) fyne.Resource { + return theme.DefaultTheme().Icon(name) +} + +func (f *forcedVariant) Size(name fyne.ThemeSizeName) float32 { + return theme.DefaultTheme().Size(name) +} diff --git a/internal/controller/convertor.go b/internal/controller/convertor.go new file mode 100644 index 0000000..445a32d --- /dev/null +++ b/internal/controller/convertor.go @@ -0,0 +1,111 @@ +package controller + +import ( + "errors" + "fyne.io/fyne/v2/lang" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/download/service" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/gui/view" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/utils" +) + +func (c *controller) convertor() { + formats, err := c.app.GetConvertorService().GetSupportFormats() + if err != nil { + c.startWithError(err) + return + } + + content := view.Convertor( + c.window, + c.addFileForConversion, + c.app.GetSetting().GetDirectoryForSaving(), + c.setDirectoryForSaving, + formats, + c.addToConversion, + ) + c.window.SetContent(content) +} + +func (c *controller) addFileForConversion(file ffmpeg.File) { + c.app.GetItemsToConvert().Add(&file) + c.window.GetLayout().GetRContainer().SelectAddedFilesTab() +} + +func (c *controller) setDirectoryForSaving(path string) { + c.app.GetSetting().SetDirectoryForSaving(path) +} + +func (c *controller) addToConversion(convertSetting view.ConvertSetting) error { + if len(c.app.GetItemsToConvert().GetItems()) == 0 { + return errors.New(lang.L("errorNoFilesAddedForConversion")) + } + c.window.GetLayout().GetRContainer().SelectFileQueueTab() + for _, item := range c.app.GetItemsToConvert().GetItems() { + file := item.GetFile() + if file == nil { + continue + } + + c.app.GetQueueService().Add(&ffmpeg.ConvertSetting{ + FileInput: *file, + FileOut: ffmpeg.File{ + Path: convertSetting.DirectoryForSave + utils.PathSeparator() + file.Name + "." + convertSetting.Format, + Name: file.Name, + Ext: "." + convertSetting.Format, + }, + OverwriteOutputFiles: convertSetting.OverwriteOutputFiles, + Encoder: convertSetting.Encoder, + }) + } + c.app.GetItemsToConvert().AfterAddingQueue() + + return nil +} + +func (c *controller) settingConvertor(isAllowCancellation bool) { + ffmpegPath := c.app.GetFFmpegService().GetFFmpegPath() + ffprobePath := c.app.GetFFmpegService().GetFFprobePath() + ffplayPath := c.app.GetFFmpegService().GetFFplayPath() + + var cancel func() + cancel = nil + if isAllowCancellation { + cancel = func() { + c.convertor() + } + } + + content := view.ConfiguringFFmpegUtilities( + c.window, + ffmpegPath, + ffprobePath, + ffplayPath, + c.saveSettingConvertor, + cancel, + service.DownloadFFmpeg(c.app, c.saveSettingConvertor), + ) + c.window.SetContent(content) +} + +func (c *controller) saveSettingConvertor(ffmpegPath string, ffprobePath string, ffplayPath string) error { + var err error + + err = c.app.GetFFmpegService().ChangeFFmpeg(ffmpegPath) + if err != nil { + return err + } + + c.app.GetFFmpegService().ChangeFFprobe(ffprobePath) + if err != nil { + return err + } + + c.app.GetFFmpegService().ChangeFFplay(ffplayPath) + if err != nil { + return err + } + + c.convertor() + return nil +} diff --git a/internal/controller/error.go b/internal/controller/error.go new file mode 100644 index 0000000..0f66887 --- /dev/null +++ b/internal/controller/error.go @@ -0,0 +1,16 @@ +package controller + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application/setting" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/gui/view" +) + +func (c *controller) startWithError(err error) { + languages := c.app.GetSetting().GetLanguages() + + content := view.StartWithError(err, languages, func(lang setting.Lang) { + _ = setting.ChangeLang(lang) + c.startWithError(err) + }) + c.window.SetContent(content) +} diff --git a/internal/controller/main.go b/internal/controller/main.go new file mode 100644 index 0000000..e6f0e75 --- /dev/null +++ b/internal/controller/main.go @@ -0,0 +1,96 @@ +package controller + +import ( + "fyne.io/fyne/v2" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application/setting" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/gui/menu" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/gui/view" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/gui/window" +) + +type ControllerContract interface { + Start() +} + +type controller struct { + app application.AppContract + window window.WindowContract +} + +func NewController(app application.AppContract) ControllerContract { + fyneWindow := app.FyneApp().NewWindow("GUI for FFmpeg") + fyneWindow.SetMaster() + queueLayout := window.NewQueueLayout(app.GetFFmpegService()) + app.GetQueueService().AddListener(queueLayout) + + return &controller{ + app: app, + window: window.NewMainWindow( + fyneWindow, + app.GetProgressBarService(), + app.GetItemsToConvert(), + queueLayout, + ), + } +} + +func (c *controller) Start() { + isDefault, err := c.initLanguage() + if err != nil { + c.startWithError(err) + c.window.Show() + return + } + + c.app.GetSetting().ThemeInit() + + if isDefault { + languages := c.app.GetSetting().GetLanguages() + content := view.StartWithoutSupportLang(languages, func(lang setting.Lang) { + err = c.app.GetSetting().SetLang(lang) + if err != nil { + c.startWithError(err) + return + } + c.initLayout() + c.verificareaFFmpeg() + }) + c.window.SetContent(content) + c.window.Show() + return + } + + c.initLayout() + c.verificareaFFmpeg() + c.window.Show() +} + +func (c *controller) verificareaFFmpeg() { + if !c.app.GetFFmpegService().UtilityCheck() { + c.settingConvertor(false) + return + } + + c.convertor() +} + +func (c *controller) initLanguage() (isDefault bool, err error) { + lang, isDefault := c.app.GetSetting().GetCurrentLangOrDefaultLang() + err = setting.ChangeLang(lang) + return isDefault, err +} + +func (c *controller) initLayout() { + c.window.SetMainMenu(fyne.NewMainMenu( + menu.MainMenuSettings( + c.actionMainSettings, + c.actionSettingConvertor, + ), + menu.MainMenuHelp( + c.actionAbout, + c.actionHelpFFplay, + ), + )) + c.window.InitLayout() +} diff --git a/internal/controller/menu.go b/internal/controller/menu.go new file mode 100644 index 0000000..71e45ca --- /dev/null +++ b/internal/controller/menu.go @@ -0,0 +1,67 @@ +package controller + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/lang" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/gui/view" +) + +func (c *controller) actionSettingConvertor() { + c.settingConvertor(true) +} + +func (c *controller) actionMainSettings() { + currentLang, _ := c.app.GetSetting().GetCurrentLangOrDefaultLang() + content := view.MainSettings( + currentLang, + c.app.GetSetting().GetLanguages(), + + c.app.GetSetting().GetTheme(), + c.app.GetSetting().GetThemes(), + + c.actionMainSettingsSave, + c.convertor, + ) + c.window.SetContent(content) +} + +func (c *controller) actionMainSettingsSave(setting *view.MainSettingForm) error { + err := c.app.GetSetting().SetLang(setting.Language) + if err != nil { + return err + } + c.app.GetSetting().SetTheme(setting.ThemeInfo) + c.initLayout() + + c.convertor() + return nil +} + +func (c *controller) actionAbout() { + ffmpegVersion := c.app.GetFFmpegService().GetFFmpegVersion() + ffprobeVersion := c.app.GetFFmpegService().GetFFprobeVersion() + ffplayVersion := c.app.GetFFmpegService().GetFFplayVersion() + appVersion := c.app.FyneApp().Metadata().Version + + window := c.app.FyneApp().NewWindow(lang.L("about")) + window.Resize(fyne.Size{Width: 793, Height: 550}) + window.SetFixedSize(true) + + content := view.About(appVersion, ffmpegVersion, ffprobeVersion, ffplayVersion) + + window.SetContent(content) + window.CenterOnScreen() + window.Show() +} + +func (c *controller) actionHelpFFplay() { + window := c.app.FyneApp().NewWindow(lang.L("helpFFplay")) + window.Resize(fyne.Size{Width: 800, Height: 550}) + window.SetFixedSize(true) + + content := view.HelpFFplay() + + window.SetContent(content) + window.CenterOnScreen() + window.Show() +} diff --git a/internal/ffmpeg/download/gui/download_anyos.go b/internal/ffmpeg/download/gui/download_anyos.go new file mode 100644 index 0000000..6957f9d --- /dev/null +++ b/internal/ffmpeg/download/gui/download_anyos.go @@ -0,0 +1,14 @@ +//go:build !windows && !linux +// +build !windows,!linux + +package gui + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/widget" +) + +func DownloadFFmpeg(donwloadFFmpeg func(progressBar *widget.ProgressBar, progressMessage *canvas.Text) error) fyne.CanvasObject { + return container.NewVBox() +} diff --git a/convertor/view_setting_button_download_ffmpeg_linux.go b/internal/ffmpeg/download/gui/download_linux.go similarity index 66% rename from convertor/view_setting_button_download_ffmpeg_linux.go rename to internal/ffmpeg/download/gui/download_linux.go index aa462d3..4884408 100644 --- a/convertor/view_setting_button_download_ffmpeg_linux.go +++ b/internal/ffmpeg/download/gui/download_linux.go @@ -1,22 +1,19 @@ //go:build linux // +build linux -package convertor +package gui import ( "fyne.io/fyne/v2" "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/lang" "fyne.io/fyne/v2/widget" - "github.com/nicksnyder/go-i18n/v2/i18n" "golang.org/x/image/colornames" "image/color" ) -func (v View) blockDownloadFFmpeg( - donwloadFFmpeg func(progressBar *widget.ProgressBar, progressMessage *canvas.Text) error, -) *fyne.Container { - +func DownloadFFmpeg(donwloadFFmpeg func(progressBar *widget.ProgressBar, progressMessage *canvas.Text) error) fyne.CanvasObject { errorDownloadFFmpegMessage := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255}) errorDownloadFFmpegMessage.TextSize = 16 errorDownloadFFmpegMessage.TextStyle = fyne.TextStyle{Bold: true} @@ -29,9 +26,7 @@ func (v View) blockDownloadFFmpeg( var buttonDownloadFFmpeg *widget.Button - buttonDownloadFFmpeg = widget.NewButton(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "download", - }), func() { + buttonDownloadFFmpeg = widget.NewButton(lang.L("download"), func() { fyne.Do(func() { buttonDownloadFFmpeg.Disable() }) @@ -47,20 +42,16 @@ func (v View) blockDownloadFFmpeg( }) - downloadFFmpegFromSiteMessage := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "downloadFFmpegFromSite", - }) + downloadFFmpegFromSiteMessage := lang.L("downloadFFmpegFromSite") return container.NewVBox( canvas.NewLine(colornames.Darkgreen), - widget.NewCard(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "buttonDownloadFFmpeg", - }), "", container.NewVBox( + widget.NewCard(lang.L("buttonDownloadFFmpeg"), "", container.NewVBox( widget.NewRichTextFromMarkdown( downloadFFmpegFromSiteMessage+" [https://github.com/BtbN/FFmpeg-Builds/releases](https://github.com/BtbN/FFmpeg-Builds/releases)", ), buttonDownloadFFmpeg, - errorDownloadFFmpegMessage, + container.NewHScroll(errorDownloadFFmpegMessage), progressDownloadFFmpegMessage, progressBar, )), diff --git a/convertor/view_setting_button_download_ffmpeg_windows.go b/internal/ffmpeg/download/gui/download_windows.go similarity index 66% rename from convertor/view_setting_button_download_ffmpeg_windows.go rename to internal/ffmpeg/download/gui/download_windows.go index 5b706b6..1841311 100644 --- a/convertor/view_setting_button_download_ffmpeg_windows.go +++ b/internal/ffmpeg/download/gui/download_windows.go @@ -1,22 +1,19 @@ //go:build windows // +build windows -package convertor +package gui import ( "fyne.io/fyne/v2" "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/lang" "fyne.io/fyne/v2/widget" - "github.com/nicksnyder/go-i18n/v2/i18n" "golang.org/x/image/colornames" "image/color" ) -func (v View) blockDownloadFFmpeg( - donwloadFFmpeg func(progressBar *widget.ProgressBar, progressMessage *canvas.Text) error, -) *fyne.Container { - +func DownloadFFmpeg(donwloadFFmpeg func(progressBar *widget.ProgressBar, progressMessage *canvas.Text) error) fyne.CanvasObject { errorDownloadFFmpegMessage := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255}) errorDownloadFFmpegMessage.TextSize = 16 errorDownloadFFmpegMessage.TextStyle = fyne.TextStyle{Bold: true} @@ -29,9 +26,7 @@ func (v View) blockDownloadFFmpeg( var buttonDownloadFFmpeg *widget.Button - buttonDownloadFFmpeg = widget.NewButton(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "download", - }), func() { + buttonDownloadFFmpeg = widget.NewButton(lang.L("download"), func() { go func() { fyne.Do(func() { @@ -47,20 +42,16 @@ func (v View) blockDownloadFFmpeg( }() }) - downloadFFmpegFromSiteMessage := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "downloadFFmpegFromSite", - }) + downloadFFmpegFromSiteMessage := lang.L("downloadFFmpegFromSite") return container.NewVBox( canvas.NewLine(colornames.Darkgreen), - widget.NewCard(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "buttonDownloadFFmpeg", - }), "", container.NewVBox( + widget.NewCard(lang.L("buttonDownloadFFmpeg"), "", container.NewVBox( widget.NewRichTextFromMarkdown( downloadFFmpegFromSiteMessage+" [https://github.com/BtbN/FFmpeg-Builds/releases](https://github.com/BtbN/FFmpeg-Builds/releases)", ), buttonDownloadFFmpeg, - errorDownloadFFmpegMessage, + container.NewHScroll(errorDownloadFFmpegMessage), progressDownloadFFmpegMessage, progressBar, )), diff --git a/internal/ffmpeg/download/service/download.go b/internal/ffmpeg/download/service/download.go new file mode 100644 index 0000000..f0a8438 --- /dev/null +++ b/internal/ffmpeg/download/service/download.go @@ -0,0 +1,21 @@ +package service + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/widget" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/download/gui" +) + +func DownloadFFmpeg(app application.AppContract, save func(ffmpegPath string, ffprobePath string, ffplayPath string) error) fyne.CanvasObject { + return gui.DownloadFFmpeg(func(progressBar *widget.ProgressBar, progressMessage *canvas.Text) error { + var err error + err = startDownload(app, progressBar, progressMessage, save) + if err != nil { + return err + } + + return nil + }) +} diff --git a/internal/ffmpeg/download/service/download_anyos.go b/internal/ffmpeg/download/service/download_anyos.go new file mode 100644 index 0000000..edee036 --- /dev/null +++ b/internal/ffmpeg/download/service/download_anyos.go @@ -0,0 +1,15 @@ +//go:build !windows && !linux +// +build !windows,!linux + +package service + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/widget" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application" +) + +func startDownload(app application.AppContract, progressBar *widget.ProgressBar, progressMessage *canvas.Text, save func(ffmpegPath string, ffprobePath string, ffplayPath string) error) error { + return nil +} diff --git a/handler/convertor_linux.go b/internal/ffmpeg/download/service/download_linux.go similarity index 67% rename from handler/convertor_linux.go rename to internal/ffmpeg/download/service/download_linux.go index c1a032a..32d0af0 100644 --- a/handler/convertor_linux.go +++ b/internal/ffmpeg/download/service/download_linux.go @@ -1,16 +1,16 @@ //go:build linux // +build linux -package handler +package service import ( "archive/tar" "errors" "fyne.io/fyne/v2" "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/lang" "fyne.io/fyne/v2/widget" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" - "github.com/nicksnyder/go-i18n/v2/i18n" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application" "github.com/ulikunitz/xz" "io" "net/http" @@ -18,67 +18,72 @@ import ( "path/filepath" ) -func getPathsToFF() []kernel.FFPathUtilities { - return []kernel.FFPathUtilities{{FFmpeg: "ffmpeg/bin/ffmpeg", FFprobe: "ffmpeg/bin/ffprobe", FFplay: "ffmpeg/bin/ffplay"}, {FFmpeg: "ffmpeg", FFprobe: "ffprobe", FFplay: "ffplay"}} -} +func startDownload(app application.AppContract, progressBar *widget.ProgressBar, progressMessage *canvas.Text, save func(ffmpegPath string, ffprobePath string, ffplayPath string) error) error { + var err error -func (h ConvertorHandler) downloadFFmpeg(progressBar *widget.ProgressBar, progressMessage *canvas.Text) (err error) { - isDirectoryFFmpeg := isDirectory("ffmpeg") - if isDirectoryFFmpeg == false { - err = os.Mkdir("ffmpeg", 0777) - if err != nil { - return err - } + dir, err := localSharePath() + if err != nil { + return err } - progressMessage.Text = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "downloadRun", - }) - fyne.Do(func() { - progressMessage.Refresh() - }) - err = downloadFile("ffmpeg/ffmpeg.tar.xz", "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-linux64-gpl.tar.xz", progressBar) + dir = filepath.Join(dir, "fyne", app.FyneApp().UniqueID()) + err = os.MkdirAll(dir, 0755) if err != nil { return err } - progressMessage.Text = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "unzipRun", - }) fyne.Do(func() { + progressMessage.Text = lang.L("downloadRun") progressMessage.Refresh() }) - err = unTarXz("ffmpeg/ffmpeg.tar.xz", "ffmpeg", progressBar) + err = downloadFile(dir+"/ffmpeg.tar.xz", "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-linux64-gpl.tar.xz", progressBar) if err != nil { return err } - _ = os.Remove("ffmpeg/ffmpeg.tar.xz") - progressMessage.Text = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "testFF", - }) fyne.Do(func() { + progressMessage.Text = lang.L("unzipRun") + progressMessage.Refresh() + }) + err = unTarXz(dir+"/ffmpeg.tar.xz", dir, progressBar) + if err != nil { + return err + } + _ = os.Remove(dir + "/ffmpeg.tar.xz") + + fyne.Do(func() { + progressMessage.Text = lang.L("testFF") progressMessage.Refresh() }) - err = h.saveSettingFFPath( - "ffmpeg/ffmpeg-master-latest-linux64-gpl/bin/ffmpeg", - "ffmpeg/ffmpeg-master-latest-linux64-gpl/bin/ffprobe", - "ffmpeg/ffmpeg-master-latest-linux64-gpl/bin/ffplay", + err = save( + dir+"/ffmpeg-master-latest-linux64-gpl/bin/ffmpeg", + dir+"/ffmpeg-master-latest-linux64-gpl/bin/ffprobe", + dir+"/ffmpeg-master-latest-linux64-gpl/bin/ffplay", ) if err != nil { return err } - progressMessage.Text = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "completedQueue", - }) fyne.Do(func() { + progressMessage.Text = lang.L("completedQueue") progressMessage.Refresh() }) return nil } +func localSharePath() (string, error) { + xdgDataHome := os.Getenv("XDG_DATA_HOME") + if xdgDataHome != "" { + return xdgDataHome, nil + } + homeDir, err := os.UserHomeDir() + if err != nil { + return "", err + } + return filepath.Join(homeDir, ".local", "share"), nil +} + func downloadFile(filepath string, url string, progressBar *widget.ProgressBar) (err error) { progressBar.Value = 0 progressBar.Max = 100 @@ -229,12 +234,3 @@ func unTarXz(fileTar string, directory string, progressBar *widget.ProgressBar) return nil } - -func isDirectory(path string) bool { - fileInfo, err := os.Stat(path) - if err != nil { - return false - } - - return fileInfo.IsDir() -} diff --git a/handler/convertor_windows.go b/internal/ffmpeg/download/service/download_windows.go similarity index 62% rename from handler/convertor_windows.go rename to internal/ffmpeg/download/service/download_windows.go index fb08d87..f3bc33f 100644 --- a/handler/convertor_windows.go +++ b/internal/ffmpeg/download/service/download_windows.go @@ -1,16 +1,16 @@ //go:build windows // +build windows -package handler +package service import ( "archive/zip" "errors" "fyne.io/fyne/v2" "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/lang" "fyne.io/fyne/v2/widget" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" - "github.com/nicksnyder/go-i18n/v2/i18n" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application" "io" "net/http" "os" @@ -18,60 +18,50 @@ import ( "strings" ) -func getPathsToFF() []kernel.FFPathUtilities { - return []kernel.FFPathUtilities{{FFmpeg: "ffmpeg\\bin\\ffmpeg.exe", FFprobe: "ffmpeg\\bin\\ffprobe.exe", FFplay: "ffmpeg\\bin\\ffplay.exe"}} -} +func startDownload(app application.AppContract, progressBar *widget.ProgressBar, progressMessage *canvas.Text, save func(ffmpegPath string, ffprobePath string, ffplayPath string) error) error { + var err error -func (h ConvertorHandler) downloadFFmpeg(progressBar *widget.ProgressBar, progressMessage *canvas.Text) (err error) { - isDirectoryFFmpeg := isDirectory("ffmpeg") - if isDirectoryFFmpeg == false { - err = os.Mkdir("ffmpeg", 0777) - if err != nil { - return err - } - } - progressMessage.Text = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "downloadRun", - }) - fyne.Do(func() { - progressMessage.Refresh() - }) - err = downloadFile("ffmpeg/ffmpeg.zip", "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl.zip", progressBar) + dir := os.Getenv("APPDATA") + dir = filepath.Join(dir, "fyne", app.FyneApp().UniqueID()) + err = os.MkdirAll(dir, 0755) if err != nil { return err } - progressMessage.Text = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "unzipRun", - }) fyne.Do(func() { + progressMessage.Text = lang.L("downloadRun") progressMessage.Refresh() }) - err = unZip("ffmpeg/ffmpeg.zip", "ffmpeg", progressBar) + err = downloadFile(dir+"/ffmpeg.zip", "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl.zip", progressBar) if err != nil { return err } - _ = os.Remove("ffmpeg/ffmpeg.zip") - progressMessage.Text = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "testFF", - }) fyne.Do(func() { + progressMessage.Text = lang.L("unzipRun") progressMessage.Refresh() }) - err = h.saveSettingFFPath( - "ffmpeg/ffmpeg-master-latest-win64-gpl/bin/ffmpeg.exe", - "ffmpeg/ffmpeg-master-latest-win64-gpl/bin/ffprobe.exe", - "ffmpeg/ffmpeg-master-latest-win64-gpl/bin/ffplay.exe", + err = unZip(dir+"/ffmpeg.zip", dir, progressBar) + if err != nil { + return err + } + _ = os.Remove(dir + "/ffmpeg.zip") + + fyne.Do(func() { + progressMessage.Text = lang.L("testFF") + progressMessage.Refresh() + }) + err = save( + dir+"/ffmpeg-master-latest-win64-gpl/bin/ffmpeg.exe", + dir+"/ffmpeg-master-latest-win64-gpl/bin/ffprobe.exe", + dir+"/ffmpeg-master-latest-win64-gpl/bin/ffplay.exe", ) if err != nil { return err } - progressMessage.Text = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "completedQueue", - }) fyne.Do(func() { + progressMessage.Text = lang.L("completedQueue") progressMessage.Refresh() }) @@ -183,12 +173,3 @@ func unZip(fileZip string, directory string, progressBar *widget.ProgressBar) er return nil } - -func isDirectory(path string) bool { - fileInfo, err := os.Stat(path) - if err != nil { - return false - } - - return fileInfo.IsDir() -} diff --git a/internal/ffmpeg/encoder/apng/encoder.go b/internal/ffmpeg/encoder/apng/encoder.go new file mode 100644 index 0000000..8aad4f9 --- /dev/null +++ b/internal/ffmpeg/encoder/apng/encoder.go @@ -0,0 +1,21 @@ +package apng + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:v", "apng"} + } + + return encoder.NewEncoder("apng", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "apng" + formats := []string{"apng"} + fileType := encoder.FileType(encoder.Image) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/bmp/encoder.go b/internal/ffmpeg/encoder/bmp/encoder.go new file mode 100644 index 0000000..2d12f86 --- /dev/null +++ b/internal/ffmpeg/encoder/bmp/encoder.go @@ -0,0 +1,21 @@ +package bmp + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:v", "bmp"} + } + + return encoder.NewEncoder("bmp", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "bmp" + formats := []string{"bmp"} + fileType := encoder.FileType(encoder.Image) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/encoder.go b/internal/ffmpeg/encoder/encoder.go similarity index 73% rename from encoder/encoder.go rename to internal/ffmpeg/encoder/encoder.go index 7aedf47..a3eb0ed 100644 --- a/encoder/encoder.go +++ b/internal/ffmpeg/encoder/encoder.go @@ -24,15 +24,15 @@ type EncoderDataContract interface { NewEncoder() EncoderContract } -type Data struct { +type data struct { title string formats []string fileType FileTypeContract encoder func() EncoderContract } -func NewData(title string, formats []string, fileType FileTypeContract, encoder func() EncoderContract) *Data { - return &Data{ +func NewData(title string, formats []string, fileType FileTypeContract, encoder func() EncoderContract) EncoderDataContract { + return &data{ title: title, formats: formats, fileType: fileType, @@ -40,19 +40,19 @@ func NewData(title string, formats []string, fileType FileTypeContract, encoder } } -func (data Data) GetTitle() string { +func (data *data) GetTitle() string { return data.title } -func (data Data) GetFormats() []string { +func (data *data) GetFormats() []string { return data.formats } -func (data Data) NewEncoder() EncoderContract { +func (data *data) NewEncoder() EncoderContract { return data.encoder() } -func (data Data) GetFileType() FileTypeContract { +func (data *data) GetFileType() FileTypeContract { return data.fileType } @@ -91,29 +91,29 @@ func GetListFileType() []FileTypeContract { } } -type Encoder struct { +type encoder struct { name string parameters map[string]ParameterContract getParams func(parameters map[string]ParameterContract) []string } -func NewEncoder(name string, parameters map[string]ParameterContract, getParams func(parameters map[string]ParameterContract) []string) *Encoder { - return &Encoder{ +func NewEncoder(name string, parameters map[string]ParameterContract, getParams func(parameters map[string]ParameterContract) []string) EncoderContract { + return &encoder{ name: name, parameters: parameters, getParams: getParams, } } -func (e *Encoder) GetName() string { +func (e *encoder) GetName() string { return e.name } -func (e *Encoder) GetParams() []string { +func (e *encoder) GetParams() []string { return e.getParams(e.parameters) } -func (e *Encoder) GetParameter(name string) (ParameterContract, error) { +func (e *encoder) GetParameter(name string) (ParameterContract, error) { if e.parameters[name] == nil { return nil, errors.New("parameter not found") } @@ -121,15 +121,15 @@ func (e *Encoder) GetParameter(name string) (ParameterContract, error) { return e.parameters[name], nil } -type Parameter struct { +type parameter struct { name string isEnabled bool parameter string setParameter func(string) (string, error) } -func NewParameter(name string, isEnabled bool, defaultParameter string, setParameter func(string) (string, error)) *Parameter { - return &Parameter{ +func NewParameter(name string, isEnabled bool, defaultParameter string, setParameter func(string) (string, error)) ParameterContract { + return ¶meter{ name: name, isEnabled: isEnabled, parameter: defaultParameter, @@ -137,11 +137,11 @@ func NewParameter(name string, isEnabled bool, defaultParameter string, setParam } } -func (p *Parameter) GetName() string { +func (p *parameter) GetName() string { return p.name } -func (p *Parameter) Set(s string) (err error) { +func (p *parameter) Set(s string) (err error) { if p.setParameter != nil { s, err = p.setParameter(s) if err != nil { @@ -152,18 +152,18 @@ func (p *Parameter) Set(s string) (err error) { return nil } -func (p *Parameter) Get() string { +func (p *parameter) Get() string { return p.parameter } -func (p *Parameter) IsEnabled() bool { +func (p *parameter) IsEnabled() bool { return p.isEnabled } -func (p *Parameter) SetEnable() { +func (p *parameter) SetEnable() { p.isEnabled = true } -func (p *Parameter) SetDisable() { +func (p *parameter) SetDisable() { p.isEnabled = false } diff --git a/internal/ffmpeg/encoder/flv/encoder.go b/internal/ffmpeg/encoder/flv/encoder.go new file mode 100644 index 0000000..5a2fa14 --- /dev/null +++ b/internal/ffmpeg/encoder/flv/encoder.go @@ -0,0 +1,21 @@ +package flv + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:v", "flv"} + } + + return encoder.NewEncoder("flv", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "flv" + formats := []string{"flv"} + fileType := encoder.FileType(encoder.Video) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/gif/encoder.go b/internal/ffmpeg/encoder/gif/encoder.go new file mode 100644 index 0000000..98ae1e9 --- /dev/null +++ b/internal/ffmpeg/encoder/gif/encoder.go @@ -0,0 +1,21 @@ +package gif + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:v", "gif"} + } + + return encoder.NewEncoder("gif", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "gif" + formats := []string{"gif"} + fileType := encoder.FileType(encoder.Image) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/h264_nvenc/encoder.go b/internal/ffmpeg/encoder/h264_nvenc/encoder.go similarity index 53% rename from encoder/h264_nvenc/encoder.go rename to internal/ffmpeg/encoder/h264_nvenc/encoder.go index b064975..f52e3f5 100644 --- a/encoder/h264_nvenc/encoder.go +++ b/internal/ffmpeg/encoder/h264_nvenc/encoder.go @@ -2,7 +2,7 @@ package h264_nvenc import ( "errors" - encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" ) var Presets = []string{ @@ -20,11 +20,11 @@ var Presets = []string{ "losslesshp", } -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{ +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{ "preset": newParameterPreset(), } - getParams := func(parameters map[string]encoder2.ParameterContract) []string { + getParams := func(parameters map[string]encoder.ParameterContract) []string { params := []string{"-c:v", "h264_nvenc"} if parameters["preset"] != nil && parameters["preset"].IsEnabled() { @@ -34,17 +34,17 @@ func NewEncoder() encoder2.EncoderContract { return params } - return encoder2.NewEncoder("h264_nvenc", parameters, getParams) + return encoder.NewEncoder("h264_nvenc", parameters, getParams) } -func NewData() encoder2.EncoderDataContract { +func NewData() encoder.EncoderDataContract { title := "h264_nvenc" formats := []string{"mp4"} - fileType := encoder2.FileType(encoder2.Video) - return encoder2.NewData(title, formats, fileType, NewEncoder) + fileType := encoder.FileType(encoder.Video) + return encoder.NewData(title, formats, fileType, NewEncoder) } -func newParameterPreset() encoder2.ParameterContract { +func newParameterPreset() encoder.ParameterContract { setParameter := func(s string) (string, error) { for _, value := range Presets { if value == s { @@ -54,5 +54,5 @@ func newParameterPreset() encoder2.ParameterContract { return "", errors.New("preset not found") } - return encoder2.NewParameter("preset", false, "default", setParameter) + return encoder.NewParameter("preset", false, "default", setParameter) } diff --git a/internal/ffmpeg/encoder/libmp3lame/encoder.go b/internal/ffmpeg/encoder/libmp3lame/encoder.go new file mode 100644 index 0000000..86112cb --- /dev/null +++ b/internal/ffmpeg/encoder/libmp3lame/encoder.go @@ -0,0 +1,21 @@ +package libmp3lame + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:a", "libmp3lame"} + } + + return encoder.NewEncoder("libmp3lame", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "libmp3lame" + formats := []string{"mp3"} + fileType := encoder.FileType(encoder.Audio) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/libshine/encoder.go b/internal/ffmpeg/encoder/libshine/encoder.go new file mode 100644 index 0000000..87df433 --- /dev/null +++ b/internal/ffmpeg/encoder/libshine/encoder.go @@ -0,0 +1,21 @@ +package libshine + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:a", "libshine"} + } + + return encoder.NewEncoder("libshine", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "libshine" + formats := []string{"mp3"} + fileType := encoder.FileType(encoder.Audio) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/libtwolame/encoder.go b/internal/ffmpeg/encoder/libtwolame/encoder.go new file mode 100644 index 0000000..9e6623e --- /dev/null +++ b/internal/ffmpeg/encoder/libtwolame/encoder.go @@ -0,0 +1,21 @@ +package libtwolame + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:a", "libtwolame"} + } + + return encoder.NewEncoder("libtwolame", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "libtwolame" + formats := []string{"mp2"} + fileType := encoder.FileType(encoder.Audio) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/libvpx/encoder.go b/internal/ffmpeg/encoder/libvpx/encoder.go new file mode 100644 index 0000000..42bd1f2 --- /dev/null +++ b/internal/ffmpeg/encoder/libvpx/encoder.go @@ -0,0 +1,21 @@ +package libvpx + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:v", "libvpx"} + } + + return encoder.NewEncoder("libvpx", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "libvpx" + formats := []string{"webm", "mkv"} + fileType := encoder.FileType(encoder.Video) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/libvpx_vp9/encoder.go b/internal/ffmpeg/encoder/libvpx_vp9/encoder.go new file mode 100644 index 0000000..717eb56 --- /dev/null +++ b/internal/ffmpeg/encoder/libvpx_vp9/encoder.go @@ -0,0 +1,21 @@ +package libvpx_vp9 + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:v", "libvpx-vp9"} + } + + return encoder.NewEncoder("libvpx_vp9", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "libvpx-vp9" + formats := []string{"webm", "mkv"} + fileType := encoder.FileType(encoder.Video) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/libwebp/encoder.go b/internal/ffmpeg/encoder/libwebp/encoder.go new file mode 100644 index 0000000..5418bbc --- /dev/null +++ b/internal/ffmpeg/encoder/libwebp/encoder.go @@ -0,0 +1,21 @@ +package libwebp + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:v", "libwebp"} + } + + return encoder.NewEncoder("libwebp", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "libwebp" + formats := []string{"webp"} + fileType := encoder.FileType(encoder.Image) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/libwebp_anim/encoder.go b/internal/ffmpeg/encoder/libwebp_anim/encoder.go new file mode 100644 index 0000000..549e589 --- /dev/null +++ b/internal/ffmpeg/encoder/libwebp_anim/encoder.go @@ -0,0 +1,21 @@ +package libwebp_anim + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:v", "libwebp_anim"} + } + + return encoder.NewEncoder("libwebp_anim", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "libwebp_anim" + formats := []string{"webp"} + fileType := encoder.FileType(encoder.Image) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/libx264/encoder.go b/internal/ffmpeg/encoder/libx264/encoder.go similarity index 53% rename from encoder/libx264/encoder.go rename to internal/ffmpeg/encoder/libx264/encoder.go index 5543570..0a3070f 100644 --- a/encoder/libx264/encoder.go +++ b/internal/ffmpeg/encoder/libx264/encoder.go @@ -2,7 +2,7 @@ package libx264 import ( "errors" - encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" ) var Presets = []string{ @@ -18,11 +18,11 @@ var Presets = []string{ "placebo", } -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{ +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{ "preset": newParameterPreset(), } - getParams := func(parameters map[string]encoder2.ParameterContract) []string { + getParams := func(parameters map[string]encoder.ParameterContract) []string { params := []string{"-c:v", "libx264"} if parameters["preset"] != nil && parameters["preset"].IsEnabled() { @@ -32,17 +32,17 @@ func NewEncoder() encoder2.EncoderContract { return params } - return encoder2.NewEncoder("libx264", parameters, getParams) + return encoder.NewEncoder("libx264", parameters, getParams) } -func NewData() encoder2.EncoderDataContract { +func NewData() encoder.EncoderDataContract { title := "libx264" formats := []string{"mp4"} - fileType := encoder2.FileType(encoder2.Video) - return encoder2.NewData(title, formats, fileType, NewEncoder) + fileType := encoder.FileType(encoder.Video) + return encoder.NewData(title, formats, fileType, NewEncoder) } -func newParameterPreset() encoder2.ParameterContract { +func newParameterPreset() encoder.ParameterContract { setParameter := func(s string) (string, error) { for _, value := range Presets { if value == s { @@ -52,5 +52,5 @@ func newParameterPreset() encoder2.ParameterContract { return "", errors.New("preset not found") } - return encoder2.NewParameter("preset", false, "medium", setParameter) + return encoder.NewParameter("preset", false, "medium", setParameter) } diff --git a/encoder/libx265/encoder.go b/internal/ffmpeg/encoder/libx265/encoder.go similarity index 53% rename from encoder/libx265/encoder.go rename to internal/ffmpeg/encoder/libx265/encoder.go index 1d4a1f3..0d4dbc3 100644 --- a/encoder/libx265/encoder.go +++ b/internal/ffmpeg/encoder/libx265/encoder.go @@ -2,7 +2,7 @@ package libx265 import ( "errors" - encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" ) var Presets = []string{ @@ -18,11 +18,11 @@ var Presets = []string{ "placebo", } -func NewEncoder() encoder2.EncoderContract { - parameters := map[string]encoder2.ParameterContract{ +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{ "preset": newParameterPreset(), } - getParams := func(parameters map[string]encoder2.ParameterContract) []string { + getParams := func(parameters map[string]encoder.ParameterContract) []string { params := []string{"-c:v", "libx265"} if parameters["preset"] != nil && parameters["preset"].IsEnabled() { @@ -32,17 +32,17 @@ func NewEncoder() encoder2.EncoderContract { return params } - return encoder2.NewEncoder("libx265", parameters, getParams) + return encoder.NewEncoder("libx265", parameters, getParams) } -func NewData() encoder2.EncoderDataContract { +func NewData() encoder.EncoderDataContract { title := "libx265" formats := []string{"mp4"} - fileType := encoder2.FileType(encoder2.Video) - return encoder2.NewData(title, formats, fileType, NewEncoder) + fileType := encoder.FileType(encoder.Video) + return encoder.NewData(title, formats, fileType, NewEncoder) } -func newParameterPreset() encoder2.ParameterContract { +func newParameterPreset() encoder.ParameterContract { setParameter := func(s string) (string, error) { for _, value := range Presets { if value == s { @@ -52,5 +52,5 @@ func newParameterPreset() encoder2.ParameterContract { return "", errors.New("preset not found") } - return encoder2.NewParameter("preset", false, "medium", setParameter) + return encoder.NewParameter("preset", false, "medium", setParameter) } diff --git a/internal/ffmpeg/encoder/libxvid/encoder.go b/internal/ffmpeg/encoder/libxvid/encoder.go new file mode 100644 index 0000000..6f09a1e --- /dev/null +++ b/internal/ffmpeg/encoder/libxvid/encoder.go @@ -0,0 +1,21 @@ +package libxvid + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:v", "libxvid"} + } + + return encoder.NewEncoder("libxvid", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "libxvid" + formats := []string{"avi"} + fileType := encoder.FileType(encoder.Video) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/mjpeg/encoder.go b/internal/ffmpeg/encoder/mjpeg/encoder.go new file mode 100644 index 0000000..3252837 --- /dev/null +++ b/internal/ffmpeg/encoder/mjpeg/encoder.go @@ -0,0 +1,21 @@ +package mjpeg + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:v", "mjpeg"} + } + + return encoder.NewEncoder("mjpeg", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "mjpeg" + formats := []string{"jpg"} + fileType := encoder.FileType(encoder.Image) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/mp2/encoder.go b/internal/ffmpeg/encoder/mp2/encoder.go new file mode 100644 index 0000000..da4221d --- /dev/null +++ b/internal/ffmpeg/encoder/mp2/encoder.go @@ -0,0 +1,21 @@ +package mp2 + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:a", "mp2"} + } + + return encoder.NewEncoder("mp2", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "mp2" + formats := []string{"mp2"} + fileType := encoder.FileType(encoder.Audio) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/mp2fixed/encoder.go b/internal/ffmpeg/encoder/mp2fixed/encoder.go new file mode 100644 index 0000000..79cb7c7 --- /dev/null +++ b/internal/ffmpeg/encoder/mp2fixed/encoder.go @@ -0,0 +1,21 @@ +package mp2fixed + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:a", "mp2fixed"} + } + + return encoder.NewEncoder("mp2fixed", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "mp2fixed" + formats := []string{"mp2"} + fileType := encoder.FileType(encoder.Audio) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/mpeg1video/encoder.go b/internal/ffmpeg/encoder/mpeg1video/encoder.go new file mode 100644 index 0000000..18e7753 --- /dev/null +++ b/internal/ffmpeg/encoder/mpeg1video/encoder.go @@ -0,0 +1,21 @@ +package mpeg1video + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:v", "mpeg1video"} + } + + return encoder.NewEncoder("mpeg1video", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "mpeg1video" + formats := []string{"mpg", "mpeg"} + fileType := encoder.FileType(encoder.Video) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/mpeg2video/encoder.go b/internal/ffmpeg/encoder/mpeg2video/encoder.go new file mode 100644 index 0000000..a27bb0c --- /dev/null +++ b/internal/ffmpeg/encoder/mpeg2video/encoder.go @@ -0,0 +1,21 @@ +package mpeg2video + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:v", "mpeg2video"} + } + + return encoder.NewEncoder("mpeg2video", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "mpeg2video" + formats := []string{"mpg", "mpeg"} + fileType := encoder.FileType(encoder.Video) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/mpeg4/encoder.go b/internal/ffmpeg/encoder/mpeg4/encoder.go new file mode 100644 index 0000000..6f22876 --- /dev/null +++ b/internal/ffmpeg/encoder/mpeg4/encoder.go @@ -0,0 +1,21 @@ +package mpeg4 + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:v", "mpeg4"} + } + + return encoder.NewEncoder("mpeg4", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "mpeg4" + formats := []string{"avi"} + fileType := encoder.FileType(encoder.Video) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/msmpeg4/encoder.go b/internal/ffmpeg/encoder/msmpeg4/encoder.go new file mode 100644 index 0000000..3bcf5d8 --- /dev/null +++ b/internal/ffmpeg/encoder/msmpeg4/encoder.go @@ -0,0 +1,21 @@ +package msmpeg4 + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:v", "msmpeg4"} + } + + return encoder.NewEncoder("msmpeg4", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "msmpeg4" + formats := []string{"avi"} + fileType := encoder.FileType(encoder.Video) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/msmpeg4v2/encoder.go b/internal/ffmpeg/encoder/msmpeg4v2/encoder.go new file mode 100644 index 0000000..296730f --- /dev/null +++ b/internal/ffmpeg/encoder/msmpeg4v2/encoder.go @@ -0,0 +1,21 @@ +package msmpeg4v2 + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:v", "msmpeg4v2"} + } + + return encoder.NewEncoder("msmpeg4v2", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "msmpeg4v2" + formats := []string{"avi"} + fileType := encoder.FileType(encoder.Video) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/msvideo1/encoder.go b/internal/ffmpeg/encoder/msvideo1/encoder.go new file mode 100644 index 0000000..d9e22ca --- /dev/null +++ b/internal/ffmpeg/encoder/msvideo1/encoder.go @@ -0,0 +1,21 @@ +package msvideo1 + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:v", "msvideo1"} + } + + return encoder.NewEncoder("msvideo1", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "msvideo1" + formats := []string{"avi"} + fileType := encoder.FileType(encoder.Video) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/png/encoder.go b/internal/ffmpeg/encoder/png/encoder.go new file mode 100644 index 0000000..06bac92 --- /dev/null +++ b/internal/ffmpeg/encoder/png/encoder.go @@ -0,0 +1,21 @@ +package png + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:v", "png"} + } + + return encoder.NewEncoder("png", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "png" + formats := []string{"png"} + fileType := encoder.FileType(encoder.Image) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/qtrle/encoder.go b/internal/ffmpeg/encoder/qtrle/encoder.go new file mode 100644 index 0000000..0641480 --- /dev/null +++ b/internal/ffmpeg/encoder/qtrle/encoder.go @@ -0,0 +1,21 @@ +package qtrle + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:v", "qtrle"} + } + + return encoder.NewEncoder("qtrle", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "qtrle" + formats := []string{"mov"} + fileType := encoder.FileType(encoder.Video) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/sgi/encoder.go b/internal/ffmpeg/encoder/sgi/encoder.go new file mode 100644 index 0000000..3d49a3c --- /dev/null +++ b/internal/ffmpeg/encoder/sgi/encoder.go @@ -0,0 +1,21 @@ +package sgi + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:v", "sgi"} + } + + return encoder.NewEncoder("sgi", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "sgi" + formats := []string{"sgi"} + fileType := encoder.FileType(encoder.Image) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/tiff/encoder.go b/internal/ffmpeg/encoder/tiff/encoder.go new file mode 100644 index 0000000..ad43a9d --- /dev/null +++ b/internal/ffmpeg/encoder/tiff/encoder.go @@ -0,0 +1,21 @@ +package tiff + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:v", "tiff"} + } + + return encoder.NewEncoder("tiff", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "tiff" + formats := []string{"tiff"} + fileType := encoder.FileType(encoder.Image) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/wmav1/encoder.go b/internal/ffmpeg/encoder/wmav1/encoder.go new file mode 100644 index 0000000..2aa5af1 --- /dev/null +++ b/internal/ffmpeg/encoder/wmav1/encoder.go @@ -0,0 +1,21 @@ +package wmav1 + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:a", "wmav1"} + } + + return encoder.NewEncoder("wmav1", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "wmav1" + formats := []string{"wma"} + fileType := encoder.FileType(encoder.Audio) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/wmav2/encoder.go b/internal/ffmpeg/encoder/wmav2/encoder.go new file mode 100644 index 0000000..61e2cb5 --- /dev/null +++ b/internal/ffmpeg/encoder/wmav2/encoder.go @@ -0,0 +1,21 @@ +package wmav2 + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:a", "wmav2"} + } + + return encoder.NewEncoder("wmav2", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "wmav2" + formats := []string{"wma"} + fileType := encoder.FileType(encoder.Audio) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/wmv1/encoder.go b/internal/ffmpeg/encoder/wmv1/encoder.go new file mode 100644 index 0000000..cab9f49 --- /dev/null +++ b/internal/ffmpeg/encoder/wmv1/encoder.go @@ -0,0 +1,21 @@ +package wmv1 + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:v", "wmv1"} + } + + return encoder.NewEncoder("wmv1", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "wmv1" + formats := []string{"wmv"} + fileType := encoder.FileType(encoder.Video) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/wmv2/encoder.go b/internal/ffmpeg/encoder/wmv2/encoder.go new file mode 100644 index 0000000..092ea88 --- /dev/null +++ b/internal/ffmpeg/encoder/wmv2/encoder.go @@ -0,0 +1,21 @@ +package wmv2 + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:v", "wmv2"} + } + + return encoder.NewEncoder("wmv2", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "wmv2" + formats := []string{"wmv"} + fileType := encoder.FileType(encoder.Video) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/encoder/xbm/encoder.go b/internal/ffmpeg/encoder/xbm/encoder.go new file mode 100644 index 0000000..bc2e612 --- /dev/null +++ b/internal/ffmpeg/encoder/xbm/encoder.go @@ -0,0 +1,21 @@ +package xbm + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +func NewEncoder() encoder.EncoderContract { + parameters := map[string]encoder.ParameterContract{} + getParams := func(parameters map[string]encoder.ParameterContract) []string { + return []string{"-c:v", "xbm"} + } + + return encoder.NewEncoder("xbm", parameters, getParams) +} + +func NewData() encoder.EncoderDataContract { + title := "xbm" + formats := []string{"xbm"} + fileType := encoder.FileType(encoder.Image) + return encoder.NewData(title, formats, fileType, NewEncoder) +} diff --git a/internal/ffmpeg/ffmpeg.go b/internal/ffmpeg/ffmpeg.go new file mode 100644 index 0000000..e4046d1 --- /dev/null +++ b/internal/ffmpeg/ffmpeg.go @@ -0,0 +1,139 @@ +package ffmpeg + +import ( + "bufio" + "errors" + "fyne.io/fyne/v2/lang" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/utils" + "io" + "os/exec" + "regexp" + "strings" +) + +type ProgressContract interface { + GetProtocole() string + Run(stdOut io.ReadCloser, stdErr io.ReadCloser) error +} + +type FFmpegContract interface { + GetPath() string + GetVersion() (string, error) + GetEncoders(scanner func(scanner *bufio.Reader)) error + RunConvert(setting ConvertSetting, progress ProgressContract, beforeWait func(cmd *exec.Cmd), afterWait func(cmd *exec.Cmd)) error +} + +type ffmpeg struct { + path string +} + +func newFFmpeg(path string) (FFmpegContract, error) { + if path == "" { + return nil, errors.New(lang.L("errorFFmpeg")) + } + + isCheck, err := checkFFmpegPath(path) + if err != nil { + return nil, err + } + if isCheck == false { + return nil, errors.New(lang.L("errorFFmpeg")) + } + + return &ffmpeg{ + path: path, + }, nil +} + +func (f *ffmpeg) GetPath() string { + return f.path +} + +func (f *ffmpeg) GetVersion() (string, error) { + cmd := exec.Command(f.path, "-version") + utils.PrepareBackgroundCommand(cmd) + out, err := cmd.CombinedOutput() + if err != nil { + return "", err + } + text := regexp.MustCompile("\r?\n").Split(strings.TrimSpace(string(out)), -1) + return text[0], nil +} + +func (f *ffmpeg) RunConvert(setting ConvertSetting, progress ProgressContract, beforeWait func(cmd *exec.Cmd), afterWait func(cmd *exec.Cmd)) error { + overwriteOutputFiles := "-n" + if setting.OverwriteOutputFiles == true { + overwriteOutputFiles = "-y" + } + args := []string{overwriteOutputFiles, "-i", setting.FileInput.Path} + args = append(args, setting.Encoder.GetParams()...) + args = append(args, "-progress", progress.GetProtocole(), setting.FileOut.Path) + cmd := exec.Command(f.path, args...) + utils.PrepareBackgroundCommand(cmd) + + stdOut, err := cmd.StdoutPipe() + if err != nil { + return err + } + stdErr, err := cmd.StderrPipe() + if err != nil { + return err + } + + err = cmd.Start() + if err != nil { + return err + } + + if beforeWait != nil { + beforeWait(cmd) + } + + errProgress := progress.Run(stdOut, stdErr) + + err = cmd.Wait() + if afterWait != nil { + afterWait(cmd) + } + if errProgress != nil { + return errProgress + } + if err != nil { + return err + } + + return nil +} + +func (f *ffmpeg) GetEncoders(scanner func(scanner *bufio.Reader)) error { + cmd := exec.Command(f.path, "-encoders") + utils.PrepareBackgroundCommand(cmd) + + stdOut, err := cmd.StdoutPipe() + if err != nil { + return err + } + + err = cmd.Start() + if err != nil { + return err + } + + scannerErr := bufio.NewReader(stdOut) + scanner(scannerErr) + + return cmd.Wait() +} + +func checkFFmpegPath(path string) (bool, error) { + cmd := exec.Command(path, "-version") + utils.PrepareBackgroundCommand(cmd) + out, err := cmd.CombinedOutput() + if err != nil { + return false, err + } + if strings.Contains(strings.TrimSpace(string(out)), "ffmpeg") == false { + return false, nil + } + return true, nil +} diff --git a/internal/ffmpeg/ffplay.go b/internal/ffmpeg/ffplay.go new file mode 100644 index 0000000..bb6454d --- /dev/null +++ b/internal/ffmpeg/ffplay.go @@ -0,0 +1,73 @@ +package ffmpeg + +import ( + "errors" + "fyne.io/fyne/v2/lang" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/utils" + "os/exec" + "regexp" + "strings" +) + +type FFplayContract interface { + GetPath() string + GetVersion() (string, error) + Play(file *File) error +} + +type ffplay struct { + path string +} + +func newFFplay(path string) (FFplayContract, error) { + if path == "" { + return nil, errors.New(lang.L("errorFFplay")) + } + + isCheck, err := checkFFplayPath(path) + if err != nil { + return nil, err + } + if isCheck == false { + return nil, errors.New(lang.L("errorFFplay")) + } + + return &ffplay{ + path: path, + }, nil +} + +func (f *ffplay) GetPath() string { + return f.path +} + +func (f *ffplay) GetVersion() (string, error) { + cmd := exec.Command(f.path, "-version") + utils.PrepareBackgroundCommand(cmd) + out, err := cmd.CombinedOutput() + if err != nil { + return "", err + } + text := regexp.MustCompile("\r?\n").Split(strings.TrimSpace(string(out)), -1) + return text[0], nil +} + +func (f *ffplay) Play(file *File) error { + args := []string{file.Path} + cmd := exec.Command(f.GetPath(), args...) + + return cmd.Run() +} + +func checkFFplayPath(path string) (bool, error) { + cmd := exec.Command(path, "-version") + utils.PrepareBackgroundCommand(cmd) + out, err := cmd.CombinedOutput() + if err != nil { + return false, err + } + if strings.Contains(strings.TrimSpace(string(out)), "ffplay") == false { + return false, nil + } + return true, nil +} diff --git a/internal/ffmpeg/ffprobe.go b/internal/ffmpeg/ffprobe.go new file mode 100644 index 0000000..e56299d --- /dev/null +++ b/internal/ffmpeg/ffprobe.go @@ -0,0 +1,124 @@ +package ffmpeg + +import ( + "errors" + "fyne.io/fyne/v2/lang" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/utils" + "os/exec" + "regexp" + "strconv" + "strings" + "unicode" +) + +type FFprobeContract interface { + GetPath() string + GetVersion() (string, error) + GetTotalDuration(file *File) (float64, error) +} + +type ffprobe struct { + path string +} + +func newFFprobe(path string) (FFprobeContract, error) { + if path == "" { + return nil, errors.New(lang.L("errorFFprobe")) + } + + isCheck, err := checkFFprobePath(path) + if err != nil { + return nil, err + } + if isCheck == false { + return nil, errors.New(lang.L("errorFFprobe")) + } + + return &ffprobe{ + path: path, + }, nil +} + +func (f *ffprobe) GetPath() string { + return f.path +} + +func (f *ffprobe) GetVersion() (string, error) { + cmd := exec.Command(f.path, "-version") + utils.PrepareBackgroundCommand(cmd) + out, err := cmd.CombinedOutput() + if err != nil { + return "", err + } + text := regexp.MustCompile("\r?\n").Split(strings.TrimSpace(string(out)), -1) + return text[0], nil +} + +func (f *ffprobe) GetTotalDuration(file *File) (duration float64, err error) { + args := []string{"-v", "error", "-select_streams", "v:0", "-count_packets", "-show_entries", "stream=nb_read_packets", "-of", "csv=p=0", file.Path} + cmd := exec.Command(f.path, args...) + utils.PrepareBackgroundCommand(cmd) + out, err := cmd.CombinedOutput() + if err != nil { + errString := strings.TrimSpace(string(out)) + if len(errString) > 1 { + return 0, errors.New(errString) + } + return 0, err + } + frames := strings.TrimSpace(string(out)) + if len(frames) == 0 { + return f.getAlternativeTotalDuration(file) + } + + duration, err = strconv.ParseFloat(frames, 64) + if err != nil { + // fix .mts duration + return strconv.ParseFloat(getFirstDigits(frames), 64) + } + return duration, err +} + +func (f *ffprobe) getAlternativeTotalDuration(file *File) (duration float64, err error) { + args := []string{"-v", "error", "-select_streams", "a:0", "-count_packets", "-show_entries", "stream=nb_read_packets", "-of", "csv=p=0", file.Path} + cmd := exec.Command(f.path, args...) + utils.PrepareBackgroundCommand(cmd) + out, err := cmd.CombinedOutput() + if err != nil { + errString := strings.TrimSpace(string(out)) + if len(errString) > 1 { + return 0, errors.New(errString) + } + return 0, err + } + frames := strings.TrimSpace(string(out)) + if len(frames) == 0 { + return 0, errors.New("error getting number of frames") + } + return strconv.ParseFloat(frames, 64) +} + +func checkFFprobePath(path string) (bool, error) { + cmd := exec.Command(path, "-version") + utils.PrepareBackgroundCommand(cmd) + out, err := cmd.CombinedOutput() + if err != nil { + return false, err + } + if strings.Contains(strings.TrimSpace(string(out)), "ffprobe") == false { + return false, nil + } + return true, nil +} + +func getFirstDigits(s string) string { + result := "" + for _, r := range s { + if unicode.IsDigit(r) { + result += string(r) + } else { + break + } + } + return result +} diff --git a/internal/ffmpeg/utilities.go b/internal/ffmpeg/utilities.go new file mode 100644 index 0000000..06dae49 --- /dev/null +++ b/internal/ffmpeg/utilities.go @@ -0,0 +1,221 @@ +package ffmpeg + +import ( + "errors" + "fyne.io/fyne/v2/lang" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application/setting" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" +) + +type File struct { + Path string + Name string + Ext string +} + +type ConvertSetting struct { + FileInput File + FileOut File + OverwriteOutputFiles bool + Encoder encoder.EncoderContract +} + +type UtilitiesContract interface { + UtilityCheck() bool + + GetFFmpeg() (FFmpegContract, error) + GetFFmpegVersion() string + GetFFmpegPath() string + ChangeFFmpeg(path string) error + + GetFFprobe() (FFprobeContract, error) + GetFFprobeVersion() string + GetFFprobePath() string + ChangeFFprobe(path string) error + + GetFFplay() (FFplayContract, error) + GetFFplayVersion() string + GetFFplayPath() string + ChangeFFplay(path string) error +} + +type utilities struct { + setting setting.SettingContract + ffmpeg FFmpegContract + ffprobe FFprobeContract + ffplay FFplayContract +} + +func NewUtilities(setting setting.SettingContract) UtilitiesContract { + return &utilities{ + setting: setting, + } +} + +func (u *utilities) UtilityCheck() bool { + var err error + + _, err = u.GetFFmpeg() + if err != nil { + return false + } + + _, err = u.GetFFprobe() + if err != nil { + return false + } + + _, err = u.GetFFplay() + if err != nil { + return false + } + + return true +} + +func (u *utilities) GetFFmpeg() (FFmpegContract, error) { + if u.ffmpeg == nil { + createFFmpeg, err := newFFmpeg(u.setting.GetFFmpegPath()) + if err != nil { + return nil, err + } + u.ffmpeg = createFFmpeg + } + + return u.ffmpeg, nil +} + +func (u *utilities) GetFFmpegVersion() string { + ffmpegService, err := u.GetFFmpeg() + if err != nil { + return lang.L("errorFFmpegVersion") + } + + version, err := ffmpegService.GetVersion() + if err != nil { + return lang.L("errorFFmpegVersion") + } + return version +} + +func (u *utilities) GetFFmpegPath() string { + ffmpegService, err := u.GetFFmpeg() + if err != nil { + return "" + } + return ffmpegService.GetPath() +} + +func (u *utilities) ChangeFFmpeg(path string) error { + if path == "" { + return errors.New(lang.L("errorFFmpeg")) + } + + createFFmpeg, err := newFFmpeg(path) + if err != nil { + return err + } + + u.ffmpeg = createFFmpeg + u.setting.SetFFmpegPath(path) + + return nil +} + +func (u *utilities) GetFFprobe() (FFprobeContract, error) { + if u.ffprobe == nil { + createFFprobe, err := newFFprobe(u.setting.GetFFprobePath()) + if err != nil { + return nil, err + } + u.ffprobe = createFFprobe + } + + return u.ffprobe, nil +} + +func (u *utilities) GetFFprobeVersion() string { + ffprobeService, err := u.GetFFprobe() + if err != nil { + return lang.L("errorFFprobeVersion") + } + + ffprobeVersion, err := ffprobeService.GetVersion() + if err != nil { + return lang.L("errorFFprobeVersion") + } + return ffprobeVersion +} + +func (u *utilities) GetFFprobePath() string { + ffprobeService, err := u.GetFFprobe() + if err != nil { + return "" + } + return ffprobeService.GetPath() +} + +func (u *utilities) ChangeFFprobe(path string) error { + if path == "" { + return errors.New(lang.L("errorFFprobe")) + } + + createFFprobe, err := newFFprobe(path) + if err != nil { + return err + } + + u.ffprobe = createFFprobe + u.setting.SetFFprobePath(path) + + return nil +} + +func (u *utilities) GetFFplay() (FFplayContract, error) { + if u.ffplay == nil { + createFFplay, err := newFFplay(u.setting.GetFFplayPath()) + if err != nil { + return nil, err + } + u.ffplay = createFFplay + } + + return u.ffplay, nil +} + +func (u *utilities) GetFFplayVersion() string { + ffplayService, err := u.GetFFplay() + if err != nil { + return lang.L("errorFFplayVersion") + } + + ffplayVersion, err := ffplayService.GetVersion() + if err != nil { + return lang.L("errorFFplayVersion") + } + return ffplayVersion +} + +func (u *utilities) GetFFplayPath() string { + ffplayService, err := u.GetFFplay() + if err != nil { + return "" + } + return ffplayService.GetPath() +} + +func (u *utilities) ChangeFFplay(path string) error { + if path == "" { + return errors.New(lang.L("errorFFplay")) + } + + createFFplay, err := newFFplay(path) + if err != nil { + return err + } + + u.ffplay = createFFplay + u.setting.SetFFplayPath(path) + + return nil +} diff --git a/internal/gui/menu/main.go b/internal/gui/menu/main.go new file mode 100644 index 0000000..6afe023 --- /dev/null +++ b/internal/gui/menu/main.go @@ -0,0 +1,29 @@ +package menu + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/lang" +) + +func MainMenuSettings( + actionMainSettings func(), + actionFFPathSelection func(), +) *fyne.Menu { + quit := fyne.NewMenuItem(lang.L("exit"), nil) + quit.IsQuit = true + + settingsSelection := fyne.NewMenuItem(lang.L("settings"), actionMainSettings) + ffPathSelection := fyne.NewMenuItem(lang.L("changeFFPath"), actionFFPathSelection) + + return fyne.NewMenu(lang.L("settings"), settingsSelection, ffPathSelection, quit) +} + +func MainMenuHelp( + actionAbout func(), + actionHelpFFplay func(), +) *fyne.Menu { + about := fyne.NewMenuItem(lang.L("about"), actionAbout) + helpFFplay := fyne.NewMenuItem(lang.L("helpFFplay"), actionHelpFFplay) + + return fyne.NewMenu(lang.L("help"), helpFFplay, about) +} diff --git a/menu/view.go b/internal/gui/view/about.go similarity index 63% rename from menu/view.go rename to internal/gui/view/about.go index 324b012..a92fe73 100644 --- a/menu/view.go +++ b/internal/gui/view/about.go @@ -1,316 +1,97 @@ -package menu +package view import ( "fyne.io/fyne/v2" "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/container" - "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/lang" "fyne.io/fyne/v2/widget" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" - "github.com/nicksnyder/go-i18n/v2/i18n" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/resources" "golang.org/x/image/colornames" "net/url" ) -type ViewContract interface { - About(ffmpegVersion string, ffprobeVersion string, ffplayVersion string) - Gratitude() - HelpFFplay() -} - -type View struct { - app kernel.AppContract -} - -func NewView(app kernel.AppContract) *View { - return &View{ - app: app, - } -} - -func (v View) Gratitude() { - view := v.app.GetAppFyne().NewWindow(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "gratitude", - })) - view.Resize(fyne.Size{Width: 500, Height: 400}) - view.SetFixedSize(true) - - image := canvas.NewImageFromFile("icon.png") - image.SetMinSize(fyne.Size{Width: 100, Height: 100}) - image.FillMode = canvas.ImageFillContain - - gratitude := canvas.NewText(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "gratitude", - }), colornames.Darkgreen) - gratitude.TextStyle = fyne.TextStyle{Bold: true} - gratitude.TextSize = 20 - - view.SetContent( - container.NewScroll(container.NewVBox( - container.NewBorder(nil, nil, container.NewVBox(image), nil, container.NewVBox( - gratitude, - widget.NewLabel(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "gratitudeText", - })), - widget.NewLabel("Екатерина"), - widget.NewLabel("Евгений"), - ), - ))), - ) - - view.CenterOnScreen() - view.Show() -} - -func (v View) About(ffmpegVersion string, ffprobeVersion string, ffplayVersion string) { - view := v.app.GetAppFyne().NewWindow(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "about", - })) - view.Resize(fyne.Size{Width: 793, Height: 550}) - view.SetFixedSize(true) - +func About(appVersion string, ffmpegVersion string, ffprobeVersion string, ffplayVersion string) fyne.CanvasObject { programmName := canvas.NewText(" GUI for FFmpeg", colornames.Darkgreen) programmName.TextStyle = fyne.TextStyle{Bold: true} programmName.TextSize = 20 - programmLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "programmLink", - }), &url.URL{ - Scheme: "https", - Host: "gui-for-ffmpeg.projects.kor-elf.net", - Path: "/", - }) - - licenseLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "licenseLink", - }), &url.URL{ - Scheme: "https", - Host: "git.kor-elf.net", - Path: "kor-elf/gui-for-ffmpeg/src/branch/main/LICENSE", - }) - - licenseLinkOther := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "licenseLinkOther", - }), &url.URL{ - Scheme: "https", - Host: "git.kor-elf.net", - Path: "kor-elf/gui-for-ffmpeg/src/branch/main/LICENSE-3RD-PARTY.txt", - }) - - programmVersion := widget.NewRichTextFromMarkdown(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "programmVersion", - TemplateData: map[string]string{ - "Version": v.app.GetAppFyne().Metadata().Version, + programmLink := widget.NewHyperlink( + lang.L("programmLink"), + &url.URL{ + Scheme: "https", + Host: "gui-for-ffmpeg.projects.kor-elf.net", + Path: "/", }, - })) + ) + + licenseLink := widget.NewHyperlink( + lang.L("licenseLink"), + &url.URL{ + Scheme: "https", + Host: "git.kor-elf.net", + Path: "kor-elf/gui-for-ffmpeg/src/branch/main/LICENSE", + }, + ) + + licenseLinkOther := widget.NewHyperlink( + lang.L("licenseLinkOther"), + &url.URL{ + Scheme: "https", + Host: "git.kor-elf.net", + Path: "kor-elf/gui-for-ffmpeg/src/branch/main/LICENSE-3RD-PARTY.txt", + }, + ) + + programmVersion := widget.NewRichTextFromMarkdown( + lang.L( + "programmVersion", + map[string]any{"Version": appVersion}, + ), + ) aboutText := widget.NewRichText( &widget.TextSegment{ - Text: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "aboutText", - }), + Text: lang.L("aboutText"), }, ) - image := canvas.NewImageFromFile("icon.png") + image := canvas.NewImageFromResource(resources.IconAppLogoResource()) image.SetMinSize(fyne.Size{Width: 100, Height: 100}) image.FillMode = canvas.ImageFillContain - ffmpegTrademark := widget.NewRichTextFromMarkdown(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "ffmpegTrademark", - })) - ffmpegLGPL := widget.NewRichTextFromMarkdown(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "ffmpegLGPL", - })) + ffmpegTrademark := widget.NewRichTextFromMarkdown(lang.L("ffmpegTrademark")) + ffmpegLGPL := widget.NewRichTextFromMarkdown(lang.L("ffmpegLGPL")) - view.SetContent( - container.NewScroll(container.NewVBox( - container.NewBorder(nil, nil, container.NewVBox(image), nil, container.NewVBox( - programmName, - programmVersion, - aboutText, - ffmpegTrademark, - ffmpegLGPL, - v.getCopyright(), - container.NewHBox(programmLink, licenseLink), - container.NewHBox(licenseLinkOther), - )), - v.getAboutFfmpeg(ffmpegVersion), - v.getAboutFfprobe(ffprobeVersion), - v.getAboutFfplay(ffplayVersion), - widget.NewCard(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "AlsoUsedProgram", - }), "", v.getOther()), + return container.NewScroll(container.NewVBox( + container.NewBorder(nil, nil, container.NewVBox(image), nil, container.NewVBox( + programmName, + programmVersion, + aboutText, + ffmpegTrademark, + ffmpegLGPL, + widget.NewRichTextFromMarkdown("Copyright (c) 2024 **[Leonid Nikitin (kor-elf)](https://git.kor-elf.net/kor-elf/)**."), + container.NewHBox(programmLink, licenseLink), + container.NewHBox(licenseLinkOther), )), - ) - view.CenterOnScreen() - view.Show() + aboutFFmpeg(ffmpegVersion), + aboutFFprobe(ffprobeVersion), + aboutFFplay(ffplayVersion), + widget.NewCard(lang.L("AlsoUsedProgram"), "", license3RDParty()), + )) } -func (v View) HelpFFplay() { - view := v.app.GetAppFyne().NewWindow(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplay", - })) - view.Resize(fyne.Size{Width: 800, Height: 550}) - view.SetFixedSize(true) - - data := [][]string{ - []string{ - v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplayKeys", - }), - v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplayDescription", - }), - }, - []string{ - "Q, ESC", - v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplayQuit", - }), - }, - []string{ - "F, " + v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplayDoubleClickLeftMouseButton", - }), - v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplayToggleFullScreen", - }), - }, - []string{ - "P, " + - v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplayKeySpace", - }), - v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplayPause", - }), - }, - []string{ - "M", - v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplayToggleMute", - }), - }, - []string{ - "9, /", - v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplayDecreaseVolume", - }), - }, - []string{ - "0, *", - v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplayIncreaseVolume", - }), - }, - []string{ - v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplayKeyLeft", - }), - v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplaySeekBackward10Seconds", - }), - }, - []string{ - v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplayKeyRight", - }), - v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplaySeekForward10Seconds", - }), - }, - []string{ - v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplayKeyDown", - }), - v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplaySeekBackward1Minute", - }), - }, - []string{ - v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplayKeyUp", - }), - v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplaySeekBForward1Minute", - }), - }, - []string{ - "Page Down", - v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplaySeekBackward10Minutes", - }), - }, - []string{ - "Page Up", - v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplaySeekBForward10Minutes", - }), - }, - []string{ - "S, " + v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplayKeyHoldS", - }), - v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplayActivateFrameStepMode", - }), - }, - []string{ - "W", - v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "helpFFplayCycleVideoFiltersOrShowModes", - }), - }, - } - - list := widget.NewTable( - func() (int, int) { - return len(data), len(data[0]) - }, - func() fyne.CanvasObject { - return widget.NewLabel("") - }, - func(i widget.TableCellID, o fyne.CanvasObject) { - if i.Row == 0 { - o.(*widget.Label).TextStyle.Bold = true - o.(*widget.Label).SizeName = theme.SizeNameSubHeadingText - } - if i.Col == 0 { - o.(*widget.Label).TextStyle.Bold = true - } - o.(*widget.Label).SetText(data[i.Row][i.Col]) - }) - list.SetRowHeight(0, 40) - list.SetColumnWidth(0, 200) - list.SetColumnWidth(1, 585) - list.SetRowHeight(2, 55) - view.SetContent( - container.NewScroll(list), - ) - view.CenterOnScreen() - view.Show() -} - -func (v View) getCopyright() *widget.RichText { - return widget.NewRichTextFromMarkdown("Copyright (c) 2024 **[Leonid Nikitin (kor-elf)](https://git.kor-elf.net/kor-elf/)**.") -} - -func (v View) getAboutFfmpeg(version string) *fyne.Container { +func aboutFFmpeg(version string) *fyne.Container { programmName := canvas.NewText(" FFmpeg", colornames.Darkgreen) programmName.TextStyle = fyne.TextStyle{Bold: true} programmName.TextSize = 20 - programmLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "programmLink", - }), &url.URL{ + programmLink := widget.NewHyperlink(lang.L("programmLink"), &url.URL{ Scheme: "https", Host: "ffmpeg.org", Path: "", }) - licenseLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "licenseLink", - }), &url.URL{ + licenseLink := widget.NewHyperlink(lang.L("licenseLink"), &url.URL{ Scheme: "https", Host: "ffmpeg.org", Path: "legal.html", @@ -319,28 +100,24 @@ func (v View) getAboutFfmpeg(version string) *fyne.Container { return container.NewVBox( programmName, widget.NewLabel(version), - widget.NewRichTextFromMarkdown("**FFmpeg** is a trademark of **[Fabrice Bellard](http://bellard.org/)**, originator of the **[FFmpeg](https://ffmpeg.org/about.html)** project."), - widget.NewRichTextFromMarkdown("This software uses libraries from the **FFmpeg** project under the **[LGPLv2.1](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)**."), + widget.NewRichTextFromMarkdown("**FFmpeg** is a trademark of **[Fabrice Bellard](https://bellard.org/)**, originator of the **[FFmpeg](https://ffmpeg.org/about.html)** project."), + widget.NewRichTextFromMarkdown("This software uses libraries from the **FFmpeg** project under the **[LGPLv2.1](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)**."), container.NewHBox(programmLink, licenseLink), ) } -func (v View) getAboutFfprobe(version string) *fyne.Container { +func aboutFFprobe(version string) *fyne.Container { programmName := canvas.NewText(" FFprobe", colornames.Darkgreen) programmName.TextStyle = fyne.TextStyle{Bold: true} programmName.TextSize = 20 - programmLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "programmLink", - }), &url.URL{ + programmLink := widget.NewHyperlink(lang.L("programmLink"), &url.URL{ Scheme: "https", Host: "ffmpeg.org", Path: "ffprobe.html", }) - licenseLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "licenseLink", - }), &url.URL{ + licenseLink := widget.NewHyperlink(lang.L("licenseLink"), &url.URL{ Scheme: "https", Host: "ffmpeg.org", Path: "legal.html", @@ -349,28 +126,24 @@ func (v View) getAboutFfprobe(version string) *fyne.Container { return container.NewVBox( programmName, widget.NewLabel(version), - widget.NewRichTextFromMarkdown("**FFmpeg** is a trademark of **[Fabrice Bellard](http://bellard.org/)**, originator of the **[FFmpeg](https://ffmpeg.org/about.html)** project."), - widget.NewRichTextFromMarkdown("This software uses libraries from the **FFmpeg** project under the **[LGPLv2.1](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)**."), + widget.NewRichTextFromMarkdown("**FFmpeg** is a trademark of **[Fabrice Bellard](https://bellard.org/)**, originator of the **[FFmpeg](https://ffmpeg.org/about.html)** project."), + widget.NewRichTextFromMarkdown("This software uses libraries from the **FFmpeg** project under the **[LGPLv2.1](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)**."), container.NewHBox(programmLink, licenseLink), ) } -func (v View) getAboutFfplay(version string) *fyne.Container { +func aboutFFplay(version string) *fyne.Container { programmName := canvas.NewText(" FFplay", colornames.Darkgreen) programmName.TextStyle = fyne.TextStyle{Bold: true} programmName.TextSize = 20 - programmLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "programmLink", - }), &url.URL{ + programmLink := widget.NewHyperlink(lang.L("programmLink"), &url.URL{ Scheme: "https", Host: "ffmpeg.org", Path: "ffplay.html", }) - licenseLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "licenseLink", - }), &url.URL{ + licenseLink := widget.NewHyperlink(lang.L("licenseLink"), &url.URL{ Scheme: "https", Host: "ffmpeg.org", Path: "legal.html", @@ -379,13 +152,13 @@ func (v View) getAboutFfplay(version string) *fyne.Container { return container.NewVBox( programmName, widget.NewLabel(version), - widget.NewRichTextFromMarkdown("**FFmpeg** is a trademark of **[Fabrice Bellard](http://bellard.org/)**, originator of the **[FFmpeg](https://ffmpeg.org/about.html)** project."), - widget.NewRichTextFromMarkdown("This software uses libraries from the **FFmpeg** project under the **[LGPLv2.1](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)**."), + widget.NewRichTextFromMarkdown("**FFmpeg** is a trademark of **[Fabrice Bellard](https://bellard.org/)**, originator of the **[FFmpeg](https://ffmpeg.org/about.html)** project."), + widget.NewRichTextFromMarkdown("This software uses libraries from the **FFmpeg** project under the **[LGPLv2.1](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)**."), container.NewHBox(programmLink, licenseLink), ) } -func (v View) getOther() *fyne.Container { +func license3RDParty() *fyne.Container { return container.NewVBox( canvas.NewLine(colornames.Darkgreen), @@ -751,19 +524,6 @@ func (v View) getOther() *fyne.Container { widget.NewLabel("Copyright (c) 2019 Yusuke Inuzuka"), canvas.NewLine(colornames.Darkgreen), - container.NewHBox(widget.NewHyperlink("go.etcd.io/bbolt", &url.URL{ - Scheme: "https", - Host: "pkg.go.dev", - Path: "go.etcd.io/bbolt", - })), - container.NewHBox(widget.NewHyperlink("MIT License", &url.URL{ - Scheme: "https", - Host: "github.com", - Path: "etcd-io/bbolt/blob/main/LICENSE", - })), - widget.NewLabel("Copyright (c) 2013 Ben Johnson"), - canvas.NewLine(colornames.Darkgreen), - container.NewHBox(widget.NewHyperlink("golang.org/x/image", &url.URL{ Scheme: "https", Host: "pkg.go.dev", diff --git a/internal/gui/view/configuring_ffmpeg_utilities.go b/internal/gui/view/configuring_ffmpeg_utilities.go new file mode 100644 index 0000000..11d8d80 --- /dev/null +++ b/internal/gui/view/configuring_ffmpeg_utilities.go @@ -0,0 +1,129 @@ +package view + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/lang" + "fyne.io/fyne/v2/storage" + "fyne.io/fyne/v2/widget" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/gui/window" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/utils" + "image/color" + "net/url" + "path/filepath" +) + +func ConfiguringFFmpegUtilities( + window window.WindowContract, + currentPathFFmpeg string, + currentPathFFprobe string, + currentPathFFplay string, + save func(ffmpegPath string, ffprobePath string, ffplayPath string) error, + cancel func(), + donwloadFFmpeg fyne.CanvasObject, +) fyne.CanvasObject { + errorMessage := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255}) + errorMessage.TextSize = 16 + errorMessage.TextStyle = fyne.TextStyle{Bold: true} + + link := widget.NewHyperlink("https://ffmpeg.org/download.html", &url.URL{ + Scheme: "https", + Host: "ffmpeg.org", + Path: "download.html", + }) + + ffmpegPath, buttonFFmpeg, buttonFFmpegMessage := configuringFFmpegUtilitiesButtonSelectFile(window, currentPathFFmpeg) + ffprobePath, buttonFFprobe, buttonFFprobeMessage := configuringFFmpegUtilitiesButtonSelectFile(window, currentPathFFprobe) + ffplayPath, buttonFFplay, buttonFFplayMessage := configuringFFmpegUtilitiesButtonSelectFile(window, currentPathFFplay) + + form := &widget.Form{ + Items: []*widget.FormItem{ + { + Text: lang.L("titleDownloadLink"), + Widget: link, + }, + { + Text: lang.L("pathToFfmpeg"), + Widget: buttonFFmpeg, + }, + { + Widget: container.NewHScroll(buttonFFmpegMessage), + }, + { + Text: lang.L("pathToFfprobe"), + Widget: buttonFFprobe, + }, + { + Widget: container.NewHScroll(buttonFFprobeMessage), + }, + { + Text: lang.L("pathToFfplay"), + Widget: buttonFFplay, + }, + { + Widget: container.NewHScroll(buttonFFplayMessage), + }, + { + Widget: container.NewHScroll(errorMessage), + }, + }, + SubmitText: lang.L("save"), + OnSubmit: func() { + err := save(*ffmpegPath, *ffprobePath, *ffplayPath) + if err != nil { + errorMessage.Text = err.Error() + } + }, + } + if cancel != nil { + form.OnCancel = cancel + form.CancelText = lang.L("cancel") + } + + selectFFPathTitle := lang.L("selectFFPathTitle") + + return widget.NewCard(selectFFPathTitle, "", container.NewVBox( + form, + donwloadFFmpeg, + )) +} + +func configuringFFmpegUtilitiesButtonSelectFile(window window.WindowContract, path string) (filePath *string, button *widget.Button, buttonMessage *canvas.Text) { + filePath = &path + + buttonMessage = canvas.NewText(path, color.RGBA{R: 49, G: 127, B: 114, A: 255}) + buttonMessage.TextSize = 16 + buttonMessage.TextStyle = fyne.TextStyle{Bold: true} + + buttonTitle := lang.L("choose") + + var locationURI fyne.ListableURI + if len(path) > 0 { + listableURI := storage.NewFileURI(filepath.Dir(path)) + locationURI, _ = storage.ListerForURI(listableURI) + } + + button = widget.NewButton(buttonTitle, func() { + window.NewFileOpen(func(r fyne.URIReadCloser, err error) { + if err != nil { + buttonMessage.Text = err.Error() + utils.SetStringErrorStyle(buttonMessage) + return + } + if r == nil { + return + } + + path = r.URI().Path() + + buttonMessage.Text = r.URI().Path() + utils.SetStringSuccessStyle(buttonMessage) + + listableURI := storage.NewFileURI(filepath.Dir(r.URI().Path())) + locationURI, _ = storage.ListerForURI(listableURI) + }, locationURI) + }) + + return filePath, button, buttonMessage +} diff --git a/internal/gui/view/convertor.go b/internal/gui/view/convertor.go new file mode 100644 index 0000000..cac1bc2 --- /dev/null +++ b/internal/gui/view/convertor.go @@ -0,0 +1,414 @@ +package view + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/lang" + "fyne.io/fyne/v2/storage" + "fyne.io/fyne/v2/widget" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application/convertor/encoder" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg" + encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/gui/view/convertor/encoders" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/gui/window" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/utils" + "image/color" + "os" + "path/filepath" +) + +type ConvertSetting struct { + DirectoryForSave string + OverwriteOutputFiles bool + Format string + Encoder encoder2.EncoderContract +} + +func Convertor( + window window.WindowContract, + addFileForConversion func(file ffmpeg.File), + directoryForSavingPath string, + directoryForSaving func(path string), + formats encoder.ConvertorFormatsContract, + addToConversion func(convertSetting ConvertSetting) error, +) fyne.CanvasObject { + conversionMessage := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255}) + conversionMessage.TextSize = 16 + conversionMessage.TextStyle = fyne.TextStyle{Bold: true} + + form := newFormConvertor( + window, + addFileForConversion, + directoryForSavingPath, + directoryForSaving, + formats, + addToConversion, + conversionMessage, + ) + + converterVideoFilesTitle := lang.L("converterVideoFilesTitle") + return widget.NewCard(converterVideoFilesTitle, "", container.NewVScroll(form.getForm())) +} + +type formConvertor struct { + form *widget.Form + items []*widget.FormItem + conversionMessage *canvas.Text + + window window.WindowContract + addFileForConversion func(file ffmpeg.File) + directoryForSaving func(path string) +} + +func newFormConvertor( + window window.WindowContract, + addFileForConversion func(file ffmpeg.File), + directoryForSavingPath string, + directoryForSaving func(path string), + formats encoder.ConvertorFormatsContract, + addToConversion func(convertSetting ConvertSetting) error, + conversionMessage *canvas.Text, +) *formConvertor { + f := widget.NewForm() + f.SubmitText = lang.L("converterVideoFilesSubmitTitle") + + formConvertor := &formConvertor{ + form: f, + window: window, + addFileForConversion: addFileForConversion, + directoryForSaving: directoryForSaving, + conversionMessage: conversionMessage, + } + + fileForConversion := formConvertor.newFileForConversion() + directoryForSavingButton := formConvertor.newDirectoryForSaving(directoryForSavingPath) + isOverwriteOutputFiles := false + checkboxOverwriteOutputFiles := widget.NewCheck(lang.L("checkboxOverwriteOutputFilesTitle"), func(b bool) { + isOverwriteOutputFiles = b + }) + checkboxOverwriteOutputFiles.SetChecked(isOverwriteOutputFiles) + selectEncoder := formConvertor.newSelectEncoder(formats) + + items := []*widget.FormItem{ + { + Text: lang.L("fileForConversionTitle"), + Widget: fileForConversion.button, + }, + { + Widget: container.NewHScroll(fileForConversion.message), + }, + + { + Text: lang.L("buttonForSelectedDirTitle"), + Widget: directoryForSavingButton.button, + }, + { + Widget: container.NewHScroll(directoryForSavingButton.message), + }, + + { + Widget: checkboxOverwriteOutputFiles, + }, + { + Widget: selectEncoder.SelectFileType, + }, + { + Text: lang.L("selectFormat"), + Widget: selectEncoder.SelectFormat, + }, + { + Text: lang.L("selectEncoder"), + Widget: selectEncoder.SelectEncoder, + }, + } + formConvertor.form.Items = items + formConvertor.items = items + formConvertor.changeEncoder(selectEncoder.Encoder) + selectEncoder.ChangeEncoder = formConvertor.changeEncoder + + formConvertor.form.OnSubmit = func() { + formConvertor.conversionMessage.Text = "" + if len(directoryForSavingButton.path) == 0 { + formConvertor.conversionMessage.Text = lang.L("errorSelectedFolderSave") + return + } + + if len(selectEncoder.SelectFormat.Selected) == 0 { + formConvertor.conversionMessage.Text = lang.L("errorSelectedFormat") + return + } + + if selectEncoder.Encoder == nil { + formConvertor.conversionMessage.Text = lang.L("errorSelectedEncoder") + return + } + + fileForConversion.button.Disable() + directoryForSavingButton.button.Disable() + formConvertor.form.Disable() + + fyne.Do(func() { + err := addToConversion(ConvertSetting{ + DirectoryForSave: directoryForSavingButton.path, + OverwriteOutputFiles: isOverwriteOutputFiles, + Format: selectEncoder.SelectFormat.Selected, + Encoder: selectEncoder.Encoder, + }) + if err != nil { + formConvertor.conversionMessage.Text = err.Error() + } + fileForConversion.button.Enable() + directoryForSavingButton.button.Enable() + formConvertor.form.Enable() + }) + } + + return formConvertor +} + +func (f *formConvertor) getForm() fyne.CanvasObject { + return container.NewVBox( + f.form, + container.NewHScroll(f.conversionMessage), + ) +} + +type fileForConversion struct { + button *widget.Button + message *canvas.Text + file *ffmpeg.File +} + +func (f *formConvertor) newFileForConversion() *fileForConversion { + message := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255}) + fileForConversion := &fileForConversion{ + message: message, + } + + buttonTitle := lang.L("choose") + "\n" + + lang.L("or") + "\n" + + lang.L("dragAndDropFiles") + + var locationURI fyne.ListableURI + + fileForConversion.button = widget.NewButton(buttonTitle, func() { + f.window.NewFileOpen(func(r fyne.URIReadCloser, err error) { + fyne.Do(func() { + fileForConversion.message.Text = "" + fileForConversion.message.Refresh() + }) + + if err != nil { + fyne.Do(func() { + fileForConversion.message.Text = err.Error() + fileForConversion.message.Refresh() + }) + return + } + if r == nil { + return + } + + f.addFileForConversion(ffmpeg.File{ + Path: r.URI().Path(), + Name: r.URI().Name(), + Ext: r.URI().Extension(), + }) + + listableURI := storage.NewFileURI(filepath.Dir(r.URI().Path())) + locationURI, _ = storage.ListerForURI(listableURI) + }, locationURI) + }) + + f.window.SetOnDropped(func(position fyne.Position, uris []fyne.URI) { + if len(uris) == 0 { + return + } + + isError := false + for _, uri := range uris { + info, err := os.Stat(uri.Path()) + if err != nil { + isError = true + continue + } + if info.IsDir() { + isError = true + continue + } + + f.addFileForConversion(ffmpeg.File{ + Path: uri.Path(), + Name: uri.Name(), + Ext: uri.Extension(), + }) + + listableURI := storage.NewFileURI(filepath.Dir(uri.Path())) + locationURI, _ = storage.ListerForURI(listableURI) + } + + if isError { + fileForConversion.message.Text = lang.L("errorDragAndDropFile") + utils.SetStringErrorStyle(fileForConversion.message) + } else { + fyne.Do(func() { + fileForConversion.message.Text = "" + fileForConversion.message.Refresh() + }) + } + }) + + return fileForConversion +} + +type directoryForSaving struct { + button *widget.Button + message *canvas.Text + path string +} + +func (f *formConvertor) newDirectoryForSaving(directoryForSavingPath string) *directoryForSaving { + directoryForSaving := &directoryForSaving{ + path: "", + } + + directoryForSaving.message = canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255}) + directoryForSaving.message.TextSize = 16 + directoryForSaving.message.TextStyle = fyne.TextStyle{Bold: true} + + buttonTitle := lang.L("choose") + + locationURI, err := utils.PathToListableURI(directoryForSavingPath) + if err == nil { + directoryForSaving.path = locationURI.Path() + directoryForSaving.message.Text = locationURI.Path() + utils.SetStringSuccessStyle(directoryForSaving.message) + } + + directoryForSaving.button = widget.NewButton(buttonTitle, func() { + f.window.NewFolderOpen(func(r fyne.ListableURI, err error) { + if err != nil { + directoryForSaving.message.Text = err.Error() + utils.SetStringErrorStyle(directoryForSaving.message) + return + } + if r == nil { + return + } + + directoryForSaving.path = r.Path() + + directoryForSaving.message.Text = r.Path() + utils.SetStringSuccessStyle(directoryForSaving.message) + locationURI, err = storage.ListerForURI(r) + + if err == nil { + f.directoryForSaving(locationURI.Path()) + } + + }, locationURI) + }) + + return directoryForSaving +} + +type selectEncoder struct { + SelectFileType *widget.RadioGroup + SelectFormat *widget.Select + SelectEncoder *widget.Select + Encoder encoder2.EncoderContract + + ChangeEncoder func(encoder encoder2.EncoderContract) +} + +func (f *formConvertor) newSelectEncoder(formats encoder.ConvertorFormatsContract) *selectEncoder { + selectEncoder := &selectEncoder{} + + encoderMap := map[int]encoder2.EncoderDataContract{} + selectEncoder.SelectEncoder = widget.NewSelect([]string{}, func(s string) { + if encoderMap[selectEncoder.SelectEncoder.SelectedIndex()] == nil { + return + } + selectEncoderData := encoderMap[selectEncoder.SelectEncoder.SelectedIndex()] + selectEncoder.Encoder = selectEncoderData.NewEncoder() + if selectEncoder.ChangeEncoder != nil { + selectEncoder.ChangeEncoder(selectEncoder.Encoder) + } + }) + + formatSelected := "" + selectEncoder.SelectFormat = widget.NewSelect([]string{}, func(s string) { + if formatSelected == s { + return + } + formatSelected = s + format, err := formats.GetFormat(s) + if err != nil { + return + } + var encoderOptions []string + encoderMap = map[int]encoder2.EncoderDataContract{} + for _, e := range format.GetEncoders() { + encoderMap[len(encoderMap)] = e + encoderOptions = append(encoderOptions, lang.L("encoder_"+e.GetTitle())) + } + selectEncoder.SelectEncoder.SetOptions(encoderOptions) + selectEncoder.SelectEncoder.SetSelectedIndex(0) + }) + + var fileTypeOptions []string + for _, fileType := range encoder2.GetListFileType() { + fileTypeOptions = append(fileTypeOptions, fileType.Name()) + } + + encoderGroupVideo := lang.L("encoderGroupVideo") + encoderGroupAudio := lang.L("encoderGroupAudio") + encoderGroupImage := lang.L("encoderGroupImage") + encoderGroup := map[string]string{ + encoderGroupVideo: "video", + encoderGroupAudio: "audio", + encoderGroupImage: "image", + } + selectEncoder.SelectFileType = widget.NewRadioGroup([]string{encoderGroupVideo, encoderGroupAudio, encoderGroupImage}, func(s string) { + groupCode := encoderGroup[s] + + var formatOptions []string + for _, f := range formats.GetFormats() { + if groupCode != f.GetFileType().Name() { + continue + } + formatOptions = append(formatOptions, f.GetTitle()) + } + selectEncoder.SelectFormat.SetOptions(formatOptions) + if groupCode == encoder2.FileType(encoder2.Video).Name() { + selectEncoder.SelectFormat.SetSelected("mp4") + } else { + selectEncoder.SelectFormat.SetSelectedIndex(0) + } + }) + selectEncoder.SelectFileType.Horizontal = true + selectEncoder.SelectFileType.Required = true + selectEncoder.SelectFileType.SetSelected(encoderGroupVideo) + + return selectEncoder +} + +func (f *formConvertor) changeEncoder(encoder encoder2.EncoderContract) { + var items []*widget.FormItem + + if encoders.Views[encoder.GetName()] != nil { + items = encoders.Views[encoder.GetName()](encoder) + } + + f.changeItems(items) +} + +func (f *formConvertor) changeItems(items []*widget.FormItem) { + fyne.Do(func() { + f.form.Items = f.items + f.form.Refresh() + f.form.Items = append(f.form.Items, items...) + f.form.Refresh() + }) +} diff --git a/internal/gui/view/convertor/encoders/encoders.go b/internal/gui/view/convertor/encoders/encoders.go new file mode 100644 index 0000000..ddbf806 --- /dev/null +++ b/internal/gui/view/convertor/encoders/encoders.go @@ -0,0 +1,15 @@ +package encoders + +import ( + "fyne.io/fyne/v2/widget" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/gui/view/convertor/encoders/h264_nvenc" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/gui/view/convertor/encoders/libx264" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/gui/view/convertor/encoders/libx265" +) + +var Views = map[string]func(encoder encoder.EncoderContract) []*widget.FormItem{ + "libx264": libx264.View, + "h264_nvenc": h264_nvenc.View, + "libx265": libx265.View, +} diff --git a/convertor/view/form_items/h264_nvenc/view.go b/internal/gui/view/convertor/encoders/h264_nvenc/view.go similarity index 60% rename from convertor/view/form_items/h264_nvenc/view.go rename to internal/gui/view/convertor/encoders/h264_nvenc/view.go index 81b3c15..a506a12 100644 --- a/convertor/view/form_items/h264_nvenc/view.go +++ b/internal/gui/view/convertor/encoders/h264_nvenc/view.go @@ -2,22 +2,21 @@ package h264_nvenc import ( "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/lang" "fyne.io/fyne/v2/widget" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/h264_nvenc" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" - "github.com/nicksnyder/go-i18n/v2/i18n" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/h264_nvenc" ) -func View(encoder encoder.EncoderContract, app kernel.AppContract) []*widget.FormItem { +func View(encoder encoder.EncoderContract) []*widget.FormItem { items := []*widget.FormItem{} - items = append(items, presetParameter(encoder, app)...) + items = append(items, presetParameter(encoder)...) return items } -func presetParameter(encoder encoder.EncoderContract, app kernel.AppContract) []*widget.FormItem { +func presetParameter(encoder encoder.EncoderContract) []*widget.FormItem { parameter, err := encoder.GetParameter("preset") if err != nil { return nil @@ -45,7 +44,7 @@ func presetParameter(encoder encoder.EncoderContract, app kernel.AppContract) [] elementSelect.SetSelected(presetDefault) elementSelect.Hide() - checkboxTitle := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "parameterCheckbox"}) + checkboxTitle := lang.L("parameterCheckbox") elementCheckbox := widget.NewCheck(checkboxTitle, func(b bool) { if b == true { parameter.SetEnable() @@ -58,7 +57,7 @@ func presetParameter(encoder encoder.EncoderContract, app kernel.AppContract) [] return []*widget.FormItem{ { - Text: app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "formPreset"}), + Text: lang.L("formPreset"), Widget: container.NewVBox(elementCheckbox, elementSelect), }, } diff --git a/convertor/view/form_items/libx264/view.go b/internal/gui/view/convertor/encoders/libx264/view.go similarity index 57% rename from convertor/view/form_items/libx264/view.go rename to internal/gui/view/convertor/encoders/libx264/view.go index f552c66..8457d94 100644 --- a/convertor/view/form_items/libx264/view.go +++ b/internal/gui/view/convertor/encoders/libx264/view.go @@ -2,22 +2,21 @@ package libx264 import ( "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/lang" "fyne.io/fyne/v2/widget" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libx264" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" - "github.com/nicksnyder/go-i18n/v2/i18n" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/libx264" ) -func View(encoder encoder.EncoderContract, app kernel.AppContract) []*widget.FormItem { +func View(encoder encoder.EncoderContract) []*widget.FormItem { items := []*widget.FormItem{} - items = append(items, presetParameter(encoder, app)...) + items = append(items, presetParameter(encoder)...) return items } -func presetParameter(encoder encoder.EncoderContract, app kernel.AppContract) []*widget.FormItem { +func presetParameter(encoder encoder.EncoderContract) []*widget.FormItem { parameter, err := encoder.GetParameter("preset") if err != nil { return nil @@ -28,7 +27,7 @@ func presetParameter(encoder encoder.EncoderContract, app kernel.AppContract) [] presetDefault := "" for _, name := range libx264.Presets { - title := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "preset_" + name}) + title := lang.L("preset_" + name) presetsForSelect = append(presetsForSelect, title) presets[title] = name if name == parameter.Get() { @@ -45,7 +44,7 @@ func presetParameter(encoder encoder.EncoderContract, app kernel.AppContract) [] elementSelect.SetSelected(presetDefault) elementSelect.Hide() - checkboxTitle := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "parameterCheckbox"}) + checkboxTitle := lang.L("parameterCheckbox") elementCheckbox := widget.NewCheck(checkboxTitle, func(b bool) { if b == true { parameter.SetEnable() @@ -58,7 +57,7 @@ func presetParameter(encoder encoder.EncoderContract, app kernel.AppContract) [] return []*widget.FormItem{ { - Text: app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "formPreset"}), + Text: lang.L("formPreset"), Widget: container.NewVBox(elementCheckbox, elementSelect), }, } diff --git a/convertor/view/form_items/libx265/view.go b/internal/gui/view/convertor/encoders/libx265/view.go similarity index 57% rename from convertor/view/form_items/libx265/view.go rename to internal/gui/view/convertor/encoders/libx265/view.go index 434defd..f0fccf0 100644 --- a/convertor/view/form_items/libx265/view.go +++ b/internal/gui/view/convertor/encoders/libx265/view.go @@ -2,22 +2,21 @@ package libx265 import ( "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/lang" "fyne.io/fyne/v2/widget" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libx265" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" - "github.com/nicksnyder/go-i18n/v2/i18n" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/libx265" ) -func View(encoder encoder.EncoderContract, app kernel.AppContract) []*widget.FormItem { +func View(encoder encoder.EncoderContract) []*widget.FormItem { items := []*widget.FormItem{} - items = append(items, presetParameter(encoder, app)...) + items = append(items, presetParameter(encoder)...) return items } -func presetParameter(encoder encoder.EncoderContract, app kernel.AppContract) []*widget.FormItem { +func presetParameter(encoder encoder.EncoderContract) []*widget.FormItem { parameter, err := encoder.GetParameter("preset") if err != nil { return nil @@ -28,7 +27,7 @@ func presetParameter(encoder encoder.EncoderContract, app kernel.AppContract) [] presetDefault := "" for _, name := range libx265.Presets { - title := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "preset_" + name}) + title := lang.L("preset_" + name) presetsForSelect = append(presetsForSelect, title) presets[title] = name if name == parameter.Get() { @@ -45,7 +44,7 @@ func presetParameter(encoder encoder.EncoderContract, app kernel.AppContract) [] elementSelect.SetSelected(presetDefault) elementSelect.Hide() - checkboxTitle := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "parameterCheckbox"}) + checkboxTitle := lang.L("parameterCheckbox") elementCheckbox := widget.NewCheck(checkboxTitle, func(b bool) { if b == true { parameter.SetEnable() @@ -58,7 +57,7 @@ func presetParameter(encoder encoder.EncoderContract, app kernel.AppContract) [] return []*widget.FormItem{ { - Text: app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "formPreset"}), + Text: lang.L("formPreset"), Widget: container.NewVBox(elementCheckbox, elementSelect), }, } diff --git a/internal/gui/view/error.go b/internal/gui/view/error.go new file mode 100644 index 0000000..f1860e3 --- /dev/null +++ b/internal/gui/view/error.go @@ -0,0 +1,39 @@ +package view + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/lang" + "fyne.io/fyne/v2/widget" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application/setting" +) + +func StartWithError(err error, languages []setting.Lang, funcSelected func(lang setting.Lang)) fyne.CanvasObject { + messageHead := lang.L("error") + + listView := widget.NewList( + func() int { + return len(languages) + }, + func() fyne.CanvasObject { + return widget.NewLabel("template") + }, + func(i widget.ListItemID, o fyne.CanvasObject) { + block := o.(*widget.Label) + block.SetText(languages[i].Title) + }) + listView.OnSelected = func(id widget.ListItemID) { + funcSelected(languages[id]) + } + + return container.NewBorder( + container.NewVBox( + widget.NewLabel(messageHead), + widget.NewLabel(err.Error()), + ), + nil, + nil, + nil, + listView, + ) +} diff --git a/internal/gui/view/help_ffplay.go b/internal/gui/view/help_ffplay.go new file mode 100644 index 0000000..07fbcd0 --- /dev/null +++ b/internal/gui/view/help_ffplay.go @@ -0,0 +1,98 @@ +package view + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/lang" + "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/widget" +) + +func HelpFFplay() fyne.CanvasObject { + data := [][]string{ + { + lang.L("helpFFplayKeys"), + lang.L("helpFFplayDescription"), + }, + { + "Q, ESC", + lang.L("helpFFplayQuit"), + }, + { + "F, " + lang.L("helpFFplayDoubleClickLeftMouseButton"), + lang.L("helpFFplayToggleFullScreen"), + }, + { + "P, " + lang.L("helpFFplayKeySpace"), + lang.L("helpFFplayPause"), + }, + { + "M", + lang.L("helpFFplayToggleMute"), + }, + { + "9, /", + lang.L("helpFFplayDecreaseVolume"), + }, + { + "0, *", + lang.L("helpFFplayIncreaseVolume"), + }, + { + lang.L("helpFFplayKeyLeft"), + lang.L("helpFFplaySeekBackward10Seconds"), + }, + { + lang.L("helpFFplayKeyRight"), + lang.L("helpFFplaySeekForward10Seconds"), + }, + { + lang.L("helpFFplayKeyDown"), + lang.L("helpFFplaySeekBackward1Minute"), + }, + { + lang.L("helpFFplayKeyUp"), + lang.L("helpFFplaySeekBForward1Minute"), + }, + { + "Page Down", + lang.L("helpFFplaySeekBackward10Minutes"), + }, + { + "Page Up", + lang.L("helpFFplaySeekBForward10Minutes"), + }, + { + "S, " + lang.L("helpFFplayKeyHoldS"), + lang.L("helpFFplayActivateFrameStepMode"), + }, + { + "W", + lang.L("helpFFplayCycleVideoFiltersOrShowModes"), + }, + } + + list := widget.NewTable( + func() (int, int) { + return len(data), len(data[0]) + }, + func() fyne.CanvasObject { + return widget.NewLabel("") + }, + func(i widget.TableCellID, o fyne.CanvasObject) { + if i.Row == 0 { + o.(*widget.Label).TextStyle.Bold = true + o.(*widget.Label).SizeName = theme.SizeNameSubHeadingText + } + if i.Col == 0 { + o.(*widget.Label).TextStyle.Bold = true + } + o.(*widget.Label).SetText(data[i.Row][i.Col]) + }) + list.SetRowHeight(0, 40) + list.SetColumnWidth(0, 200) + list.SetColumnWidth(1, 585) + list.SetRowHeight(2, 55) + + return container.NewScroll(list) +} diff --git a/internal/gui/view/mainSettings.go b/internal/gui/view/mainSettings.go new file mode 100644 index 0000000..b9e19e7 --- /dev/null +++ b/internal/gui/view/mainSettings.go @@ -0,0 +1,92 @@ +package view + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/lang" + "fyne.io/fyne/v2/widget" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application/setting" + "image/color" +) + +type MainSettingForm struct { + Language setting.Lang + ThemeInfo setting.ThemeInfoContract +} + +func MainSettings( + currentLang setting.Lang, + langList []setting.Lang, + + themeInfo setting.ThemeInfoContract, + themeList map[string]setting.ThemeInfoContract, + + save func(form *MainSettingForm) error, + cancel func(), +) fyne.CanvasObject { + + errorMessage := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255}) + errorMessage.TextSize = 16 + errorMessage.TextStyle = fyne.TextStyle{Bold: true} + + viewSettingForm := &MainSettingForm{ + Language: currentLang, + ThemeInfo: themeInfo, + } + + var languageItems []string + langByTitle := map[string]setting.Lang{} + for _, language := range langList { + languageItems = append(languageItems, language.Title) + langByTitle[language.Title] = language + } + selectLanguage := widget.NewSelect(languageItems, func(s string) { + if lang, ok := langByTitle[s]; ok { + viewSettingForm.Language = lang + } + }) + selectLanguage.Selected = currentLang.Title + + var themeItems []string + themeByTitle := map[string]setting.ThemeInfoContract{} + for _, themeInfo := range themeList { + themeItems = append(themeItems, themeInfo.GetTitle()) + themeByTitle[themeInfo.GetTitle()] = themeInfo + } + selectTheme := widget.NewSelect(themeItems, func(s string) { + if themeInfo, ok := themeByTitle[s]; ok { + viewSettingForm.ThemeInfo = themeInfo + } + }) + selectTheme.Selected = themeInfo.GetTitle() + + form := &widget.Form{ + Items: []*widget.FormItem{ + { + Text: lang.L("menuSettingsLanguage"), + Widget: selectLanguage, + }, + { + Text: lang.L("menuSettingsTheme"), + Widget: selectTheme, + }, + { + Widget: errorMessage, + }, + }, + SubmitText: lang.L("save"), + OnSubmit: func() { + err := save(viewSettingForm) + if err != nil { + errorMessage.Text = err.Error() + } + }, + } + if cancel != nil { + form.OnCancel = cancel + form.CancelText = lang.L("cancel") + } + + messageHead := lang.L("settings") + return widget.NewCard(messageHead, "", form) +} diff --git a/internal/gui/view/start_without_support_lang.go b/internal/gui/view/start_without_support_lang.go new file mode 100644 index 0000000..6d9a3e3 --- /dev/null +++ b/internal/gui/view/start_without_support_lang.go @@ -0,0 +1,28 @@ +package view + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/lang" + "fyne.io/fyne/v2/widget" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application/setting" +) + +func StartWithoutSupportLang(languages []setting.Lang, funcSelected func(lang setting.Lang)) fyne.CanvasObject { + listView := widget.NewList( + func() int { + return len(languages) + }, + func() fyne.CanvasObject { + return widget.NewLabel("template") + }, + func(i widget.ListItemID, o fyne.CanvasObject) { + block := o.(*widget.Label) + block.SetText(languages[i].Title) + }) + listView.OnSelected = func(id widget.ListItemID) { + funcSelected(languages[id]) + } + + messageHead := lang.L("languageSelectionHead") + return widget.NewCard(messageHead, "", listView) +} diff --git a/internal/gui/window/layout.go b/internal/gui/window/layout.go new file mode 100644 index 0000000..33e4b25 --- /dev/null +++ b/internal/gui/window/layout.go @@ -0,0 +1,172 @@ +package window + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/lang" + "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/widget" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application/convertor" +) + +type LayoutContract interface { + SetContent(content fyne.CanvasObject) fyne.CanvasObject + GetRContainer() RightMainContainerContract +} + +type layout struct { + layoutContainer *fyne.Container + rContainer RightMainContainerContract +} + +func NewLayout(progressBarService convertor.ProgressBarContract, itemsToConvert convertor.ItemsToConvertContract, queueLayout QueueLayoutContract) LayoutContract { + rContainer := newRightContainer(progressBarService.GetContainer(), itemsToConvert, queueLayout) + layoutContainer := container.NewAdaptiveGrid(2, widget.NewLabel(""), rContainer.GetCanvasObject()) + + return &layout{ + layoutContainer: layoutContainer, + rContainer: rContainer, + } +} + +func (l *layout) SetContent(content fyne.CanvasObject) fyne.CanvasObject { + l.layoutContainer.Objects[0] = content + return l.layoutContainer +} + +func (l *layout) GetRContainer() RightMainContainerContract { + return l.rContainer +} + +type RightMainContainerContract interface { + GetCanvasObject() fyne.CanvasObject + GetTabs() *container.AppTabs + SelectFileQueueTab() + SelectAddedFilesTab() +} + +type rightMainContainer struct { + container fyne.CanvasObject + tabs *container.AppTabs + addedFilesTab *container.TabItem + fileQueueTab *container.TabItem +} + +func newRightContainer(blockProgressbar *fyne.Container, itemsToConvert convertor.ItemsToConvertContract, queueLayout QueueLayoutContract) RightMainContainerContract { + addedFilesTab := container.NewTabItem(lang.L("addedFilesTitle"), addedFilesContainer(itemsToConvert)) + fileQueueTab := container.NewTabItem(lang.L("fileQueueTitle"), fileQueueContainer(queueLayout)) + + tabs := container.NewAppTabs( + addedFilesTab, + fileQueueTab, + ) + + rightContainer := container.NewBorder( + container.NewVBox( + blockProgressbar, + widget.NewSeparator(), + ), + nil, + nil, + nil, + tabs, + ) + + return &rightMainContainer{ + container: rightContainer, + tabs: tabs, + addedFilesTab: addedFilesTab, + fileQueueTab: fileQueueTab, + } +} + +func (r *rightMainContainer) GetCanvasObject() fyne.CanvasObject { + return r.container +} + +func (r *rightMainContainer) GetTabs() *container.AppTabs { + return r.tabs +} + +func (r *rightMainContainer) SelectFileQueueTab() { + fyne.Do(func() { + r.tabs.Select(r.fileQueueTab) + }) +} + +func (r *rightMainContainer) SelectAddedFilesTab() { + fyne.Do(func() { + r.tabs.Select(r.addedFilesTab) + }) +} + +func addedFilesContainer(itemsToConvert convertor.ItemsToConvertContract) *fyne.Container { + line := canvas.NewLine(theme.Color(theme.ColorNameFocus)) + line.StrokeWidth = 5 + checkboxAutoRemove := widget.NewCheck( + lang.L("autoClearAfterAddingToQueue"), + func(checked bool) { + itemsToConvert.SetIsAutoRemove(checked) + }, + ) + checkboxAutoRemove.SetChecked(itemsToConvert.GetIsAutoRemove()) + + buttonClear := widget.NewButton( + lang.L("clearAll"), + func() { + itemsToConvert.Clear() + }, + ) + buttonClear.Importance = widget.DangerImportance + return container.NewBorder( + container.NewVBox( + container.NewPadded(), + container.NewBorder(nil, nil, nil, buttonClear, container.NewHScroll(checkboxAutoRemove)), + container.NewPadded(), + line, + ), nil, nil, nil, + container.NewVScroll( + container.NewBorder( + nil, nil, nil, container.NewPadded(), + container.NewVBox( + container.NewPadded(), + itemsToConvert.GetItemsContainer(), + ), + ), + ), + ) +} + +func fileQueueContainer(queueLayout QueueLayoutContract) *fyne.Container { + title := widget.NewLabel(lang.L("queue")) + title.TextStyle.Bold = true + + line := canvas.NewLine(theme.Color(theme.ColorNameFocus)) + line.StrokeWidth = 5 + + queueLayout.GetQueueStatistics().GetWaiting().SetTitle(lang.L("waitingQueue")) + queueLayout.GetQueueStatistics().GetInProgress().SetTitle(lang.L("inProgressQueue")) + queueLayout.GetQueueStatistics().GetCompleted().SetTitle(lang.L("completedQueue")) + queueLayout.GetQueueStatistics().GetError().SetTitle(lang.L("errorQueue")) + queueLayout.GetQueueStatistics().GetTotal().SetTitle(lang.L("total")) + + return container.NewBorder( + container.NewVBox( + container.NewPadded(), + container.NewHBox(title, queueLayout.GetQueueStatistics().GetCompleted().GetCheckbox(), queueLayout.GetQueueStatistics().GetError().GetCheckbox()), + container.NewHBox(queueLayout.GetQueueStatistics().GetInProgress().GetCheckbox(), queueLayout.GetQueueStatistics().GetWaiting().GetCheckbox(), queueLayout.GetQueueStatistics().GetTotal().GetCheckbox()), + container.NewPadded(), + line, + ), nil, nil, nil, + container.NewVScroll( + container.NewBorder( + nil, nil, nil, container.NewPadded(), + container.NewVBox( + container.NewPadded(), + queueLayout.GetItemsContainer(), + ), + ), + ), + ) +} diff --git a/internal/gui/window/main.go b/internal/gui/window/main.go new file mode 100644 index 0000000..4fcb823 --- /dev/null +++ b/internal/gui/window/main.go @@ -0,0 +1,101 @@ +package window + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/dialog" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application/convertor" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/utils" +) + +type WindowContract interface { + SetContent(content fyne.CanvasObject) + SetMainMenu(menu *fyne.MainMenu) + Show() + InitLayout() + NewFileOpen(callback func(fyne.URIReadCloser, error), location fyne.ListableURI) *dialog.FileDialog + NewFolderOpen(callback func(fyne.ListableURI, error), location fyne.ListableURI) *dialog.FileDialog + SetOnDropped(callback func(position fyne.Position, uris []fyne.URI)) + GetLayout() LayoutContract +} + +type mainWindow struct { + fyneWindow fyne.Window + layout LayoutContract + itemsToConvert convertor.ItemsToConvertContract + progressBarService convertor.ProgressBarContract + queueLayout QueueLayoutContract +} + +func NewMainWindow( + fyneWindow fyne.Window, + progressBarService convertor.ProgressBarContract, + itemsToConvert convertor.ItemsToConvertContract, + queueLayout QueueLayoutContract, +) WindowContract { + fyneWindow.Resize(fyne.Size{Width: 1039, Height: 599}) + fyneWindow.CenterOnScreen() + + return &mainWindow{ + fyneWindow: fyneWindow, + progressBarService: progressBarService, + itemsToConvert: itemsToConvert, + queueLayout: queueLayout, + } +} + +func (w *mainWindow) SetMainMenu(menu *fyne.MainMenu) { + fyne.Do(func() { + w.fyneWindow.SetMainMenu(menu) + }) +} + +func (w *mainWindow) InitLayout() { + fyne.Do(func() { + w.layout = NewLayout(w.progressBarService, w.itemsToConvert, w.queueLayout) + }) +} + +func (w *mainWindow) GetLayout() LayoutContract { + return w.layout +} + +func (w *mainWindow) NewFileOpen(callback func(fyne.URIReadCloser, error), location fyne.ListableURI) *dialog.FileDialog { + fileDialog := dialog.NewFileOpen(callback, w.fyneWindow) + utils.FileDialogResize(fileDialog, w.fyneWindow) + fileDialog.Show() + if location != nil { + fileDialog.SetLocation(location) + } + return fileDialog +} + +func (w *mainWindow) NewFolderOpen(callback func(fyne.ListableURI, error), location fyne.ListableURI) *dialog.FileDialog { + fileDialog := dialog.NewFolderOpen(callback, w.fyneWindow) + utils.FileDialogResize(fileDialog, w.fyneWindow) + fileDialog.Show() + if location != nil { + fileDialog.SetLocation(location) + } + return fileDialog +} + +func (w *mainWindow) SetContent(content fyne.CanvasObject) { + fyne.Do(func() { + if w.layout == nil { + w.fyneWindow.SetContent(content) + return + } + + w.fyneWindow.SetContent(w.layout.SetContent(content)) + }) +} + +func (w *mainWindow) Show() { + w.fyneWindow.Show() +} + +func (w *mainWindow) SetOnDropped(callback func(position fyne.Position, uris []fyne.URI)) { + fyne.Do(func() { + w.fyneWindow.SetOnDropped(callback) + }) +} diff --git a/internal/gui/window/queue.go b/internal/gui/window/queue.go new file mode 100644 index 0000000..c801c1e --- /dev/null +++ b/internal/gui/window/queue.go @@ -0,0 +1,459 @@ +package window + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/lang" + "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/widget" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application/convertor" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg" + "image/color" + "strconv" + "strings" +) + +type QueueLayoutContract interface { + GetItemsContainer() *fyne.Container + GetQueueStatistics() QueueStatisticsAllContract + + AddQueue(key int, queue *convertor.Queue) + ChangeQueue(key int, queue *convertor.Queue) + RemoveQueue(key int, status convertor.StatusContract) +} + +type queueLayout struct { + itemsContainer *fyne.Container + queueAllStatistics QueueStatisticsAllContract + items map[int]queueLayoutItem + ffmpegService ffmpeg.UtilitiesContract +} + +func NewQueueLayout(ffmpegService ffmpeg.UtilitiesContract) QueueLayoutContract { + items := map[int]queueLayoutItem{} + + return &queueLayout{ + itemsContainer: container.NewVBox(), + queueAllStatistics: newQueueAllStatistics(&items), + items: items, + ffmpegService: ffmpegService, + } +} + +func (l *queueLayout) GetItemsContainer() *fyne.Container { + return l.itemsContainer +} + +func (l *queueLayout) GetQueueStatistics() QueueStatisticsAllContract { + return l.queueAllStatistics +} + +func (l *queueLayout) AddQueue(queueID int, queue *convertor.Queue) { + + statusMessage := canvas.NewText(l.getStatusTitle(queue.Status), theme.Color(theme.ColorNamePrimary)) + messageError := canvas.NewText("", theme.Color(theme.ColorNameError)) + buttonPlay := widget.NewButtonWithIcon("", theme.Icon(theme.IconNameMediaPlay), func() { + + }) + buttonPlay.Hide() + blockMessageError := container.NewHScroll(messageError) + blockMessageError.Hide() + + content := container.NewVBox( + container.NewHScroll(widget.NewLabel(queue.Setting.FileInput.Name)), + container.NewHBox( + buttonPlay, + statusMessage, + ), + blockMessageError, + container.NewPadded(), + canvas.NewLine(theme.Color(theme.ColorNameFocus)), + container.NewPadded(), + ) + + l.addQueueStatistics() + if l.GetQueueStatistics().IsChecked(queue.Status) == false { + content.Hide() + } + + l.items[queueID] = queueLayoutItem{ + CanvasObject: content, + StatusMessage: statusMessage, + BlockMessageError: blockMessageError, + MessageError: messageError, + buttonPlay: buttonPlay, + status: &queue.Status, + } + l.itemsContainer.Add(content) +} + +func (l *queueLayout) ChangeQueue(queueID int, queue *convertor.Queue) { + if item, ok := l.items[queueID]; ok { + statusColor := l.getStatusColor(queue.Status) + fyne.Do(func() { + item.StatusMessage.Text = l.getStatusTitle(queue.Status) + item.StatusMessage.Color = statusColor + item.StatusMessage.Refresh() + }) + if queue.Error != nil { + fyne.Do(func() { + item.MessageError.Text = queue.Error.Error() + item.MessageError.Color = statusColor + item.BlockMessageError.Show() + item.MessageError.Refresh() + }) + } + if queue.Status == convertor.StatusType(convertor.Completed) { + item.buttonPlay.Show() + item.buttonPlay.OnTapped = func() { + item.buttonPlay.Disable() + go func() { + ffplay, err := l.ffmpegService.GetFFplay() + if err == nil { + _ = ffplay.Play(&queue.Setting.FileOut) + } + fyne.Do(func() { + item.buttonPlay.Enable() + }) + }() + } + } + if l.GetQueueStatistics().IsChecked(queue.Status) == false && item.CanvasObject.Visible() == true { + item.CanvasObject.Hide() + } + + if l.GetQueueStatistics().IsChecked(queue.Status) == true && item.CanvasObject.Visible() == false { + item.CanvasObject.Show() + } + + l.changeQueueStatistics(queue.Status) + } +} + +func (l *queueLayout) RemoveQueue(queueID int, status convertor.StatusContract) { + if item, ok := l.items[queueID]; ok { + l.itemsContainer.Remove(item.CanvasObject) + l.removeQueueStatistics(status) + l.items[queueID] = queueLayoutItem{} + } +} + +func (l *queueLayout) addQueueStatistics() { + l.GetQueueStatistics().GetWaiting().Add() + l.GetQueueStatistics().GetTotal().Add() +} + +func (l *queueLayout) changeQueueStatistics(status convertor.StatusContract) { + if status == convertor.StatusType(convertor.InProgress) { + l.GetQueueStatistics().GetWaiting().Remove() + l.GetQueueStatistics().GetInProgress().Add() + return + } + + if status == convertor.StatusType(convertor.Completed) { + l.GetQueueStatistics().GetInProgress().Remove() + l.GetQueueStatistics().GetCompleted().Add() + return + } + + if status == convertor.StatusType(convertor.Error) { + l.GetQueueStatistics().GetInProgress().Remove() + l.GetQueueStatistics().GetError().Add() + return + } +} + +func (l *queueLayout) removeQueueStatistics(status convertor.StatusContract) { + l.GetQueueStatistics().GetTotal().Remove() + + if status == convertor.StatusType(convertor.Completed) { + l.GetQueueStatistics().GetCompleted().Remove() + return + } + + if status == convertor.StatusType(convertor.Error) { + l.GetQueueStatistics().GetError().Remove() + return + } + + if status == convertor.StatusType(convertor.InProgress) { + l.GetQueueStatistics().GetInProgress().Remove() + return + } + + if status == convertor.StatusType(convertor.Waiting) { + l.GetQueueStatistics().GetWaiting().Remove() + return + } +} + +func (l *queueLayout) getStatusTitle(status convertor.StatusContract) string { + return lang.L(status.Name() + "Queue") +} + +func (l *queueLayout) getStatusColor(status convertor.StatusContract) color.Color { + if status == convertor.StatusType(convertor.Error) { + return theme.Color(theme.ColorNameError) + } + + if status == convertor.StatusType(convertor.Completed) { + return color.RGBA{R: 49, G: 127, B: 114, A: 255} + } + + return theme.Color(theme.ColorNamePrimary) +} + +type QueueStatisticsAllContract interface { + GetWaiting() QueueStatisticsContract + GetInProgress() QueueStatisticsContract + GetCompleted() QueueStatisticsContract + GetError() QueueStatisticsContract + GetTotal() QueueStatisticsContract + + IsChecked(status convertor.StatusContract) bool +} + +type queueAllStatistics struct { + waiting QueueStatisticsContract + inProgress QueueStatisticsContract + completed QueueStatisticsContract + error QueueStatisticsContract + total QueueStatisticsContract +} + +func newQueueAllStatistics(queueItems *map[int]queueLayoutItem) QueueStatisticsAllContract { + checkboxWaiting := newQueueStatistics() + checkboxInProgress := newQueueStatistics() + checkboxCompleted := newQueueStatistics() + checkboxError := newQueueStatistics() + CheckboxTotal := newQueueStatistics() + + queueAllStatistics := &queueAllStatistics{ + waiting: checkboxWaiting, + inProgress: checkboxInProgress, + completed: checkboxCompleted, + error: checkboxError, + total: CheckboxTotal, + } + + CheckboxTotal.GetCheckbox().OnChanged = func(b bool) { + if b == true { + queueAllStatistics.allCheckboxChecked() + } else { + queueAllStatistics.allUnCheckboxChecked() + } + queueAllStatistics.redrawingQueueItems(queueItems) + } + + checkboxWaiting.GetCheckbox().OnChanged = func(b bool) { + if b == true { + queueAllStatistics.checkboxChecked() + } else { + queueAllStatistics.unCheckboxChecked() + } + queueAllStatistics.redrawingQueueItems(queueItems) + } + + checkboxInProgress.GetCheckbox().OnChanged = func(b bool) { + if b == true { + queueAllStatistics.checkboxChecked() + } else { + queueAllStatistics.unCheckboxChecked() + } + queueAllStatistics.redrawingQueueItems(queueItems) + } + + checkboxCompleted.GetCheckbox().OnChanged = func(b bool) { + if b == true { + queueAllStatistics.checkboxChecked() + } else { + queueAllStatistics.unCheckboxChecked() + } + queueAllStatistics.redrawingQueueItems(queueItems) + } + + checkboxError.GetCheckbox().OnChanged = func(b bool) { + if b == true { + queueAllStatistics.checkboxChecked() + } else { + queueAllStatistics.unCheckboxChecked() + } + queueAllStatistics.redrawingQueueItems(queueItems) + } + + return queueAllStatistics +} + +func (s *queueAllStatistics) GetWaiting() QueueStatisticsContract { + return s.waiting +} + +func (s *queueAllStatistics) GetInProgress() QueueStatisticsContract { + return s.inProgress +} + +func (s *queueAllStatistics) GetCompleted() QueueStatisticsContract { + return s.completed +} + +func (s *queueAllStatistics) GetError() QueueStatisticsContract { + return s.error +} + +func (s *queueAllStatistics) GetTotal() QueueStatisticsContract { + return s.total +} + +func (s *queueAllStatistics) IsChecked(status convertor.StatusContract) bool { + if status == convertor.StatusType(convertor.InProgress) { + return s.inProgress.GetCheckbox().Checked + } + if status == convertor.StatusType(convertor.Completed) { + return s.completed.GetCheckbox().Checked + } + if status == convertor.StatusType(convertor.Error) { + return s.error.GetCheckbox().Checked + } + if status == convertor.StatusType(convertor.Waiting) { + return s.waiting.GetCheckbox().Checked + } + + return true +} + +func (s *queueAllStatistics) redrawingQueueItems(queueItems *map[int]queueLayoutItem) { + for _, item := range *queueItems { + if s.IsChecked(*item.status) == true && item.CanvasObject.Visible() == false { + item.CanvasObject.Show() + continue + } + if s.IsChecked(*item.status) == false && item.CanvasObject.Visible() == true { + item.CanvasObject.Hide() + } + } +} + +func (s *queueAllStatistics) checkboxChecked() { + if s.total.GetCheckbox().Checked == true { + return + } + + if s.waiting.GetCheckbox().Checked == false { + return + } + + if s.inProgress.GetCheckbox().Checked == false { + return + } + + if s.completed.GetCheckbox().Checked == false { + return + } + + if s.error.GetCheckbox().Checked == false { + return + } + + s.total.GetCheckbox().Checked = true + s.total.GetCheckbox().Refresh() +} + +func (s *queueAllStatistics) unCheckboxChecked() { + if s.total.GetCheckbox().Checked == false { + return + } + + s.total.GetCheckbox().Checked = false + s.total.GetCheckbox().Refresh() +} + +func (s *queueAllStatistics) allCheckboxChecked() { + s.waiting.GetCheckbox().Checked = true + s.waiting.GetCheckbox().Refresh() + + s.inProgress.GetCheckbox().Checked = true + s.inProgress.GetCheckbox().Refresh() + + s.completed.GetCheckbox().Checked = true + s.completed.GetCheckbox().Refresh() + + s.error.GetCheckbox().Checked = true + s.error.GetCheckbox().Refresh() +} + +func (s *queueAllStatistics) allUnCheckboxChecked() { + s.waiting.GetCheckbox().Checked = false + s.waiting.GetCheckbox().Refresh() + + s.inProgress.GetCheckbox().Checked = false + s.inProgress.GetCheckbox().Refresh() + + s.completed.GetCheckbox().Checked = false + s.completed.GetCheckbox().Refresh() + + s.error.GetCheckbox().Checked = false + s.error.GetCheckbox().Refresh() +} + +type QueueStatisticsContract interface { + SetTitle(title string) + GetCheckbox() *widget.Check + Add() + Remove() +} + +type queueStatistics struct { + checkbox *widget.Check + title string + count int64 +} + +func newQueueStatistics() QueueStatisticsContract { + checkbox := widget.NewCheck(": 0", nil) + checkbox.Checked = true + + return &queueStatistics{ + checkbox: checkbox, + title: "", + count: 0, + } +} + +func (s *queueStatistics) SetTitle(title string) { + s.title = strings.ToLower(title) + s.checkbox.Text = title + ": " + strconv.FormatInt(s.count, 10) +} + +func (s *queueStatistics) GetCheckbox() *widget.Check { + return s.checkbox +} + +func (s *queueStatistics) Add() { + s.count += 1 + s.formatText() +} + +func (s *queueStatistics) Remove() { + if s.count == 0 { + return + } + s.count -= 1 + s.formatText() +} + +func (s *queueStatistics) formatText() { + fyne.Do(func() { + s.checkbox.Text = s.title + ": " + strconv.FormatInt(s.count, 10) + s.checkbox.Refresh() + }) +} + +type queueLayoutItem struct { + CanvasObject fyne.CanvasObject + BlockMessageError *container.Scroll + StatusMessage *canvas.Text + MessageError *canvas.Text + buttonPlay *widget.Button + status *convertor.StatusContract +} diff --git a/internal/resources/icon.go b/internal/resources/icon.go new file mode 100644 index 0000000..003278e --- /dev/null +++ b/internal/resources/icon.go @@ -0,0 +1,13 @@ +package resources + +import ( + _ "embed" + "fyne.io/fyne/v2" +) + +//go:embed icons/logo.png +var iconAppLogo []byte + +func IconAppLogoResource() *fyne.StaticResource { + return fyne.NewStaticResource("icon.png", iconAppLogo) +} diff --git a/internal/resources/icons/logo.png b/internal/resources/icons/logo.png new file mode 100644 index 0000000..6eaf8f6 Binary files /dev/null and b/internal/resources/icons/logo.png differ diff --git a/internal/resources/translation.go b/internal/resources/translation.go new file mode 100644 index 0000000..401a291 --- /dev/null +++ b/internal/resources/translation.go @@ -0,0 +1,12 @@ +package resources + +import ( + "embed" +) + +//go:embed translations +var translations embed.FS + +func GetTranslations() embed.FS { + return translations +} diff --git a/internal/resources/translations/app.en.json b/internal/resources/translations/app.en.json new file mode 100644 index 0000000..9919f8d --- /dev/null +++ b/internal/resources/translations/app.en.json @@ -0,0 +1,143 @@ +{ + "AlsoUsedProgram": "The program also uses:", + "about": "About", + "aboutText": "A simple interface for the FFmpeg console utility. \nBut I am not the author of the FFmpeg utility itself.", + "addedFilesTitle": "Added files", + "autoClearAfterAddingToQueue": "Auto-clear after adding to queue", + "buttonDownloadFFmpeg": "Download FFmpeg automatically", + "buttonForSelectedDirTitle": "Save to folder:", + "cancel": "Cancel", + "changeFFPath": "FFmpeg, FFprobe and FFplay", + "changeLanguage": "Change language", + "checkboxOverwriteOutputFilesTitle": "Allow file to be overwritten", + "choose": "choose", + "clearAll": "Clear List", + "completedQueue": "Completed", + "converterVideoFilesSubmitTitle": "Convert", + "converterVideoFilesTitle": "Video, audio and picture converter", + "download": "Download", + "downloadFFmpegFromSite": "Will be downloaded from the site:", + "downloadRun": "Downloading...", + "dragAndDropFiles": "drag and drop files", + "encoderGroupAudio": "Audio", + "encoderGroupImage": "Images", + "encoderGroupVideo": "Video", + "encoder_apng": "APNG image", + "encoder_bmp": "BMP image", + "encoder_flv": "FLV", + "encoder_gif": "GIF image", + "encoder_h264_nvenc": "H.264 with NVIDIA support", + "encoder_libmp3lame": "libmp3lame MP3 (MPEG audio layer 3)", + "encoder_libshine": "libshine MP3 (MPEG audio layer 3)", + "encoder_libtwolame": "libtwolame MP2 (MPEG audio layer 2)", + "encoder_libvpx": "libvpx VP8 (codec vp8)", + "encoder_libvpx-vp9": "libvpx VP9 (codec vp9)", + "encoder_libwebp": "libwebp WebP image", + "encoder_libwebp_anim": "libwebp_anim WebP image", + "encoder_libx264": "H.264 libx264", + "encoder_libx265": "H.265 libx265", + "encoder_libxvid": "libxvidcore MPEG-4 part 2", + "encoder_mjpeg": "MJPEG (Motion JPEG)", + "encoder_mp2": "MP2 (MPEG audio layer 2)", + "encoder_mp2fixed": "MP2 fixed point (MPEG audio layer 2)", + "encoder_mpeg1video": "MPEG-1", + "encoder_mpeg2video": "MPEG-2", + "encoder_mpeg4": "MPEG-4 part 2", + "encoder_msmpeg4": "MPEG-4 part 2 Microsoft variant version 3", + "encoder_msmpeg4v2": "MPEG-4 part 2 Microsoft variant version 2", + "encoder_msvideo1": "Microsoft Video-1", + "encoder_png": "PNG image", + "encoder_qtrle": "QuickTime Animation (RLE) video", + "encoder_sgi": "SGI image", + "encoder_tiff": "TIFF image", + "encoder_wmav1": "Windows Media Audio 1", + "encoder_wmav2": "Windows Media Audio 2", + "encoder_wmv1": "Windows Media Video 7", + "encoder_wmv2": "Windows Media Video 8", + "encoder_xbm": "XBM (X BitMap) image", + "error": "An error has occurred!", + "errorConverter": "Couldn't convert video", + "errorDragAndDropFile": "Not all files were added", + "errorFFmpeg": "this is not FFmpeg", + "errorFFmpegVersion": "Could not determine FFmpeg version", + "errorFFplay": "this is not FFplay", + "errorFFplayVersion": "Could not determine FFplay version", + "errorFFprobe": "this is not FFprobe", + "errorFFprobeVersion": "Failed to determine FFprobe version", + "errorNoFilesAddedForConversion": "There are no files to convert", + "errorQueue": "Error", + "errorSelectedEncoder": "Converter not selected", + "errorSelectedFolderSave": "No save folder selected!", + "errorSelectedFormat": "File extension not selected", + "exit": "Exit", + "ffmpegLGPL": "This software uses libraries from the **FFmpeg** project under the **[LGPLv2.1](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)**.", + "ffmpegTrademark": "**FFmpeg** is a trademark of **[Fabrice Bellard](http://bellard.org/)**, originator of the **[FFmpeg](https://ffmpeg.org/about.html)** project.", + "fileForConversionTitle": "File:", + "fileQueueTitle": "Queue", + "formPreset": "Preset", + "gratitude": "Gratitude", + "gratitudeText": "I sincerely thank you for your invaluable\n\r and timely assistance:", + "help": "Help", + "helpFFplay": "FFplay Player Keys", + "helpFFplayActivateFrameStepMode": "Activate frame-by-frame mode.", + "helpFFplayCycleVideoFiltersOrShowModes": "A cycle of video filters or display modes.", + "helpFFplayDecreaseVolume": "Decrease the volume.", + "helpFFplayDescription": "Description", + "helpFFplayDoubleClickLeftMouseButton": "double click\nleft mouse button", + "helpFFplayIncreaseVolume": "Increase the volume.", + "helpFFplayKeyDown": "down", + "helpFFplayKeyHoldS": "hold S", + "helpFFplayKeyLeft": "left", + "helpFFplayKeyRight": "right", + "helpFFplayKeySpace": "SPACE", + "helpFFplayKeyUp": "up", + "helpFFplayKeys": "Keys", + "helpFFplayPause": "Pause or continue playing.", + "helpFFplayQuit": "Close the player.", + "helpFFplaySeekBForward10Minutes": "Fast forward 10 minutes.", + "helpFFplaySeekBForward1Minute": "Fast forward 1 minute.", + "helpFFplaySeekBackward10Minutes": "Rewind 10 minutes.", + "helpFFplaySeekBackward10Seconds": "Rewind 10 seconds.", + "helpFFplaySeekBackward1Minute": "Rewind 1 minute.", + "helpFFplaySeekForward10Seconds": "Fast forward 10 seconds.", + "helpFFplayToggleFullScreen": "Switch to full screen or exit full screen.", + "helpFFplayToggleMute": "Mute or unmute.", + "inProgressQueue": "In Progress", + "languageSelectionFormHead": "Switch language", + "languageSelectionHead": "Choose language", + "licenseLink": "License information", + "licenseLinkOther": "Licenses from other products used in the program", + "menuSettingsLanguage": "Language", + "menuSettingsTheme": "Theme", + "or": "or", + "parameterCheckbox": "Enable option", + "pathToFfmpeg": "Path to FFmpeg:", + "pathToFfplay": "Path to FFplay:", + "pathToFfprobe": "Path to FFprobe:", + "preset_fast": "fast (slower than \"faster\", but the file will weigh less)", + "preset_faster": "faster (slower than \"veryfast\", but the file will weigh less)", + "preset_medium": "medium (slower than \"fast\", but the file will weigh less)", + "preset_placebo": "placebo (not recommended)", + "preset_slow": "slow (slower than \"medium\", but the file will weigh less)", + "preset_slower": "slower (slower than \"slow\", but the file will weigh less)", + "preset_superfast": "superfast (slower than \"ultrafast\", but the file will weigh less)", + "preset_ultrafast": "ultrafast (fast, but the file will weigh a lot)", + "preset_veryfast": "veryfast (slower than \"superfast\", but the file will weigh less)", + "preset_veryslow": "veryslow (slower than \"slower\", but the file will weigh less)", + "programmLink": "Project website", + "programmVersion": "**Program version:** {{.Version}}", + "queue": "Queue", + "save": "Save", + "selectEncoder": "Encoder:", + "selectFFPathTitle": "Specify the path to FFmpeg and FFprobe", + "selectFormat": "File extension:", + "settings": "Settings", + "testFF": "Checking FFmpeg for serviceability...", + "themesNameDark": "Dark", + "themesNameDefault": "Default", + "themesNameLight": "Light", + "titleDownloadLink": "You can download it from here", + "total": "Total", + "unzipRun": "Unpacked...", + "waitingQueue": "Waiting" +} \ No newline at end of file diff --git a/internal/resources/translations/app.kk.json b/internal/resources/translations/app.kk.json new file mode 100644 index 0000000..6fb1b81 --- /dev/null +++ b/internal/resources/translations/app.kk.json @@ -0,0 +1,143 @@ +{ + "AlsoUsedProgram": "Бағдарлама сонымен қатар пайдаланады:", + "about": "Бағдарлама туралы", + "aboutText": "FFmpeg консоль утилитасы үшін қарапайым интерфейс. \nБірақ мен FFmpeg утилитасының авторы емеспін.", + "addedFilesTitle": "Қосылған файлдар", + "autoClearAfterAddingToQueue": "Кезекке қосқаннан кейін тазалаңыз", + "buttonDownloadFFmpeg": "FFmpeg автоматты түрде жүктеп алыңыз", + "buttonForSelectedDirTitle": "Қалтаға сақтаңыз:", + "cancel": "Болдырмау", + "changeFFPath": "FFmpeg, FFprobe және FFplay", + "changeLanguage": "Тілді өзгерту", + "checkboxOverwriteOutputFilesTitle": "Файлды қайта жазуға рұқсат беріңіз", + "choose": "таңдау", + "clearAll": "Тізімді өшіру", + "completedQueue": "Дайын", + "converterVideoFilesSubmitTitle": "Файлды түрлендіру", + "converterVideoFilesTitle": "Бейне, аудио және суретті түрлендіргіш", + "download": "Жүктеп алу", + "downloadFFmpegFromSite": "Сайттан жүктеледі:", + "downloadRun": "Жүктеп алынуда...", + "dragAndDropFiles": "файлдарды сүйреп апарыңыз", + "encoderGroupAudio": "Аудио", + "encoderGroupImage": "Суреттер", + "encoderGroupVideo": "Бейне", + "encoder_apng": "APNG image", + "encoder_bmp": "BMP image", + "encoder_flv": "FLV", + "encoder_gif": "GIF image", + "encoder_h264_nvenc": "NVIDIA қолдауымен H.264", + "encoder_libmp3lame": "libmp3lame MP3 (MPEG audio layer 3)", + "encoder_libshine": "libshine MP3 (MPEG audio layer 3)", + "encoder_libtwolame": "libtwolame MP2 (MPEG audio layer 2)", + "encoder_libvpx": "libvpx VP8 (codec vp8)", + "encoder_libvpx-vp9": "libvpx VP9 (codec vp9)", + "encoder_libwebp": "libwebp WebP image", + "encoder_libwebp_anim": "libwebp_anim WebP image", + "encoder_libx264": "H.264 libx264", + "encoder_libx265": "H.265 libx265", + "encoder_libxvid": "libxvidcore MPEG-4 part 2", + "encoder_mjpeg": "MJPEG (Motion JPEG)", + "encoder_mp2": "MP2 (MPEG audio layer 2)", + "encoder_mp2fixed": "MP2 fixed point (MPEG audio layer 2)", + "encoder_mpeg1video": "MPEG-1", + "encoder_mpeg2video": "MPEG-2", + "encoder_mpeg4": "MPEG-4 part 2", + "encoder_msmpeg4": "MPEG-4 part 2 Microsoft variant version 3", + "encoder_msmpeg4v2": "MPEG-4 part 2 Microsoft variant version 2", + "encoder_msvideo1": "Microsoft Video-1", + "encoder_png": "PNG image", + "encoder_qtrle": "QuickTime Animation (RLE) video", + "encoder_sgi": "SGI image", + "encoder_tiff": "TIFF image", + "encoder_wmav1": "Windows Media Audio 1", + "encoder_wmav2": "Windows Media Audio 2", + "encoder_wmv1": "Windows Media Video 7", + "encoder_wmv2": "Windows Media Video 8", + "encoder_xbm": "XBM (X BitMap) image", + "error": "Қате орын алды!", + "errorConverter": "Бейнені түрлендіру мүмкін болмады", + "errorDragAndDropFile": "Барлық файлдар қосылмаған", + "errorFFmpeg": "бұл FFmpeg емес", + "errorFFmpegVersion": "FFmpeg нұсқасын анықтау мүмкін болмады", + "errorFFplay": "бұл FFplay емес", + "errorFFplayVersion": "FFplay нұсқасын анықтау мүмкін болмады", + "errorFFprobe": "бұл FFprobe емес", + "errorFFprobeVersion": "FFprobe нұсқасын анықтау мүмкін болмады", + "errorNoFilesAddedForConversion": "Түрлендіруге арналған файлдар жоқ", + "errorQueue": "Қате", + "errorSelectedEncoder": "Түрлендіргіш таңдалмаған", + "errorSelectedFolderSave": "Сақтау қалтасы таңдалмаған!", + "errorSelectedFormat": "Файл кеңейтімі таңдалмаған", + "exit": "Шығу", + "ffmpegLGPL": "Бұл бағдарламалық құрал **[LGPLv2.1](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)** астында **FFmpeg** жобасының кітапханаларын пайдаланады.", + "ffmpegTrademark": "FFmpeg — **[FFmpeg](https://ffmpeg.org/about.html)** жобасын жасаушы **[Fabrice Bellard](http://bellard.org/)** сауда белгісі.", + "fileForConversionTitle": "Файл:", + "fileQueueTitle": "Кезек", + "formPreset": "Алдын ала орнатылған", + "gratitude": "Алғыс", + "gratitudeText": "Сізге баға жетпес және уақтылы көмектескеніңіз\n\r үшін шын жүректен алғыс айтамын:", + "help": "Анықтама", + "helpFFplay": "FFplay ойнатқышының пернелері", + "helpFFplayActivateFrameStepMode": "Уақыт аралығын іске қосыңыз.", + "helpFFplayCycleVideoFiltersOrShowModes": "Бейне сүзгілерінің немесе дисплей режимдерінің циклі.", + "helpFFplayDecreaseVolume": "Дыбыс деңгейін төмендетіңіз.", + "helpFFplayDescription": "Сипаттама", + "helpFFplayDoubleClickLeftMouseButton": "тінтуірдің сол жақ\nбатырмасын екі рет басу", + "helpFFplayIncreaseVolume": "Дыбыс деңгейін арттыру.", + "helpFFplayKeyDown": "төмен", + "helpFFplayKeyHoldS": "ұстау S", + "helpFFplayKeyLeft": "сол", + "helpFFplayKeyRight": "құқық", + "helpFFplayKeySpace": "SPACE (пробел)", + "helpFFplayKeyUp": "жоғары", + "helpFFplayKeys": "Кілттер", + "helpFFplayPause": "Кідіртіңіз немесе жоғалтуды жалғастырыңыз.", + "helpFFplayQuit": "Ойнатқышты жабыңыз.", + "helpFFplaySeekBForward10Minutes": "10 минутқа алға айналдырыңыз.", + "helpFFplaySeekBForward1Minute": "1 минутқа алға айналдырыңыз.", + "helpFFplaySeekBackward10Minutes": "10 минутқа артқа айналдырыңыз.", + "helpFFplaySeekBackward10Seconds": "10 секундқа артқа айналдырыңыз.", + "helpFFplaySeekBackward1Minute": "1 минутқа артқа айналдырыңыз.", + "helpFFplaySeekForward10Seconds": "10 секунд алға айналдырыңыз.", + "helpFFplayToggleFullScreen": "Толық экранға ауысу немесе толық экраннан шығу.", + "helpFFplayToggleMute": "Дыбысты өшіріңіз немесе дыбысты қосыңыз.", + "inProgressQueue": "Орындалуда", + "languageSelectionFormHead": "Тілді ауыстыру", + "languageSelectionHead": "Тілді таңдаңыз", + "licenseLink": "Лицензия туралы ақпарат", + "licenseLinkOther": "Бағдарламада пайдаланылатын басқа өнімдердің лицензиялары", + "menuSettingsLanguage": "Тіл", + "menuSettingsTheme": "Тақырып", + "or": "немесе", + "parameterCheckbox": "Опцияны қосу", + "pathToFfmpeg": "FFmpeg жол:", + "pathToFfplay": "FFplay жол:", + "pathToFfprobe": "FFprobe жол:", + "preset_fast": "fast («faster» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)", + "preset_faster": "faster («veryfast» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)", + "preset_medium": "medium («fast» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)", + "preset_placebo": "placebo (ұсынылмайды)", + "preset_slow": "slow («medium» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)", + "preset_slower": "slower («slow» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)", + "preset_superfast": "superfast («ultrafast» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)", + "preset_ultrafast": "ultrafast (жылдам, бірақ файлдың салмағы көп болады)", + "preset_veryfast": "veryfast («superfast» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)", + "preset_veryslow": "veryslow («slower» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)", + "programmLink": "Жобаның веб-сайты", + "programmVersion": "**Бағдарлама нұсқасы:** {{.Version}}", + "queue": "Кезек", + "save": "Сақтау", + "selectEncoder": "Кодировщик:", + "selectFFPathTitle": "FFmpeg және FFprobe жолын көрсетіңіз", + "selectFormat": "Файл кеңейтімі:", + "settings": "Параметрлер", + "testFF": "FFmpeg функционалдығы тексерілуде...", + "themesNameDark": "Қараңғы тақырып", + "themesNameDefault": "Әдепкі бойынша", + "themesNameLight": "Жеңіл тақырып", + "titleDownloadLink": "Сіз оны осы жерден жүктей аласыз", + "total": "Барлығы", + "unzipRun": "Орамнан шығарылуда...", + "waitingQueue": "Күту" +} \ No newline at end of file diff --git a/internal/resources/translations/app.ru.json b/internal/resources/translations/app.ru.json new file mode 100644 index 0000000..20a5da8 --- /dev/null +++ b/internal/resources/translations/app.ru.json @@ -0,0 +1,143 @@ +{ + "AlsoUsedProgram": "Также в программе используется:", + "about": "О программе", + "aboutText": "Простенький интерфейс для консольной утилиты FFmpeg. \nНо я не являюсь автором самой утилиты FFmpeg.", + "addedFilesTitle": "Добавленные файлы", + "autoClearAfterAddingToQueue": "Очищать после добавления в очередь", + "buttonDownloadFFmpeg": "Скачать автоматически FFmpeg", + "buttonForSelectedDirTitle": "Сохранить в папку:", + "cancel": "Отмена", + "changeFFPath": "FFmpeg, FFprobe и FFplay", + "changeLanguage": "Поменять язык", + "checkboxOverwriteOutputFilesTitle": "Разрешить перезаписать файл", + "choose": "выбрать", + "clearAll": "Очистить список", + "completedQueue": "Готово", + "converterVideoFilesSubmitTitle": "Конвертировать", + "converterVideoFilesTitle": "Конвертер видео, аудио и картинок", + "download": "Скачать", + "downloadFFmpegFromSite": "Будет скачано с сайта:", + "downloadRun": "Скачивается...", + "dragAndDropFiles": "перетащить файлы", + "encoderGroupAudio": "Аудио", + "encoderGroupImage": "Картинки", + "encoderGroupVideo": "Видео", + "encoder_apng": "APNG image", + "encoder_bmp": "BMP image", + "encoder_flv": "FLV", + "encoder_gif": "GIF image", + "encoder_h264_nvenc": "H.264 с поддержкой NVIDIA", + "encoder_libmp3lame": "libmp3lame MP3 (MPEG audio layer 3)", + "encoder_libshine": "libshine MP3 (MPEG audio layer 3)", + "encoder_libtwolame": "libtwolame MP2 (MPEG audio layer 2)", + "encoder_libvpx": "libvpx VP8 (codec vp8)", + "encoder_libvpx-vp9": "libvpx VP9 (codec vp9)", + "encoder_libwebp": "libwebp WebP image", + "encoder_libwebp_anim": "libwebp_anim WebP image", + "encoder_libx264": "H.264 libx264", + "encoder_libx265": "H.265 libx265", + "encoder_libxvid": "libxvidcore MPEG-4 part 2", + "encoder_mjpeg": "MJPEG (Motion JPEG)", + "encoder_mp2": "MP2 (MPEG audio layer 2)", + "encoder_mp2fixed": "MP2 fixed point (MPEG audio layer 2)", + "encoder_mpeg1video": "MPEG-1", + "encoder_mpeg2video": "MPEG-2", + "encoder_mpeg4": "MPEG-4 part 2", + "encoder_msmpeg4": "MPEG-4 part 2 Microsoft variant version 3", + "encoder_msmpeg4v2": "MPEG-4 part 2 Microsoft variant version 2", + "encoder_msvideo1": "Microsoft Video-1", + "encoder_png": "PNG image", + "encoder_qtrle": "QuickTime Animation (RLE) video", + "encoder_sgi": "SGI image", + "encoder_tiff": "TIFF image", + "encoder_wmav1": "Windows Media Audio 1", + "encoder_wmav2": "Windows Media Audio 2", + "encoder_wmv1": "Windows Media Video 7", + "encoder_wmv2": "Windows Media Video 8", + "encoder_xbm": "XBM (X BitMap) image", + "error": "Произошла ошибка!", + "errorConverter": "не смогли отконвертировать видео", + "errorDragAndDropFile": "Не все файлы добавились", + "errorFFmpeg": "это не FFmpeg", + "errorFFmpegVersion": "Не смогли определить версию FFmpeg", + "errorFFplay": "это не FFplay", + "errorFFplayVersion": "Не смогли определить версию FFplay", + "errorFFprobe": "это не FFprobe", + "errorFFprobeVersion": "Не смогли определить версию FFprobe", + "errorNoFilesAddedForConversion": "Нет файлов для конвертации", + "errorQueue": "Ошибка", + "errorSelectedEncoder": "Конвертер не выбран", + "errorSelectedFolderSave": "Папка для сохранения не выбрана!", + "errorSelectedFormat": "Расширение файла не выбрана", + "exit": "Выход", + "ffmpegLGPL": "Это программное обеспечение использует библиотеки из проекта **FFmpeg** под **[LGPLv2.1](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)**.", + "ffmpegTrademark": "**FFmpeg** — торговая марка **[Fabrice Bellard](http://bellard.org/)** , создателя проекта **[FFmpeg](https://ffmpeg.org/about.html)**.", + "fileForConversionTitle": "Файл:", + "fileQueueTitle": "Очередь", + "formPreset": "Предустановка", + "gratitude": "Благодарность", + "gratitudeText": "Я искренне благодарю вас за неоценимую\n\rи своевременную помощь:", + "help": "Справка", + "helpFFplay": "Клавиши проигрывателя FFplay", + "helpFFplayActivateFrameStepMode": "Активировать покадровый режим.", + "helpFFplayCycleVideoFiltersOrShowModes": "Цикл видеофильтров или режимов показа.", + "helpFFplayDecreaseVolume": "Уменьшить громкость.", + "helpFFplayDescription": "Описание", + "helpFFplayDoubleClickLeftMouseButton": "двойной щелчок\nлевой кнопкой мыши", + "helpFFplayIncreaseVolume": "Увеличить громкость.", + "helpFFplayKeyDown": "вниз", + "helpFFplayKeyHoldS": "держать S", + "helpFFplayKeyLeft": "лево", + "helpFFplayKeyRight": "право", + "helpFFplayKeySpace": "SPACE (пробел)", + "helpFFplayKeyUp": "вверх", + "helpFFplayKeys": "Клавиши", + "helpFFplayPause": "Поставить на паузу или продолжить проигрывать.", + "helpFFplayQuit": "Закрыть проигрыватель.", + "helpFFplaySeekBForward10Minutes": "Перемотать вперёд на 10 минут.", + "helpFFplaySeekBForward1Minute": "Перемотать вперёд на 1 минуту.", + "helpFFplaySeekBackward10Minutes": "Перемотать назад на 10 минут.", + "helpFFplaySeekBackward10Seconds": "Перемотать назад на 10 секунд.", + "helpFFplaySeekBackward1Minute": "Перемотать назад на 1 минуту.", + "helpFFplaySeekForward10Seconds": "Перемотать вперёд на 10 секунд.", + "helpFFplayToggleFullScreen": "Переключиться на полный экран или выйти с полного экрана.", + "helpFFplayToggleMute": "Отключить звук или включить звук.", + "inProgressQueue": "Выполняется", + "languageSelectionFormHead": "Переключить язык", + "languageSelectionHead": "Выберите язык", + "licenseLink": "Сведения о лицензии", + "licenseLinkOther": "Лицензии от других продуктов, которые используются в программе", + "menuSettingsLanguage": "Язык", + "menuSettingsTheme": "Тема", + "or": "или", + "parameterCheckbox": "Включить параметр", + "pathToFfmpeg": "Путь к FFmpeg:", + "pathToFfplay": "Путь к FFplay:", + "pathToFfprobe": "Путь к FFprobe:", + "preset_fast": "fast (медленней чем faster, но будет файл и меньше весить)", + "preset_faster": "faster (медленней чем veryfast, но будет файл и меньше весить)", + "preset_medium": "medium (медленней чем fast, но будет файл и меньше весить)", + "preset_placebo": "placebo (не рекомендуется)", + "preset_slow": "slow (медленней чем medium, но будет файл и меньше весить)", + "preset_slower": "slower (медленней чем slow, но будет файл и меньше весить)", + "preset_superfast": "superfast (медленней чем ultrafast, но будет файл и меньше весить)", + "preset_ultrafast": "ultrafast (быстро, но файл будет много весить)", + "preset_veryfast": "veryfast (медленней чем superfast, но будет файл и меньше весить)", + "preset_veryslow": "veryslow (медленней чем slower, но будет файл и меньше весить)", + "programmLink": "Сайт проекта", + "programmVersion": "**Версия программы:** {{.Version}}", + "queue": "Очередь", + "save": "Сохранить", + "selectEncoder": "Кодировщик:", + "selectFFPathTitle": "Укажите путь к FFmpeg и к FFprobe", + "selectFormat": "Расширение файла:", + "settings": "Настройки", + "testFF": "Проверка FFmpeg на работоспособность...", + "themesNameDark": "Тёмная", + "themesNameDefault": "По умолчанию", + "themesNameLight": "Светлая", + "titleDownloadLink": "Скачать можно от сюда", + "total": "Всего", + "unzipRun": "Распаковывается...", + "waitingQueue": "В очереди" +} \ No newline at end of file diff --git a/internal/resources/translations/base.en.json b/internal/resources/translations/base.en.json new file mode 100644 index 0000000..3f5dd3d --- /dev/null +++ b/internal/resources/translations/base.en.json @@ -0,0 +1,45 @@ +{ + "Advanced": "Advanced", + "Cancel": "Cancel", + "Confirm": "Confirm", + "Copy": "Copy", + "Create Folder": "Create Folder", + "Cut": "Cut", + "Enter filename": "Enter filename", + "Error": "Error", + "Favourites": "Favourites", + "File": "File", + "Folder": "Folder", + "New Folder": "New Folder", + "No": "No", + "OK": "OK", + "Open": "Open", + "Paste": "Paste", + "Quit": "Quit", + "Redo": "Redo", + "Save": "Save", + "Select all": "Select all", + "Show Hidden Files": "Show Hidden Files", + "Undo": "Undo", + "Yes": "Yes", + "file.name": { + "other": "Name" + }, + "file.parent": { + "other": "Parent" + }, + "friday": "Friday", + "friday.short": "Fri", + "monday": "Monday", + "monday.short": "Mon", + "saturday": "Saturday", + "saturday.short": "Sat", + "sunday": "Sunday", + "sunday.short": "Sun", + "thursday": "Thursday", + "thursday.short": "Thu", + "tuesday": "Tuesday", + "tuesday.short": "Tue", + "wednesday": "Wednesday", + "wednesday.short": "Wed" +} \ No newline at end of file diff --git a/internal/resources/translations/base.kk.json b/internal/resources/translations/base.kk.json new file mode 100644 index 0000000..469566b --- /dev/null +++ b/internal/resources/translations/base.kk.json @@ -0,0 +1,45 @@ +{ + "Advanced": "Кеңейтілген", + "Cancel": "Бас тарту", + "Confirm": "Растау", + "Copy": "Көшіру", + "Create Folder": "Қалта жасау", + "Cut": "Кесу", + "Enter filename": "Файл атауын енгізіңіз", + "Error": "Қате", + "Favourites": "Таңдаулылар", + "File": "Файл", + "Folder": "Қалта", + "New Folder": "Жаңа қалта", + "No": "Жоқ", + "OK": "ОК", + "Open": "Ашу", + "Paste": "Кірістіру", + "Quit": "Шығу", + "Redo": "Қайталау", + "Save": "Сақтау", + "Select all": "Барлығын таңдаңыз", + "Show Hidden Files": "Жасырын файлдарды көрсету", + "Undo": "Бас тарту", + "Yes": "Иә", + "file.name": { + "other": "Аты" + }, + "file.parent": { + "other": "Жоғары" + }, + "friday": "Жұма", + "friday.short": "Жұ", + "monday": "Дүйсенбі", + "monday.short": "Дү", + "saturday": "Сенбі", + "saturday.short": "Сен", + "sunday": "Жексенбі", + "sunday.short": "Же", + "thursday": "Сейсенбі", + "thursday.short": "Се", + "tuesday": "Бейсенбі", + "tuesday.short": "Бе", + "wednesday": "Сәрсенбі", + "wednesday.short": "Сә" +} \ No newline at end of file diff --git a/internal/resources/translations/base.ru.json b/internal/resources/translations/base.ru.json new file mode 100644 index 0000000..ac22058 --- /dev/null +++ b/internal/resources/translations/base.ru.json @@ -0,0 +1,45 @@ +{ + "Advanced": "Расширенные", + "Cancel": "Отмена", + "Confirm": "Подтвердить", + "Copy": "Копировать", + "Create Folder": "Создать папку", + "Cut": "Вырезать", + "Enter filename": "Введите имя файла", + "Error": "Ошибка", + "Favourites": "Избранное", + "File": "Файл", + "Folder": "Папка", + "New Folder": "Новая папка", + "No": "Нет", + "OK": "ОК", + "Open": "Открыть", + "Paste": "Вставить", + "Quit": "Выйти", + "Redo": "Повторить", + "Save": "Сохранить", + "Select all": "Выбрать всё", + "Show Hidden Files": "Показать скрытые файлы", + "Undo": "Отменить", + "Yes": "Да", + "file.name": { + "other": "Имя" + }, + "file.parent": { + "other": "Вверх" + }, + "friday": "Пятница", + "friday.short": "Пт", + "monday": "Понедельник", + "monday.short": "Пн", + "saturday": "Суббота", + "saturday.short": "Сб", + "sunday": "Воскресенье", + "sunday.short": "Вс", + "thursday": "Вторник", + "thursday.short": "Вт", + "tuesday": "Четверг", + "tuesday.short": "Чт", + "wednesday": "Среда", + "wednesday.short": "Ср" +} \ No newline at end of file diff --git a/helper/helper.go b/internal/utils/dialog.go similarity index 94% rename from helper/helper.go rename to internal/utils/dialog.go index 682704c..992a68a 100644 --- a/helper/helper.go +++ b/internal/utils/dialog.go @@ -1,4 +1,4 @@ -package helper +package utils import ( "fyne.io/fyne/v2" diff --git a/internal/utils/path.go b/internal/utils/path.go new file mode 100644 index 0000000..4a35d20 --- /dev/null +++ b/internal/utils/path.go @@ -0,0 +1,19 @@ +package utils + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/storage" +) + +func PathToListableURI(path string) (fyne.ListableURI, error) { + if len(path) > 0 { + path = "file://" + path + } + + uri, err := storage.ParseURI(path) + if err != nil { + return nil, err + } + + return storage.ListerForURI(uri) +} diff --git a/helper/path_separator.go b/internal/utils/path_separator.go similarity index 85% rename from helper/path_separator.go rename to internal/utils/path_separator.go index 905b935..9fa65cf 100644 --- a/helper/path_separator.go +++ b/internal/utils/path_separator.go @@ -1,7 +1,7 @@ //go:build !windows // +build !windows -package helper +package utils func PathSeparator() string { return "/" diff --git a/helper/path_separator_window.go b/internal/utils/path_separator_window.go similarity index 84% rename from helper/path_separator_window.go rename to internal/utils/path_separator_window.go index 433e550..2acaa53 100644 --- a/helper/path_separator_window.go +++ b/internal/utils/path_separator_window.go @@ -1,7 +1,7 @@ //go:build windows // +build windows -package helper +package utils func PathSeparator() string { return "\\" diff --git a/helper/prepare_background_command.go b/internal/utils/prepare_background_command.go similarity index 88% rename from helper/prepare_background_command.go rename to internal/utils/prepare_background_command.go index f8aab96..da5f9b4 100644 --- a/helper/prepare_background_command.go +++ b/internal/utils/prepare_background_command.go @@ -1,7 +1,7 @@ //go:build !windows // +build !windows -package helper +package utils import ( "os/exec" diff --git a/helper/prepare_background_command_windows.go b/internal/utils/prepare_background_command_windows.go similarity index 92% rename from helper/prepare_background_command_windows.go rename to internal/utils/prepare_background_command_windows.go index 3e3ebbc..00af640 100644 --- a/helper/prepare_background_command_windows.go +++ b/internal/utils/prepare_background_command_windows.go @@ -1,7 +1,7 @@ //go:build windows // +build windows -package helper +package utils import ( "os/exec" diff --git a/internal/utils/text.go b/internal/utils/text.go new file mode 100644 index 0000000..20104dd --- /dev/null +++ b/internal/utils/text.go @@ -0,0 +1,21 @@ +package utils + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" + "image/color" +) + +func SetStringErrorStyle(text *canvas.Text) { + fyne.Do(func() { + text.Color = color.RGBA{R: 255, G: 0, B: 0, A: 255} + text.Refresh() + }) +} + +func SetStringSuccessStyle(text *canvas.Text) { + fyne.Do(func() { + text.Color = color.RGBA{R: 49, G: 127, B: 114, A: 255} + text.Refresh() + }) +} diff --git a/kernel/app.go b/kernel/app.go deleted file mode 100644 index 3babb14..0000000 --- a/kernel/app.go +++ /dev/null @@ -1,127 +0,0 @@ -package kernel - -import ( - "fyne.io/fyne/v2" - "fyne.io/fyne/v2/app" - "time" -) - -type AppContract interface { - GetAppFyne() fyne.App - GetWindow() WindowContract - GetQueue() QueueListContract - GetLocalizerService() LocalizerContract - GetConvertorService() ConvertorContract - GetFFplayService() FFplayContract - AfterClosing() - RunConvertor() -} - -type App struct { - AppFyne fyne.App - Window WindowContract - Queue QueueListContract - - localizerService LocalizerContract - convertorService ConvertorContract - blockProgressbarService BlockProgressbarContract - ffplayService FFplayContract -} - -func NewApp( - metadata *fyne.AppMetadata, - localizerService LocalizerContract, - queue QueueListContract, - ffplayService FFplayContract, - convertorService ConvertorContract, -) *App { - app.SetMetadata(*metadata) - a := app.New() - - statusesText := GetBlockProgressbarStatusesText(localizerService) - blockProgressbarService := NewBlockProgressbar(statusesText, ffplayService) - rightTabsService := NewRightTabs(localizerService) - queueLayoutObject := NewQueueLayoutObject(queue, localizerService, ffplayService, rightTabsService, blockProgressbarService.GetContainer()) - - return &App{ - AppFyne: a, - Window: newWindow(a.NewWindow("GUI for FFmpeg"), NewLayout(queueLayoutObject, localizerService, rightTabsService)), - Queue: queue, - - localizerService: localizerService, - convertorService: convertorService, - blockProgressbarService: blockProgressbarService, - ffplayService: ffplayService, - } -} - -func (a App) GetAppFyne() fyne.App { - return a.AppFyne -} - -func (a App) GetQueue() QueueListContract { - return a.Queue -} - -func (a App) GetWindow() WindowContract { - return a.Window -} - -func (a App) GetLocalizerService() LocalizerContract { - return a.localizerService -} - -func (a App) GetConvertorService() ConvertorContract { - return a.convertorService -} - -func (a App) GetFFplayService() FFplayContract { - return a.ffplayService -} - -func (a App) AfterClosing() { - for _, cmd := range a.convertorService.GetRunningProcesses() { - _ = cmd.Process.Kill() - } -} - -func (a App) RunConvertor() { - go func() { - for { - time.Sleep(time.Millisecond * 3000) - queueId, queue := a.Queue.Next() - if queue == nil { - continue - } - queue.Status = StatusType(InProgress) - a.Window.GetLayout().ChangeQueueStatus(queueId, queue) - if a.blockProgressbarService.GetContainer().Hidden { - a.blockProgressbarService.GetContainer().Show() - } - - totalDuration, err := a.convertorService.GetTotalDuration(&queue.Setting.VideoFileInput) - if err != nil { - totalDuration = 0 - } - - progress := a.blockProgressbarService.GetProgressbar( - totalDuration, - queue.Setting.VideoFileInput.Path, - a.localizerService, - ) - - err = a.convertorService.RunConvert(*queue.Setting, progress) - if err != nil { - queue.Status = StatusType(Error) - queue.Error = err - a.Window.GetLayout().ChangeQueueStatus(queueId, queue) - a.blockProgressbarService.ProcessEndedWithError(err.Error()) - - continue - } - queue.Status = StatusType(Completed) - a.Window.GetLayout().ChangeQueueStatus(queueId, queue) - a.blockProgressbarService.ProcessEndedWithSuccess(queue.Setting.VideoFileOut.Path) - } - }() -} diff --git a/kernel/convertor.go b/kernel/convertor.go deleted file mode 100644 index 179cff3..0000000 --- a/kernel/convertor.go +++ /dev/null @@ -1,290 +0,0 @@ -package kernel - -import ( - "bufio" - "errors" - encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/helper" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel/encoder" - "io" - "os/exec" - "regexp" - "strconv" - "strings" - "unicode" -) - -type File struct { - Path string - Name string - Ext string -} - -type ConvertSetting struct { - VideoFileInput File - VideoFileOut File - OverwriteOutputFiles bool - Encoder encoder2.EncoderContract -} - -type ConvertorContract interface { - RunConvert(setting ConvertSetting, progress ProgressContract) error - GetTotalDuration(file *File) (float64, error) - GetFFmpegVesrion() (string, error) - GetFFprobeVersion() (string, error) - GetFFplayVersion() (string, error) - ChangeFFmpegPath(path string) (bool, error) - ChangeFFprobePath(path string) (bool, error) - ChangeFFplayPath(path string) (bool, error) - GetRunningProcesses() map[int]*exec.Cmd - GetSupportFormats() (encoder.ConvertorFormatsContract, error) -} - -type ProgressContract interface { - GetProtocole() string - Run(stdOut io.ReadCloser, stdErr io.ReadCloser) error -} - -type FFPathUtilities struct { - FFmpeg string - FFprobe string - FFplay string -} - -type runningProcesses struct { - items map[int]*exec.Cmd - numberOfStarts int -} - -type Convertor struct { - ffPathUtilities *FFPathUtilities - runningProcesses runningProcesses -} - -type ConvertData struct { - totalDuration float64 -} - -func NewService(ffPathUtilities *FFPathUtilities) *Convertor { - return &Convertor{ - ffPathUtilities: ffPathUtilities, - runningProcesses: runningProcesses{items: map[int]*exec.Cmd{}, numberOfStarts: 0}, - } -} - -func (s Convertor) RunConvert(setting ConvertSetting, progress ProgressContract) error { - overwriteOutputFiles := "-n" - if setting.OverwriteOutputFiles == true { - overwriteOutputFiles = "-y" - } - args := []string{overwriteOutputFiles, "-i", setting.VideoFileInput.Path} - args = append(args, setting.Encoder.GetParams()...) - args = append(args, "-progress", progress.GetProtocole(), setting.VideoFileOut.Path) - cmd := exec.Command(s.ffPathUtilities.FFmpeg, args...) - helper.PrepareBackgroundCommand(cmd) - - stdOut, err := cmd.StdoutPipe() - if err != nil { - return err - } - stdErr, err := cmd.StderrPipe() - if err != nil { - return err - } - - err = cmd.Start() - if err != nil { - return err - } - index := s.runningProcesses.numberOfStarts - s.runningProcesses.numberOfStarts++ - s.runningProcesses.items[index] = cmd - - errProgress := progress.Run(stdOut, stdErr) - - err = cmd.Wait() - delete(s.runningProcesses.items, index) - if errProgress != nil { - return errProgress - } - if err != nil { - return err - } - - return nil -} - -func (s Convertor) GetTotalDuration(file *File) (duration float64, err error) { - args := []string{"-v", "error", "-select_streams", "v:0", "-count_packets", "-show_entries", "stream=nb_read_packets", "-of", "csv=p=0", file.Path} - cmd := exec.Command(s.ffPathUtilities.FFprobe, args...) - helper.PrepareBackgroundCommand(cmd) - out, err := cmd.CombinedOutput() - if err != nil { - errString := strings.TrimSpace(string(out)) - if len(errString) > 1 { - return 0, errors.New(errString) - } - return 0, err - } - frames := strings.TrimSpace(string(out)) - if len(frames) == 0 { - return s.getAlternativeTotalDuration(file) - } - - duration, err = strconv.ParseFloat(frames, 64) - if err != nil { - // fix .mts duration - return strconv.ParseFloat(getFirstDigits(frames), 64) - } - return duration, err -} - -func (s Convertor) getAlternativeTotalDuration(file *File) (duration float64, err error) { - args := []string{"-v", "error", "-select_streams", "a:0", "-count_packets", "-show_entries", "stream=nb_read_packets", "-of", "csv=p=0", file.Path} - cmd := exec.Command(s.ffPathUtilities.FFprobe, args...) - helper.PrepareBackgroundCommand(cmd) - out, err := cmd.CombinedOutput() - if err != nil { - errString := strings.TrimSpace(string(out)) - if len(errString) > 1 { - return 0, errors.New(errString) - } - return 0, err - } - frames := strings.TrimSpace(string(out)) - if len(frames) == 0 { - return 0, errors.New("error getting number of frames") - } - return strconv.ParseFloat(frames, 64) -} - -func (s Convertor) GetFFmpegVesrion() (string, error) { - cmd := exec.Command(s.ffPathUtilities.FFmpeg, "-version") - helper.PrepareBackgroundCommand(cmd) - out, err := cmd.CombinedOutput() - if err != nil { - return "", err - } - text := regexp.MustCompile("\r?\n").Split(strings.TrimSpace(string(out)), -1) - return text[0], nil -} - -func (s Convertor) GetFFprobeVersion() (string, error) { - cmd := exec.Command(s.ffPathUtilities.FFprobe, "-version") - helper.PrepareBackgroundCommand(cmd) - out, err := cmd.CombinedOutput() - if err != nil { - return "", err - } - text := regexp.MustCompile("\r?\n").Split(strings.TrimSpace(string(out)), -1) - return text[0], nil -} - -func (s Convertor) GetFFplayVersion() (string, error) { - cmd := exec.Command(s.ffPathUtilities.FFplay, "-version") - helper.PrepareBackgroundCommand(cmd) - out, err := cmd.CombinedOutput() - if err != nil { - return "", err - } - text := regexp.MustCompile("\r?\n").Split(strings.TrimSpace(string(out)), -1) - return text[0], nil -} - -func (s Convertor) ChangeFFmpegPath(path string) (bool, error) { - cmd := exec.Command(path, "-version") - helper.PrepareBackgroundCommand(cmd) - out, err := cmd.CombinedOutput() - if err != nil { - return false, err - } - if strings.Contains(strings.TrimSpace(string(out)), "ffmpeg") == false { - return false, nil - } - s.ffPathUtilities.FFmpeg = path - return true, nil -} - -func (s Convertor) ChangeFFprobePath(path string) (bool, error) { - cmd := exec.Command(path, "-version") - helper.PrepareBackgroundCommand(cmd) - out, err := cmd.CombinedOutput() - if err != nil { - return false, err - } - if strings.Contains(strings.TrimSpace(string(out)), "ffprobe") == false { - return false, nil - } - s.ffPathUtilities.FFprobe = path - return true, nil -} - -func (s Convertor) ChangeFFplayPath(path string) (bool, error) { - cmd := exec.Command(path, "-version") - helper.PrepareBackgroundCommand(cmd) - out, err := cmd.CombinedOutput() - if err != nil { - return false, err - } - if strings.Contains(strings.TrimSpace(string(out)), "ffplay") == false { - return false, nil - } - s.ffPathUtilities.FFplay = path - return true, nil -} - -func (s Convertor) GetSupportFormats() (encoder.ConvertorFormatsContract, error) { - formats := encoder.NewConvertorFormats() - cmd := exec.Command(s.ffPathUtilities.FFmpeg, "-encoders") - helper.PrepareBackgroundCommand(cmd) - - stdOut, err := cmd.StdoutPipe() - if err != nil { - return formats, err - } - - err = cmd.Start() - if err != nil { - return formats, err - } - - scannerErr := bufio.NewReader(stdOut) - for { - line, _, err := scannerErr.ReadLine() - if err != nil { - if err == io.EOF { - break - } - continue - } - text := strings.Split(strings.TrimSpace(string(line)), " ") - encoderType := string(text[0][0]) - if len(text) < 2 || (encoderType != "V" && encoderType != "A") { - continue - } - formats.NewEncoder(text[1]) - } - - err = cmd.Wait() - if err != nil { - return formats, err - } - - return formats, nil -} - -func (s Convertor) GetRunningProcesses() map[int]*exec.Cmd { - return s.runningProcesses.items -} - -func getFirstDigits(s string) string { - result := "" - for _, r := range s { - if unicode.IsDigit(r) { - result += string(r) - } else { - break - } - } - return result -} diff --git a/kernel/encoder/encoders.go b/kernel/encoder/encoders.go deleted file mode 100644 index d9330f4..0000000 --- a/kernel/encoder/encoders.go +++ /dev/null @@ -1,74 +0,0 @@ -package encoder - -import ( - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/apng" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/bmp" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/flv" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/gif" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/h264_nvenc" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libmp3lame" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libshine" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libtwolame" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libvpx" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libvpx_vp9" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libwebp" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libwebp_anim" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libx264" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libx265" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libxvid" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/mjpeg" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/mp2" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/mp2fixed" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/mpeg1video" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/mpeg2video" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/mpeg4" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/msmpeg4" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/msmpeg4v2" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/msvideo1" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/png" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/qtrle" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/sgi" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/tiff" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/wmav1" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/wmav2" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/wmv1" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/wmv2" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/xbm" -) - -var supportEncoders = map[string]func() encoder.EncoderDataContract{ - "libx264": libx264.NewData, - "h264_nvenc": h264_nvenc.NewData, - "libx265": libx265.NewData, - "png": png.NewData, - "gif": gif.NewData, - "flv": flv.NewData, - "apng": apng.NewData, - "bmp": bmp.NewData, - "mjpeg": mjpeg.NewData, - "mpeg1video": mpeg1video.NewData, - "mpeg2video": mpeg2video.NewData, - "mpeg4": mpeg4.NewData, - "libxvid": libxvid.NewData, - "msmpeg4v2": msmpeg4v2.NewData, - "msmpeg4": msmpeg4.NewData, - "msvideo1": msvideo1.NewData, - "qtrle": qtrle.NewData, - "tiff": tiff.NewData, - "sgi": sgi.NewData, - "libvpx": libvpx.NewData, - "libvpx-vp9": libvpx_vp9.NewData, - "libwebp_anim": libwebp_anim.NewData, - "libwebp": libwebp.NewData, - "wmv1": wmv1.NewData, - "wmv2": wmv2.NewData, - "xbm": xbm.NewData, - "mp2": mp2.NewData, - "mp2fixed": mp2fixed.NewData, - "libtwolame": libtwolame.NewData, - "libmp3lame": libmp3lame.NewData, - "libshine": libshine.NewData, - "wmav1": wmav1.NewData, - "wmav2": wmav2.NewData, -} diff --git a/kernel/error.go b/kernel/error.go deleted file mode 100644 index 7f03ea1..0000000 --- a/kernel/error.go +++ /dev/null @@ -1,20 +0,0 @@ -package kernel - -import ( - "fyne.io/fyne/v2" - "fyne.io/fyne/v2/app" - "fyne.io/fyne/v2/container" - "fyne.io/fyne/v2/widget" -) - -func PanicErrorLang(err error, metadata *fyne.AppMetadata) { - app.SetMetadata(*metadata) - a := app.New() - window := a.NewWindow("GUI for FFmpeg") - window.SetContent(container.NewVBox( - widget.NewLabel("Произошла ошибка!"), - widget.NewLabel("произошла ошибка при получении языковых переводах. \n\r"+err.Error()), - )) - window.ShowAndRun() - panic(err.Error()) -} diff --git a/kernel/ffplay.go b/kernel/ffplay.go deleted file mode 100644 index 0b5538f..0000000 --- a/kernel/ffplay.go +++ /dev/null @@ -1,28 +0,0 @@ -package kernel - -import ( - "os/exec" -) - -type FFplay struct { - ffPathUtilities *FFPathUtilities -} - -type FFplaySetting struct { - PathToFile string -} - -type FFplayContract interface { - Run(setting FFplaySetting) error -} - -func NewFFplay(ffPathUtilities *FFPathUtilities) *FFplay { - return &FFplay{ffPathUtilities: ffPathUtilities} -} - -func (ffplay FFplay) Run(setting FFplaySetting) error { - args := []string{setting.PathToFile} - cmd := exec.Command(ffplay.ffPathUtilities.FFplay, args...) - - return cmd.Start() -} diff --git a/kernel/items_to_convert.go b/kernel/items_to_convert.go deleted file mode 100644 index 0adbc3d..0000000 --- a/kernel/items_to_convert.go +++ /dev/null @@ -1,151 +0,0 @@ -package kernel - -import ( - "fyne.io/fyne/v2" - "fyne.io/fyne/v2/canvas" - "fyne.io/fyne/v2/container" - "fyne.io/fyne/v2/theme" - "fyne.io/fyne/v2/widget" - "github.com/nicksnyder/go-i18n/v2/i18n" -) - -type ItemsToConvertContract interface { - Add(file *File) - GetItems() map[int]ItemToConvertContract - AfterAddingQueue() -} - -type ItemsToConvert struct { - nextId int - items map[int]ItemToConvertContract - itemsContainer *fyne.Container - ffplayService FFplayContract - isAutoRemove bool -} - -func NewItemsToConvert(itemsContainer *fyne.Container, ffplayService FFplayContract, localizerService LocalizerContract) *ItemsToConvert { - containerForItems := container.NewVBox() - ItemsToConvert := &ItemsToConvert{ - nextId: 0, - items: map[int]ItemToConvertContract{}, - itemsContainer: containerForItems, - ffplayService: ffplayService, - isAutoRemove: true, - } - - line := canvas.NewLine(theme.Color(theme.ColorNameFocus)) - line.StrokeWidth = 5 - checkboxAutoRemove := widget.NewCheck(localizerService.GetMessage(&i18n.LocalizeConfig{ - MessageID: "autoClearAfterAddingToQueue", - }), func(checked bool) { - ItemsToConvert.isAutoRemove = checked - }) - checkboxAutoRemove.SetChecked(ItemsToConvert.isAutoRemove) - localizerService.AddChangeCallback("autoClearAfterAddingToQueue", func(text string) { - checkboxAutoRemove.Text = text - }) - - buttonClear := widget.NewButton(localizerService.GetMessage(&i18n.LocalizeConfig{ - MessageID: "clearAll", - }), func() { - ItemsToConvert.clear() - }) - buttonClear.Importance = widget.DangerImportance - localizerService.AddChangeCallback("clearAll", func(text string) { - buttonClear.Text = text - }) - - itemsContainer.Add(container.NewVBox( - container.NewPadded(), - container.NewBorder(nil, nil, nil, buttonClear, container.NewHScroll(checkboxAutoRemove)), - container.NewPadded(), - line, - container.NewPadded(), - containerForItems, - )) - - return ItemsToConvert -} - -func (items *ItemsToConvert) Add(file *File) { - nextId := items.nextId - var content *fyne.Container - var buttonPlay *widget.Button - - buttonPlay = widget.NewButtonWithIcon("", theme.Icon(theme.IconNameMediaPlay), func() { - buttonPlay.Disable() - go func() { - _ = items.ffplayService.Run(FFplaySetting{ - PathToFile: file.Path, - }) - fyne.Do(func() { - buttonPlay.Enable() - }) - }() - }) - - buttonRemove := widget.NewButtonWithIcon("", theme.Icon(theme.IconNameDelete), func() { - items.itemsContainer.Remove(content) - items.itemsContainer.Refresh() - delete(items.items, nextId) - }) - buttonRemove.Importance = widget.DangerImportance - - content = container.NewVBox( - container.NewBorder( - nil, - nil, - buttonPlay, - buttonRemove, - container.NewHScroll(widget.NewLabel(file.Name)), - ), - container.NewHScroll(widget.NewLabel(file.Path)), - container.NewPadded(), - canvas.NewLine(theme.Color(theme.ColorNameFocus)), - container.NewPadded(), - ) - - items.itemsContainer.Add(content) - items.items[nextId] = NewItemToConvert(file, content) - items.nextId++ -} - -func (items *ItemsToConvert) GetItems() map[int]ItemToConvertContract { - return items.items -} - -func (items *ItemsToConvert) AfterAddingQueue() { - if items.isAutoRemove { - items.clear() - } -} - -func (items *ItemsToConvert) clear() { - items.itemsContainer.RemoveAll() - items.items = map[int]ItemToConvertContract{} -} - -type ItemToConvertContract interface { - GetFile() *File - GetContent() *fyne.Container -} - -type ItemToConvert struct { - file *File - content *fyne.Container -} - -func NewItemToConvert(file *File, content *fyne.Container) *ItemToConvert { - return &ItemToConvert{ - file: file, - content: content, - } -} - -func (item ItemToConvert) GetFile() *File { - return item.file -} - -func (item ItemToConvert) GetContent() *fyne.Container { - return item.content -} diff --git a/kernel/layout.go b/kernel/layout.go deleted file mode 100644 index 3d34179..0000000 --- a/kernel/layout.go +++ /dev/null @@ -1,482 +0,0 @@ -package kernel - -import ( - "fyne.io/fyne/v2" - "fyne.io/fyne/v2/canvas" - "fyne.io/fyne/v2/container" - "fyne.io/fyne/v2/theme" - "fyne.io/fyne/v2/widget" - "github.com/nicksnyder/go-i18n/v2/i18n" - "image/color" - "strconv" - "strings" -) - -type LayoutContract interface { - SetContent(content fyne.CanvasObject) *fyne.Container - ChangeQueueStatus(queueId int, queue *Queue) - GetRightTabs() RightTabsContract -} - -type Layout struct { - layout *fyne.Container - queueLayoutObject QueueLayoutObjectContract - localizerService LocalizerContract - rightTabsService RightTabsContract -} - -func NewLayout(queueLayoutObject QueueLayoutObjectContract, localizerService LocalizerContract, rightTabsService RightTabsContract) *Layout { - layout := container.NewAdaptiveGrid(2, widget.NewLabel(""), queueLayoutObject.GetCanvasObject()) - - return &Layout{ - layout: layout, - queueLayoutObject: queueLayoutObject, - localizerService: localizerService, - rightTabsService: rightTabsService, - } -} - -func (l Layout) SetContent(content fyne.CanvasObject) *fyne.Container { - l.layout.Objects[0] = content - return l.layout -} - -func (l Layout) ChangeQueueStatus(queueId int, queue *Queue) { - l.queueLayoutObject.ChangeQueueStatus(queueId, queue) -} - -func (l Layout) GetRightTabs() RightTabsContract { - return l.rightTabsService -} - -type QueueLayoutObjectContract interface { - GetCanvasObject() fyne.CanvasObject - ChangeQueueStatus(queueId int, queue *Queue) -} - -type QueueLayoutObject struct { - QueueListContract QueueListContract - - queue QueueListContract - container *fyne.Container - containerItems *fyne.Container - items map[int]QueueLayoutItem - localizerService LocalizerContract - queueStatisticsFormat *queueStatisticsFormat - ffplayService FFplayContract -} - -type QueueLayoutItem struct { - CanvasObject fyne.CanvasObject - BlockMessageError *container.Scroll - StatusMessage *canvas.Text - MessageError *canvas.Text - buttonPlay *widget.Button - - status *StatusContract -} - -func NewQueueLayoutObject(queue QueueListContract, localizerService LocalizerContract, ffplayService FFplayContract, rightTabsService RightTabsContract, blockProgressbar *fyne.Container) *QueueLayoutObject { - title := widget.NewLabel(localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: "queue"})) - title.TextStyle.Bold = true - - localizerService.AddChangeCallback("queue", func(text string) { - title.Text = text - title.Refresh() - }) - - items := map[int]QueueLayoutItem{} - queueStatisticsFormat := newQueueStatisticsFormat(localizerService, &items) - - line := canvas.NewLine(theme.Color(theme.ColorNameFocus)) - line.StrokeWidth = 5 - - rightTabsService.GetFileQueueContainer().Add(container.NewVBox( - container.NewPadded(), - container.NewHBox(title, queueStatisticsFormat.completed.widget, queueStatisticsFormat.error.widget), - container.NewHBox(queueStatisticsFormat.inProgress.widget, queueStatisticsFormat.waiting.widget, queueStatisticsFormat.total.widget), - container.NewPadded(), - line, - container.NewPadded(), - )) - queueLayoutObject := &QueueLayoutObject{ - queue: queue, - container: container.NewBorder( - container.NewVBox( - blockProgressbar, - widget.NewSeparator(), - ), - nil, nil, nil, container.NewVScroll(rightTabsService.GetTabs()), - ), - containerItems: rightTabsService.GetFileQueueContainer(), - items: items, - localizerService: localizerService, - queueStatisticsFormat: queueStatisticsFormat, - ffplayService: ffplayService, - } - - queue.AddListener(queueLayoutObject) - - return queueLayoutObject -} - -func (o QueueLayoutObject) GetCanvasObject() fyne.CanvasObject { - return o.container -} - -func (o QueueLayoutObject) Add(id int, queue *Queue) { - statusMessage := canvas.NewText(o.getStatusTitle(queue.Status), theme.Color(theme.ColorNamePrimary)) - messageError := canvas.NewText("", theme.Color(theme.ColorNameError)) - buttonPlay := widget.NewButtonWithIcon("", theme.Icon(theme.IconNameMediaPlay), func() { - - }) - buttonPlay.Hide() - blockMessageError := container.NewHScroll(messageError) - blockMessageError.Hide() - - content := container.NewVBox( - container.NewHScroll(widget.NewLabel(queue.Setting.VideoFileInput.Name)), - container.NewHBox( - buttonPlay, - statusMessage, - ), - blockMessageError, - container.NewPadded(), - canvas.NewLine(theme.Color(theme.ColorNameFocus)), - container.NewPadded(), - ) - - o.queueStatisticsFormat.addQueue() - if o.queueStatisticsFormat.isChecked(queue.Status) == false { - content.Hide() - } - - o.items[id] = QueueLayoutItem{ - CanvasObject: content, - StatusMessage: statusMessage, - BlockMessageError: blockMessageError, - MessageError: messageError, - buttonPlay: buttonPlay, - status: &queue.Status, - } - o.containerItems.Add(content) -} - -func (o QueueLayoutObject) Remove(id int) { - if item, ok := o.items[id]; ok { - o.container.Remove(item.CanvasObject) - o.queueStatisticsFormat.removeQueue(*item.status) - o.items[id] = QueueLayoutItem{} - } -} - -func (o QueueLayoutObject) ChangeQueueStatus(queueId int, queue *Queue) { - if item, ok := o.items[queueId]; ok { - statusColor := o.getStatusColor(queue.Status) - item.StatusMessage.Text = o.getStatusTitle(queue.Status) - item.StatusMessage.Color = statusColor - fyne.Do(func() { - item.StatusMessage.Refresh() - }) - if queue.Error != nil { - item.MessageError.Text = queue.Error.Error() - item.MessageError.Color = statusColor - fyne.Do(func() { - item.BlockMessageError.Show() - item.MessageError.Refresh() - }) - } - if queue.Status == StatusType(Completed) { - item.buttonPlay.Show() - item.buttonPlay.OnTapped = func() { - item.buttonPlay.Disable() - go func() { - _ = o.ffplayService.Run(FFplaySetting{ - PathToFile: queue.Setting.VideoFileOut.Path, - }) - fyne.Do(func() { - item.buttonPlay.Enable() - }) - }() - } - } - if o.queueStatisticsFormat.isChecked(queue.Status) == false && item.CanvasObject.Visible() == true { - item.CanvasObject.Hide() - } else if item.CanvasObject.Visible() == false { - item.CanvasObject.Show() - } - o.queueStatisticsFormat.changeQueue(queue.Status) - } -} - -func (o QueueLayoutObject) getStatusColor(status StatusContract) color.Color { - if status == StatusType(Error) { - return theme.Color(theme.ColorNameError) - } - - if status == StatusType(Completed) { - return color.RGBA{R: 49, G: 127, B: 114, A: 255} - } - - return theme.Color(theme.ColorNamePrimary) -} - -func (o QueueLayoutObject) getStatusTitle(status StatusContract) string { - return o.localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: status.Name() + "Queue"}) -} - -type queueStatistics struct { - widget *widget.Check - title string - count *int64 -} -type queueStatisticsFormat struct { - waiting *queueStatistics - inProgress *queueStatistics - completed *queueStatistics - error *queueStatistics - total *queueStatistics -} - -func newQueueStatisticsFormat(localizerService LocalizerContract, queueItems *map[int]QueueLayoutItem) *queueStatisticsFormat { - checkWaiting := newQueueStatistics("waitingQueue", localizerService) - checkInProgress := newQueueStatistics("inProgressQueue", localizerService) - checkCompleted := newQueueStatistics("completedQueue", localizerService) - checkError := newQueueStatistics("errorQueue", localizerService) - checkTotal := newQueueStatistics("total", localizerService) - - queueStatisticsFormat := &queueStatisticsFormat{ - waiting: checkWaiting, - inProgress: checkInProgress, - completed: checkCompleted, - error: checkError, - total: checkTotal, - } - - checkTotal.widget.OnChanged = func(b bool) { - if b == true { - queueStatisticsFormat.allCheckboxChecked() - } else { - queueStatisticsFormat.allUnCheckboxChecked() - } - queueStatisticsFormat.redrawingQueueItems(queueItems) - } - - queueStatisticsFormat.waiting.widget.OnChanged = func(b bool) { - if b == true { - queueStatisticsFormat.checkboxChecked() - } else { - queueStatisticsFormat.unCheckboxChecked() - } - queueStatisticsFormat.redrawingQueueItems(queueItems) - } - - queueStatisticsFormat.inProgress.widget.OnChanged = func(b bool) { - if b == true { - queueStatisticsFormat.checkboxChecked() - } else { - queueStatisticsFormat.unCheckboxChecked() - } - queueStatisticsFormat.redrawingQueueItems(queueItems) - } - - queueStatisticsFormat.completed.widget.OnChanged = func(b bool) { - if b == true { - queueStatisticsFormat.checkboxChecked() - } else { - queueStatisticsFormat.unCheckboxChecked() - } - queueStatisticsFormat.redrawingQueueItems(queueItems) - } - - queueStatisticsFormat.error.widget.OnChanged = func(b bool) { - if b == true { - queueStatisticsFormat.checkboxChecked() - } else { - queueStatisticsFormat.unCheckboxChecked() - } - queueStatisticsFormat.redrawingQueueItems(queueItems) - } - - return queueStatisticsFormat -} - -func (f queueStatisticsFormat) redrawingQueueItems(queueItems *map[int]QueueLayoutItem) { - for _, item := range *queueItems { - if f.isChecked(*item.status) == true && item.CanvasObject.Visible() == false { - item.CanvasObject.Show() - continue - } - if f.isChecked(*item.status) == false && item.CanvasObject.Visible() == true { - item.CanvasObject.Hide() - } - } -} - -func (f queueStatisticsFormat) isChecked(status StatusContract) bool { - if status == StatusType(InProgress) { - return f.inProgress.widget.Checked - } - if status == StatusType(Completed) { - return f.completed.widget.Checked - } - if status == StatusType(Error) { - return f.error.widget.Checked - } - if status == StatusType(Waiting) { - return f.waiting.widget.Checked - } - - return true -} - -func (f queueStatisticsFormat) addQueue() { - f.waiting.add() - f.total.add() -} - -func (f queueStatisticsFormat) changeQueue(status StatusContract) { - if status == StatusType(InProgress) { - f.waiting.remove() - f.inProgress.add() - return - } - - if status == StatusType(Completed) { - f.inProgress.remove() - f.completed.add() - return - } - - if status == StatusType(Error) { - f.inProgress.remove() - f.error.add() - return - } -} - -func (f queueStatisticsFormat) removeQueue(status StatusContract) { - f.total.remove() - - if status == StatusType(Completed) { - f.completed.remove() - return - } - - if status == StatusType(Error) { - f.error.remove() - return - } - - if status == StatusType(InProgress) { - f.inProgress.remove() - return - } - - if status == StatusType(Waiting) { - f.waiting.remove() - return - } -} - -func (f queueStatisticsFormat) checkboxChecked() { - if f.total.widget.Checked == true { - return - } - - if f.waiting.widget.Checked == false { - return - } - - if f.inProgress.widget.Checked == false { - return - } - - if f.completed.widget.Checked == false { - return - } - - if f.error.widget.Checked == false { - return - } - - f.total.widget.Checked = true - f.total.widget.Refresh() -} - -func (f queueStatisticsFormat) unCheckboxChecked() { - if f.total.widget.Checked == false { - return - } - - f.total.widget.Checked = false - f.total.widget.Refresh() -} - -func (f queueStatisticsFormat) allCheckboxChecked() { - f.waiting.widget.Checked = true - f.waiting.widget.Refresh() - f.inProgress.widget.Checked = true - f.inProgress.widget.Refresh() - f.completed.widget.Checked = true - f.completed.widget.Refresh() - f.error.widget.Checked = true - f.error.widget.Refresh() -} - -func (f queueStatisticsFormat) allUnCheckboxChecked() { - f.waiting.widget.Checked = false - f.waiting.widget.Refresh() - f.inProgress.widget.Checked = false - f.inProgress.widget.Refresh() - f.completed.widget.Checked = false - f.completed.widget.Refresh() - f.error.widget.Checked = false - f.error.widget.Refresh() -} - -func newQueueStatistics(messaigeID string, localizerService LocalizerContract) *queueStatistics { - checkbox := widget.NewCheck("", nil) - checkbox.Checked = true - - count := int64(0) - - title := localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: messaigeID}) - queueStatistics := &queueStatistics{ - widget: checkbox, - title: strings.ToLower(title), - count: &count, - } - - queueStatistics.formatText(false) - - localizerService.AddChangeCallback(messaigeID, func(text string) { - queueStatistics.title = strings.ToLower(text) - queueStatistics.formatText(true) - queueStatistics.widget.Refresh() - }) - - return queueStatistics -} - -func (s queueStatistics) add() { - *s.count += 1 - s.formatText(true) -} - -func (s queueStatistics) remove() { - if *s.count == 0 { - return - } - *s.count -= 1 - s.formatText(true) -} - -func (s queueStatistics) formatText(refresh bool) { - s.widget.Text = s.title + ": " + strconv.FormatInt(*s.count, 10) - if refresh == true { - fyne.Do(func() { - s.widget.Refresh() - }) - } -} diff --git a/kernel/localizer.go b/kernel/localizer.go deleted file mode 100644 index 3823c7d..0000000 --- a/kernel/localizer.go +++ /dev/null @@ -1,158 +0,0 @@ -package kernel - -import ( - "github.com/BurntSushi/toml" - "github.com/nicksnyder/go-i18n/v2/i18n" - "golang.org/x/text/cases" - "golang.org/x/text/language" - "golang.org/x/text/language/display" - "path/filepath" - "sort" -) - -type LocalizerContract interface { - GetLanguages() []Lang - GetMessage(localizeConfig *i18n.LocalizeConfig) string - SetCurrentLanguage(lang Lang) error - SetCurrentLanguageByCode(code string) error - GetCurrentLanguage() *CurrentLanguage - AddChangeCallback(messageID string, callback func(text string)) -} - -type Lang struct { - Code string - Title string -} - -type CurrentLanguage struct { - Lang Lang - localizer *i18n.Localizer - localizerDefault *i18n.Localizer -} - -type changeCallback struct { - messageID string - callback func(text string) -} - -type Localizer struct { - bundle *i18n.Bundle - languages []Lang - currentLanguage *CurrentLanguage - changeCallbacks map[int]*changeCallback -} - -func NewLocalizer(directory string, languageDefault language.Tag) (*Localizer, error) { - bundle := i18n.NewBundle(languageDefault) - bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal) - - languages, err := initLanguages(directory, bundle) - if err != nil { - return nil, err - } - - localizerDefault := i18n.NewLocalizer(bundle, languageDefault.String()) - - return &Localizer{ - bundle: bundle, - languages: languages, - currentLanguage: &CurrentLanguage{ - Lang: Lang{ - Code: languageDefault.String(), - Title: cases.Title(languageDefault).String(display.Self.Name(languageDefault)), - }, - localizer: localizerDefault, - localizerDefault: localizerDefault, - }, - changeCallbacks: map[int]*changeCallback{}, - }, nil -} - -func initLanguages(directory string, bundle *i18n.Bundle) ([]Lang, error) { - var languages []Lang - - files, err := filepath.Glob(directory + "/active.*.toml") - if err != nil { - return nil, err - } - for _, file := range files { - lang, err := bundle.LoadMessageFile(file) - if err != nil { - return nil, err - } - title := cases.Title(lang.Tag).String(display.Self.Name(lang.Tag)) - languages = append(languages, Lang{Code: lang.Tag.String(), Title: title}) - } - - sort.Sort(languagesSort(languages)) - - return languages, nil -} - -func (l Localizer) GetLanguages() []Lang { - return l.languages -} - -func (l Localizer) GetMessage(localizeConfig *i18n.LocalizeConfig) string { - message, err := l.GetCurrentLanguage().localizer.Localize(localizeConfig) - if err != nil { - message, err = l.GetCurrentLanguage().localizerDefault.Localize(localizeConfig) - if err != nil { - return err.Error() - } - } - return message -} - -func (l Localizer) SetCurrentLanguage(lang Lang) error { - l.currentLanguage.Lang = lang - l.currentLanguage.localizer = i18n.NewLocalizer(l.bundle, lang.Code) - l.eventSetCurrentLanguage() - return nil -} - -func (l Localizer) SetCurrentLanguageByCode(code string) error { - lang, err := language.Parse(code) - if err != nil { - return err - } - title := cases.Title(lang).String(display.Self.Name(lang)) - return l.SetCurrentLanguage(Lang{Code: lang.String(), Title: title}) -} - -func (l Localizer) GetCurrentLanguage() *CurrentLanguage { - return l.currentLanguage -} - -func (l Localizer) AddChangeCallback(messageID string, callback func(text string)) { - l.changeCallbacks[len(l.changeCallbacks)] = &changeCallback{messageID: messageID, callback: callback} -} - -func (l Localizer) eventSetCurrentLanguage() { - for _, changeCallback := range l.changeCallbacks { - text := l.GetMessage(&i18n.LocalizeConfig{MessageID: changeCallback.messageID}) - changeCallback.callback(text) - } -} - -type languagesSort []Lang - -func (l languagesSort) Len() int { return len(l) } -func (l languagesSort) Swap(i, j int) { l[i], l[j] = l[j], l[i] } -func (l languagesSort) Less(i, j int) bool { - return languagePriority(l[i]) < languagePriority(l[j]) -} -func languagePriority(l Lang) int { - priority := 0 - - switch l.Code { - case "ru": - priority = -3 - case "kk": - priority = -2 - case "en": - priority = -1 - } - - return priority -} diff --git a/kernel/progressbar.go b/kernel/progressbar.go deleted file mode 100644 index 48b8485..0000000 --- a/kernel/progressbar.go +++ /dev/null @@ -1,254 +0,0 @@ -package kernel - -import ( - "bufio" - "errors" - "fyne.io/fyne/v2" - "fyne.io/fyne/v2/canvas" - "fyne.io/fyne/v2/container" - "fyne.io/fyne/v2/theme" - "fyne.io/fyne/v2/widget" - "github.com/nicksnyder/go-i18n/v2/i18n" - "image/color" - "io" - "regexp" - "strconv" - "strings" -) - -type BlockProgressbarContract interface { - GetContainer() *fyne.Container - GetProgressbar(totalDuration float64, filePath string, localizerService LocalizerContract) Progress - ProcessEndedWithError(errorText string) - ProcessEndedWithSuccess(filePath string) -} - -type BlockProgressbar struct { - container *fyne.Container - label *widget.Label - progressbar *widget.ProgressBar - errorBlock *container.Scroll - messageError *canvas.Text - statusMessage *canvas.Text - buttonPlay *widget.Button - statusesText *BlockProgressbarStatusesText - ffplayService FFplayContract -} - -func NewBlockProgressbar(statusesText *BlockProgressbarStatusesText, ffplayService FFplayContract) *BlockProgressbar { - label := widget.NewLabel("") - progressbar := widget.NewProgressBar() - - statusMessage := canvas.NewText("", theme.Color(theme.ColorNamePrimary)) - messageError := canvas.NewText("", theme.Color(theme.ColorNameError)) - buttonPlay := widget.NewButtonWithIcon("", theme.Icon(theme.IconNameMediaPlay), func() { - - }) - buttonPlay.Hide() - - errorBlock := container.NewHScroll(messageError) - errorBlock.Hide() - - content := container.NewVBox( - container.NewHScroll(label), - progressbar, - container.NewHScroll(container.NewHBox( - buttonPlay, - statusMessage, - )), - errorBlock, - ) - content.Hide() - - return &BlockProgressbar{ - container: content, - label: label, - progressbar: progressbar, - errorBlock: errorBlock, - messageError: messageError, - statusMessage: statusMessage, - buttonPlay: buttonPlay, - statusesText: statusesText, - ffplayService: ffplayService, - } -} - -func (block BlockProgressbar) GetContainer() *fyne.Container { - return block.container -} - -func (block BlockProgressbar) GetProgressbar(totalDuration float64, filePath string, localizerService LocalizerContract) Progress { - block.label.Text = filePath - block.statusMessage.Color = theme.Color(theme.ColorNamePrimary) - block.statusMessage.Text = block.statusesText.inProgress - block.messageError.Text = "" - fyne.Do(func() { - block.buttonPlay.Hide() - if block.errorBlock.Visible() { - block.errorBlock.Hide() - } - block.statusMessage.Refresh() - block.container.Refresh() - block.errorBlock.Refresh() - }) - - block.progressbar.Value = 0 - return NewProgress(totalDuration, block.progressbar, localizerService) -} - -func (block BlockProgressbar) ProcessEndedWithError(errorText string) { - fyne.Do(func() { - block.statusMessage.Color = theme.Color(theme.ColorNameError) - block.statusMessage.Text = block.statusesText.error - block.messageError.Text = errorText - block.errorBlock.Show() - }) -} - -func (block BlockProgressbar) ProcessEndedWithSuccess(filePath string) { - fyne.Do(func() { - block.statusMessage.Color = color.RGBA{R: 49, G: 127, B: 114, A: 255} - block.statusMessage.Text = block.statusesText.completed - block.buttonPlay.Show() - block.buttonPlay.OnTapped = func() { - block.buttonPlay.Disable() - go func() { - _ = block.ffplayService.Run(FFplaySetting{ - PathToFile: filePath, - }) - fyne.Do(func() { - block.buttonPlay.Enable() - }) - }() - } - }) -} - -type Progress struct { - totalDuration float64 - progressbar *widget.ProgressBar - protocol string - localizerService LocalizerContract -} - -func NewProgress(totalDuration float64, progressbar *widget.ProgressBar, localizerService LocalizerContract) Progress { - return Progress{ - totalDuration: totalDuration, - progressbar: progressbar, - protocol: "pipe:", - localizerService: localizerService, - } -} - -func (p Progress) GetProtocole() string { - return p.protocol -} - -func (p Progress) Run(stdOut io.ReadCloser, stdErr io.ReadCloser) error { - isProcessCompleted := false - var errorText string - - p.progressbar.Value = 0 - p.progressbar.Max = p.totalDuration - fyne.Do(func() { - p.progressbar.Refresh() - }) - progress := 0.0 - - go func() { - scannerErr := bufio.NewReader(stdErr) - for { - line, _, err := scannerErr.ReadLine() - if err != nil { - if err == io.EOF { - break - } - continue - } - data := strings.TrimSpace(string(line)) - errorText = data - } - }() - - scannerOut := bufio.NewReader(stdOut) - for { - line, _, err := scannerOut.ReadLine() - if err != nil { - if err == io.EOF { - break - } - continue - } - data := strings.TrimSpace(string(line)) - if strings.Contains(data, "progress=end") { - p.progressbar.Value = p.totalDuration - fyne.Do(func() { - p.progressbar.Refresh() - }) - isProcessCompleted = true - break - } - - re := regexp.MustCompile(`frame=(\d+)`) - a := re.FindAllStringSubmatch(data, -1) - - if len(a) > 0 && len(a[len(a)-1]) > 0 { - c, err := strconv.Atoi(a[len(a)-1][len(a[len(a)-1])-1]) - if err != nil { - continue - } - progress = float64(c) - } - if p.progressbar.Value != progress { - p.progressbar.Value = progress - fyne.Do(func() { - p.progressbar.Refresh() - }) - } - } - - if isProcessCompleted == false { - if len(errorText) == 0 { - errorText = p.localizerService.GetMessage(&i18n.LocalizeConfig{ - MessageID: "errorConverter", - }) - } - return errors.New(errorText) - } - - return nil -} - -type BlockProgressbarStatusesText struct { - inProgress string - completed string - error string -} - -func GetBlockProgressbarStatusesText(localizerService LocalizerContract) *BlockProgressbarStatusesText { - statusesText := &BlockProgressbarStatusesText{ - inProgress: localizerService.GetMessage(&i18n.LocalizeConfig{ - MessageID: "inProgressQueue", - }), - completed: localizerService.GetMessage(&i18n.LocalizeConfig{ - MessageID: "completedQueue", - }), - error: localizerService.GetMessage(&i18n.LocalizeConfig{ - MessageID: "errorQueue", - }), - } - - localizerService.AddChangeCallback("inProgressQueue", func(text string) { - statusesText.inProgress = text - }) - - localizerService.AddChangeCallback("completedQueue", func(text string) { - statusesText.completed = text - }) - - localizerService.AddChangeCallback("errorQueue", func(text string) { - statusesText.error = text - }) - - return statusesText -} diff --git a/kernel/queue.go b/kernel/queue.go deleted file mode 100644 index 6d2f6c4..0000000 --- a/kernel/queue.go +++ /dev/null @@ -1,125 +0,0 @@ -package kernel - -import ( - "errors" -) - -type Queue struct { - Setting *ConvertSetting - Status StatusContract - Error error -} - -type StatusContract interface { - Name() string - Ordinal() int -} - -const ( - Waiting = iota - InProgress - Completed - Error -) - -type StatusType uint - -var statusTypeStrings = []string{ - "waiting", - "inProgress", - "completed", - "error", -} - -func (status StatusType) Name() string { - return statusTypeStrings[status] -} - -func (status StatusType) Ordinal() int { - return int(status) -} - -type QueueListenerContract interface { - Add(key int, queue *Queue) - Remove(key int) -} - -type QueueListContract interface { - AddListener(queueListener QueueListenerContract) - GetItems() map[int]*Queue - Add(setting *ConvertSetting) - Remove(key int) - GetItem(key int) (*Queue, error) - Next() (key int, queue *Queue) -} - -type QueueList struct { - currentKey *int - items map[int]*Queue - queueListener map[int]QueueListenerContract -} - -func NewQueueList() *QueueList { - currentKey := 0 - return &QueueList{ - currentKey: ¤tKey, - items: map[int]*Queue{}, - queueListener: map[int]QueueListenerContract{}, - } -} - -func (l QueueList) GetItems() map[int]*Queue { - return l.items -} - -func (l QueueList) Add(setting *ConvertSetting) { - queue := Queue{ - Setting: setting, - Status: StatusType(Waiting), - } - - *l.currentKey += 1 - l.items[*l.currentKey] = &queue - l.eventAdd(*l.currentKey, &queue) -} - -func (l QueueList) Remove(key int) { - if _, ok := l.items[key]; ok { - delete(l.items, key) - l.eventRemove(key) - } -} - -func (l QueueList) GetItem(key int) (*Queue, error) { - if item, ok := l.items[key]; ok { - return item, nil - } - - return nil, errors.New("key not found") -} - -func (l QueueList) AddListener(queueListener QueueListenerContract) { - l.queueListener[len(l.queueListener)] = queueListener -} - -func (l QueueList) eventAdd(key int, queue *Queue) { - for _, listener := range l.queueListener { - listener.Add(key, queue) - } -} - -func (l QueueList) eventRemove(key int) { - for _, listener := range l.queueListener { - listener.Remove(key) - } -} - -func (l QueueList) Next() (key int, queue *Queue) { - statusWaiting := StatusType(Waiting) - for key, item := range l.items { - if item.Status == statusWaiting { - return key, item - } - } - return -1, nil -} diff --git a/kernel/right_tabs.go b/kernel/right_tabs.go deleted file mode 100644 index 16f8de1..0000000 --- a/kernel/right_tabs.go +++ /dev/null @@ -1,76 +0,0 @@ -package kernel - -import ( - "fyne.io/fyne/v2" - "fyne.io/fyne/v2/container" - "github.com/nicksnyder/go-i18n/v2/i18n" -) - -type RightTabsContract interface { - GetTabs() *container.AppTabs - GetAddedFilesContainer() *fyne.Container - GetFileQueueContainer() *fyne.Container - SelectFileQueueTab() - SelectAddedFilesTab() -} - -type RightTabs struct { - tabs *container.AppTabs - - addedFilesContainer *fyne.Container - addedFilesTab *container.TabItem - - fileQueueContainer *fyne.Container - fileQueueTab *container.TabItem -} - -func NewRightTabs(localizerService LocalizerContract) *RightTabs { - addedFilesContainer := container.NewVBox() - addedFilesTab := container.NewTabItem(localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: "addedFilesTitle"}), addedFilesContainer) - localizerService.AddChangeCallback("addedFilesTitle", func(text string) { - addedFilesTab.Text = text - }) - - fileQueueContainer := container.NewVBox() - fileQueueTab := container.NewTabItem(localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: "fileQueueTitle"}), fileQueueContainer) - localizerService.AddChangeCallback("fileQueueTitle", func(text string) { - fileQueueTab.Text = text - }) - - tabs := container.NewAppTabs( - addedFilesTab, - fileQueueTab, - ) - - return &RightTabs{ - tabs: tabs, - addedFilesContainer: addedFilesContainer, - addedFilesTab: addedFilesTab, - fileQueueContainer: fileQueueContainer, - fileQueueTab: fileQueueTab, - } -} - -func (t RightTabs) GetTabs() *container.AppTabs { - return t.tabs -} - -func (t RightTabs) GetAddedFilesContainer() *fyne.Container { - return t.addedFilesContainer -} - -func (t RightTabs) GetFileQueueContainer() *fyne.Container { - return t.fileQueueContainer -} - -func (t RightTabs) SelectFileQueueTab() { - fyne.Do(func() { - t.tabs.Select(t.fileQueueTab) - }) -} - -func (t RightTabs) SelectAddedFilesTab() { - fyne.Do(func() { - t.tabs.Select(t.addedFilesTab) - }) -} diff --git a/kernel/window.go b/kernel/window.go deleted file mode 100644 index cec7b5b..0000000 --- a/kernel/window.go +++ /dev/null @@ -1,89 +0,0 @@ -package kernel - -import ( - "fyne.io/fyne/v2" - "fyne.io/fyne/v2/dialog" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/helper" - "time" -) - -type WindowContract interface { - SetContent(content fyne.CanvasObject) - SetMainMenu(menu *fyne.MainMenu) - NewFileOpen(callback func(fyne.URIReadCloser, error), location fyne.ListableURI) *dialog.FileDialog - NewFolderOpen(callback func(fyne.ListableURI, error), location fyne.ListableURI) *dialog.FileDialog - SetOnDropped(callback func(position fyne.Position, uris []fyne.URI)) - ShowAndRun() - GetLayout() LayoutContract -} - -type Window struct { - windowFyne fyne.Window - layout LayoutContract -} - -func newWindow(w fyne.Window, layout LayoutContract) Window { - windowSize := fyne.Size{Width: 1039, Height: 599} - w.Resize(windowSize) - w.CenterOnScreen() - - fyne.Do(func() { - /** - * Bug fixed. - * When starting the program, sometimes the window was displayed incorrectly. - */ - windowSize.Width += 1 - windowSize.Height += 1 - time.Sleep(time.Millisecond * 500) - w.Resize(windowSize) - }) - - return Window{ - windowFyne: w, - layout: layout, - } -} - -func (w Window) SetContent(content fyne.CanvasObject) { - fyne.Do(func() { - w.windowFyne.SetContent(w.layout.SetContent(content)) - }) -} - -func (w Window) NewFileOpen(callback func(fyne.URIReadCloser, error), location fyne.ListableURI) *dialog.FileDialog { - fileDialog := dialog.NewFileOpen(callback, w.windowFyne) - helper.FileDialogResize(fileDialog, w.windowFyne) - fileDialog.Show() - if location != nil { - fileDialog.SetLocation(location) - } - return fileDialog -} - -func (w Window) NewFolderOpen(callback func(fyne.ListableURI, error), location fyne.ListableURI) *dialog.FileDialog { - fileDialog := dialog.NewFolderOpen(callback, w.windowFyne) - helper.FileDialogResize(fileDialog, w.windowFyne) - fileDialog.Show() - if location != nil { - fileDialog.SetLocation(location) - } - return fileDialog -} - -func (w Window) SetMainMenu(menu *fyne.MainMenu) { - w.windowFyne.SetMainMenu(menu) -} - -func (w Window) ShowAndRun() { - w.windowFyne.ShowAndRun() -} - -func (w Window) GetLayout() LayoutContract { - return w.layout -} - -func (w Window) SetOnDropped(callback func(position fyne.Position, uris []fyne.URI)) { - fyne.Do(func() { - w.windowFyne.SetOnDropped(callback) - }) -} diff --git a/languages/.gitignore b/languages/.gitignore deleted file mode 100644 index bc8acef..0000000 --- a/languages/.gitignore +++ /dev/null @@ -1 +0,0 @@ -translate.*.toml diff --git a/languages/active.en.toml b/languages/active.en.toml deleted file mode 100644 index c5c158d..0000000 --- a/languages/active.en.toml +++ /dev/null @@ -1,571 +0,0 @@ -[AlsoUsedProgram] -hash = "sha1-a72be72e7808bb8a0144ed7a93acb29c568b1ed4" -other = "The program also uses:" - -[about] -hash = "sha1-3da0b9ef719fd707f443ac00404447f29445976f" -other = "About" - -[aboutText] -hash = "sha1-8bd565814118ba8b90c40eb5b62acf8d2676e7d6" -other = "A simple interface for the FFmpeg console utility. \nBut I am not the author of the FFmpeg utility itself." - -[addedFilesTitle] -hash = "sha1-8ba0f6e477b0d78df2cc06f1d8b41b888623b851" -other = "Added files" - -[autoClearAfterAddingToQueue] -hash = "sha1-b3781695a4c35380d2cd075bb52f27a2a6d8f19c" -other = "Auto-clear after adding to queue" - -[buttonDownloadFFmpeg] -hash = "sha1-c223c2e15171156192bc3146aee91e6094bb475b" -other = "Download FFmpeg automatically" - -[buttonForSelectedDirTitle] -hash = "sha1-8cbe5c67bcf89e4624635a79cbea104227faedda" -other = "Save to folder:" - -[cancel] -hash = "sha1-0ec753be8df955a117404fb634b01b45eb386e2a" -other = "Cancel" - -[changeFFPath] -hash = "sha1-1f704de0560f8135eb6924cd232ed919ca2e5af0" -other = "FFmpeg, FFprobe and FFplay" - -[changeLanguage] -hash = "sha1-8b276eaf378d485c769fb3d5dcc06dfc25b0c01b" -other = "Change language" - -[checkboxOverwriteOutputFilesTitle] -hash = "sha1-5860124bb781e7ef680f573fa93977e96328d4e7" -other = "Allow file to be overwritten" - -[choose] -hash = "sha1-f60bb5f761024d973834b5e9d25ceebce2c85f94" -other = "choose" - -[clearAll] -hash = "sha1-f32702d79ac206432400ac6b041695d020f6fa77" -other = "Clear List" - -[completedQueue] -hash = "sha1-398c7d4f7b0d522afb930769c0fbb1a9f4b61fbe" -other = "Completed" - -[converterVideoFilesSubmitTitle] -hash = "sha1-7ac460f3c24c9952082f2db6e4d62f752598709c" -other = "Convert" - -[converterVideoFilesTitle] -hash = "sha1-1ab29597cc9dfefab08e54ea5442e7ffa15f0394" -other = "Video, audio and picture converter" - -[download] -hash = "sha1-fe8f79f29da457de2f6bc9531de6e536e0c426ad" -other = "Download" - -[downloadFFmpegFromSite] -hash = "sha1-0889c95aa3a8659d8d903b4dab7097699c4d8aa4" -other = "Will be downloaded from the site:" - -[downloadRun] -hash = "sha1-55f87f114628fa2d5d8e67d1e1cda22c0e4f9271" -other = "Downloading..." - -[dragAndDropFiles] -hash = "sha1-07bb747cc7590d7a51cdf96dff49a74139097766" -other = "drag and drop files" - -[encoderGroupAudio] -hash = "sha1-24321cb5400df96be8f3e2131918bebdb3a01bba" -other = "Audio" - -[encoderGroupImage] -hash = "sha1-a7e528bc7ac9538aec87d1593c38b80be95d4745" -other = "Images" - -[encoderGroupVideo] -hash = "sha1-8e7b9894c7ef0f57ac0bf910f6a8aac1c8a53683" -other = "Video" - -[encoder_apng] -hash = "sha1-1cbd9abfef96d5614a7e569161b41bd6ad87bbaf" -other = "APNG image" - -[encoder_bmp] -hash = "sha1-e0b9c16b016961a5abdc2217e8ffd1ba7ddebc40" -other = "BMP image" - -[encoder_flv] -hash = "sha1-3602bbf1cc90e48254f81975c7879b5fc0c4d602" -other = "FLV" - -[encoder_gif] -hash = "sha1-d092a779172291b5215aa095390a5b11659128a4" -other = "GIF image" - -[encoder_h264_nvenc] -hash = "sha1-169389f8c4a2518410159c363378ab5c978c32e5" -other = "H.264 with NVIDIA support" - -[encoder_libmp3lame] -hash = "sha1-cd2c8d6f246c8bc18554b7105cb50b78d3cb2b98" -other = "libmp3lame MP3 (MPEG audio layer 3)" - -[encoder_libshine] -hash = "sha1-891d56c85857e5d83ef5a1fe077c1f1540788f49" -other = "libshine MP3 (MPEG audio layer 3)" - -[encoder_libtwolame] -hash = "sha1-b2f53be810b74edc3c454ac75de7ddecfee322ca" -other = "libtwolame MP2 (MPEG audio layer 2)" - -[encoder_libvpx] -hash = "sha1-b85c923aecfb48de0e87e71b6a21bfc2c547c70e" -other = "libvpx VP8 (codec vp8)" - -[encoder_libvpx-vp9] -hash = "sha1-3106417bd89bee87daa691e87614caf78cb934fe" -other = "libvpx VP9 (codec vp9)" - -[encoder_libwebp] -hash = "sha1-1d590d47d46f7880246061fce0e0de6d743db39e" -other = "libwebp WebP image" - -[encoder_libwebp_anim] -hash = "sha1-f141a9c8f23d79c13d44c30d8f34e05b363771ad" -other = "libwebp_anim WebP image" - -[encoder_libx264] -hash = "sha1-6d764ac459c0bf3c819d76618418cdfbb7a749eb" -other = "H.264 libx264" - -[encoder_libx265] -hash = "sha1-55544c166b1e15fd71a58096518e528109599eea" -other = "H.265 libx265" - -[encoder_libxvid] -hash = "sha1-d4bed46d6cdd2bfa8fd1689801164a83ab10c3f5" -other = "libxvidcore MPEG-4 part 2" - -[encoder_mjpeg] -hash = "sha1-94ba63a322b493a04da65e566781fe1cf8bb0d50" -other = "MJPEG (Motion JPEG)" - -[encoder_mp2] -hash = "sha1-a9154b7203349e5d6fbfd67d1ea97715f54b2065" -other = "MP2 (MPEG audio layer 2)" - -[encoder_mp2fixed] -hash = "sha1-dd2ee670d8bc8a60a96a717ebd26f16b5748cf3f" -other = "MP2 fixed point (MPEG audio layer 2)" - -[encoder_mpeg1video] -hash = "sha1-30043660719a3cb19dab5c33450665a8a9cc1c01" -other = "MPEG-1" - -[encoder_mpeg2video] -hash = "sha1-ccb2dcd8510cfdc9d52e5258af1863e5f2c51e77" -other = "MPEG-2" - -[encoder_mpeg4] -hash = "sha1-67fe42f18421b2f6c90fcdc579f9199bfca4b182" -other = "MPEG-4 part 2" - -[encoder_msmpeg4] -hash = "sha1-313ee597e4f0d9bd63a2bc6ac1618f028aef76f4" -other = "MPEG-4 part 2 Microsoft variant version 3" - -[encoder_msmpeg4v2] -hash = "sha1-adc442ce88f2717693b2da3010a1937d77ee522f" -other = "MPEG-4 part 2 Microsoft variant version 2" - -[encoder_msvideo1] -hash = "sha1-00f43ac0dc162bca10e0d98d6b70c0c6a902f66f" -other = "Microsoft Video-1" - -[encoder_png] -hash = "sha1-6715d4b82f5d9dfe3e53e30b402ffa1a6fbf30a5" -other = "PNG image" - -[encoder_qtrle] -hash = "sha1-31bf155cffaf6842ebc54084e4337ca08fdd9848" -other = "QuickTime Animation (RLE) video" - -[encoder_sgi] -hash = "sha1-f4510e237f7fc3c02caa728f9e500f4b069f9c11" -other = "SGI image" - -[encoder_tiff] -hash = "sha1-ed09d78c38e0b17ed695f35740c756dd7340eeac" -other = "TIFF image" - -[encoder_wmav1] -hash = "sha1-cd4a4c5eeac694b6699d55d0f9b477b3b50f18c7" -other = "Windows Media Audio 1" - -[encoder_wmav2] -hash = "sha1-eb2e5306cb33a702577ecfbdca0461862c66c053" -other = "Windows Media Audio 2" - -[encoder_wmv1] -hash = "sha1-f9b748554c590c36a56bcba2cd317196b7bdeddb" -other = "Windows Media Video 7" - -[encoder_wmv2] -hash = "sha1-5b21c87f5c6104797ead60b488b2948428f6b1b7" -other = "Windows Media Video 8" - -[encoder_xbm] -hash = "sha1-2dfc35881da62e9a1379d8238cf7839b24f79566" -other = "XBM (X BitMap) image" - -[error] -hash = "sha1-a7df8f8b5d754f226ac4cb320577fe692b33e483" -other = "An error has occurred!" - -[errorConverter] -hash = "sha1-55ebddceddb8b044e33cc3893ec2eba7bbd9fcf9" -other = "Couldn't convert video" - -[errorDatabase] -hash = "sha1-531abc3f0d12727e542df6e5a22de91098380fc1" -other = "could not create file 'database' in folder 'data'" - -[errorDatabaseTimeout] -hash = "sha1-f8153516ac2442d19be4b6daccce839d204ff09f" -other = "Could not open configuration file.\nMake sure another copy of the program is not running!" - -[errorDragAndDropFile] -hash = "sha1-863cf1ad9c820d5b0c2006ceeaa29e25f81c1714" -other = "Not all files were added" - -[errorFFmpeg] -hash = "sha1-ccf0b95c0d1b392dc215258d917eb4e5d0b88ed0" -other = "this is not FFmpeg" - -[errorFFmpegVersion] -hash = "sha1-9a4148d42186b6b32cf83bef726e23022c53283f" -other = "Could not determine FFmpeg version" - -[errorFFplay] -hash = "sha1-988122112ac6002094e25518cfb5f0d606217298" -other = "this is not FFplay" - -[errorFFplayVersion] -hash = "sha1-cd60928d20d93210e103dd464306ab138bf1b184" -other = "Could not determine FFplay version" - -[errorFFprobe] -hash = "sha1-86d1b0b4c4ccd6a4f71e758fc67ce11aff4ba9b8" -other = "this is not FFprobe" - -[errorFFprobeVersion] -hash = "sha1-da7b37d7df3fafbd153665b13888413d52b24c17" -other = "Failed to determine FFprobe version" - -[errorNoFilesAddedForConversion] -hash = "sha1-5cf1f65bef15cb0382e56be98f44c6abde56a314" -other = "There are no files to convert" - -[errorQueue] -hash = "sha1-72aecd9ad85642d84d62dbbf3cf70953c5f696c7" -other = "Error" - -[errorSelectedEncoder] -hash = "sha1-33ed1aaf4cb3c2ee9d8f8c325b9b75d16ddf9979" -other = "Converter not selected" - -[errorSelectedFolderSave] -hash = "sha1-16f3ef93ee36813fdd79d8fb9bb7fc02acbb94a8" -other = "No save folder selected!" - -[errorSelectedFormat] -hash = "sha1-cda92c56a1ef1aabc92bbfc405ede8ab13087e66" -other = "File extension not selected" - -[exit] -hash = "sha1-c42457057d1ab7950cea00719cbe0b078891775f" -other = "Exit" - -[ffmpegLGPL] -hash = "sha1-d395b16cc8f8eab98a8a970307c5b010ba22dde6" -other = "This software uses libraries from the **FFmpeg** project under the **[LGPLv2.1](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)**." - -[ffmpegTrademark] -hash = "sha1-45f772b2eca5098cd6d31f2d1dc6edec1987a617" -other = "**FFmpeg** is a trademark of **[Fabrice Bellard](http://bellard.org/)**, originator of the **[FFmpeg](https://ffmpeg.org/about.html)** project." - -[fileForConversionTitle] -hash = "sha1-96ac799e1086b31fd8f5f8d4c801829d6c853f08" -other = "File:" - -[fileQueueTitle] -hash = "sha1-aec93b16baeaf55fed871075c9494a460e4a91b8" -other = "Queue" - -[formPreset] -hash = "sha1-7759891ba1ef9f7adc70defc7ac18fbf149c1a68" -other = "Preset" - -[gratitude] -hash = "sha1-51968fc38e53a9a11c861126c62404674fd6096f" -other = "Gratitude" - -[gratitudeText] -hash = "sha1-cb343e4d39ca31e6da6f72b9394cc915cb7d1258" -other = "I sincerely thank you for your invaluable\n\r and timely assistance:" - -[help] -hash = "sha1-6a45cef900c668effcb2ab10da05855c1fd10f6f" -other = "Help" - -[helpFFplay] -hash = "sha1-ecc294b8b3d217ee1c2d63dc2f0253c3d1b3712c" -other = "FFplay Player Keys" - -[helpFFplayActivateFrameStepMode] -hash = "sha1-f47ede90932d69465f6197cb2a7cc4d1e3ab150e" -other = "Activate frame-by-frame mode." - -[helpFFplayCycleVideoFiltersOrShowModes] -hash = "sha1-83bb702c777e4768cdc326a668d541c23ab759b7" -other = "A cycle of video filters or display modes." - -[helpFFplayDecreaseVolume] -hash = "sha1-de28db96a9c22be885ec5067a13f8f17fd3954bc" -other = "Decrease the volume." - -[helpFFplayDescription] -hash = "sha1-f5441f6aee76222c4120066575e80c2d177ac3c0" -other = "Description" - -[helpFFplayDoubleClickLeftMouseButton] -hash = "sha1-2657aa576055769952dfcde570fc9b4765d594ad" -other = "double click\nleft mouse button" - -[helpFFplayIncreaseVolume] -hash = "sha1-8ba7bde2d9a80f4a7cd122cf4973975698d3bd34" -other = "Increase the volume." - -[helpFFplayKeyDown] -hash = "sha1-c5aefd2f8c6908a69b08fe4a2d235b1ae0113470" -other = "down" - -[helpFFplayKeyHoldS] -hash = "sha1-89c5dd8287c15b3f40db66e06b038c34a715f02f" -other = "hold S" - -[helpFFplayKeyLeft] -hash = "sha1-feb671890703fb0300a436744d34018bbc7ba13a" -other = "left" - -[helpFFplayKeyRight] -hash = "sha1-a4f025d4bf7f90ee5bec6c48b2710bc9c5bbb267" -other = "right" - -[helpFFplayKeySpace] -hash = "sha1-a367ad00358ec44edc1d54a96df6f9114b0f8697" -other = "SPACE" - -[helpFFplayKeyUp] -hash = "sha1-e4845aa8c0e100a80eaf65446c59085236fd2098" -other = "up" - -[helpFFplayKeys] -hash = "sha1-0ad272ade8c568f394499f1492ecfab56e701e5d" -other = "Keys" - -[helpFFplayPause] -hash = "sha1-e83e107900fde0c39295f599c2cf8fba8d8cb604" -other = "Pause or continue playing." - -[helpFFplayQuit] -hash = "sha1-70785a2fd5d5a6519b7439f0d8cfcd7d54c5771d" -other = "Close the player." - -[helpFFplaySeekBForward10Minutes] -hash = "sha1-58ed63343376240f2596e447b5245c1805f35234" -other = "Fast forward 10 minutes." - -[helpFFplaySeekBForward1Minute] -hash = "sha1-3fe46b8d5413b7fdc53ae9ed9427bcb1769ec74c" -other = "Fast forward 1 minute." - -[helpFFplaySeekBackward10Minutes] -hash = "sha1-927dffe9af72ffd40f46873b452a4c90627bccf8" -other = "Rewind 10 minutes." - -[helpFFplaySeekBackward10Seconds] -hash = "sha1-e97615ecec0f8cf5647e8802bdda38dc2b0d809f" -other = "Rewind 10 seconds." - -[helpFFplaySeekBackward1Minute] -hash = "sha1-5b19e280a0850122c8ebc80c622491bb09520e1a" -other = "Rewind 1 minute." - -[helpFFplaySeekForward10Seconds] -hash = "sha1-8d840251d4a1668edaea3515df197a8a79031ec3" -other = "Fast forward 10 seconds." - -[helpFFplayToggleFullScreen] -hash = "sha1-d32df02849258c5b02f15e5711f54ee6a8a75fd4" -other = "Switch to full screen or exit full screen." - -[helpFFplayToggleMute] -hash = "sha1-4bdbb124fe8de3a8037c1e74719e9600b21b25ab" -other = "Mute or unmute." - -[inProgressQueue] -hash = "sha1-eff79c40e2100ae5fadf3a7d99336025edcca8b5" -other = "In Progress" - -[languageSelectionFormHead] -hash = "sha1-0ff5fa82cf684112660128cba1711297acf11003" -other = "Switch language" - -[languageSelectionHead] -hash = "sha1-daf1108fc10d3b1a908288d611f749b3cc651e4b" -other = "Choose language" - -[licenseLink] -hash = "sha1-ea18ab849f0eea030d770da82c2a6b3484a7bd13" -other = "License information" - -[licenseLinkOther] -hash = "sha1-359fff328717c05104e51a2d29f05bf1875d26b7" -other = "Licenses from other products used in the program" - -[menuSettingsLanguage] -hash = "sha1-ed3f0e507a5b4ed0649d7c768fe0d47413d839ba" -other = "Language" - -[menuSettingsTheme] -hash = "sha1-553c45f1b84a92b08dc1f088c13f924cde95765e" -other = "Theme" - -[or] -hash = "sha1-30bb0333ca1583110e4ced513b5d2455b86f529b" -other = "or" - -[parameterCheckbox] -hash = "sha1-9e35221d454870996fd51d576249cf47d1784a3c" -other = "Enable option" - -[pathToFfmpeg] -hash = "sha1-fafc50f1db0f720fe83a96cd70a9e1ad824e96b6" -other = "Path to FFmpeg:" - -[pathToFfplay] -hash = "sha1-5389830dd75a63aa8a5e41e8f07c5fadd8385398" -other = "Path to FFplay:" - -[pathToFfprobe] -hash = "sha1-b872edc9633a2e81ef678dc46fe46a7e91732024" -other = "Path to FFprobe:" - -[preset_fast] -hash = "sha1-935e1ac9d3c8ba4478326c909ba66662acb0540e" -other = "fast (slower than \"faster\", but the file will weigh less)" - -[preset_faster] -hash = "sha1-98620b73c896440c39ea6ec4b9b19d41301c9a7e" -other = "faster (slower than \"veryfast\", but the file will weigh less)" - -[preset_medium] -hash = "sha1-f7d1c30135c22c2f07c247075c0df103bb3c3ea5" -other = "medium (slower than \"fast\", but the file will weigh less)" - -[preset_placebo] -hash = "sha1-7bcff099104bb192881139e6404981bd426b3f91" -other = "placebo (not recommended)" - -[preset_slow] -hash = "sha1-681bf587275a45b48af49bb2ad8f0947919530e7" -other = "slow (slower than \"medium\", but the file will weigh less)" - -[preset_slower] -hash = "sha1-d1c692ee2b7643ae2c71a48bea880327a3c6b1e3" -other = "slower (slower than \"slow\", but the file will weigh less)" - -[preset_superfast] -hash = "sha1-41c39959e8f1547cc9259a5b459c4ccbf368cc23" -other = "superfast (slower than \"ultrafast\", but the file will weigh less)" - -[preset_ultrafast] -hash = "sha1-dfed981573ac2046832f9a9450bc9388958753fa" -other = "ultrafast (fast, but the file will weigh a lot)" - -[preset_veryfast] -hash = "sha1-370b82509887d02d7a2ef9b110df4616b16123ce" -other = "veryfast (slower than \"superfast\", but the file will weigh less)" - -[preset_veryslow] -hash = "sha1-d428bfa6deea9dd5c7c1f80ceba24e123ae96d0d" -other = "veryslow (slower than \"slower\", but the file will weigh less)" - -[programmLink] -hash = "sha1-18f9a3fad6aacefe1b05eed23122800b391ff5ca" -other = "Project website" - -[programmVersion] -hash = "sha1-fa2e4994a301bb24bc2a8fa166e5486ea95a7475" -other = "**Program version:** {{.Version}}" - -[queue] -hash = "sha1-aec93b16baeaf55fed871075c9494a460e4a91b8" -other = "Queue" - -[save] -hash = "sha1-4864057d626a868fa60f999bed3191d61d045ddc" -other = "Save" - -[selectEncoder] -hash = "sha1-88f3670b09758a3336057520a215058d61006abd" -other = "Encoder:" - -[selectFFPathTitle] -hash = "sha1-95581446a28d968ff1a027c623159a7eb08654cf" -other = "Specify the path to FFmpeg and FFprobe" - -[selectFormat] -hash = "sha1-f3809b0b48886570cd4cf1d7099de6da5b6d4524" -other = "File extension:" - -[settings] -hash = "sha1-7f17c7c62a7fd8d1a508481f4778688927734c2f" -other = "Settings" - -[testFF] -hash = "sha1-f5b8ed88e9609963035d2235be0a79bbec619976" -other = "Checking FFmpeg for serviceability..." - -[themesNameDark] -hash = "sha1-bd16b234708a2515a9f2d0ca41fb11e7fe8a38a2" -other = "Dark" - -[themesNameDefault] -hash = "sha1-469631cb165dcbbfea9e747056c25fbccb28c481" -other = "Default" - -[themesNameLight] -hash = "sha1-8080010c5e7d7edf56e89a99d8a2422898417845" -other = "Light" - -[titleDownloadLink] -hash = "sha1-92df86371f6c3a06ca1e4754f113142776a32d49" -other = "You can download it from here" - -[total] -hash = "sha1-3b5143902e0c5c84459aedf918e17604d9735b94" -other = "Total" - -[unzipRun] -hash = "sha1-c554dad13026668a1f6adf3171837c5d51bbac36" -other = "Unpacked..." - -[waitingQueue] -hash = "sha1-307429dd84150877080c4bbff2b340d1e7dadff2" -other = "Waiting" diff --git a/languages/active.kk.toml b/languages/active.kk.toml deleted file mode 100644 index 2074652..0000000 --- a/languages/active.kk.toml +++ /dev/null @@ -1,571 +0,0 @@ -[AlsoUsedProgram] -hash = "sha1-a72be72e7808bb8a0144ed7a93acb29c568b1ed4" -other = "Бағдарлама сонымен қатар пайдаланады:" - -[about] -hash = "sha1-3da0b9ef719fd707f443ac00404447f29445976f" -other = "Бағдарлама туралы" - -[aboutText] -hash = "sha1-8bd565814118ba8b90c40eb5b62acf8d2676e7d6" -other = "FFmpeg консоль утилитасы үшін қарапайым интерфейс. \nБірақ мен FFmpeg утилитасының авторы емеспін." - -[addedFilesTitle] -hash = "sha1-8ba0f6e477b0d78df2cc06f1d8b41b888623b851" -other = "Қосылған файлдар" - -[autoClearAfterAddingToQueue] -hash = "sha1-b3781695a4c35380d2cd075bb52f27a2a6d8f19c" -other = "Кезекке қосқаннан кейін тазалаңыз" - -[buttonDownloadFFmpeg] -hash = "sha1-c223c2e15171156192bc3146aee91e6094bb475b" -other = "FFmpeg автоматты түрде жүктеп алыңыз" - -[buttonForSelectedDirTitle] -hash = "sha1-8cbe5c67bcf89e4624635a79cbea104227faedda" -other = "Қалтаға сақтаңыз:" - -[cancel] -hash = "sha1-0ec753be8df955a117404fb634b01b45eb386e2a" -other = "Болдырмау" - -[changeFFPath] -hash = "sha1-1f704de0560f8135eb6924cd232ed919ca2e5af0" -other = "FFmpeg, FFprobe және FFplay" - -[changeLanguage] -hash = "sha1-8b276eaf378d485c769fb3d5dcc06dfc25b0c01b" -other = "Тілді өзгерту" - -[checkboxOverwriteOutputFilesTitle] -hash = "sha1-5860124bb781e7ef680f573fa93977e96328d4e7" -other = "Файлды қайта жазуға рұқсат беріңіз" - -[choose] -hash = "sha1-f60bb5f761024d973834b5e9d25ceebce2c85f94" -other = "таңдау" - -[clearAll] -hash = "sha1-f32702d79ac206432400ac6b041695d020f6fa77" -other = "Тізімді өшіру" - -[completedQueue] -hash = "sha1-398c7d4f7b0d522afb930769c0fbb1a9f4b61fbe" -other = "Дайын" - -[converterVideoFilesSubmitTitle] -hash = "sha1-7ac460f3c24c9952082f2db6e4d62f752598709c" -other = "Файлды түрлендіру" - -[converterVideoFilesTitle] -hash = "sha1-1ab29597cc9dfefab08e54ea5442e7ffa15f0394" -other = "Бейне, аудио және суретті түрлендіргіш" - -[download] -hash = "sha1-fe8f79f29da457de2f6bc9531de6e536e0c426ad" -other = "Жүктеп алу" - -[downloadFFmpegFromSite] -hash = "sha1-0889c95aa3a8659d8d903b4dab7097699c4d8aa4" -other = "Сайттан жүктеледі:" - -[downloadRun] -hash = "sha1-55f87f114628fa2d5d8e67d1e1cda22c0e4f9271" -other = "Жүктеп алынуда..." - -[dragAndDropFiles] -hash = "sha1-07bb747cc7590d7a51cdf96dff49a74139097766" -other = "файлдарды сүйреп апарыңыз" - -[encoderGroupAudio] -hash = "sha1-24321cb5400df96be8f3e2131918bebdb3a01bba" -other = "Аудио" - -[encoderGroupImage] -hash = "sha1-a7e528bc7ac9538aec87d1593c38b80be95d4745" -other = "Суреттер" - -[encoderGroupVideo] -hash = "sha1-8e7b9894c7ef0f57ac0bf910f6a8aac1c8a53683" -other = "Бейне" - -[encoder_apng] -hash = "sha1-1cbd9abfef96d5614a7e569161b41bd6ad87bbaf" -other = "APNG image" - -[encoder_bmp] -hash = "sha1-e0b9c16b016961a5abdc2217e8ffd1ba7ddebc40" -other = "BMP image" - -[encoder_flv] -hash = "sha1-3602bbf1cc90e48254f81975c7879b5fc0c4d602" -other = "FLV" - -[encoder_gif] -hash = "sha1-d092a779172291b5215aa095390a5b11659128a4" -other = "GIF image" - -[encoder_h264_nvenc] -hash = "sha1-169389f8c4a2518410159c363378ab5c978c32e5" -other = "NVIDIA қолдауымен H.264" - -[encoder_libmp3lame] -hash = "sha1-cd2c8d6f246c8bc18554b7105cb50b78d3cb2b98" -other = "libmp3lame MP3 (MPEG audio layer 3)" - -[encoder_libshine] -hash = "sha1-891d56c85857e5d83ef5a1fe077c1f1540788f49" -other = "libshine MP3 (MPEG audio layer 3)" - -[encoder_libtwolame] -hash = "sha1-b2f53be810b74edc3c454ac75de7ddecfee322ca" -other = "libtwolame MP2 (MPEG audio layer 2)" - -[encoder_libvpx] -hash = "sha1-b85c923aecfb48de0e87e71b6a21bfc2c547c70e" -other = "libvpx VP8 (codec vp8)" - -[encoder_libvpx-vp9] -hash = "sha1-3106417bd89bee87daa691e87614caf78cb934fe" -other = "libvpx VP9 (codec vp9)" - -[encoder_libwebp] -hash = "sha1-1d590d47d46f7880246061fce0e0de6d743db39e" -other = "libwebp WebP image" - -[encoder_libwebp_anim] -hash = "sha1-f141a9c8f23d79c13d44c30d8f34e05b363771ad" -other = "libwebp_anim WebP image" - -[encoder_libx264] -hash = "sha1-6d764ac459c0bf3c819d76618418cdfbb7a749eb" -other = "H.264 libx264" - -[encoder_libx265] -hash = "sha1-55544c166b1e15fd71a58096518e528109599eea" -other = "H.265 libx265" - -[encoder_libxvid] -hash = "sha1-d4bed46d6cdd2bfa8fd1689801164a83ab10c3f5" -other = "libxvidcore MPEG-4 part 2" - -[encoder_mjpeg] -hash = "sha1-94ba63a322b493a04da65e566781fe1cf8bb0d50" -other = "MJPEG (Motion JPEG)" - -[encoder_mp2] -hash = "sha1-a9154b7203349e5d6fbfd67d1ea97715f54b2065" -other = "MP2 (MPEG audio layer 2)" - -[encoder_mp2fixed] -hash = "sha1-dd2ee670d8bc8a60a96a717ebd26f16b5748cf3f" -other = "MP2 fixed point (MPEG audio layer 2)" - -[encoder_mpeg1video] -hash = "sha1-30043660719a3cb19dab5c33450665a8a9cc1c01" -other = "MPEG-1" - -[encoder_mpeg2video] -hash = "sha1-ccb2dcd8510cfdc9d52e5258af1863e5f2c51e77" -other = "MPEG-2" - -[encoder_mpeg4] -hash = "sha1-67fe42f18421b2f6c90fcdc579f9199bfca4b182" -other = "MPEG-4 part 2" - -[encoder_msmpeg4] -hash = "sha1-313ee597e4f0d9bd63a2bc6ac1618f028aef76f4" -other = "MPEG-4 part 2 Microsoft variant version 3" - -[encoder_msmpeg4v2] -hash = "sha1-adc442ce88f2717693b2da3010a1937d77ee522f" -other = "MPEG-4 part 2 Microsoft variant version 2" - -[encoder_msvideo1] -hash = "sha1-00f43ac0dc162bca10e0d98d6b70c0c6a902f66f" -other = "Microsoft Video-1" - -[encoder_png] -hash = "sha1-6715d4b82f5d9dfe3e53e30b402ffa1a6fbf30a5" -other = "PNG image" - -[encoder_qtrle] -hash = "sha1-31bf155cffaf6842ebc54084e4337ca08fdd9848" -other = "QuickTime Animation (RLE) video" - -[encoder_sgi] -hash = "sha1-f4510e237f7fc3c02caa728f9e500f4b069f9c11" -other = "SGI image" - -[encoder_tiff] -hash = "sha1-ed09d78c38e0b17ed695f35740c756dd7340eeac" -other = "TIFF image" - -[encoder_wmav1] -hash = "sha1-cd4a4c5eeac694b6699d55d0f9b477b3b50f18c7" -other = "Windows Media Audio 1" - -[encoder_wmav2] -hash = "sha1-eb2e5306cb33a702577ecfbdca0461862c66c053" -other = "Windows Media Audio 2" - -[encoder_wmv1] -hash = "sha1-f9b748554c590c36a56bcba2cd317196b7bdeddb" -other = "Windows Media Video 7" - -[encoder_wmv2] -hash = "sha1-5b21c87f5c6104797ead60b488b2948428f6b1b7" -other = "Windows Media Video 8" - -[encoder_xbm] -hash = "sha1-2dfc35881da62e9a1379d8238cf7839b24f79566" -other = "XBM (X BitMap) image" - -[error] -hash = "sha1-a7df8f8b5d754f226ac4cb320577fe692b33e483" -other = "Қате орын алды!" - -[errorConverter] -hash = "sha1-55ebddceddb8b044e33cc3893ec2eba7bbd9fcf9" -other = "Бейнені түрлендіру мүмкін болмады" - -[errorDatabase] -hash = "sha1-531abc3f0d12727e542df6e5a22de91098380fc1" -other = "'data' қалтасында 'database' файлын жасау мүмкін болмады" - -[errorDatabaseTimeout] -hash = "sha1-f8153516ac2442d19be4b6daccce839d204ff09f" -other = "Конфигурация файлын аша алмады.\nБағдарламаның басқа көшірмесі іске қосылмағанына көз жеткізіңіз!" - -[errorDragAndDropFile] -hash = "sha1-863cf1ad9c820d5b0c2006ceeaa29e25f81c1714" -other = "Барлық файлдар қосылмаған" - -[errorFFmpeg] -hash = "sha1-ccf0b95c0d1b392dc215258d917eb4e5d0b88ed0" -other = "бұл FFmpeg емес" - -[errorFFmpegVersion] -hash = "sha1-9a4148d42186b6b32cf83bef726e23022c53283f" -other = "FFmpeg нұсқасын анықтау мүмкін болмады" - -[errorFFplay] -hash = "sha1-988122112ac6002094e25518cfb5f0d606217298" -other = "бұл FFplay емес" - -[errorFFplayVersion] -hash = "sha1-cd60928d20d93210e103dd464306ab138bf1b184" -other = "FFplay нұсқасын анықтау мүмкін болмады" - -[errorFFprobe] -hash = "sha1-86d1b0b4c4ccd6a4f71e758fc67ce11aff4ba9b8" -other = "бұл FFprobe емес" - -[errorFFprobeVersion] -hash = "sha1-da7b37d7df3fafbd153665b13888413d52b24c17" -other = "FFprobe нұсқасын анықтау мүмкін болмады" - -[errorNoFilesAddedForConversion] -hash = "sha1-5cf1f65bef15cb0382e56be98f44c6abde56a314" -other = "Түрлендіруге арналған файлдар жоқ" - -[errorQueue] -hash = "sha1-72aecd9ad85642d84d62dbbf3cf70953c5f696c7" -other = "Қате" - -[errorSelectedEncoder] -hash = "sha1-33ed1aaf4cb3c2ee9d8f8c325b9b75d16ddf9979" -other = "Түрлендіргіш таңдалмаған" - -[errorSelectedFolderSave] -hash = "sha1-16f3ef93ee36813fdd79d8fb9bb7fc02acbb94a8" -other = "Сақтау қалтасы таңдалмаған!" - -[errorSelectedFormat] -hash = "sha1-cda92c56a1ef1aabc92bbfc405ede8ab13087e66" -other = "Файл кеңейтімі таңдалмаған" - -[exit] -hash = "sha1-c42457057d1ab7950cea00719cbe0b078891775f" -other = "Шығу" - -[ffmpegLGPL] -hash = "sha1-d395b16cc8f8eab98a8a970307c5b010ba22dde6" -other = "Бұл бағдарламалық құрал **[LGPLv2.1](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)** астында **FFmpeg** жобасының кітапханаларын пайдаланады." - -[ffmpegTrademark] -hash = "sha1-45f772b2eca5098cd6d31f2d1dc6edec1987a617" -other = "FFmpeg — **[FFmpeg](https://ffmpeg.org/about.html)** жобасын жасаушы **[Fabrice Bellard](http://bellard.org/)** сауда белгісі." - -[fileForConversionTitle] -hash = "sha1-96ac799e1086b31fd8f5f8d4c801829d6c853f08" -other = "Файл:" - -[fileQueueTitle] -hash = "sha1-aec93b16baeaf55fed871075c9494a460e4a91b8" -other = "Кезек" - -[formPreset] -hash = "sha1-7759891ba1ef9f7adc70defc7ac18fbf149c1a68" -other = "Алдын ала орнатылған" - -[gratitude] -hash = "sha1-51968fc38e53a9a11c861126c62404674fd6096f" -other = "Алғыс" - -[gratitudeText] -hash = "sha1-cb343e4d39ca31e6da6f72b9394cc915cb7d1258" -other = "Сізге баға жетпес және уақтылы көмектескеніңіз\n\r үшін шын жүректен алғыс айтамын:" - -[help] -hash = "sha1-6a45cef900c668effcb2ab10da05855c1fd10f6f" -other = "Анықтама" - -[helpFFplay] -hash = "sha1-ecc294b8b3d217ee1c2d63dc2f0253c3d1b3712c" -other = "FFplay ойнатқышының пернелері" - -[helpFFplayActivateFrameStepMode] -hash = "sha1-f47ede90932d69465f6197cb2a7cc4d1e3ab150e" -other = "Уақыт аралығын іске қосыңыз." - -[helpFFplayCycleVideoFiltersOrShowModes] -hash = "sha1-83bb702c777e4768cdc326a668d541c23ab759b7" -other = "Бейне сүзгілерінің немесе дисплей режимдерінің циклі." - -[helpFFplayDecreaseVolume] -hash = "sha1-de28db96a9c22be885ec5067a13f8f17fd3954bc" -other = "Дыбыс деңгейін төмендетіңіз." - -[helpFFplayDescription] -hash = "sha1-f5441f6aee76222c4120066575e80c2d177ac3c0" -other = "Сипаттама" - -[helpFFplayDoubleClickLeftMouseButton] -hash = "sha1-2657aa576055769952dfcde570fc9b4765d594ad" -other = "тінтуірдің сол жақ\nбатырмасын екі рет басу" - -[helpFFplayIncreaseVolume] -hash = "sha1-8ba7bde2d9a80f4a7cd122cf4973975698d3bd34" -other = "Дыбыс деңгейін арттыру." - -[helpFFplayKeyDown] -hash = "sha1-c5aefd2f8c6908a69b08fe4a2d235b1ae0113470" -other = "төмен" - -[helpFFplayKeyHoldS] -hash = "sha1-89c5dd8287c15b3f40db66e06b038c34a715f02f" -other = "ұстау S" - -[helpFFplayKeyLeft] -hash = "sha1-feb671890703fb0300a436744d34018bbc7ba13a" -other = "сол" - -[helpFFplayKeyRight] -hash = "sha1-a4f025d4bf7f90ee5bec6c48b2710bc9c5bbb267" -other = "құқық" - -[helpFFplayKeySpace] -hash = "sha1-a367ad00358ec44edc1d54a96df6f9114b0f8697" -other = "SPACE (пробел)" - -[helpFFplayKeyUp] -hash = "sha1-e4845aa8c0e100a80eaf65446c59085236fd2098" -other = "жоғары" - -[helpFFplayKeys] -hash = "sha1-0ad272ade8c568f394499f1492ecfab56e701e5d" -other = "Кілттер" - -[helpFFplayPause] -hash = "sha1-e83e107900fde0c39295f599c2cf8fba8d8cb604" -other = "Кідіртіңіз немесе жоғалтуды жалғастырыңыз." - -[helpFFplayQuit] -hash = "sha1-70785a2fd5d5a6519b7439f0d8cfcd7d54c5771d" -other = "Ойнатқышты жабыңыз." - -[helpFFplaySeekBForward10Minutes] -hash = "sha1-58ed63343376240f2596e447b5245c1805f35234" -other = "10 минутқа алға айналдырыңыз." - -[helpFFplaySeekBForward1Minute] -hash = "sha1-3fe46b8d5413b7fdc53ae9ed9427bcb1769ec74c" -other = "1 минутқа алға айналдырыңыз." - -[helpFFplaySeekBackward10Minutes] -hash = "sha1-927dffe9af72ffd40f46873b452a4c90627bccf8" -other = "10 минутқа артқа айналдырыңыз." - -[helpFFplaySeekBackward10Seconds] -hash = "sha1-e97615ecec0f8cf5647e8802bdda38dc2b0d809f" -other = "10 секундқа артқа айналдырыңыз." - -[helpFFplaySeekBackward1Minute] -hash = "sha1-5b19e280a0850122c8ebc80c622491bb09520e1a" -other = "1 минутқа артқа айналдырыңыз." - -[helpFFplaySeekForward10Seconds] -hash = "sha1-8d840251d4a1668edaea3515df197a8a79031ec3" -other = "10 секунд алға айналдырыңыз." - -[helpFFplayToggleFullScreen] -hash = "sha1-d32df02849258c5b02f15e5711f54ee6a8a75fd4" -other = "Толық экранға ауысу немесе толық экраннан шығу." - -[helpFFplayToggleMute] -hash = "sha1-4bdbb124fe8de3a8037c1e74719e9600b21b25ab" -other = "Дыбысты өшіріңіз немесе дыбысты қосыңыз." - -[inProgressQueue] -hash = "sha1-eff79c40e2100ae5fadf3a7d99336025edcca8b5" -other = "Орындалуда" - -[languageSelectionFormHead] -hash = "sha1-0ff5fa82cf684112660128cba1711297acf11003" -other = "Тілді ауыстыру" - -[languageSelectionHead] -hash = "sha1-daf1108fc10d3b1a908288d611f749b3cc651e4b" -other = "Тілді таңдаңыз" - -[licenseLink] -hash = "sha1-ea18ab849f0eea030d770da82c2a6b3484a7bd13" -other = "Лицензия туралы ақпарат" - -[licenseLinkOther] -hash = "sha1-359fff328717c05104e51a2d29f05bf1875d26b7" -other = "Бағдарламада пайдаланылатын басқа өнімдердің лицензиялары" - -[menuSettingsLanguage] -hash = "sha1-ed3f0e507a5b4ed0649d7c768fe0d47413d839ba" -other = "Тіл" - -[menuSettingsTheme] -hash = "sha1-553c45f1b84a92b08dc1f088c13f924cde95765e" -other = "Тақырып" - -[or] -hash = "sha1-30bb0333ca1583110e4ced513b5d2455b86f529b" -other = "немесе" - -[parameterCheckbox] -hash = "sha1-9e35221d454870996fd51d576249cf47d1784a3c" -other = "Опцияны қосу" - -[pathToFfmpeg] -hash = "sha1-fafc50f1db0f720fe83a96cd70a9e1ad824e96b6" -other = "FFmpeg жол:" - -[pathToFfplay] -hash = "sha1-5389830dd75a63aa8a5e41e8f07c5fadd8385398" -other = "FFplay жол:" - -[pathToFfprobe] -hash = "sha1-b872edc9633a2e81ef678dc46fe46a7e91732024" -other = "FFprobe жол:" - -[preset_fast] -hash = "sha1-935e1ac9d3c8ba4478326c909ba66662acb0540e" -other = "fast («faster» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)" - -[preset_faster] -hash = "sha1-98620b73c896440c39ea6ec4b9b19d41301c9a7e" -other = "faster («veryfast» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)" - -[preset_medium] -hash = "sha1-f7d1c30135c22c2f07c247075c0df103bb3c3ea5" -other = "medium («fast» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)" - -[preset_placebo] -hash = "sha1-7bcff099104bb192881139e6404981bd426b3f91" -other = "placebo (ұсынылмайды)" - -[preset_slow] -hash = "sha1-681bf587275a45b48af49bb2ad8f0947919530e7" -other = "slow («medium» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)" - -[preset_slower] -hash = "sha1-d1c692ee2b7643ae2c71a48bea880327a3c6b1e3" -other = "slower («slow» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)" - -[preset_superfast] -hash = "sha1-41c39959e8f1547cc9259a5b459c4ccbf368cc23" -other = "superfast («ultrafast» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)" - -[preset_ultrafast] -hash = "sha1-dfed981573ac2046832f9a9450bc9388958753fa" -other = "ultrafast (жылдам, бірақ файлдың салмағы көп болады)" - -[preset_veryfast] -hash = "sha1-370b82509887d02d7a2ef9b110df4616b16123ce" -other = "veryfast («superfast» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)" - -[preset_veryslow] -hash = "sha1-d428bfa6deea9dd5c7c1f80ceba24e123ae96d0d" -other = "veryslow («slower» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)" - -[programmLink] -hash = "sha1-18f9a3fad6aacefe1b05eed23122800b391ff5ca" -other = "Жобаның веб-сайты" - -[programmVersion] -hash = "sha1-fa2e4994a301bb24bc2a8fa166e5486ea95a7475" -other = "**Бағдарлама нұсқасы:** {{.Version}}" - -[queue] -hash = "sha1-aec93b16baeaf55fed871075c9494a460e4a91b8" -other = "Кезек" - -[save] -hash = "sha1-4864057d626a868fa60f999bed3191d61d045ddc" -other = "Сақтау" - -[selectEncoder] -hash = "sha1-88f3670b09758a3336057520a215058d61006abd" -other = "Кодировщик:" - -[selectFFPathTitle] -hash = "sha1-95581446a28d968ff1a027c623159a7eb08654cf" -other = "FFmpeg және FFprobe жолын көрсетіңіз" - -[selectFormat] -hash = "sha1-f3809b0b48886570cd4cf1d7099de6da5b6d4524" -other = "Файл кеңейтімі:" - -[settings] -hash = "sha1-7f17c7c62a7fd8d1a508481f4778688927734c2f" -other = "Параметрлер" - -[testFF] -hash = "sha1-f5b8ed88e9609963035d2235be0a79bbec619976" -other = "FFmpeg функционалдығы тексерілуде..." - -[themesNameDark] -hash = "sha1-bd16b234708a2515a9f2d0ca41fb11e7fe8a38a2" -other = "Қараңғы тақырып" - -[themesNameDefault] -hash = "sha1-469631cb165dcbbfea9e747056c25fbccb28c481" -other = "Әдепкі бойынша" - -[themesNameLight] -hash = "sha1-8080010c5e7d7edf56e89a99d8a2422898417845" -other = "Жеңіл тақырып" - -[titleDownloadLink] -hash = "sha1-92df86371f6c3a06ca1e4754f113142776a32d49" -other = "Сіз оны осы жерден жүктей аласыз" - -[total] -hash = "sha1-3b5143902e0c5c84459aedf918e17604d9735b94" -other = "Барлығы" - -[unzipRun] -hash = "sha1-c554dad13026668a1f6adf3171837c5d51bbac36" -other = "Орамнан шығарылуда..." - -[waitingQueue] -hash = "sha1-307429dd84150877080c4bbff2b340d1e7dadff2" -other = "Күту" diff --git a/languages/active.ru.toml b/languages/active.ru.toml deleted file mode 100644 index 2383c00..0000000 --- a/languages/active.ru.toml +++ /dev/null @@ -1,143 +0,0 @@ -AlsoUsedProgram = "Также в программе используется:" -about = "О программе" -aboutText = "Простенький интерфейс для консольной утилиты FFmpeg. \nНо я не являюсь автором самой утилиты FFmpeg." -addedFilesTitle = "Добавленные файлы" -autoClearAfterAddingToQueue = "Очищать после добавления в очередь" -buttonDownloadFFmpeg = "Скачать автоматически FFmpeg" -buttonForSelectedDirTitle = "Сохранить в папку:" -cancel = "Отмена" -changeFFPath = "FFmpeg, FFprobe и FFplay" -changeLanguage = "Поменять язык" -checkboxOverwriteOutputFilesTitle = "Разрешить перезаписать файл" -choose = "выбрать" -clearAll = "Очистить список" -completedQueue = "Готово" -converterVideoFilesSubmitTitle = "Конвертировать" -converterVideoFilesTitle = "Конвертер видео, аудио и картинок" -download = "Скачать" -downloadFFmpegFromSite = "Будет скачано с сайта:" -downloadRun = "Скачивается..." -dragAndDropFiles = "перетащить файлы" -encoderGroupAudio = "Аудио" -encoderGroupImage = "Картинки" -encoderGroupVideo = "Видео" -encoder_apng = "APNG image" -encoder_bmp = "BMP image" -encoder_flv = "FLV" -encoder_gif = "GIF image" -encoder_h264_nvenc = "H.264 с поддержкой NVIDIA" -encoder_libmp3lame = "libmp3lame MP3 (MPEG audio layer 3)" -encoder_libshine = "libshine MP3 (MPEG audio layer 3)" -encoder_libtwolame = "libtwolame MP2 (MPEG audio layer 2)" -encoder_libvpx = "libvpx VP8 (codec vp8)" -encoder_libvpx-vp9 = "libvpx VP9 (codec vp9)" -encoder_libwebp = "libwebp WebP image" -encoder_libwebp_anim = "libwebp_anim WebP image" -encoder_libx264 = "H.264 libx264" -encoder_libx265 = "H.265 libx265" -encoder_libxvid = "libxvidcore MPEG-4 part 2" -encoder_mjpeg = "MJPEG (Motion JPEG)" -encoder_mp2 = "MP2 (MPEG audio layer 2)" -encoder_mp2fixed = "MP2 fixed point (MPEG audio layer 2)" -encoder_mpeg1video = "MPEG-1" -encoder_mpeg2video = "MPEG-2" -encoder_mpeg4 = "MPEG-4 part 2" -encoder_msmpeg4 = "MPEG-4 part 2 Microsoft variant version 3" -encoder_msmpeg4v2 = "MPEG-4 part 2 Microsoft variant version 2" -encoder_msvideo1 = "Microsoft Video-1" -encoder_png = "PNG image" -encoder_qtrle = "QuickTime Animation (RLE) video" -encoder_sgi = "SGI image" -encoder_tiff = "TIFF image" -encoder_wmav1 = "Windows Media Audio 1" -encoder_wmav2 = "Windows Media Audio 2" -encoder_wmv1 = "Windows Media Video 7" -encoder_wmv2 = "Windows Media Video 8" -encoder_xbm = "XBM (X BitMap) image" -error = "Произошла ошибка!" -errorConverter = "не смогли отконвертировать видео" -errorDatabase = "не смогли создать файл 'database' в папке 'data'" -errorDatabaseTimeout = "Не смогли открыть файл конфигурации.\nУбедитесь, что другая копия программы не запущена!" -errorDragAndDropFile = "Не все файлы добавились" -errorFFmpeg = "это не FFmpeg" -errorFFmpegVersion = "Не смогли определить версию FFmpeg" -errorFFplay = "это не FFplay" -errorFFplayVersion = "Не смогли определить версию FFplay" -errorFFprobe = "это не FFprobe" -errorFFprobeVersion = "Не смогли определить версию FFprobe" -errorNoFilesAddedForConversion = "Нет файлов для конвертации" -errorQueue = "Ошибка" -errorSelectedEncoder = "Конвертер не выбран" -errorSelectedFolderSave = "Папка для сохранения не выбрана!" -errorSelectedFormat = "Расширение файла не выбрана" -exit = "Выход" -ffmpegLGPL = "Это программное обеспечение использует библиотеки из проекта **FFmpeg** под **[LGPLv2.1](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)**." -ffmpegTrademark = "**FFmpeg** — торговая марка **[Fabrice Bellard](http://bellard.org/)** , создателя проекта **[FFmpeg](https://ffmpeg.org/about.html)**." -fileForConversionTitle = "Файл:" -fileQueueTitle = "Очередь" -formPreset = "Предустановка" -gratitude = "Благодарность" -gratitudeText = "Я искренне благодарю вас за неоценимую\n\rи своевременную помощь:" -help = "Справка" -helpFFplay = "Клавиши проигрывателя FFplay" -helpFFplayActivateFrameStepMode = "Активировать покадровый режим." -helpFFplayCycleVideoFiltersOrShowModes = "Цикл видеофильтров или режимов показа." -helpFFplayDecreaseVolume = "Уменьшить громкость." -helpFFplayDescription = "Описание" -helpFFplayDoubleClickLeftMouseButton = "двойной щелчок\nлевой кнопкой мыши" -helpFFplayIncreaseVolume = "Увеличить громкость." -helpFFplayKeyDown = "вниз" -helpFFplayKeyHoldS = "держать S" -helpFFplayKeyLeft = "лево" -helpFFplayKeyRight = "право" -helpFFplayKeySpace = "SPACE (пробел)" -helpFFplayKeyUp = "вверх" -helpFFplayKeys = "Клавиши" -helpFFplayPause = "Поставить на паузу или продолжить проигрывать." -helpFFplayQuit = "Закрыть проигрыватель." -helpFFplaySeekBForward10Minutes = "Перемотать вперёд на 10 минут." -helpFFplaySeekBForward1Minute = "Перемотать вперёд на 1 минуту." -helpFFplaySeekBackward10Minutes = "Перемотать назад на 10 минут." -helpFFplaySeekBackward10Seconds = "Перемотать назад на 10 секунд." -helpFFplaySeekBackward1Minute = "Перемотать назад на 1 минуту." -helpFFplaySeekForward10Seconds = "Перемотать вперёд на 10 секунд." -helpFFplayToggleFullScreen = "Переключиться на полный экран или выйти с полного экрана." -helpFFplayToggleMute = "Отключить звук или включить звук." -inProgressQueue = "Выполняется" -languageSelectionFormHead = "Переключить язык" -languageSelectionHead = "Выберите язык" -licenseLink = "Сведения о лицензии" -licenseLinkOther = "Лицензии от других продуктов, которые используются в программе" -menuSettingsLanguage = "Язык" -menuSettingsTheme = "Тема" -or = "или" -parameterCheckbox = "Включить параметр" -pathToFfmpeg = "Путь к FFmpeg:" -pathToFfplay = "Путь к FFplay:" -pathToFfprobe = "Путь к FFprobe:" -preset_fast = "fast (медленней чем faster, но будет файл и меньше весить)" -preset_faster = "faster (медленней чем veryfast, но будет файл и меньше весить)" -preset_medium = "medium (медленней чем fast, но будет файл и меньше весить)" -preset_placebo = "placebo (не рекомендуется)" -preset_slow = "slow (медленней чем medium, но будет файл и меньше весить)" -preset_slower = "slower (медленней чем slow, но будет файл и меньше весить)" -preset_superfast = "superfast (медленней чем ultrafast, но будет файл и меньше весить)" -preset_ultrafast = "ultrafast (быстро, но файл будет много весить)" -preset_veryfast = "veryfast (медленней чем superfast, но будет файл и меньше весить)" -preset_veryslow = "veryslow (медленней чем slower, но будет файл и меньше весить)" -programmLink = "Сайт проекта" -programmVersion = "**Версия программы:** {{.Version}}" -queue = "Очередь" -save = "Сохранить" -selectEncoder = "Кодировщик:" -selectFFPathTitle = "Укажите путь к FFmpeg и к FFprobe" -selectFormat = "Расширение файла:" -settings = "Настройки" -testFF = "Проверка FFmpeg на работоспособность..." -themesNameDark = "Тёмная" -themesNameDefault = "По умолчанию" -themesNameLight = "Светлая" -titleDownloadLink = "Скачать можно от сюда" -total = "Всего" -unzipRun = "Распаковывается..." -waitingQueue = "В очереди" diff --git a/localizer/repository.go b/localizer/repository.go deleted file mode 100644 index 46e36f2..0000000 --- a/localizer/repository.go +++ /dev/null @@ -1,26 +0,0 @@ -package localizer - -import ( - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/setting" -) - -type RepositoryContract interface { - GetCode() (string, error) - Save(code string) (setting.Setting, error) -} - -type Repository struct { - settingRepository setting.RepositoryContract -} - -func NewRepository(settingRepository setting.RepositoryContract) *Repository { - return &Repository{settingRepository: settingRepository} -} - -func (r Repository) GetCode() (string, error) { - return r.settingRepository.GetValue("language") -} - -func (r Repository) Save(code string) (setting.Setting, error) { - return r.settingRepository.CreateOrUpdate("language", code) -} diff --git a/localizer/view.go b/localizer/view.go deleted file mode 100644 index c0e5af6..0000000 --- a/localizer/view.go +++ /dev/null @@ -1,74 +0,0 @@ -package localizer - -import ( - "fyne.io/fyne/v2" - "fyne.io/fyne/v2/widget" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" - "github.com/nicksnyder/go-i18n/v2/i18n" -) - -type ViewContract interface { - LanguageSelection(funcSelected func(lang kernel.Lang)) -} - -type View struct { - app kernel.AppContract -} - -func NewView(app kernel.AppContract) *View { - return &View{ - app: app, - } -} - -func (v View) LanguageSelection(funcSelected func(lang kernel.Lang)) { - languages := v.app.GetLocalizerService().GetLanguages() - listView := widget.NewList( - func() int { - return len(languages) - }, - func() fyne.CanvasObject { - return widget.NewLabel("template") - }, - func(i widget.ListItemID, o fyne.CanvasObject) { - block := o.(*widget.Label) - block.SetText(languages[i].Title) - }) - listView.OnSelected = func(id widget.ListItemID) { - _ = v.app.GetLocalizerService().SetCurrentLanguage(languages[id]) - funcSelected(languages[id]) - } - - messageHead := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "languageSelectionHead", - }) - v.app.GetWindow().SetContent(widget.NewCard(messageHead, "", listView)) -} - -func LanguageSelectionForm(localizerService kernel.LocalizerContract, funcSelected func(lang kernel.Lang)) fyne.CanvasObject { - languages := localizerService.GetLanguages() - currentLanguage := localizerService.GetCurrentLanguage() - listView := widget.NewList( - func() int { - return len(languages) - }, - func() fyne.CanvasObject { - return widget.NewLabel("template") - }, - func(i widget.ListItemID, o fyne.CanvasObject) { - block := o.(*widget.Label) - block.SetText(languages[i].Title) - if languages[i].Code == currentLanguage.Lang.Code { - block.TextStyle = fyne.TextStyle{Bold: true} - } - }) - listView.OnSelected = func(id widget.ListItemID) { - _ = localizerService.SetCurrentLanguage(languages[id]) - funcSelected(languages[id]) - } - - messageHead := localizerService.GetMessage(&i18n.LocalizeConfig{ - MessageID: "languageSelectionFormHead", - }) - return widget.NewCard(messageHead, "", listView) -} diff --git a/main.go b/main.go index 5f30405..41749c5 100644 --- a/main.go +++ b/main.go @@ -1,140 +1,36 @@ package main import ( - "errors" - "fyne.io/fyne/v2" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/convertor" - dberror "git.kor-elf.net/kor-elf/gui-for-ffmpeg/db" - error2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/error" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/handler" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/localizer" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/menu" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/migration" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/setting" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/theme" - "go.etcd.io/bbolt" - "golang.org/x/text/language" - "os" - "time" + "fyne.io/fyne/v2/app" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application/convertor" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application/setting" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/controller" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg" ) -var application kernel.AppContract -var ffPathUtilities *kernel.FFPathUtilities - -func init() { - iconResource, _ := fyne.LoadResourceFromPath("icon.png") - appMetadata := &fyne.AppMetadata{ - ID: "net.kor-elf.projects.gui-for-ffmpeg", - Name: "GUI for FFmpeg", - Version: "0.9.0", - Icon: iconResource, - } - - localizerService, err := kernel.NewLocalizer("languages", language.Russian) - if err != nil { - kernel.PanicErrorLang(err, appMetadata) - return - } - - ffPathUtilities = &kernel.FFPathUtilities{FFmpeg: "", FFprobe: "", FFplay: ""} - convertorService := kernel.NewService(ffPathUtilities) - ffplayService := kernel.NewFFplay(ffPathUtilities) - - queue := kernel.NewQueueList() - application = kernel.NewApp( - appMetadata, - localizerService, +func main() { + fyneApp := app.New() + appSetting := setting.NewSetting(fyneApp) + ffmpegService := ffmpeg.NewUtilities(appSetting) + progressBarService := convertor.NewProgressBar(ffmpegService) + convertorService := convertor.NewConvertor(ffmpegService) + itemsToConvert := convertor.NewItemsToConvert(ffmpegService) + queue := convertor.NewQueueList() + myApp := application.NewApp( + fyneApp, + appSetting, + progressBarService, + ffmpegService, + itemsToConvert, queue, - ffplayService, convertorService, ) -} - -func main() { - errorView := error2.NewView(application) - if canCreateFile("data/database.db") != true { - errorView.PanicErrorWriteDirectoryData() - application.GetWindow().ShowAndRun() - return - } - - db, err := bbolt.Open("data/database.db", 0600, &bbolt.Options{Timeout: 3 * time.Second}) - if err != nil { - errorView.PanicError(err) - application.GetWindow().ShowAndRun() - return - } - - defer db.Close() - - err = migration.Run(db) - if err != nil { - errorView.PanicError(err) - application.GetWindow().ShowAndRun() - return - } - - settingRepository := setting.NewRepository(db) - settingDirectoryForSaving := setting.NewSettingDirectoryForSaving(settingRepository) - - convertorRepository := convertor.NewRepository(settingRepository) - pathFFmpeg, err := convertorRepository.GetPathFfmpeg() - if err != nil && errors.Is(err, dberror.ErrRecordNotFound) == false { - errorView.PanicError(err) - application.GetWindow().ShowAndRun() - return - } - ffPathUtilities.FFmpeg = pathFFmpeg - - pathFFprobe, err := convertorRepository.GetPathFfprobe() - if err != nil && errors.Is(err, dberror.ErrRecordNotFound) == false { - errorView.PanicError(err) - application.GetWindow().ShowAndRun() - return - } - ffPathUtilities.FFprobe = pathFFprobe - - pathFFplay, err := convertorRepository.GetPathFfplay() - if err != nil && errors.Is(err, dberror.ErrRecordNotFound) == false { - errorView.PanicError(err) - application.GetWindow().ShowAndRun() - return - } - ffPathUtilities.FFplay = pathFFplay - - application.RunConvertor() - defer application.AfterClosing() - - localizerView := localizer.NewView(application) - convertorView := convertor.NewView(application) - itemsToConvertService := kernel.NewItemsToConvert( - application.GetWindow().GetLayout().GetRightTabs().GetAddedFilesContainer(), - application.GetFFplayService(), - application.GetLocalizerService(), - ) - convertorHandler := handler.NewConvertorHandler(application, convertorView, errorView, convertorRepository, settingDirectoryForSaving, itemsToConvertService) - - themeRepository := theme.NewRepository(settingRepository) - themeService := theme.NewTheme(application, themeRepository) - - localizerRepository := localizer.NewRepository(settingRepository) - menuView := menu.NewView(application) - menuSettingView := menu.NewViewSetting(application, themeService) - mainMenu := handler.NewMenuHandler(application, convertorHandler, menuView, menuSettingView, localizerView, localizerRepository, themeService) - - mainHandler := handler.NewMainHandler(application, convertorHandler, mainMenu, localizerRepository) - mainHandler.Start() - - application.GetWindow().SetMainMenu(mainMenu.GetMainMenu()) - application.GetWindow().ShowAndRun() -} - -func canCreateFile(path string) bool { - file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0666) - if err != nil { - return false - } - _ = file.Close() - return true + mainController := controller.NewController(myApp) + mainController.Start() + + myApp.RunConvertor() + defer myApp.AfterClosing() + + myApp.Run() } diff --git a/menu/view_setting.go b/menu/view_setting.go deleted file mode 100644 index 045662e..0000000 --- a/menu/view_setting.go +++ /dev/null @@ -1,112 +0,0 @@ -package menu - -import ( - "fyne.io/fyne/v2" - "fyne.io/fyne/v2/canvas" - "fyne.io/fyne/v2/widget" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/theme" - "github.com/nicksnyder/go-i18n/v2/i18n" - "image/color" -) - -type ViewSettingContract interface { - Main( - save func(*SettingForm) error, - cancel func(), - ) -} - -type SettingForm struct { - Language kernel.Lang - ThemeInfo theme.ThemeInfoContract -} - -type ViewSetting struct { - app kernel.AppContract - themeService theme.ThemeContract -} - -func NewViewSetting(app kernel.AppContract, themeService theme.ThemeContract) *ViewSetting { - return &ViewSetting{ - app: app, - themeService: themeService, - } -} - -func (v ViewSetting) Main(save func(*SettingForm) error, cancel func()) { - errorMessage := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255}) - errorMessage.TextSize = 16 - errorMessage.TextStyle = fyne.TextStyle{Bold: true} - - viewSettingForm := &SettingForm{ - Language: v.app.GetLocalizerService().GetCurrentLanguage().Lang, - ThemeInfo: v.themeService.GetCurrentThemeInfo(), - } - - languageItems := []string{} - langByTitle := map[string]kernel.Lang{} - for _, language := range v.app.GetLocalizerService().GetLanguages() { - languageItems = append(languageItems, language.Title) - langByTitle[language.Title] = language - } - selectLanguage := widget.NewSelect(languageItems, func(s string) { - if lang, ok := langByTitle[s]; ok { - viewSettingForm.Language = lang - } - }) - selectLanguage.Selected = v.app.GetLocalizerService().GetCurrentLanguage().Lang.Title - - themeItems := []string{} - themeByTitle := map[string]theme.ThemeInfoContract{} - for _, themeInfo := range v.themeService.List() { - themeItems = append(themeItems, themeInfo.GetTitle()) - themeByTitle[themeInfo.GetTitle()] = themeInfo - } - selectTheme := widget.NewSelect(themeItems, func(s string) { - if themeInfo, ok := themeByTitle[s]; ok { - viewSettingForm.ThemeInfo = themeInfo - } - }) - selectTheme.Selected = v.themeService.GetCurrentThemeInfo().GetTitle() - - form := &widget.Form{ - Items: []*widget.FormItem{ - { - Text: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "menuSettingsLanguage", - }), - Widget: selectLanguage, - }, - { - Text: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "menuSettingsTheme", - }), - Widget: selectTheme, - }, - { - Widget: errorMessage, - }, - }, - SubmitText: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "save", - }), - OnSubmit: func() { - err := save(viewSettingForm) - if err != nil { - errorMessage.Text = err.Error() - } - }, - } - if cancel != nil { - form.OnCancel = cancel - form.CancelText = v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "cancel", - }) - } - - messageHead := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "settings", - }) - v.app.GetWindow().SetContent(widget.NewCard(messageHead, "", form)) -} diff --git a/migration/migration.go b/migration/migration.go deleted file mode 100644 index 39af7af..0000000 --- a/migration/migration.go +++ /dev/null @@ -1,12 +0,0 @@ -package migration - -import ( - "go.etcd.io/bbolt" -) - -func Run(db *bbolt.DB) error { - return db.Update(func(tx *bbolt.Tx) error { - _, err := tx.CreateBucketIfNotExists([]byte("settings")) - return err - }) -} diff --git a/setting/directory_for_saving.go b/setting/directory_for_saving.go deleted file mode 100644 index 706560a..0000000 --- a/setting/directory_for_saving.go +++ /dev/null @@ -1,22 +0,0 @@ -package setting - -type DirectoryForSavingContract interface { - GetDirectoryForSaving() (string, error) - SaveDirectoryForSaving(path string) (Setting, error) -} - -type DirectoryForSaving struct { - settingRepository RepositoryContract -} - -func NewSettingDirectoryForSaving(settingRepository RepositoryContract) *DirectoryForSaving { - return &DirectoryForSaving{settingRepository: settingRepository} -} - -func (setting DirectoryForSaving) GetDirectoryForSaving() (string, error) { - return setting.settingRepository.GetValue("directoryForSaving") -} - -func (setting DirectoryForSaving) SaveDirectoryForSaving(path string) (Setting, error) { - return setting.settingRepository.CreateOrUpdate("directoryForSaving", path) -} diff --git a/setting/entity.go b/setting/entity.go deleted file mode 100644 index e3c18f3..0000000 --- a/setting/entity.go +++ /dev/null @@ -1,6 +0,0 @@ -package setting - -type Setting struct { - Code string `json:"code"` - Value string `json:"value"` -} diff --git a/setting/repository.go b/setting/repository.go deleted file mode 100644 index 9f2f671..0000000 --- a/setting/repository.go +++ /dev/null @@ -1,82 +0,0 @@ -package setting - -import ( - "encoding/json" - "errors" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/db" - "go.etcd.io/bbolt" -) - -type RepositoryContract interface { - Create(setting Setting) (Setting, error) - CreateOrUpdate(code string, value string) (Setting, error) - GetValue(code string) (value string, err error) -} - -type Repository struct { - db *bbolt.DB -} - -func NewRepository(db *bbolt.DB) *Repository { - return &Repository{db} -} - -func (r Repository) GetValue(code string) (value string, err error) { - var setting Setting - - err = r.db.View(func(tx *bbolt.Tx) error { - b := tx.Bucket([]byte("settings")) - if b == nil { - return errors.New("bucket 'settings' not found") - } - val := b.Get([]byte(code)) - if val == nil { - return db.ErrRecordNotFound - } - return json.Unmarshal(val, &setting) - }) - if err != nil { - return "", err - } - - return setting.Value, nil - -} - -func (r Repository) Create(setting Setting) (Setting, error) { - err := r.db.Update(func(tx *bbolt.Tx) error { - b := tx.Bucket([]byte("settings")) - if b == nil { - return errors.New("bucket 'settings' not found") - } - - data, err := json.Marshal(setting) - if err != nil { - return err - } - - return b.Put([]byte(setting.Code), data) - }) - return setting, err -} - -func (r Repository) CreateOrUpdate(code string, value string) (Setting, error) { - var setting Setting - setting.Code = code - setting.Value = value - - err := r.db.Update(func(tx *bbolt.Tx) error { - b := tx.Bucket([]byte("settings")) - if b == nil { - return errors.New("bucket 'settings' not found") - } - - data, err := json.Marshal(setting) - if err != nil { - return err - } - - return b.Put([]byte(code), data) - }) - return setting, err -} diff --git a/theme/repository.go b/theme/repository.go deleted file mode 100644 index 91b2351..0000000 --- a/theme/repository.go +++ /dev/null @@ -1,28 +0,0 @@ -package theme - -import "git.kor-elf.net/kor-elf/gui-for-ffmpeg/setting" - -type RepositoryContract interface { - GetCode() string - Save(code string) (setting.Setting, error) -} - -type Repository struct { - settingRepository setting.RepositoryContract -} - -func NewRepository(settingRepository setting.RepositoryContract) *Repository { - return &Repository{settingRepository: settingRepository} -} - -func (r Repository) GetCode() string { - name, err := r.settingRepository.GetValue("theme") - if err != nil { - return "default" - } - return name -} - -func (r Repository) Save(code string) (setting.Setting, error) { - return r.settingRepository.CreateOrUpdate("theme", code) -} diff --git a/theme/theme.go b/theme/theme.go deleted file mode 100644 index ba0dfa0..0000000 --- a/theme/theme.go +++ /dev/null @@ -1,158 +0,0 @@ -package theme - -import ( - "fyne.io/fyne/v2" - fyneTheme "fyne.io/fyne/v2/theme" - "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" - "github.com/nicksnyder/go-i18n/v2/i18n" - "image/color" -) - -type ThemeContract interface { - List() map[string]ThemeInfoContract - GetCurrentThemeInfo() ThemeInfoContract - SetCurrentTheme(themeInfo ThemeInfoContract) error -} - -type theme struct { - app kernel.AppContract - repository RepositoryContract - list map[string]ThemeInfoContract -} - -func NewTheme(app kernel.AppContract, repository RepositoryContract) ThemeContract { - theme := &theme{ - app: app, - repository: repository, - list: getThemes(app.GetLocalizerService()), - } - - theme.init() - - return theme -} - -func (t theme) init() { - themeInfo := t.GetCurrentThemeInfo() - if themeInfo.GetName() == "default" { - t.app.GetAppFyne().Settings().SetTheme(fyneTheme.DefaultTheme()) - return - } - t.app.GetAppFyne().Settings().SetTheme(&forcedVariant{theme: fyneTheme.DefaultTheme(), variant: themeInfo.GetVariant()}) -} - -func (t theme) GetCurrentThemeInfo() ThemeInfoContract { - themes := t.List() - if themeInfo, ok := themes[t.repository.GetCode()]; ok { - return themeInfo - } - - return themes["default"] -} - -func (t theme) List() map[string]ThemeInfoContract { - return t.list -} - -func (t theme) SetCurrentTheme(themeInfo ThemeInfoContract) error { - _, err := t.repository.Save(themeInfo.GetName()) - if err != nil { - return err - } - - if themeInfo.GetName() == "default" { - t.app.GetAppFyne().Settings().SetTheme(fyneTheme.DefaultTheme()) - return nil - } - t.app.GetAppFyne().Settings().SetTheme(&forcedVariant{theme: fyneTheme.DefaultTheme(), variant: themeInfo.GetVariant()}) - - return nil -} - -type ThemeInfoContract interface { - GetName() string - GetTitle() string - GetVariant() fyne.ThemeVariant -} - -type themeInfo struct { - name string - title string - variant fyne.ThemeVariant -} - -func (inf themeInfo) GetName() string { - return inf.name -} - -func (inf themeInfo) GetTitle() string { - return inf.title -} - -func (inf themeInfo) GetVariant() fyne.ThemeVariant { - return inf.variant -} - -func getThemes(localizer kernel.LocalizerContract) map[string]ThemeInfoContract { - themesNameDefault := &themeInfo{ - name: "default", - title: localizer.GetMessage(&i18n.LocalizeConfig{ - MessageID: "themesNameDefault", - }), - } - - themesNameLight := &themeInfo{ - name: "light", - title: localizer.GetMessage(&i18n.LocalizeConfig{ - MessageID: "themesNameLight", - }), - variant: fyneTheme.VariantLight, - } - - themesNameDark := &themeInfo{ - name: "dark", - title: localizer.GetMessage(&i18n.LocalizeConfig{ - MessageID: "themesNameDark", - }), - variant: fyneTheme.VariantDark, - } - - list := map[string]ThemeInfoContract{ - "default": themesNameDefault, - "light": themesNameLight, - "dark": themesNameDark, - } - - localizer.AddChangeCallback("themesNameDefault", func(text string) { - themesNameDefault.title = text - }) - localizer.AddChangeCallback("themesNameLight", func(text string) { - themesNameLight.title = text - }) - localizer.AddChangeCallback("themesNameDark", func(text string) { - themesNameDark.title = text - }) - - return list -} - -type forcedVariant struct { - theme fyne.Theme - variant fyne.ThemeVariant -} - -func (f *forcedVariant) Color(name fyne.ThemeColorName, _ fyne.ThemeVariant) color.Color { - return f.theme.Color(name, f.variant) -} - -func (f *forcedVariant) Font(style fyne.TextStyle) fyne.Resource { - return fyneTheme.DefaultTheme().Font(style) -} - -func (f *forcedVariant) Icon(name fyne.ThemeIconName) fyne.Resource { - return fyneTheme.DefaultTheme().Icon(name) -} - -func (f *forcedVariant) Size(name fyne.ThemeSizeName) float32 { - return fyneTheme.DefaultTheme().Size(name) -}