Compare commits

..

No commits in common. "main" and "0.4.0" have entirely different histories.
main ... 0.4.0

134 changed files with 3644 additions and 6996 deletions

2
.gitignore vendored
View File

@ -1,2 +0,0 @@
fyne-cross/*
ffmpeg/*

View File

@ -1,14 +0,0 @@
Website = "https://gui-for-ffmpeg.projects.kor-elf.net/language/en"
[Details]
Icon = "assets/icon.png"
Name = "GUI for FFmpeg"
ID = "net.kor-elf.projects.gui-for-ffmpeg"
Version = "1.0.0"
Build = 75
[LinuxAndBSD]
GenericName = "GUI for FFmpeg"
Categories = ["AudioVideo", "Utility"]
Comment = "A simple interface for the FFmpeg console utility."
Keywords = ["ffmpeg", "media", "convert", "transcode", "audio", "video", "конвертер", "видео", "аудио", "кодек"]

View File

@ -431,40 +431,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
github.com/fyne-io/oksvg
BSD 3-Clause License
Copyright (c) 2018, Steven R Wiley
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
github.com/go-gl/gl
The MIT License (MIT)
@ -673,423 +639,40 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
github.com/hack-pad/go-indexeddb
github.com/gopherjs/gopherjs
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Copyright (c) 2013 Richard Musiol. All rights reserved.
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Definitions.
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
github.com/hack-pad/safejs
github.com/jinzhu/inflection
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
The MIT License (MIT)
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--------------------------------------------------------------------------------
github.com/jeandeaual/go-locale
MIT License
Copyright (c) 2020 Alexis Jeandeau
Copyright (c) 2015 - Jinzhu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -1135,21 +718,29 @@ THE SOFTWARE.
--------------------------------------------------------------------------------
github.com/nfnt/resize
github.com/mattn/go-sqlite3
Copyright (c) 2012, Jan Schlicht <jan.schlicht@gmail.com>
The MIT License (MIT)
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
Copyright (c) 2014 Yasuhiro Matsumoto
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
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.
--------------------------------------------------------------------------------
@ -1209,212 +800,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
github.com/rymdport/portal
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--------------------------------------------------------------------------------
github.com/srwiley/oksvg
BSD 3-Clause License
@ -1509,34 +894,29 @@ SOFTWARE.
--------------------------------------------------------------------------------
github.com/ulikunitz/xz
github.com/tevino/abool
Copyright (c) 2014-2022 Ulrich Kunitz
All rights reserved.
The MIT License (MIT)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Copyright (c) 2016 Tevin Zhang
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
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:
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
* My name, Ulrich Kunitz, may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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.
--------------------------------------------------------------------------------
@ -1568,7 +948,7 @@ SOFTWARE.
golang.org/x/image
Copyright 2009 The Go Authors.
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
@ -1580,7 +960,39 @@ notice, this list of conditions and the following disclaimer.
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google LLC nor the names of its
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
golang.org/x/mobile
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
@ -1600,7 +1012,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
golang.org/x/net
Copyright 2009 The Go Authors.
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
@ -1612,7 +1024,7 @@ notice, this list of conditions and the following disclaimer.
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google LLC nor the names of its
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
@ -1632,7 +1044,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
golang.org/x/sys
Copyright 2009 The Go Authors.
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
@ -1644,7 +1056,7 @@ notice, this list of conditions and the following disclaimer.
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google LLC nor the names of its
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
@ -1664,7 +1076,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
golang.org/x/text
Copyright 2009 The Go Authors.
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
@ -1676,7 +1088,7 @@ notice, this list of conditions and the following disclaimer.
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google LLC nor the names of its
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
@ -1712,9 +1124,60 @@ limitations under the License.
--------------------------------------------------------------------------------
gorm.io/gorm
The MIT License (MIT)
Copyright (c) 2013-NOW Jinzhu <wosmvp@gmail.com>
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.
--------------------------------------------------------------------------------
honnef.co/go/js/dom
Copyright (c) 2014 Dominik Honnef
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.
--------------------------------------------------------------------------------
github.com/golang/go
Copyright 2009 The Go Authors.
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
@ -1726,7 +1189,7 @@ notice, this list of conditions and the following disclaimer.
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google LLC nor the names of its
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
@ -1745,7 +1208,6 @@ 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
@ -2423,4 +1885,3 @@ 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
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View File

@ -1,88 +0,0 @@
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

View File

@ -1,11 +1,11 @@
# GUI for FFmpeg
<p>Простенький интерфейс для консольной утилиты FFmpeg. Но я <strong>не являюсь</strong> автором самой утилиты <strong>FFmpeg</strong>.</p>
<p><strong>FFmpeg</strong> — торговая марка <strong><a href="https://bellard.org/" target="_blank">Fabrice Bellard</a></strong>, создателя проекта <strong><a href="https://ffmpeg.org/about.html" target="_blank">FFmpeg</a></strong>.</p>
<p><strong>FFmpeg</strong> — торговая марка <strong><a href="http://bellard.org/" target="_blank">Fabrice Bellard</a></strong>, создателя проекта <strong><a href="https://ffmpeg.org/about.html" target="_blank">FFmpeg</a></strong>.</p>
<p>Программное обеспечение является MIT (см. <a href="https://git.kor-elf.net/kor-elf/gui-for-ffmpeg/src/branch/main/LICENSE">LICENSE</a>) и использует сторонние библиотеки, которые распространяются на их собственных условиях (см. <a href="https://git.kor-elf.net/kor-elf/gui-for-ffmpeg/src/branch/main/LICENSE-3RD-PARTY.txt">LICENSE-3RD-PARTY.txt</a>).</p>
<img src="assets/screenshot-gui-for-ffmpeg.png" alt="Скриншот программы">
<img src="images/screenshot-gui-for-ffmpeg.png">
<p>Скачать скомпилированные готовые версии можно тут: <a href="https://git.kor-elf.net/kor-elf/gui-for-ffmpeg/releases">https://git.kor-elf.net/kor-elf/gui-for-ffmpeg/releases</a>.</p>
@ -13,18 +13,6 @@
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**
@ -33,7 +21,22 @@
5. go install github.com/fyne-io/fyne-cross@latest
* У Вас так же должен быть установлен docker
* О fyne-cross можно по подробней почитать тут: https://github.com/fyne-io/fyne-cross
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** будут архивы, которые надо распаковать и пользоваться программой.
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** копируете:
* src/icon.png
* src/data
* src/languages
* LICENSE
* LICENSE-3RD-PARTY.txt
<p><strong>Структура должна получиться такая:</strong></p>
<img src="images/screenshot-folder-structure.png">
## Работа с переводами:
1. go install -v github.com/nicksnyder/go-i18n/v2/goi18n@latest
2. goi18n merge -sourceLanguage ru -outdir languages languages/active.\*.toml languages/translate.\*.toml
3. В файлах **languages/translate.\*.toml** переводим текст на нужный язык
4. goi18n merge -sourceLanguage ru -outdir languages languages/active.\*.toml languages/translate.\*.toml
Более подробно можно почитать тут: https://github.com/nicksnyder/go-i18n

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

36
convertor/repository.go Normal file
View File

@ -0,0 +1,36 @@
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)
}
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)
}

231
convertor/view.go Normal file
View File

@ -0,0 +1,231 @@
package convertor
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/kernel"
"github.com/nicksnyder/go-i18n/v2/i18n"
"image/color"
"path/filepath"
)
type ViewContract interface {
Main(
runConvert func(setting HandleConvertSetting),
)
SelectFFPath(
ffmpegPath string,
ffprobePath string,
save func(ffmpegPath string, ffprobePath string) error,
cancel func(),
donwloadFFmpeg func(progressBar *widget.ProgressBar, progressMessage *canvas.Text) error,
)
}
type View struct {
app kernel.AppContract
}
type HandleConvertSetting struct {
VideoFileInput kernel.File
DirectoryForSave string
OverwriteOutputFiles bool
}
type enableFormConversionStruct struct {
fileVideoForConversion *widget.Button
buttonForSelectedDir *widget.Button
form *widget.Form
}
func NewView(app kernel.AppContract) *View {
return &View{
app: app,
}
}
func (v View) Main(
runConvert func(setting HandleConvertSetting),
) {
form := &widget.Form{}
conversionMessage := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255})
conversionMessage.TextSize = 16
conversionMessage.TextStyle = fyne.TextStyle{Bold: true}
fileVideoForConversion, fileVideoForConversionMessage, fileInput := v.getButtonFileVideoForConversion(form, conversionMessage)
buttonForSelectedDir, buttonForSelectedDirMessage, pathToSaveDirectory := v.getButtonForSelectingDirectoryForSaving()
isOverwriteOutputFiles := false
checkboxOverwriteOutputFilesTitle := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "checkboxOverwriteOutputFilesTitle",
})
checkboxOverwriteOutputFiles := widget.NewCheck(checkboxOverwriteOutputFilesTitle, func(b bool) {
isOverwriteOutputFiles = b
})
form.Items = []*widget.FormItem{
{
Text: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "fileVideoForConversionTitle"}),
Widget: fileVideoForConversion,
},
{
Widget: container.NewHScroll(fileVideoForConversionMessage),
},
{
Text: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "buttonForSelectedDirTitle"}),
Widget: buttonForSelectedDir,
},
{
Widget: container.NewHScroll(buttonForSelectedDirMessage),
},
{
Widget: checkboxOverwriteOutputFiles,
},
}
form.SubmitText = v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "converterVideoFilesSubmitTitle",
})
enableFormConversionStruct := enableFormConversionStruct{
fileVideoForConversion: fileVideoForConversion,
buttonForSelectedDir: buttonForSelectedDir,
form: form,
}
form.OnSubmit = func() {
if len(*pathToSaveDirectory) == 0 {
showConversionMessage(conversionMessage, errors.New(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "errorSelectedFolderSave",
})))
enableFormConversion(enableFormConversionStruct)
return
}
conversionMessage.Text = ""
fileVideoForConversion.Disable()
buttonForSelectedDir.Disable()
form.Disable()
setting := HandleConvertSetting{
VideoFileInput: *fileInput,
DirectoryForSave: *pathToSaveDirectory,
OverwriteOutputFiles: isOverwriteOutputFiles,
}
runConvert(setting)
enableFormConversion(enableFormConversionStruct)
fileVideoForConversionMessage.Text = ""
form.Disable()
}
converterVideoFilesTitle := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "converterVideoFilesTitle",
})
v.app.GetWindow().SetContent(widget.NewCard(converterVideoFilesTitle, "", container.NewVBox(form, conversionMessage)))
form.Disable()
}
func (v View) getButtonFileVideoForConversion(form *widget.Form, conversionMessage *canvas.Text) (*widget.Button, *canvas.Text, *kernel.File) {
fileInput := &kernel.File{}
fileVideoForConversionMessage := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255})
fileVideoForConversionMessage.TextSize = 16
fileVideoForConversionMessage.TextStyle = fyne.TextStyle{Bold: true}
buttonTitle := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "choose",
})
var locationURI fyne.ListableURI
button := widget.NewButton(buttonTitle, func() {
v.app.GetWindow().NewFileOpen(func(r fyne.URIReadCloser, err error) {
if err != nil {
fileVideoForConversionMessage.Text = err.Error()
setStringErrorStyle(fileVideoForConversionMessage)
return
}
if r == nil {
return
}
fileInput.Path = r.URI().Path()
fileInput.Name = r.URI().Name()
fileInput.Ext = r.URI().Extension()
fileVideoForConversionMessage.Text = r.URI().Path()
setStringSuccessStyle(fileVideoForConversionMessage)
form.Enable()
conversionMessage.Text = ""
listableURI := storage.NewFileURI(filepath.Dir(r.URI().Path()))
locationURI, err = storage.ListerForURI(listableURI)
}, locationURI)
})
return button, fileVideoForConversionMessage, fileInput
}
func (v View) getButtonForSelectingDirectoryForSaving() (button *widget.Button, buttonMessage *canvas.Text, dirPath *string) {
buttonMessage = canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255})
buttonMessage.TextSize = 16
buttonMessage.TextStyle = fyne.TextStyle{Bold: true}
path := ""
dirPath = &path
buttonTitle := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "choose",
})
var locationURI fyne.ListableURI
button = widget.NewButton(buttonTitle, func() {
v.app.GetWindow().NewFolderOpen(func(r fyne.ListableURI, err error) {
if err != nil {
buttonMessage.Text = err.Error()
setStringErrorStyle(buttonMessage)
return
}
if r == nil {
return
}
path = r.Path()
buttonMessage.Text = r.Path()
setStringSuccessStyle(buttonMessage)
locationURI, _ = storage.ListerForURI(r)
}, locationURI)
})
return
}
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)
}
func enableFormConversion(enableFormConversionStruct enableFormConversionStruct) {
enableFormConversionStruct.fileVideoForConversion.Enable()
enableFormConversionStruct.buttonForSelectedDir.Enable()
enableFormConversionStruct.form.Enable()
}

130
convertor/view_setting.go Normal file
View File

@ -0,0 +1,130 @@
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,
save func(ffmpegPath string, ffprobePath 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)
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),
},
{
Widget: errorMessage,
},
},
SubmitText: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "save",
}),
OnSubmit: func() {
err := save(*ffmpegPath, *ffprobePath)
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",
})
v.app.GetWindow().SetContent(widget.NewCard(selectFFPathTitle, "", container.NewVBox(
form,
v.blockDownloadFFmpeg(donwloadFFmpeg),
)))
}
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
}

View File

@ -0,0 +1,17 @@
//go:build !windows
// +build !windows
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()
}

View File

@ -1,19 +1,22 @@
//go:build windows
// +build windows
package gui
package convertor
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 DownloadFFmpeg(donwloadFFmpeg func(progressBar *widget.ProgressBar, progressMessage *canvas.Text) error) fyne.CanvasObject {
func (v View) blockDownloadFFmpeg(
donwloadFFmpeg func(progressBar *widget.ProgressBar, progressMessage *canvas.Text) error,
) *fyne.Container {
errorDownloadFFmpegMessage := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255})
errorDownloadFFmpegMessage.TextSize = 16
errorDownloadFFmpegMessage.TextStyle = fyne.TextStyle{Bold: true}
@ -26,32 +29,33 @@ func DownloadFFmpeg(donwloadFFmpeg func(progressBar *widget.ProgressBar, progres
var buttonDownloadFFmpeg *widget.Button
buttonDownloadFFmpeg = widget.NewButton(lang.L("download"), func() {
buttonDownloadFFmpeg = widget.NewButton(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "download",
}), func() {
buttonDownloadFFmpeg.Disable()
go func() {
fyne.Do(func() {
buttonDownloadFFmpeg.Disable()
})
err := donwloadFFmpeg(progressBar, progressDownloadFFmpegMessage)
if err != nil {
errorDownloadFFmpegMessage.Text = err.Error()
}
fyne.Do(func() {
buttonDownloadFFmpeg.Enable()
})
}()
err := donwloadFFmpeg(progressBar, progressDownloadFFmpegMessage)
if err != nil {
errorDownloadFFmpegMessage.Text = err.Error()
}
buttonDownloadFFmpeg.Enable()
})
downloadFFmpegFromSiteMessage := lang.L("downloadFFmpegFromSite")
downloadFFmpegFromSiteMessage := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "downloadFFmpegFromSite",
})
return container.NewVBox(
canvas.NewLine(colornames.Darkgreen),
widget.NewCard(lang.L("buttonDownloadFFmpeg"), "", container.NewVBox(
widget.NewCard(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "buttonDownloadFFmpeg",
}), "", container.NewVBox(
widget.NewRichTextFromMarkdown(
downloadFFmpegFromSiteMessage+" [https://github.com/BtbN/FFmpeg-Builds/releases](https://github.com/BtbN/FFmpeg-Builds/releases)",
),
buttonDownloadFFmpeg,
container.NewHScroll(errorDownloadFFmpegMessage),
errorDownloadFFmpegMessage,
progressDownloadFFmpegMessage,
progressBar,
)),

2
data/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

39
dist/linux/Makefile vendored
View File

@ -1,39 +0,0 @@
# 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)

View File

@ -1,7 +0,0 @@
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.

View File

@ -1,7 +0,0 @@
Установить для пользователя (рекомендуется)
Запустите "make user-install" для установки в домашнюю папку ~/.local/
Запустите "make user-uninstall" для удаления из домашней папки ~/.local/
Установить для всей системы
Запустить "sudo make install" Для установки в систему.
Запустить "sudo make uninstall" Для удаления из системы.

View File

@ -1,9 +0,0 @@
[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;конвертер;видео;аудио;кодек;

64
error/view.go Normal file
View File

@ -0,0 +1,64 @@
package error
import (
"fyne.io/fyne/v2/container"
"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"
)
type ViewContract interface {
PanicError(err error)
}
type View struct {
app kernel.AppContract
}
func NewView(app kernel.AppContract) *View {
return &View{
app: app,
}
}
func (v View) PanicError(err error) {
messageHead := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "error",
})
v.app.GetWindow().SetContent(container.NewBorder(
container.NewVBox(
widget.NewLabel(messageHead),
widget.NewLabel(err.Error()),
),
nil,
nil,
nil,
localizer.LanguageSelectionForm(v.app.GetLocalizerService(), func(lang kernel.Lang) {
v.PanicError(err)
}),
))
}
func (v View) PanicErrorWriteDirectoryData() {
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()
}),
))
}

57
go.mod
View File

@ -1,45 +1,44 @@
module git.kor-elf.net/kor-elf/gui-for-ffmpeg
go 1.23.0
toolchain go1.23.9
go 1.21
require (
fyne.io/fyne/v2 v2.6.1
github.com/ulikunitz/xz v0.5.12
golang.org/x/image v0.27.0
fyne.io/fyne/v2 v2.4.3
github.com/BurntSushi/toml v1.3.2
github.com/mattn/go-sqlite3 v1.14.21
github.com/nicksnyder/go-i18n/v2 v2.4.0
golang.org/x/text v0.14.0
gorm.io/driver/sqlite v1.5.4
gorm.io/gorm v1.25.6
)
require (
fyne.io/systray v1.11.0 // indirect
github.com/BurntSushi/toml v1.5.0 // indirect
fyne.io/systray v1.10.1-0.20231115130155-104f5ef7839e // 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
github.com/fyne-io/gl-js v0.1.0 // indirect
github.com/fyne-io/glfw-js v0.2.0 // indirect
github.com/fyne-io/image v0.1.1 // indirect
github.com/fyne-io/oksvg v0.1.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe // indirect
github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504 // indirect
github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 // indirect
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 // indirect
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20250301202403-da16c1255728 // indirect
github.com/go-text/render v0.2.0 // indirect
github.com/go-text/typesetting v0.3.0 // indirect
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240118000515-a250818d05e3 // indirect
github.com/go-text/render v0.0.0-20230619120952-35bccb6164b8 // indirect
github.com/go-text/typesetting v0.0.0-20230616162802-9c17dd34aa4a // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/hack-pad/go-indexeddb v0.3.2 // indirect
github.com/hack-pad/safejs v0.1.1 // indirect
github.com/jeandeaual/go-locale v0.0.0-20250421151639-a9d6ed1b3d45 // indirect
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/gopherjs/gopherjs v1.17.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // 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
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
github.com/stretchr/testify v1.10.0 // indirect
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
github.com/stretchr/testify v1.8.4 // indirect
github.com/tevino/abool v1.2.0 // indirect
github.com/yuin/goldmark v1.5.5 // indirect
golang.org/x/image v0.11.0 // indirect
golang.org/x/mobile v0.0.0-20230531173138-3c911d8e3eda // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/sys v0.16.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
honnef.co/go/js/dom v0.0.0-20210725211120-f030747120f2 // indirect
)

745
go.sum
View File

@ -1,82 +1,697 @@
fyne.io/fyne/v2 v2.6.1 h1:kjPJD4/rBS9m2nHJp+npPSuaK79yj6ObMTuzR6VQ1Is=
fyne.io/fyne/v2 v2.6.1/go.mod h1:YZt7SksjvrSNJCwbWFV32WON3mE1Sr7L41D29qMZ/lU=
fyne.io/systray v1.11.0 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg=
fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
fyne.io/fyne/v2 v2.4.3 h1:v2wncjEAcwXZ8UNmTCWTGL9+sGyPc5RuzBvM96GcC78=
fyne.io/fyne/v2 v2.4.3/go.mod h1:1h3BKxmQYRJlr2g+RGVxedzr6vLVQ/AJmFWcF9CJnoQ=
fyne.io/systray v1.10.1-0.20231115130155-104f5ef7839e h1:Hvs+kW2VwCzNToF3FmnIAzmivNgrclwPgoUdVSrjkP8=
fyne.io/systray v1.10.1-0.20231115130155-104f5ef7839e/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fredbi/uri v1.0.0 h1:s4QwUAZ8fz+mbTsukND+4V5f+mJ/wjaTokwstGUAemg=
github.com/fredbi/uri v1.0.0/go.mod h1:1xC40RnIOGCaQzswaOvrzvG/3M3F0hyDVb3aO/1iGy0=
github.com/fredbi/uri v1.1.0 h1:OqLpTXtyRg9ABReqvDGdJPqZUxs8cyBDOMXBbskCaB8=
github.com/fredbi/uri v1.1.0/go.mod h1:aYTUoAXBOq7BLfVJ8GnKmfcuURosB1xyHDIfWeC/iW4=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fyne-io/gl-js v0.1.0 h1:8luJzNs0ntEAJo+8x8kfUOXujUlP8gB3QMOxO2mUdpM=
github.com/fyne-io/gl-js v0.1.0/go.mod h1:ZcepK8vmOYLu96JoxbCKJy2ybr+g1pTnaBDdl7c3ajI=
github.com/fyne-io/glfw-js v0.2.0 h1:8GUZtN2aCoTPNqgRDxK5+kn9OURINhBEBc7M4O1KrmM=
github.com/fyne-io/glfw-js v0.2.0/go.mod h1:Ri6te7rdZtBgBpxLW19uBpp3Dl6K9K/bRaYdJ22G8Jk=
github.com/fyne-io/image v0.1.1 h1:WH0z4H7qfvNUw5l4p3bC1q70sa5+YWVt6HCj7y4VNyA=
github.com/fyne-io/image v0.1.1/go.mod h1:xrfYBh6yspc+KjkgdZU/ifUC9sPA5Iv7WYUBzQKK7JM=
github.com/fyne-io/oksvg v0.1.0 h1:7EUKk3HV3Y2E+qypp3nWqMXD7mum0hCw2KEGhI1fnBw=
github.com/fyne-io/oksvg v0.1.0/go.mod h1:dJ9oEkPiWhnTFNCmRgEze+YNprJF7YRbpjgpWS4kzoI=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe h1:A/wiwvQ0CAjPkuJytaD+SsXkPU0asQ+guQEIg1BJGX4=
github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe/go.mod h1:d4clgH0/GrRwWjRzJJQXxT/h1TyuNSfF/X64zb/3Ggg=
github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504 h1:+31CdF/okdokeFNoy9L/2PccG3JFidQT3ev64/r4pYU=
github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504/go.mod h1:gLRWYfYnMA9TONeppRSikMdXlHQ97xVsPojddUv3b/E=
github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 h1:hnLq+55b7Zh7/2IRzWCpiTcAvjv/P8ERF+N7+xXbZhk=
github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2/go.mod h1:eO7W361vmlPOrykIg+Rsh1SZ3tQBaOsfzZhsIOb/Lm0=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk=
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 h1:5BVwOaUSBTlVZowGO6VZGw2H/zl9nrd3eCZfYV+NfQA=
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20250301202403-da16c1255728 h1:RkGhqHxEVAvPM0/R+8g7XRwQnHatO0KAuVcwHo8q9W8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20250301202403-da16c1255728/go.mod h1:SyRD8YfuKk+ZXlDqYiqe1qMSqjNgtHzBTG810KUagMc=
github.com/go-text/render v0.2.0 h1:LBYoTmp5jYiJ4NPqDc2pz17MLmA3wHw1dZSVGcOdeAc=
github.com/go-text/render v0.2.0/go.mod h1:CkiqfukRGKJA5vZZISkjSYrcdtgKQWRa2HIzvwNN5SU=
github.com/go-text/typesetting v0.3.0 h1:OWCgYpp8njoxSRpwrdd1bQOxdjOXDj9Rqart9ML4iF4=
github.com/go-text/typesetting v0.3.0/go.mod h1:qjZLkhRgOEYMhU9eHBr3AR4sfnGJvOXNLt8yRAySFuY=
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0=
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211213063430-748e38ca8aec/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b h1:GgabKamyOYguHqHjSkDACcgoPIz3w0Dis/zJ1wyHHHU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240118000515-a250818d05e3 h1:nanQfMsOs3gnuKRm0E5jXWomedE/9YIFXdmHJNZYeqc=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240118000515-a250818d05e3/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-text/render v0.0.0-20230619120952-35bccb6164b8 h1:VkKnvzbvHqgEfm351rfr8Uclu5fnwq8HP2ximUzJsBM=
github.com/go-text/render v0.0.0-20230619120952-35bccb6164b8/go.mod h1:h29xCucjNsDcYb7+0rJokxVwYAq+9kQ19WiFuBKkYtc=
github.com/go-text/typesetting v0.0.0-20230616162802-9c17dd34aa4a h1:VjN8ttdfklC0dnAdKbZqGNESdERUxtE3l8a/4Grgarc=
github.com/go-text/typesetting v0.0.0-20230616162802-9c17dd34aa4a/go.mod h1:evDBbvNR/KaVFZ2ZlDSOWWXIUKq0wCOEtzLxRM8SG3k=
github.com/go-text/typesetting-utils v0.0.0-20230616150549-2a7df14b6a22 h1:LBQTFxP2MfsyEDqSKmUBZaDuDHN1vpqDyOZjcqS7MYI=
github.com/go-text/typesetting-utils v0.0.0-20230616150549-2a7df14b6a22/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/hack-pad/go-indexeddb v0.3.2 h1:DTqeJJYc1usa45Q5r52t01KhvlSN02+Oq+tQbSBI91A=
github.com/hack-pad/go-indexeddb v0.3.2/go.mod h1:QvfTevpDVlkfomY498LhstjwbPW6QC4VC/lxYb0Kom0=
github.com/hack-pad/safejs v0.1.1 h1:d5qPO0iQ7h2oVtpzGnLExE+Wn9AtytxIfltcS2b9KD8=
github.com/hack-pad/safejs v0.1.1/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio=
github.com/jeandeaual/go-locale v0.0.0-20250421151639-a9d6ed1b3d45 h1:vFdvrlsVU+p/KFBWTq0lTG4fvWvG88sawGlCzM+RUEU=
github.com/jeandeaual/go-locale v0.0.0-20250421151639-a9d6ed1b3d45/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o=
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe972w/cYF+FjW34v27+9Vo5106B4M=
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/nicksnyder/go-i18n/v2 v2.6.0 h1:C/m2NNWNiTB6SK4Ao8df5EWm3JETSTIGNXBpMJTxzxQ=
github.com/nicksnyder/go-i18n/v2 v2.6.0/go.mod h1:88sRqr0C6OPyJn0/KRNaEz1uWorjxIKP7rUUcvycecE=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20211219123610-ec9572f70e60/go.mod h1:cz9oNYuRUWGdHmLF2IodMLkAhcPtXeULvcBNagUrxTI=
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
github.com/goxjs/gl v0.0.0-20210104184919-e3fafc6f8f2a/go.mod h1:dy/f2gjY09hwVfIyATps4G2ai7/hLwLkc5TrPqONuXY=
github.com/goxjs/glfw v0.0.0-20191126052801-d2efb5f20838/go.mod h1:oS8P8gVOT4ywTcjV6wZlOU4GuVFQ8F5328KY3MJ79CY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e h1:LvL4XsI70QxOGHed6yhQtAU34Kx3Qq2wwBzGFKY8zKk=
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI=
github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.21 h1:IXocQLOykluc3xPE0Lvy8FtggMz1G+U3mEjg+0zGizc=
github.com/mattn/go-sqlite3 v1.14.21/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/nicksnyder/go-i18n/v2 v2.3.0 h1:2NPsCsNFCVd7i+Su0xYsBrIhS3bE2XMv5gNTft2O+PQ=
github.com/nicksnyder/go-i18n/v2 v2.3.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM=
github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rymdport/portal v0.4.1 h1:2dnZhjf5uEaeDjeF/yBIeeRo6pNI2QAKm7kq1w/kbnA=
github.com/rymdport/portal v0.4.1/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE=
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q=
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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=
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/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=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tevino/abool v1.2.0 h1:heAkClL8H6w+mK5md9dzsuohKeXHUpY7Vw0ZCKW+huA=
github.com/tevino/abool v1.2.0/go.mod h1:qc66Pna1RiIsPa7O4Egxxs9OqkuxDX55zznh9K07Tzg=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.5.5 h1:IJznPe8wOzfIKETmMkd06F8nXkmlhaHqFRM9l1hAGsU=
github.com/yuin/goldmark v1.5.5/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo=
golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20211207041440-4e6c2922fdee/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
golang.org/x/mobile v0.0.0-20230531173138-3c911d8e3eda h1:O+EUvnBNPwI4eLthn8W5K+cS8zQZfgTABPLNm6Bna34=
golang.org/x/mobile v0.0.0-20230531173138-3c911d8e3eda/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.5.4 h1:IqXwXi8M/ZlPzH/947tn5uik3aYQslP9BVveoax0nV0=
gorm.io/driver/sqlite v1.5.4/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4=
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/gorm v1.25.6 h1:V92+vVda1wEISSOMtodHVRcUIOPYa2tgQtyF+DfFx+A=
gorm.io/gorm v1.25.6/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
honnef.co/go/js/dom v0.0.0-20210725211120-f030747120f2 h1:oomkgU6VaQDsV6qZby2uz1Lap0eXmku8+2em3A/l700=
honnef.co/go/js/dom v0.0.0-20210725211120-f030747120f2/go.mod h1:sUMDUKNB2ZcVjt92UnLy3cdGs+wDAcrPdV3JP6sVgA4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

130
handler/convertor.go Normal file
View File

@ -0,0 +1,130 @@
package handler
import (
"errors"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/convertor"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/helper"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
"github.com/nicksnyder/go-i18n/v2/i18n"
)
type ConvertorHandlerContract interface {
MainConvertor()
FfPathSelection()
GetFfmpegVersion() (string, error)
GetFfprobeVersion() (string, error)
}
type ConvertorHandler struct {
app kernel.AppContract
convertorView convertor.ViewContract
convertorRepository convertor.RepositoryContract
}
func NewConvertorHandler(
app kernel.AppContract,
convertorView convertor.ViewContract,
convertorRepository convertor.RepositoryContract,
) *ConvertorHandler {
return &ConvertorHandler{
app: app,
convertorView: convertorView,
convertorRepository: convertorRepository,
}
}
func (h ConvertorHandler) MainConvertor() {
if h.checkingFFPathUtilities() == true {
h.convertorView.Main(h.runConvert)
return
}
h.convertorView.SelectFFPath("", "", h.saveSettingFFPath, nil, h.downloadFFmpeg)
}
func (h ConvertorHandler) FfPathSelection() {
ffmpeg, _ := h.convertorRepository.GetPathFfmpeg()
ffprobe, _ := h.convertorRepository.GetPathFfprobe()
h.convertorView.SelectFFPath(ffmpeg, ffprobe, 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) runConvert(setting convertor.HandleConvertSetting) {
h.app.GetQueue().Add(&kernel.ConvertSetting{
VideoFileInput: setting.VideoFileInput,
VideoFileOut: kernel.File{
Path: setting.DirectoryForSave + helper.PathSeparator() + setting.VideoFileInput.Name + ".mp4",
Name: setting.VideoFileInput.Name,
Ext: ".mp4",
},
OverwriteOutputFiles: setting.OverwriteOutputFiles,
})
}
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
}
_, _ = h.convertorRepository.SavePathFfmpeg(item.FFmpeg)
_, _ = h.convertorRepository.SavePathFfprobe(item.FFprobe)
return true
}
return false
}
func (h ConvertorHandler) saveSettingFFPath(ffmpegPath string, ffprobePath 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)
}
_, _ = h.convertorRepository.SavePathFfmpeg(ffmpegPath)
_, _ = h.convertorRepository.SavePathFfprobe(ffprobePath)
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
}
return true
}

View File

@ -0,0 +1,18 @@
//go:build !windows
// +build !windows
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/bin/ffmpeg", "ffmpeg/bin/ffprobe"}, {"ffmpeg", "ffprobe"}}
}
func (h ConvertorHandler) downloadFFmpeg(progressBar *widget.ProgressBar, progressMessage *canvas.Text) (err error) {
return nil
}

View File

@ -1,16 +1,15 @@
//go:build windows
// +build windows
package service
package handler
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/internal/application"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
"github.com/nicksnyder/go-i18n/v2/i18n"
"io"
"net/http"
"os"
@ -18,52 +17,45 @@ import (
"strings"
)
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 getPathsToFF() []kernel.FFPathUtilities {
return []kernel.FFPathUtilities{{"ffmpeg\\bin\\ffmpeg.exe", "ffmpeg\\bin\\ffprobe.exe"}}
}
dir := os.Getenv("APPDATA")
dir = filepath.Join(dir, "fyne", app.FyneApp().UniqueID())
err = os.MkdirAll(dir, 0755)
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",
})
progressMessage.Refresh()
err = downloadFile("ffmpeg/ffmpeg.zip", "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl.zip", progressBar)
if err != nil {
return err
}
fyne.Do(func() {
progressMessage.Text = lang.L("downloadRun")
progressMessage.Refresh()
progressMessage.Text = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "unzipRun",
})
err = downloadFile(dir+"/ffmpeg.zip", "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl.zip", progressBar)
progressMessage.Refresh()
err = unZip("ffmpeg/ffmpeg.zip", "ffmpeg")
if err != nil {
return err
}
_ = os.Remove("ffmpeg/ffmpeg.zip")
fyne.Do(func() {
progressMessage.Text = lang.L("unzipRun")
progressMessage.Refresh()
progressMessage.Text = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "testFF",
})
err = unZip(dir+"/ffmpeg.zip", dir, progressBar)
progressMessage.Refresh()
err = h.saveSettingFFPath("ffmpeg/ffmpeg-master-latest-win64-gpl/bin/ffmpeg.exe", "ffmpeg/ffmpeg-master-latest-win64-gpl/bin/ffprobe.exe")
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
}
fyne.Do(func() {
progressMessage.Text = lang.L("completedQueue")
progressMessage.Refresh()
})
return nil
}
@ -102,35 +94,19 @@ func downloadFile(filepath string, url string, progressBar *widget.ProgressBar)
f.Write(buf[:n])
downloaded += int64(n)
progressBar.Value = float64(downloaded) / float64(resp.ContentLength) * 100
fyne.Do(func() {
progressBar.Refresh()
})
progressBar.Refresh()
}
}
return nil
}
func unZip(fileZip string, directory string, progressBar *widget.ProgressBar) error {
progressBar.Value = 0
progressBar.Max = 100
fyne.Do(func() {
progressBar.Refresh()
})
func unZip(fileZip string, directory string) error {
archive, err := zip.OpenReader(fileZip)
if err != nil {
return err
}
defer archive.Close()
totalBytes := int64(0)
for _, f := range archive.File {
totalBytes += int64(f.UncompressedSize64)
}
unpackedBytes := int64(0)
for _, f := range archive.File {
filePath := filepath.Join(directory, f.Name)
@ -156,20 +132,22 @@ func unZip(fileZip string, directory string, progressBar *widget.ProgressBar) er
return err
}
bytesRead, err := io.Copy(dstFile, fileInArchive)
if err != nil {
if _, err := io.Copy(dstFile, fileInArchive); err != nil {
return err
}
unpackedBytes += bytesRead
progressBar.Value = float64(unpackedBytes) / float64(totalBytes) * 100
fyne.Do(func() {
progressBar.Refresh()
})
dstFile.Close()
fileInArchive.Close()
}
return nil
}
func isDirectory(path string) bool {
fileInfo, err := os.Stat(path)
if err != nil {
return false
}
return fileInfo.IsDir()
}

38
handler/main.go Normal file
View File

@ -0,0 +1,38 @@
package handler
import (
"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 {
h.menuHandler.LanguageSelection()
return
}
_ = h.app.GetLocalizerService().SetCurrentLanguageByCode(language)
h.convertorHandler.MainConvertor()
}

149
handler/menu.go Normal file
View File

@ -0,0 +1,149 @@
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"
"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
localizerView localizer.ViewContract
localizerRepository localizer.RepositoryContract
localizerListener localizerListenerContract
}
func NewMenuHandler(
app kernel.AppContract,
convertorHandler ConvertorHandlerContract,
menuView menu.ViewContract,
localizerView localizer.ViewContract,
localizerRepository localizer.RepositoryContract,
localizerListener localizerListenerContract,
) *MenuHandler {
return &MenuHandler{
app: app,
convertorHandler: convertorHandler,
menuView: menuView,
localizerView: localizerView,
localizerRepository: localizerRepository,
localizerListener: localizerListener,
}
}
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.localizerListener.AddMenuItem("exit", quit)
languageSelection := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "changeLanguage",
}), h.LanguageSelection)
h.localizerListener.AddMenuItem("changeLanguage", languageSelection)
ffPathSelection := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "changeFFPath",
}), h.convertorHandler.FfPathSelection)
h.localizerListener.AddMenuItem("changeFFPath", ffPathSelection)
settings := fyne.NewMenu(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "settings",
}), languageSelection, ffPathSelection, quit)
h.localizerListener.AddMenu("settings", settings)
return settings
}
func (h MenuHandler) getMenuHelp() *fyne.Menu {
about := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "about",
}), h.openAbout)
h.localizerListener.AddMenuItem("about", about)
help := fyne.NewMenu(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "help",
}), about)
h.localizerListener.AddMenu("help", help)
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",
})
}
h.menuView.About(ffmpeg, ffprobe)
}
func (h MenuHandler) LanguageSelection() {
h.localizerView.LanguageSelection(func(lang kernel.Lang) {
_, _ = h.localizerRepository.Save(lang.Code)
h.convertorHandler.MainConvertor()
})
}
type menuItems struct {
menuItem map[string]*fyne.MenuItem
menu map[string]*fyne.Menu
}
type LocalizerListener struct {
menuItems *menuItems
}
type localizerListenerContract interface {
AddMenu(messageID string, menu *fyne.Menu)
AddMenuItem(messageID string, menuItem *fyne.MenuItem)
}
func NewLocalizerListener() *LocalizerListener {
return &LocalizerListener{
&menuItems{menuItem: map[string]*fyne.MenuItem{}, menu: map[string]*fyne.Menu{}},
}
}
func (l LocalizerListener) AddMenu(messageID string, menu *fyne.Menu) {
l.menuItems.menu[messageID] = menu
}
func (l LocalizerListener) AddMenuItem(messageID string, menuItem *fyne.MenuItem) {
l.menuItems.menuItem[messageID] = menuItem
}
func (l LocalizerListener) Change(localizerService kernel.LocalizerContract) {
for messageID, menu := range l.menuItems.menuItem {
menu.Label = localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: messageID})
}
for messageID, menu := range l.menuItems.menu {
menu.Label = localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: messageID})
menu.Refresh()
}
}

View File

@ -1,4 +1,4 @@
package utils
package helper
import (
"fyne.io/fyne/v2"

View File

@ -1,7 +1,7 @@
//go:build !windows
// +build !windows
package utils
package helper
func PathSeparator() string {
return "/"

View File

@ -1,7 +1,7 @@
//go:build windows
// +build windows
package utils
package helper
func PathSeparator() string {
return "\\"

View File

@ -1,7 +1,7 @@
//go:build !windows
// +build !windows
package utils
package helper
import (
"os/exec"

View File

@ -1,7 +1,7 @@
//go:build windows
// +build windows
package utils
package helper
import (
"os/exec"

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -1,137 +0,0 @@
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()
}
}

View File

@ -1,87 +0,0 @@
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
}

View File

@ -1,84 +0,0 @@
package encoder
import (
"errors"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder"
)
type ConvertorFormatContract interface {
GetTitle() string
AddEncoder(encoder encoder.EncoderDataContract)
GetFileType() encoder.FileTypeContract
GetEncoders() map[int]encoder.EncoderDataContract
}
type ConvertorFormat struct {
title string
fileType encoder.FileTypeContract
encoders map[int]encoder.EncoderDataContract
}
func NewConvertorFormat(title string, fileType encoder.FileTypeContract) ConvertorFormatContract {
return &ConvertorFormat{
title: title,
fileType: fileType,
encoders: map[int]encoder.EncoderDataContract{},
}
}
func (f *ConvertorFormat) GetTitle() string {
return f.title
}
func (f *ConvertorFormat) AddEncoder(encoder encoder.EncoderDataContract) {
f.encoders[len(f.encoders)] = encoder
}
func (f *ConvertorFormat) GetEncoders() map[int]encoder.EncoderDataContract {
return f.encoders
}
func (f *ConvertorFormat) GetFileType() encoder.FileTypeContract {
return f.fileType
}
type ConvertorFormatsContract interface {
NewEncoder(encoderName string) bool
GetFormats() map[string]ConvertorFormatContract
GetFormat(format string) (ConvertorFormatContract, error)
}
type ConvertorFormats struct {
formats map[string]ConvertorFormatContract
}
func NewConvertorFormats() ConvertorFormatsContract {
return &ConvertorFormats{
formats: map[string]ConvertorFormatContract{},
}
}
func (f *ConvertorFormats) NewEncoder(encoderName string) bool {
if supportEncoders[encoderName] == nil {
return false
}
data := supportEncoders[encoderName]()
for _, format := range data.GetFormats() {
if f.formats[format] == nil {
f.formats[format] = NewConvertorFormat(format, data.GetFileType())
}
f.formats[format].AddEncoder(data)
}
return true
}
func (f *ConvertorFormats) GetFormats() map[string]ConvertorFormatContract {
return f.formats
}
func (f *ConvertorFormats) GetFormat(format string) (ConvertorFormatContract, error) {
if f.formats[format] == nil {
return &ConvertorFormat{}, errors.New("not found ConvertorFormat")
}
return f.formats[format], nil
}

View File

@ -1,74 +0,0 @@
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,
}

View File

@ -1,139 +0,0 @@
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
}

View File

@ -1,217 +0,0 @@
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
}

View File

@ -1,152 +0,0 @@
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)
}
}

View File

@ -1,40 +0,0 @@
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)
}

View File

@ -1,66 +0,0 @@
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)
}

View File

@ -1,84 +0,0 @@
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)
}

View File

@ -1,110 +0,0 @@
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)
}

View File

@ -1,111 +0,0 @@
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
}

View File

@ -1,16 +0,0 @@
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)
}

View File

@ -1,96 +0,0 @@
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()
}

View File

@ -1,67 +0,0 @@
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()
}

View File

@ -1,14 +0,0 @@
//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()
}

View File

@ -1,59 +0,0 @@
//go:build linux
// +build linux
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"
"golang.org/x/image/colornames"
"image/color"
)
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}
progressDownloadFFmpegMessage := canvas.NewText("", color.RGBA{R: 49, G: 127, B: 114, A: 255})
progressDownloadFFmpegMessage.TextSize = 16
progressDownloadFFmpegMessage.TextStyle = fyne.TextStyle{Bold: true}
progressBar := widget.NewProgressBar()
var buttonDownloadFFmpeg *widget.Button
buttonDownloadFFmpeg = widget.NewButton(lang.L("download"), func() {
fyne.Do(func() {
buttonDownloadFFmpeg.Disable()
})
go func() {
err := donwloadFFmpeg(progressBar, progressDownloadFFmpegMessage)
if err != nil {
errorDownloadFFmpegMessage.Text = err.Error()
}
fyne.Do(func() {
buttonDownloadFFmpeg.Enable()
})
}()
})
downloadFFmpegFromSiteMessage := lang.L("downloadFFmpegFromSite")
return container.NewVBox(
canvas.NewLine(colornames.Darkgreen),
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,
container.NewHScroll(errorDownloadFFmpegMessage),
progressDownloadFFmpegMessage,
progressBar,
)),
)
}

View File

@ -1,21 +0,0 @@
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
})
}

View File

@ -1,15 +0,0 @@
//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
}

View File

@ -1,236 +0,0 @@
//go:build linux
// +build linux
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/internal/application"
"github.com/ulikunitz/xz"
"io"
"net/http"
"os"
"path/filepath"
)
func startDownload(app application.AppContract, progressBar *widget.ProgressBar, progressMessage *canvas.Text, save func(ffmpegPath string, ffprobePath string, ffplayPath string) error) error {
var err error
dir, err := localSharePath()
if err != nil {
return err
}
dir = filepath.Join(dir, "fyne", app.FyneApp().UniqueID())
err = os.MkdirAll(dir, 0755)
if err != nil {
return err
}
fyne.Do(func() {
progressMessage.Text = lang.L("downloadRun")
progressMessage.Refresh()
})
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
}
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 = 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
}
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
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
f, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer f.Close()
buf := make([]byte, 32*1024)
var downloaded int64
for {
n, err := resp.Body.Read(buf)
if err != nil {
if err == io.EOF {
break
}
return err
}
if n > 0 {
f.Write(buf[:n])
downloaded += int64(n)
progressBar.Value = float64(downloaded) / float64(resp.ContentLength) * 100
fyne.Do(func() {
progressBar.Refresh()
})
}
}
return nil
}
func unTarXz(fileTar string, directory string, progressBar *widget.ProgressBar) error {
progressBar.Value = 0
progressBar.Max = 100
fyne.Do(func() {
progressBar.Refresh()
})
f, err := os.Open(fileTar)
if err != nil {
return err
}
defer f.Close()
xzReader, err := xz.NewReader(f)
if err != nil {
return err
}
tarReader := tar.NewReader(xzReader)
totalFiles := 0
for {
_, err := tarReader.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
totalFiles++
}
// Rewind back to the beginning of the file to re-process
_, err = f.Seek(0, 0)
if err != nil {
return err
}
xzReader, err = xz.NewReader(f)
if err != nil {
return err
}
tarReader = tar.NewReader(xzReader)
// We count the number of files already unpacked
unpackedFiles := 0
for {
header, err := tarReader.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
targetPath := filepath.Join(directory, header.Name)
switch header.Typeflag {
case tar.TypeDir:
err := os.MkdirAll(targetPath, 0755)
if err != nil {
return err
}
case tar.TypeReg:
outFile, err := os.Create(targetPath)
if err != nil {
return err
}
defer outFile.Close()
_, err = io.Copy(outFile, tarReader)
if err != nil {
return err
}
default:
return errors.New("unsupported file type")
}
unpackedFiles++
progressBar.Value = float64(unpackedFiles) / float64(totalFiles) * 100
fyne.Do(func() {
progressBar.Refresh()
})
}
ffmpegPath := filepath.Join(directory, "ffmpeg-master-latest-linux64-gpl", "bin", "ffmpeg")
err = os.Chmod(ffmpegPath, 0755)
if err != nil {
return err
}
ffprobePath := filepath.Join(directory, "ffmpeg-master-latest-linux64-gpl", "bin", "ffprobe")
err = os.Chmod(ffprobePath, 0755)
if err != nil {
return err
}
ffplayPath := filepath.Join(directory, "ffmpeg-master-latest-linux64-gpl", "bin", "ffplay")
err = os.Chmod(ffplayPath, 0755)
if err != nil {
return err
}
return nil
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,169 +0,0 @@
package encoder
import "errors"
type EncoderContract interface {
GetName() string
GetParams() []string
GetParameter(name string) (ParameterContract, error)
}
type ParameterContract interface {
GetName() string
Set(string) error
Get() string
IsEnabled() bool
SetEnable()
SetDisable()
}
type EncoderDataContract interface {
GetTitle() string
GetFormats() []string
GetFileType() FileTypeContract
NewEncoder() EncoderContract
}
type data struct {
title string
formats []string
fileType FileTypeContract
encoder func() EncoderContract
}
func NewData(title string, formats []string, fileType FileTypeContract, encoder func() EncoderContract) EncoderDataContract {
return &data{
title: title,
formats: formats,
fileType: fileType,
encoder: encoder,
}
}
func (data *data) GetTitle() string {
return data.title
}
func (data *data) GetFormats() []string {
return data.formats
}
func (data *data) NewEncoder() EncoderContract {
return data.encoder()
}
func (data *data) GetFileType() FileTypeContract {
return data.fileType
}
type FileTypeContract interface {
Name() string
Ordinal() int
}
const (
Video = iota
Audio
Image
)
type FileType uint
var fileTypeStrings = []string{
"video",
"audio",
"image",
}
func (fileType FileType) Name() string {
return fileTypeStrings[fileType]
}
func (fileType FileType) Ordinal() int {
return int(fileType)
}
func GetListFileType() []FileTypeContract {
return []FileTypeContract{
FileType(Video),
FileType(Audio),
FileType(Image),
}
}
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) EncoderContract {
return &encoder{
name: name,
parameters: parameters,
getParams: getParams,
}
}
func (e *encoder) GetName() string {
return e.name
}
func (e *encoder) GetParams() []string {
return e.getParams(e.parameters)
}
func (e *encoder) GetParameter(name string) (ParameterContract, error) {
if e.parameters[name] == nil {
return nil, errors.New("parameter not found")
}
return e.parameters[name], nil
}
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)) ParameterContract {
return &parameter{
name: name,
isEnabled: isEnabled,
parameter: defaultParameter,
setParameter: setParameter,
}
}
func (p *parameter) GetName() string {
return p.name
}
func (p *parameter) Set(s string) (err error) {
if p.setParameter != nil {
s, err = p.setParameter(s)
if err != nil {
return err
}
}
p.parameter = s
return nil
}
func (p *parameter) Get() string {
return p.parameter
}
func (p *parameter) IsEnabled() bool {
return p.isEnabled
}
func (p *parameter) SetEnable() {
p.isEnabled = true
}
func (p *parameter) SetDisable() {
p.isEnabled = false
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,58 +0,0 @@
package h264_nvenc
import (
"errors"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder"
)
var Presets = []string{
"default",
"slow",
"medium",
"fast",
"hp",
"hq",
"bd",
"ll",
"llhq",
"llhp",
"lossless",
"losslesshp",
}
func NewEncoder() encoder.EncoderContract {
parameters := map[string]encoder.ParameterContract{
"preset": newParameterPreset(),
}
getParams := func(parameters map[string]encoder.ParameterContract) []string {
params := []string{"-c:v", "h264_nvenc"}
if parameters["preset"] != nil && parameters["preset"].IsEnabled() {
params = append(params, "-preset", parameters["preset"].Get())
}
return params
}
return encoder.NewEncoder("h264_nvenc", parameters, getParams)
}
func NewData() encoder.EncoderDataContract {
title := "h264_nvenc"
formats := []string{"mp4"}
fileType := encoder.FileType(encoder.Video)
return encoder.NewData(title, formats, fileType, NewEncoder)
}
func newParameterPreset() encoder.ParameterContract {
setParameter := func(s string) (string, error) {
for _, value := range Presets {
if value == s {
return value, nil
}
}
return "", errors.New("preset not found")
}
return encoder.NewParameter("preset", false, "default", setParameter)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,56 +0,0 @@
package libx264
import (
"errors"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder"
)
var Presets = []string{
"ultrafast",
"superfast",
"veryfast",
"faster",
"fast",
"medium",
"slow",
"slower",
"veryslow",
"placebo",
}
func NewEncoder() encoder.EncoderContract {
parameters := map[string]encoder.ParameterContract{
"preset": newParameterPreset(),
}
getParams := func(parameters map[string]encoder.ParameterContract) []string {
params := []string{"-c:v", "libx264"}
if parameters["preset"] != nil && parameters["preset"].IsEnabled() {
params = append(params, "-preset", parameters["preset"].Get())
}
return params
}
return encoder.NewEncoder("libx264", parameters, getParams)
}
func NewData() encoder.EncoderDataContract {
title := "libx264"
formats := []string{"mp4"}
fileType := encoder.FileType(encoder.Video)
return encoder.NewData(title, formats, fileType, NewEncoder)
}
func newParameterPreset() encoder.ParameterContract {
setParameter := func(s string) (string, error) {
for _, value := range Presets {
if value == s {
return value, nil
}
}
return "", errors.New("preset not found")
}
return encoder.NewParameter("preset", false, "medium", setParameter)
}

View File

@ -1,56 +0,0 @@
package libx265
import (
"errors"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder"
)
var Presets = []string{
"ultrafast",
"superfast",
"veryfast",
"faster",
"fast",
"medium",
"slow",
"slower",
"veryslow",
"placebo",
}
func NewEncoder() encoder.EncoderContract {
parameters := map[string]encoder.ParameterContract{
"preset": newParameterPreset(),
}
getParams := func(parameters map[string]encoder.ParameterContract) []string {
params := []string{"-c:v", "libx265"}
if parameters["preset"] != nil && parameters["preset"].IsEnabled() {
params = append(params, "-preset", parameters["preset"].Get())
}
return params
}
return encoder.NewEncoder("libx265", parameters, getParams)
}
func NewData() encoder.EncoderDataContract {
title := "libx265"
formats := []string{"mp4"}
fileType := encoder.FileType(encoder.Video)
return encoder.NewData(title, formats, fileType, NewEncoder)
}
func newParameterPreset() encoder.ParameterContract {
setParameter := func(s string) (string, error) {
for _, value := range Presets {
if value == s {
return value, nil
}
}
return "", errors.New("preset not found")
}
return encoder.NewParameter("preset", false, "medium", setParameter)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,21 +0,0 @@
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)
}

View File

@ -1,139 +0,0 @@
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
}

View File

@ -1,73 +0,0 @@
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
}

View File

@ -1,124 +0,0 @@
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
}

View File

@ -1,221 +0,0 @@
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
}

View File

@ -1,29 +0,0 @@
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)
}

View File

@ -1,129 +0,0 @@
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
}

View File

@ -1,414 +0,0 @@
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()
})
}

View File

@ -1,15 +0,0 @@
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,
}

View File

@ -1,64 +0,0 @@
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/internal/ffmpeg/encoder"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/h264_nvenc"
)
func View(encoder encoder.EncoderContract) []*widget.FormItem {
items := []*widget.FormItem{}
items = append(items, presetParameter(encoder)...)
return items
}
func presetParameter(encoder encoder.EncoderContract) []*widget.FormItem {
parameter, err := encoder.GetParameter("preset")
if err != nil {
return nil
}
presets := map[string]string{}
presetsForSelect := []string{}
presetDefault := ""
for _, name := range h264_nvenc.Presets {
title := name
presetsForSelect = append(presetsForSelect, name)
presets[title] = name
if name == parameter.Get() {
presetDefault = title
}
}
elementSelect := widget.NewSelect(presetsForSelect, func(s string) {
if presets[s] == "" {
return
}
parameter.Set(presets[s])
})
elementSelect.SetSelected(presetDefault)
elementSelect.Hide()
checkboxTitle := lang.L("parameterCheckbox")
elementCheckbox := widget.NewCheck(checkboxTitle, func(b bool) {
if b == true {
parameter.SetEnable()
elementSelect.Show()
return
}
parameter.SetDisable()
elementSelect.Hide()
})
return []*widget.FormItem{
{
Text: lang.L("formPreset"),
Widget: container.NewVBox(elementCheckbox, elementSelect),
},
}
}

View File

@ -1,64 +0,0 @@
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/internal/ffmpeg/encoder"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/libx264"
)
func View(encoder encoder.EncoderContract) []*widget.FormItem {
items := []*widget.FormItem{}
items = append(items, presetParameter(encoder)...)
return items
}
func presetParameter(encoder encoder.EncoderContract) []*widget.FormItem {
parameter, err := encoder.GetParameter("preset")
if err != nil {
return nil
}
presets := map[string]string{}
presetsForSelect := []string{}
presetDefault := ""
for _, name := range libx264.Presets {
title := lang.L("preset_" + name)
presetsForSelect = append(presetsForSelect, title)
presets[title] = name
if name == parameter.Get() {
presetDefault = title
}
}
elementSelect := widget.NewSelect(presetsForSelect, func(s string) {
if presets[s] == "" {
return
}
parameter.Set(presets[s])
})
elementSelect.SetSelected(presetDefault)
elementSelect.Hide()
checkboxTitle := lang.L("parameterCheckbox")
elementCheckbox := widget.NewCheck(checkboxTitle, func(b bool) {
if b == true {
parameter.SetEnable()
elementSelect.Show()
return
}
parameter.SetDisable()
elementSelect.Hide()
})
return []*widget.FormItem{
{
Text: lang.L("formPreset"),
Widget: container.NewVBox(elementCheckbox, elementSelect),
},
}
}

View File

@ -1,64 +0,0 @@
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/internal/ffmpeg/encoder"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg/encoder/libx265"
)
func View(encoder encoder.EncoderContract) []*widget.FormItem {
items := []*widget.FormItem{}
items = append(items, presetParameter(encoder)...)
return items
}
func presetParameter(encoder encoder.EncoderContract) []*widget.FormItem {
parameter, err := encoder.GetParameter("preset")
if err != nil {
return nil
}
presets := map[string]string{}
presetsForSelect := []string{}
presetDefault := ""
for _, name := range libx265.Presets {
title := lang.L("preset_" + name)
presetsForSelect = append(presetsForSelect, title)
presets[title] = name
if name == parameter.Get() {
presetDefault = title
}
}
elementSelect := widget.NewSelect(presetsForSelect, func(s string) {
if presets[s] == "" {
return
}
parameter.Set(presets[s])
})
elementSelect.SetSelected(presetDefault)
elementSelect.Hide()
checkboxTitle := lang.L("parameterCheckbox")
elementCheckbox := widget.NewCheck(checkboxTitle, func(b bool) {
if b == true {
parameter.SetEnable()
elementSelect.Show()
return
}
parameter.SetDisable()
elementSelect.Hide()
})
return []*widget.FormItem{
{
Text: lang.L("formPreset"),
Widget: container.NewVBox(elementCheckbox, elementSelect),
},
}
}

View File

@ -1,39 +0,0 @@
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,
)
}

View File

@ -1,98 +0,0 @@
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)
}

View File

@ -1,92 +0,0 @@
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)
}

Some files were not shown because too many files have changed in this diff Show More