├── .gitignore ├── .idea ├── encodings.xml ├── misc.xml ├── modules.xml ├── scopes │ └── scope_settings.xml ├── vcs.xml ├── vix-core.iml └── workspace.xml ├── AUTHORS ├── ChangeLog ├── LICENSE ├── Makefile.am ├── NEWS ├── README ├── README.md ├── configure.ac ├── etc ├── Makefile.am ├── cron │ ├── Makefile.am │ ├── crontabs │ │ └── Makefile.am │ └── readme.txt └── exports ├── m4 ├── ax_pthread.m4 ├── ax_python_devel.m4 └── tuxbox.m4 ├── po ├── Makefile.am ├── ar.po ├── bg.po ├── ca.po ├── cs.po ├── da.po ├── de.po ├── el.po ├── en.po ├── en_GB.po ├── es.po ├── et.po ├── fa.po ├── fi.po ├── fr.po ├── fy.po ├── he.po ├── hr.po ├── hu.po ├── is.po ├── it.po ├── lt.po ├── lv.po ├── nb.po ├── nl.po ├── no.po ├── pl.po ├── pt.po ├── pt_BR.po ├── ro.po ├── ru.po ├── sk.po ├── sl.po ├── sr.po ├── sv.po ├── th.po ├── tr.po ├── uk.po ├── updateallpo-multiOS.sh ├── vix.pot ├── xml2po.py └── zh_CN.po └── src ├── BackupManager.py ├── H9SDmanager.py ├── IPKInstaller.py ├── ImageManager.py ├── LICENSE ├── Makefile.am ├── MountManager.py ├── Multibootmgr.py ├── RestoreWizard.py ├── ScriptRunner.py ├── SoftcamManager.py ├── SwapManager.py ├── __init__.py ├── images ├── Makefile.am ├── busy1.png ├── busy10.png ├── busy11.png ├── busy12.png ├── busy13.png ├── busy14.png ├── busy15.png ├── busy16.png ├── busy17.png ├── busy18.png ├── busy19.png ├── busy2.png ├── busy20.png ├── busy21.png ├── busy22.png ├── busy23.png ├── busy24.png ├── busy3.png ├── busy4.png ├── busy5.png ├── busy6.png ├── busy7.png ├── busy8.png ├── busy9.png ├── dev_cdrom.png ├── dev_cf.png ├── dev_hdd.png ├── dev_mmc.png ├── dev_sd.png └── dev_usb.png ├── install.png ├── installable.png ├── installed.png ├── maintainer.info ├── noprev.png ├── plugin.py ├── remove.png ├── restorewizard.xml ├── setup.xml ├── ui.py ├── update.png ├── upgrade.png └── upgradeable.png /.gitignore: -------------------------------------------------------------------------------- 1 | /*.DS_Store 2 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vix-core.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 1391448462663 37 | 1391448462663 38 | 39 | 40 | 41 | 42 | 44 | 45 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/AUTHORS -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ACLOCAL_AMFLAGS = -I m4 2 | 3 | SUBDIRS = src po etc 4 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/NEWS -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/README -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Gitter](https://badges.gitter.im/OpenViX/community.svg)](https://gitter.im/OpenViX/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) 2 | 3 | vix-core 4 | ======== 5 | 6 | OpenViX Core plugin 7 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT(plugins,0.0.1) 2 | AM_INIT_AUTOMAKE(plugins,0.0.1) 3 | 4 | TUXBOX_APPS_DIRECTORY 5 | 6 | AC_PATH_PROG(MSGFMT, msgfmt, AC_MSG_ERROR(Could not find msgfmt)) 7 | 8 | AM_PATH_PYTHON 9 | AC_PYTHON_DEVEL 10 | 11 | AC_PROG_CXX 12 | 13 | CPPFLAGS="$CPPFLAGS $PYTHON_CPPFLAGS" 14 | LDFLAGS="$LDFLAGS $PYTHON_LDFLAGS" 15 | 16 | AC_PATH_PROG(MSGFMT, msgfmt, AC_MSG_ERROR(Could not find msgfmt)) 17 | AC_PATH_PROG(MSGINIT, msginit, AC_MSG_ERROR(Could not find msginit)) 18 | AC_PATH_PROG(MSGMERGE, msgmerge, AC_MSG_ERROR(Could not find msgmerge)) 19 | AC_PATH_PROG(MSGUNIQ, msguniq, AC_MSG_ERROR(Could not find msguniq)) 20 | AC_PATH_PROG(XGETTEXT, xgettext, AC_MSG_ERROR(Could not find xgettext)) 21 | 22 | AC_OUTPUT([ 23 | Makefile 24 | etc/Makefile 25 | etc/cron/Makefile 26 | etc/cron/crontabs/Makefile 27 | po/Makefile 28 | src/Makefile 29 | src/images/Makefile 30 | ]) 31 | -------------------------------------------------------------------------------- /etc/Makefile.am: -------------------------------------------------------------------------------- 1 | installdir = /etc/ 2 | SUBDIRS = cron 3 | install_DATA = exports 4 | -------------------------------------------------------------------------------- /etc/cron/Makefile.am: -------------------------------------------------------------------------------- 1 | installdir = ${sysconfdir}/cron 2 | SUBDIRS = crontabs 3 | install_DATA = readme.txt 4 | -------------------------------------------------------------------------------- /etc/cron/crontabs/Makefile.am: -------------------------------------------------------------------------------- 1 | installdir = ${sysconfdir}/cron/crontabs 2 | -------------------------------------------------------------------------------- /etc/cron/readme.txt: -------------------------------------------------------------------------------- 1 | To use crontabs, please use the cron manager to setup your crontabs. Alternatively, manually create a file called 'root' and place your required items in there. 2 | 3 | # * * * * * command to execute 4 | # ┬ ┬ ┬ ┬ ┬ 5 | # │ │ │ │ │ 6 | # │ │ │ │ │ 7 | # │ │ │ │ └───── day of week (0 - 7) (0 to 6 are Sunday to Saturday, or use names; 7 is Sunday, the same as 0) 8 | # │ │ │ └────────── month (1 - 12) 9 | # │ │ └─────────────── day of month (1 - 31) 10 | # │ └──────────────────── hour (0 - 23) 11 | # └───────────────────────── min (0 - 59) 12 | -------------------------------------------------------------------------------- /etc/exports: -------------------------------------------------------------------------------- 1 | /media/hdd/ 192.168.0.0/255.255.0.0(rw,no_root_squash,sync,no_subtree_check) 2 | /media/hdd2/ 192.168.0.0/255.255.0.0(rw,no_root_squash,sync,no_subtree_check) 3 | /media/usb/ 192.168.0.0/255.255.0.0(rw,no_root_squash,sync,no_subtree_check) 4 | -------------------------------------------------------------------------------- /m4/ax_python_devel.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # https://www.gnu.org/software/autoconf-archive/ax_python_devel.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_PYTHON_DEVEL([version]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # Note: Defines as a precious variable "PYTHON_VERSION". Don't override it 12 | # in your configure.ac. 13 | # 14 | # This macro checks for Python and tries to get the include path to 15 | # 'Python.h'. It provides the $(PYTHON_CPPFLAGS) and $(PYTHON_LIBS) output 16 | # variables. It also exports $(PYTHON_EXTRA_LIBS) and 17 | # $(PYTHON_EXTRA_LDFLAGS) for embedding Python in your code. 18 | # 19 | # You can search for some particular version of Python by passing a 20 | # parameter to this macro, for example ">= '2.3.1'", or "== '2.4'". Please 21 | # note that you *have* to pass also an operator along with the version to 22 | # match, and pay special attention to the single quotes surrounding the 23 | # version number. Don't use "PYTHON_VERSION" for this: that environment 24 | # variable is declared as precious and thus reserved for the end-user. 25 | # 26 | # This macro should work for all versions of Python >= 2.1.0. As an end 27 | # user, you can disable the check for the python version by setting the 28 | # PYTHON_NOVERSIONCHECK environment variable to something else than the 29 | # empty string. 30 | # 31 | # If you need to use this macro for an older Python version, please 32 | # contact the authors. We're always open for feedback. 33 | # 34 | # LICENSE 35 | # 36 | # Copyright (c) 2009 Sebastian Huber 37 | # Copyright (c) 2009 Alan W. Irwin 38 | # Copyright (c) 2009 Rafael Laboissiere 39 | # Copyright (c) 2009 Andrew Collier 40 | # Copyright (c) 2009 Matteo Settenvini 41 | # Copyright (c) 2009 Horst Knorr 42 | # Copyright (c) 2013 Daniel Mullner 43 | # 44 | # This program is free software: you can redistribute it and/or modify it 45 | # under the terms of the GNU General Public License as published by the 46 | # Free Software Foundation, either version 3 of the License, or (at your 47 | # option) any later version. 48 | # 49 | # This program is distributed in the hope that it will be useful, but 50 | # WITHOUT ANY WARRANTY; without even the implied warranty of 51 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 52 | # Public License for more details. 53 | # 54 | # You should have received a copy of the GNU General Public License along 55 | # with this program. If not, see . 56 | # 57 | # As a special exception, the respective Autoconf Macro's copyright owner 58 | # gives unlimited permission to copy, distribute and modify the configure 59 | # scripts that are the output of Autoconf when processing the Macro. You 60 | # need not follow the terms of the GNU General Public License when using 61 | # or distributing such scripts, even though portions of the text of the 62 | # Macro appear in them. The GNU General Public License (GPL) does govern 63 | # all other use of the material that constitutes the Autoconf Macro. 64 | # 65 | # This special exception to the GPL applies to versions of the Autoconf 66 | # Macro released by the Autoconf Archive. When you make and distribute a 67 | # modified version of the Autoconf Macro, you may extend this special 68 | # exception to the GPL to apply to your modified version as well. 69 | 70 | #serial 21 71 | 72 | AU_ALIAS([AC_PYTHON_DEVEL], [AX_PYTHON_DEVEL]) 73 | AC_DEFUN([AX_PYTHON_DEVEL],[ 74 | # 75 | # Allow the use of a (user set) custom python version 76 | # 77 | AC_ARG_VAR([PYTHON_VERSION],[The installed Python 78 | version to use, for example '2.3'. This string 79 | will be appended to the Python interpreter 80 | canonical name.]) 81 | 82 | AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]]) 83 | if test -z "$PYTHON"; then 84 | AC_MSG_ERROR([Cannot find python$PYTHON_VERSION in your system path]) 85 | PYTHON_VERSION="" 86 | fi 87 | 88 | # 89 | # Check for a version of Python >= 2.1.0 90 | # 91 | AC_MSG_CHECKING([for a version of Python >= '2.1.0']) 92 | ac_supports_python_ver=`$PYTHON -c "import sys; \ 93 | ver = sys.version.split ()[[0]]; \ 94 | print (ver >= '2.1.0')"` 95 | if test "$ac_supports_python_ver" != "True"; then 96 | if test -z "$PYTHON_NOVERSIONCHECK"; then 97 | AC_MSG_RESULT([no]) 98 | AC_MSG_FAILURE([ 99 | This version of the AC@&t@_PYTHON_DEVEL macro 100 | doesn't work properly with versions of Python before 101 | 2.1.0. You may need to re-run configure, setting the 102 | variables PYTHON_CPPFLAGS, PYTHON_LIBS, PYTHON_SITE_PKG, 103 | PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand. 104 | Moreover, to disable this check, set PYTHON_NOVERSIONCHECK 105 | to something else than an empty string. 106 | ]) 107 | else 108 | AC_MSG_RESULT([skip at user request]) 109 | fi 110 | else 111 | AC_MSG_RESULT([yes]) 112 | fi 113 | 114 | # 115 | # if the macro parameter ``version'' is set, honour it 116 | # 117 | if test -n "$1"; then 118 | AC_MSG_CHECKING([for a version of Python $1]) 119 | ac_supports_python_ver=`$PYTHON -c "import sys; \ 120 | ver = sys.version.split ()[[0]]; \ 121 | print (ver $1)"` 122 | if test "$ac_supports_python_ver" = "True"; then 123 | AC_MSG_RESULT([yes]) 124 | else 125 | AC_MSG_RESULT([no]) 126 | AC_MSG_ERROR([this package requires Python $1. 127 | If you have it installed, but it isn't the default Python 128 | interpreter in your system path, please pass the PYTHON_VERSION 129 | variable to configure. See ``configure --help'' for reference. 130 | ]) 131 | PYTHON_VERSION="" 132 | fi 133 | fi 134 | 135 | # 136 | # Check for Python include path 137 | # 138 | AC_MSG_CHECKING([for Python include path]) 139 | if test -z "$PYTHON_CPPFLAGS"; then 140 | python_path=`$PYTHON -c "import distutils.sysconfig; \ 141 | print (distutils.sysconfig.get_python_inc ());"` 142 | plat_python_path=`$PYTHON -c "import distutils.sysconfig; \ 143 | print (distutils.sysconfig.get_python_inc (plat_specific=1));"` 144 | if test -n "${python_path}"; then 145 | if test "${plat_python_path}" != "${python_path}"; then 146 | python_path="-I$python_path -I$plat_python_path" 147 | else 148 | python_path="-I$python_path" 149 | fi 150 | fi 151 | PYTHON_CPPFLAGS=$python_path 152 | fi 153 | AC_MSG_RESULT([$PYTHON_CPPFLAGS]) 154 | AC_SUBST([PYTHON_CPPFLAGS]) 155 | 156 | # 157 | # Check for Python library path 158 | # 159 | AC_MSG_CHECKING([for Python library path]) 160 | if test -z "$PYTHON_LIBS"; then 161 | # (makes two attempts to ensure we've got a version number 162 | # from the interpreter) 163 | ac_python_version=`cat< $@ 17 | 18 | vix.pot: vix-py.pot vix-xml.pot 19 | sed --in-place vix-py.pot --expression=s/CHARSET/UTF-8/ 20 | sed --in-place vix-xml.pot --expression=s/CHARSET/UTF-8/ 21 | cat $^ | $(MSGUNIQ) --no-wrap --no-location -o $@ - 22 | 23 | %.po: vix.pot 24 | if [ -f $@ ]; then \ 25 | $(MSGMERGE) --backup=none --no-wrap --no-location -s -N -U $@ $< && touch $@; \ 26 | else \ 27 | $(MSGINIT) -l $@ -o $@ -i $< --no-translator; \ 28 | fi 29 | 30 | .po.mo: 31 | $(MSGFMT) -o $@ $< 32 | 33 | BUILT_SOURCES = $(LANGMO) 34 | CLEANFILES = $(LANGMO) vix-py.pot vix.pot 35 | 36 | dist-hook: $(LANGPO) 37 | 38 | install-data-local: $(LANGMO) 39 | for lang in $(LANGS); do \ 40 | $(mkinstalldirs) $(DESTDIR)$(plugindir)/locale/$$lang/LC_MESSAGES; \ 41 | $(INSTALL_DATA) $$lang.po $(DESTDIR)$(plugindir)/locale/vix-$$lang.po; \ 42 | $(INSTALL_DATA) $$lang.mo $(DESTDIR)$(plugindir)/locale/$$lang/LC_MESSAGES/vix.mo; \ 43 | done 44 | 45 | uninstall-local: 46 | for lang in $(LANGS); do \ 47 | $(RM) $(DESTDIR)$(plugindir)/locale/vix-$$lang.po; \ 48 | $(RM) $(DESTDIR)$(plugindir)/locale/$$lang/LC_MESSAGES/vix.mo; \ 49 | done 50 | -------------------------------------------------------------------------------- /po/fa.po: -------------------------------------------------------------------------------- 1 | # Persian translations for plugins package. 2 | # Copyright (C) 2012 THE plugins'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the plugins package. 4 | # Automatically generated, 2012. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: plugins 0.0.1\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2021-12-18 09:16+0000\n" 11 | "PO-Revision-Date: 2013-10-20 17:17+0100\n" 12 | "Last-Translator: Andy Blackburn \n" 13 | "Language-Team: none\n" 14 | "Language: fa\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | 19 | msgid "%a %e %b %-H:%M" 20 | msgstr "" 21 | 22 | #, python-format 23 | msgid "%s downloads" 24 | msgstr "" 25 | 26 | #, python-format 27 | msgid "%s: " 28 | msgstr "" 29 | 30 | msgid "- Max backups to keep (0==all)" 31 | msgstr "" 32 | 33 | msgid "- Query before backup starts" 34 | msgstr "" 35 | 36 | msgid "- Query before image backup starts" 37 | msgstr "" 38 | 39 | msgid "- Repeat how often" 40 | msgstr "" 41 | 42 | msgid "- Time of backup to start" 43 | msgstr "" 44 | 45 | msgid "128 Mb" 46 | msgstr "" 47 | 48 | msgid "16 Mb" 49 | msgstr "" 50 | 51 | msgid "256 Mb" 52 | msgstr "" 53 | 54 | msgid "30 Days" 55 | msgstr "" 56 | 57 | msgid "32 Mb" 58 | msgstr "" 59 | 60 | msgid "64 Mb" 61 | msgstr "" 62 | 63 | msgid "8 Mb" 64 | msgstr "" 65 | 66 | msgid "96 Mb" 67 | msgstr "" 68 | 69 | msgid "A background update check is in progress, please try again." 70 | msgstr "" 71 | 72 | msgid "A backup has been detected." 73 | msgstr "" 74 | 75 | msgid "Activate" 76 | msgstr "" 77 | 78 | msgid "Active" 79 | msgstr "" 80 | 81 | msgid "Active:" 82 | msgstr "" 83 | 84 | msgid "All" 85 | msgstr "" 86 | 87 | msgid "Allows the script runner to be launched from extensions." 88 | msgstr "" 89 | 90 | msgid "Allows the softcam manager to be launched from extensions." 91 | msgstr "" 92 | 93 | msgid "Allows the softcam to be checked if it stopped working, and restarted automatically if needed." 94 | msgstr "" 95 | 96 | msgid "Allows you to close the window automatically." 97 | msgstr "" 98 | 99 | msgid "Allows you to schedule your backups." 100 | msgstr "" 101 | 102 | msgid "Allows you to schedule your image backups." 103 | msgstr "" 104 | 105 | msgid "Allows you to tag your backups to a box." 106 | msgstr "" 107 | 108 | msgid "Allows you to tag your image backup to a box. (default is box name/type)" 109 | msgstr "" 110 | 111 | msgid "Are you ready to install ?" 112 | msgstr "" 113 | 114 | #, python-format 115 | msgid "Are you sure you want to delete image slot %s ?" 116 | msgstr "" 117 | 118 | msgid "" 119 | "Are you sure you want to download this image:\n" 120 | " " 121 | msgstr "" 122 | 123 | msgid "" 124 | "Are you sure you want to restore this backup:\n" 125 | " " 126 | msgstr "" 127 | 128 | msgid "Automatic settings backup" 129 | msgstr "" 130 | 131 | msgid "Automatically created" 132 | msgstr "" 133 | 134 | msgid "Autostart" 135 | msgstr "" 136 | 137 | msgid "Backing up eMMC partitions for USB flash ..." 138 | msgstr "" 139 | 140 | msgid "Backing up eMMC partitions for USB flash..." 141 | msgstr "" 142 | 143 | msgid "Backing up files..." 144 | msgstr "" 145 | 146 | msgid "Backing up kernel..." 147 | msgstr "" 148 | 149 | msgid "Backing up root file system..." 150 | msgstr "" 151 | 152 | msgid "Backup complete." 153 | msgstr "" 154 | 155 | msgid "Backup complete..." 156 | msgstr "" 157 | 158 | msgid "Backup confirmation" 159 | msgstr "" 160 | 161 | msgid "Backup created" 162 | msgstr "" 163 | 164 | msgid "Backup failed - e. g. wrong backup destination or no space left on backup device." 165 | msgstr "" 166 | 167 | msgid "" 168 | "Backup in progress,\n" 169 | "Please wait for it to finish, before trying again." 170 | msgstr "" 171 | 172 | msgid "Backup location" 173 | msgstr "" 174 | 175 | msgid "Backup manager" 176 | msgstr "" 177 | 178 | msgid "Backup manager settings" 179 | msgstr "" 180 | 181 | msgid "Backup prefix" 182 | msgstr "" 183 | 184 | msgid "Backup/Flash/ReBoot system image." 185 | msgstr "" 186 | 187 | msgid "Backups to keep" 188 | msgstr "" 189 | 190 | msgid "Backups to prune" 191 | msgstr "" 192 | 193 | msgid "CCcam can't run whilst MGcamd is running" 194 | msgstr "" 195 | 196 | msgid "CCcam can't run whilst MGcamd is running." 197 | msgstr "" 198 | 199 | msgid "Cancel" 200 | msgstr "" 201 | 202 | #, python-format 203 | msgid "Cannot unmount partition '%s'. Make sure this partition is not in use. (SWAP, record/timeshift, etc.)" 204 | msgstr "" 205 | 206 | msgid "Change location" 207 | msgstr "" 208 | 209 | msgid "Checking free RAM.." 210 | msgstr "" 211 | 212 | msgid "Checking softcams..." 213 | msgstr "" 214 | 215 | msgid "Choose IPK folder" 216 | msgstr "" 217 | 218 | msgid "Choose files" 219 | msgstr "" 220 | 221 | msgid "Choose where to mount your devices to:" 222 | msgstr "" 223 | 224 | msgid "Close" 225 | msgstr "" 226 | 227 | msgid "Close window on success" 228 | msgstr "" 229 | 230 | msgid "Comparing against backup..." 231 | msgstr "" 232 | 233 | msgid "Compress" 234 | msgstr "" 235 | 236 | msgid "Create" 237 | msgstr "" 238 | 239 | msgid "Create a settings backup before updating." 240 | msgstr "" 241 | 242 | msgid "Create and Manage your SWAP files." 243 | msgstr "" 244 | 245 | msgid "Create:" 246 | msgstr "" 247 | 248 | msgid "Creating SWAP.." 249 | msgstr "" 250 | 251 | msgid "Creating list of installed plugins..." 252 | msgstr "" 253 | 254 | msgid "Creating zip..." 255 | msgstr "" 256 | 257 | msgid "Current status:" 258 | msgstr "" 259 | 260 | msgid "Daily" 261 | msgstr "" 262 | 263 | msgid "Deactivate" 264 | msgstr "" 265 | 266 | msgid "Delete" 267 | msgstr "" 268 | 269 | msgid "Delete failure - check device available." 270 | msgstr "" 271 | 272 | msgid "Deselect" 273 | msgstr "" 274 | 275 | msgid "Developer password" 276 | msgstr "" 277 | 278 | msgid "Developer username" 279 | msgstr "" 280 | 281 | #, python-format 282 | msgid "Device %s" 283 | msgstr "" 284 | 285 | msgid "Device: " 286 | msgstr "" 287 | 288 | msgid "Device: None available" 289 | msgstr "" 290 | 291 | msgid "Device: none available" 292 | msgstr "" 293 | 294 | msgid "Disable startup" 295 | msgstr "" 296 | 297 | msgid "" 298 | "Do you want to create a full image backup?\n" 299 | "This can take about 6 minutes to complete." 300 | msgstr "" 301 | 302 | #, python-format 303 | msgid "" 304 | "Do you want to flash image\n" 305 | "%s" 306 | msgstr "" 307 | 308 | msgid "Do you want to restart GUI now ?" 309 | msgstr "" 310 | 311 | msgid "Do you want to restore your Enigma2 plugins ?" 312 | msgstr "" 313 | 314 | msgid "Do you want to restore your Enigma2 settings ?" 315 | msgstr "" 316 | 317 | msgid "Do you want to restore your enigma2 settings ?" 318 | msgstr "" 319 | 320 | msgid "DownLoad" 321 | msgstr "" 322 | 323 | msgid "Download" 324 | msgstr "" 325 | 326 | msgid "Download confirmation" 327 | msgstr "" 328 | 329 | msgid "Downloading" 330 | msgstr "" 331 | 332 | msgid "Downloads" 333 | msgstr "" 334 | 335 | msgid "ET8500 Image Restore" 336 | msgstr "" 337 | 338 | msgid "" 339 | "ET8500 Multiboot: Yes to restore OS1 No to restore OS2:\n" 340 | " " 341 | msgstr "" 342 | 343 | msgid "Empty slot" 344 | msgstr "" 345 | 346 | msgid "Enable SWAP at startup" 347 | msgstr "" 348 | 349 | msgid "Enable frozen check" 350 | msgstr "" 351 | 352 | msgid "Enable startup" 353 | msgstr "" 354 | 355 | msgid "Enough retries, delaying till next schedule." 356 | msgstr "" 357 | 358 | msgid "Enter your OpenViX developer password." 359 | msgstr "" 360 | 361 | msgid "Enter your OpenViX developer username." 362 | msgstr "" 363 | 364 | msgid "Erase" 365 | msgstr "" 366 | 367 | msgid "Exit the restore wizard" 368 | msgstr "" 369 | 370 | msgid "Expand" 371 | msgstr "" 372 | 373 | msgid "Extra IPK's" 374 | msgstr "" 375 | 376 | msgid "Flash" 377 | msgstr "" 378 | 379 | msgid "Flash image unzip successful." 380 | msgstr "" 381 | 382 | msgid "Found non-standard softcam, trying to start, this may fail." 383 | msgstr "" 384 | 385 | msgid "Free space:" 386 | msgstr "" 387 | 388 | msgid "From which image library do you want to download?" 389 | msgstr "" 390 | 391 | msgid "Frozen check interval" 392 | msgstr "" 393 | 394 | msgid "H9 SDcard manager" 395 | msgstr "" 396 | 397 | msgid "H9SDcard Manager" 398 | msgstr "" 399 | 400 | msgid "INFO" 401 | msgstr "" 402 | 403 | msgid "IPK installer" 404 | msgstr "" 405 | 406 | msgid "If you are an OpenViX developer you can enter your login details to have direct access to OpenViX developer images for your receiver. This feature is only available via 'https' connections." 407 | msgstr "" 408 | 409 | msgid "Image manager" 410 | msgstr "" 411 | 412 | msgid "Image manager settings" 413 | msgstr "" 414 | 415 | msgid "Inactive" 416 | msgstr "" 417 | 418 | msgid "Include machine name in backup name" 419 | msgstr "" 420 | 421 | msgid "Info" 422 | msgstr "" 423 | 424 | msgid "Init SDcard" 425 | msgstr "" 426 | 427 | msgid "Init USB/SDA1" 428 | msgstr "" 429 | 430 | msgid "Init Zgemma H9 SDCARD - please reboot after use." 431 | msgstr "" 432 | 433 | msgid "Init Zgemma H9 USB/SDA1 - please reboot after use." 434 | msgstr "" 435 | 436 | msgid "Install" 437 | msgstr "" 438 | 439 | msgid "Install IPK's from your tmp folder." 440 | msgstr "" 441 | 442 | msgid "Install confirmation" 443 | msgstr "" 444 | 445 | msgid "Install extensions." 446 | msgstr "" 447 | 448 | msgid "Install local extension" 449 | msgstr "" 450 | 451 | msgid "Installing..." 452 | msgstr "" 453 | 454 | msgid "Invert" 455 | msgstr "" 456 | 457 | msgid "Ipkg" 458 | msgstr "" 459 | 460 | msgid "It seems you have not setup an extra location. Please set it up in the Backup manager setup menu." 461 | msgstr "" 462 | 463 | msgid "Location of where backup should be saved." 464 | msgstr "" 465 | 466 | msgid "Location of where the image backup should be saved." 467 | msgstr "" 468 | 469 | msgid "Login as an OpenViX developer" 470 | msgstr "" 471 | 472 | msgid "Logs" 473 | msgstr "" 474 | 475 | msgid "MB" 476 | msgstr "" 477 | 478 | msgid "MENU" 479 | msgstr "" 480 | 481 | msgid "MGcamd can't run whilst CCcam is running." 482 | msgstr "" 483 | 484 | msgid "Manage settings backup." 485 | msgstr "" 486 | 487 | msgid "Manage your devices mount points." 488 | msgstr "" 489 | 490 | msgid "Max image backups to keep (0==all)" 491 | msgstr "" 492 | 493 | msgid "Maximum number of backups to keep. Older backups are given priority for removal, so the fresher ones remain." 494 | msgstr "" 495 | 496 | msgid "Mount" 497 | msgstr "" 498 | 499 | #, python-format 500 | msgid "Mount failed for '%s', error code = '%s'." 501 | msgstr "" 502 | 503 | msgid "Mount manager" 504 | msgstr "" 505 | 506 | msgid "Mount: " 507 | msgstr "" 508 | 509 | msgid "Move Nand root to SD card" 510 | msgstr "" 511 | 512 | msgid "Moving to backup Location..." 513 | msgstr "" 514 | 515 | msgid "Multiboot ERROR! - no STARTUP in boot partition." 516 | msgstr "" 517 | 518 | msgid "Multiboot image manager" 519 | msgstr "" 520 | 521 | msgid "Multiboot only able to restore this backup to mmc slot1" 522 | msgstr "" 523 | 524 | msgid "NO, do not restore plugins" 525 | msgstr "" 526 | 527 | msgid "NO, do not restore settings" 528 | msgstr "" 529 | 530 | msgid "New backup" 531 | msgstr "" 532 | 533 | msgid "Next backup: " 534 | msgstr "" 535 | 536 | msgid "" 537 | "No config files found, please setup CCcam first\n" 538 | "in /etc/CCcam.cfg." 539 | msgstr "" 540 | 541 | msgid "" 542 | "No config files found, please setup Hypercam first\n" 543 | "in /etc/hypercam.cfg." 544 | msgstr "" 545 | 546 | msgid "" 547 | "No config files found, please setup MGcamd first\n" 548 | "in /usr/keys." 549 | msgstr "" 550 | 551 | msgid "" 552 | "No config files found, please setup Ncam first\n" 553 | "in /etc/tuxbox/config" 554 | msgstr "" 555 | 556 | msgid "" 557 | "No config files found, please setup Ncam first\n" 558 | "in /etc/tuxbox/config." 559 | msgstr "" 560 | 561 | msgid "" 562 | "No config files found, please setup Oscam first\n" 563 | "in /etc/tuxbox/config" 564 | msgstr "" 565 | 566 | msgid "" 567 | "No config files found, please setup Oscam first\n" 568 | "in /etc/tuxbox/config." 569 | msgstr "" 570 | 571 | msgid "No images found on the selected download server...if password check validity" 572 | msgstr "" 573 | 574 | msgid "No plugins needed to be installed" 575 | msgstr "" 576 | 577 | msgid "None" 578 | msgstr "" 579 | 580 | msgid "Note: slot list does not show current image or empty slots." 581 | msgstr "" 582 | 583 | msgid "Now skipping restore process" 584 | msgstr "" 585 | 586 | msgid "OK" 587 | msgstr "" 588 | 589 | msgid "OK, to perform a restore" 590 | msgstr "" 591 | 592 | msgid "Only scheduled" 593 | msgstr "" 594 | 595 | #, python-format 596 | msgid "Partition: %s Mount: %s unmounted successfully; if all partitions now unmounted you can remove device." 597 | msgstr "" 598 | 599 | msgid "Please enter a folder that contains some packages." 600 | msgstr "" 601 | 602 | msgid "Please select device to use as SWAP file location." 603 | msgstr "" 604 | 605 | msgid "Please select the file to restore." 606 | msgstr "" 607 | 608 | msgid "Please wait while plugins restore completes..." 609 | msgstr "" 610 | 611 | msgid "Please wait while scanning for devices..." 612 | msgstr "" 613 | 614 | #, python-format 615 | msgid "Please wait while scanning your %s %s devices..." 616 | msgstr "" 617 | 618 | msgid "Please wait while settings restore completes..." 619 | msgstr "" 620 | 621 | msgid "Please wait while starting\n" 622 | msgstr "" 623 | 624 | msgid "Please wait while stopping\n" 625 | msgstr "" 626 | 627 | msgid "Please wait while the flash prepares." 628 | msgstr "" 629 | 630 | msgid "Please wait while the system gathers information..." 631 | msgstr "" 632 | 633 | msgid "Please wait." 634 | msgstr "" 635 | 636 | msgid "Plugin listing failed - e. g. wrong backup destination or no space left on backup device." 637 | msgstr "" 638 | 639 | msgid "Preparing extra plugins..." 640 | msgstr "" 641 | 642 | msgid "Press 'Menu' to select a storage device" 643 | msgstr "" 644 | 645 | msgid "Press OK to toggle the selection." 646 | msgstr "" 647 | 648 | msgid "Press appropiate Init to move Nand root to SDcard or USB." 649 | msgstr "" 650 | 651 | msgid "Query before starting backup." 652 | msgstr "" 653 | 654 | msgid "Query before starting image backup." 655 | msgstr "" 656 | 657 | msgid "Reboot" 658 | msgstr "" 659 | 660 | msgid "Rebooting..." 661 | msgstr "" 662 | 663 | #, python-format 664 | msgid "" 665 | "Recording(s) are in progress or coming up in few seconds!\n" 666 | "Do you still want to flash image\n" 667 | "%s?" 668 | msgstr "" 669 | 670 | #, python-format 671 | msgid "Removal of this slot will not show in %s Gui. Are you sure you want to delete image slot %s ?" 672 | msgstr "" 673 | 674 | msgid "Remove confirmation" 675 | msgstr "" 676 | 677 | msgid "Removing temp mounts..." 678 | msgstr "" 679 | 680 | msgid "Restart" 681 | msgstr "" 682 | 683 | #, python-format 684 | msgid "Restart %s %s." 685 | msgstr "" 686 | 687 | msgid "Restart GUI." 688 | msgstr "" 689 | 690 | msgid "Restore" 691 | msgstr "" 692 | 693 | msgid "Restore Confirmation" 694 | msgstr "" 695 | 696 | msgid "Restore wizard" 697 | msgstr "" 698 | 699 | msgid "Restoring backup..." 700 | msgstr "" 701 | 702 | msgid "Restoring plugins, this can take a long time..." 703 | msgstr "" 704 | 705 | msgid "Restoring plugins..." 706 | msgstr "" 707 | 708 | msgid "Retrieving image slots - Please wait..." 709 | msgstr "" 710 | 711 | msgid "Run" 712 | msgstr "" 713 | 714 | msgid "Run your shell scripts." 715 | msgstr "" 716 | 717 | msgid "SWAP file not found. You have to create the file before you try to activate it." 718 | msgstr "" 719 | 720 | msgid "SWAP manager" 721 | msgstr "" 722 | 723 | msgid "SWAP place:" 724 | msgstr "" 725 | 726 | msgid "SWAP size:" 727 | msgstr "" 728 | 729 | msgid "Save" 730 | msgstr "" 731 | 732 | msgid "Schedule backups" 733 | msgstr "" 734 | 735 | msgid "Script runner" 736 | msgstr "" 737 | 738 | msgid "Script runner settings" 739 | msgstr "" 740 | 741 | msgid "Select" 742 | msgstr "" 743 | 744 | msgid "Select a backup to restore:" 745 | msgstr "" 746 | 747 | msgid "Select a package to install:" 748 | msgstr "" 749 | 750 | #, python-format 751 | msgid "Select an image to download for %s:" 752 | msgstr "" 753 | 754 | msgid "Select an image to flash:" 755 | msgstr "" 756 | 757 | msgid "Select extra packages folder" 758 | msgstr "" 759 | 760 | msgid "Select files/folders to backup" 761 | msgstr "" 762 | 763 | msgid "Select folder that contains plugins" 764 | msgstr "" 765 | 766 | msgid "Select the SWAP file size:" 767 | msgstr "" 768 | 769 | msgid "Select:" 770 | msgstr "" 771 | 772 | msgid "Set the interval to be checked in mins." 773 | msgstr "" 774 | 775 | msgid "Set the repeat interval of backup schedule." 776 | msgstr "" 777 | 778 | msgid "Set the time of backup to start." 779 | msgstr "" 780 | 781 | msgid "Setting up..." 782 | msgstr "" 783 | 784 | msgid "Setup mounts" 785 | msgstr "" 786 | 787 | msgid "Show in extensions" 788 | msgstr "" 789 | 790 | msgid "Size: " 791 | msgstr "" 792 | 793 | #, python-format 794 | msgid "Size: %sGB" 795 | msgstr "" 796 | 797 | #, python-format 798 | msgid "Size: %sMB" 799 | msgstr "" 800 | 801 | #, python-format 802 | msgid "Size: %sTB" 803 | msgstr "" 804 | 805 | msgid "Size: unavailable" 806 | msgstr "" 807 | 808 | msgid "Softcam manager" 809 | msgstr "" 810 | 811 | msgid "Softcam manager settings" 812 | msgstr "" 813 | 814 | msgid "Softcam starting..." 815 | msgstr "" 816 | 817 | msgid "Softcam stopping..." 818 | msgstr "" 819 | 820 | msgid "SoftcamCheck" 821 | msgstr "" 822 | 823 | msgid "Sorry but that location does not exist or is not setup. Please set it up in the Backup manager setup menu." 824 | msgstr "" 825 | 826 | msgid "Sorry the feeds are down for maintenance. Please try again later." 827 | msgstr "" 828 | 829 | msgid "Sorry the feeds are down for maintenance. Please try using Backup manager to restore plugins later." 830 | msgstr "" 831 | 832 | msgid "Sorry the feeds are down for maintenance. Please try using the Backup manager to restore plugins later." 833 | msgstr "" 834 | 835 | msgid "Sorry, but the file is not compatible with this image version." 836 | msgstr "" 837 | 838 | msgid "Sorry, but the restore failed." 839 | msgstr "" 840 | 841 | msgid "Sorry, no physical devices that supports SWAP attached. Can't create SWAP file on network or fat32 file-systems." 842 | msgstr "" 843 | 844 | msgid "Sorry, not enough free RAM found, and no physical devices that supports SWAP attached. Can't create SWAP file on network or fat32 file-systems, unable to make backup." 845 | msgstr "" 846 | 847 | msgid "Start" 848 | msgstr "" 849 | 850 | msgid "Starting..." 851 | msgstr "" 852 | 853 | msgid "Status:" 854 | msgstr "" 855 | 856 | msgid "Stop" 857 | msgstr "" 858 | 859 | msgid "Temp folder" 860 | msgstr "" 861 | 862 | msgid "The backup location does not have enough free space." 863 | msgstr "" 864 | 865 | msgid "The backup location does not have enough free space.\n" 866 | msgstr "" 867 | 868 | #, python-format 869 | msgid "" 870 | "The changes need a system restart to take effect.\n" 871 | "Restart your %s %s now?" 872 | msgstr "" 873 | 874 | msgid "The chosen location does not exist, using /media/hdd." 875 | msgstr "" 876 | 877 | msgid "The name of the receiver can be included in the backup filename, but makes the filename lengthy." 878 | msgstr "" 879 | 880 | msgid "The wizard is finished now, and will reboot." 881 | msgstr "" 882 | 883 | msgid "The wizard is finished now." 884 | msgstr "" 885 | 886 | msgid "There is a problem with this device. Please reformat it and try again." 887 | msgstr "" 888 | 889 | msgid "There is no backup to restore." 890 | msgstr "" 891 | 892 | msgid "There is no image to flash." 893 | msgstr "" 894 | 895 | msgid "This device is already mounted as HDD." 896 | msgstr "" 897 | 898 | msgid "Type: " 899 | msgstr "" 900 | 901 | msgid "Types of backups to remove when stale." 902 | msgstr "" 903 | 904 | msgid "Un-mount" 905 | msgstr "" 906 | 907 | #, python-format 908 | msgid "" 909 | "Unzip error (also sent to any debug log):\n" 910 | "%s" 911 | msgstr "" 912 | 913 | msgid "Updating mount locations..." 914 | msgstr "" 915 | 916 | msgid "Use as HDD" 917 | msgstr "" 918 | 919 | msgid "Use the cursor keys to select an installed image and then Erase button." 920 | msgstr "" 921 | 922 | msgid "ViX" 923 | msgstr "" 924 | 925 | msgid "ViX Backup manager" 926 | msgstr "" 927 | 928 | msgid "ViX Image Management" 929 | msgstr "" 930 | 931 | msgid "ViX Image manager" 932 | msgstr "" 933 | 934 | msgid "ViX Mount manager" 935 | msgstr "" 936 | 937 | msgid "ViX SWAP manager" 938 | msgstr "" 939 | 940 | msgid "ViX Script runner" 941 | msgstr "" 942 | 943 | msgid "View progress" 944 | msgstr "" 945 | 946 | msgid "Wait please while creating SWAP file..." 947 | msgstr "" 948 | 949 | msgid "" 950 | "Wait please while scanning\n" 951 | "for softcam's..." 952 | msgstr "" 953 | 954 | msgid "Wait please while scanning..." 955 | msgstr "" 956 | 957 | msgid "Weekly" 958 | msgstr "" 959 | 960 | msgid "" 961 | "Welcome to the restore wizard.\n" 962 | "\n" 963 | "A backup has been detected.\n" 964 | "You can use this wizard to restore your settings and any extra plugins that were installed when the backup was created.\n" 965 | msgstr "" 966 | 967 | msgid "YES, to restore plugins" 968 | msgstr "" 969 | 970 | msgid "YES, to restore settings" 971 | msgstr "" 972 | 973 | msgid "You have decided not to flash image." 974 | msgstr "" 975 | 976 | msgid "You have to create a SWAP file before trying to activate the autostart." 977 | msgstr "" 978 | 979 | #, python-format 980 | msgid "Your %s %s could not connect to the plugin feeds at this time. Please try using the Backup manager to restore plugins later." 981 | msgstr "" 982 | 983 | #, python-format 984 | msgid "" 985 | "Your %s %s is about to create a full image backup, this can take about 6 minutes to complete.\n" 986 | "Do you want to allow this?" 987 | msgstr "" 988 | 989 | #, python-format 990 | msgid "" 991 | "Your %s %s is about to run a backup of your settings and to detect your plugins.\n" 992 | "Do you want to allow this?" 993 | msgstr "" 994 | 995 | #, python-format 996 | msgid "Your %s %s is not connected to a network. Please check your network settings and try again." 997 | msgstr "" 998 | 999 | #, python-format 1000 | msgid "Your %s %s is not connected to a network. Please try using the Backup manager to restore plugins later when a network connection is available." 1001 | msgstr "" 1002 | 1003 | #, python-format 1004 | msgid "Your %s %s is not connected to the Internet. Please check your network settings and try again." 1005 | msgstr "" 1006 | 1007 | #, python-format 1008 | msgid "Your %s %s is not connected to the Internet. Please try using Backup manager to restore plugins later." 1009 | msgstr "" 1010 | 1011 | #, python-format 1012 | msgid "Your %s %s is not connected to the Internet. Please try using the Backup manager to restore plugins later." 1013 | msgstr "" 1014 | 1015 | #, python-format 1016 | msgid "" 1017 | "ofgwrite error (also sent to any debug log):\n" 1018 | "%s" 1019 | msgstr "" 1020 | 1021 | #, python-format 1022 | msgid "slot%s - %s" 1023 | msgstr "" 1024 | 1025 | #, python-format 1026 | msgid "slot%s - %s " 1027 | msgstr "" 1028 | 1029 | #, python-format 1030 | msgid "slot%s - %s (current image)" 1031 | msgstr "" 1032 | 1033 | msgid "unavailable" 1034 | msgstr "" 1035 | -------------------------------------------------------------------------------- /po/updateallpo-multiOS.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Script to generate po and pot files 3 | # Author: Pr2 4 | # Version: 1.0 5 | # 6 | # This script is derivated from updateallpo.sh it is intended to all you 7 | # create the updated version of the pot and po files on different environment: 8 | # For Windows, please download and install the following program: 9 | # Python: 10 | # https://www.python.org/ 11 | # GitForWindows: 12 | # https://gitforwindows.org/ 13 | # 14 | # Pre-requisite for Windows: 15 | # -> install python on your PC 16 | # -> install Git for Windows, you can keep all default installation settings. 17 | # -> Start the installed: git-bash you will see a command prompt. 18 | # -> At the git-bash command prompt we will clone the repository (see below): 19 | # 20 | # For Mac OSX download and install homebrew following explanation from: 21 | # https://brew.sh/ 22 | # 23 | # For Mac OSX with homebrew and also Linux users: 24 | # The following tools must be installed on your system and accessible from path: 25 | # gawk, find, gettext, gnu-sed, python 26 | # Start and terminal and clone OpenPLi repository (see below): 27 | # 28 | # On All platforms please download and install: 29 | # 30 | # PoEdit: https://poedit.net/ 31 | # 32 | # ------------------------------------------------------------------------------------- 33 | # Run this script from within the locale folder. 34 | # 35 | remote="origin" 36 | branch="master" 37 | python="python" 38 | localgsed="sed" 39 | findoptions="" 40 | delete=1 41 | plugin="vix" 42 | 43 | function this_help () { 44 | printf "Possible options are:\n" 45 | printf " -r | --remote to specify the remote git to use, default[origin]\n" 46 | printf " -b | --branch to specify the branch to translate, default[develop]\n" 47 | printf " -p | --python to specify the python runtime name, default[python]\n" 48 | printf " -h | --help this text\n\n" 49 | printf "To translate for the develop branch simply run this script without any option.\n" 50 | printf "To translate for the rc branch simply specify:\n" 51 | printf "%s -branch rc \nor\n%s -b rc\n" $0 $0 52 | printf "\n\n" 53 | printf "Pre-requisites:\n\n" 54 | printf "Please read the OpenPLi translators wiki page:\n" 55 | printf "https://wiki.openpli.org/Information_for_Translators\n" 56 | return 0 57 | } 58 | 59 | while [ "$1" != "" ]; do 60 | case "$1" in 61 | -b|--branch) 62 | shift 63 | branch="$1" 64 | ;; 65 | -r|--remote) 66 | shift 67 | remote="$1" 68 | ;; 69 | -p|--python) 70 | shift 71 | python="$1" 72 | ;; 73 | -h|--help) 74 | this_help 75 | exit 0 76 | ;; 77 | *) 78 | printf "Error: unknown parameter [%s]\n\n" "$1" 79 | this_help 80 | exit 1 81 | esac 82 | shift 83 | done 84 | # 85 | # Checking if defined remote exist 86 | # 87 | 88 | (git remote -v | grep -q "$remote\s") \ 89 | && { printf "Remote git : [%s]\n" $remote; } \ 90 | || { printf "Sorry this remote doesn't exist: [%s]\n Valid remotes are:\n" $remote; \ 91 | git remote -v ; exit 1; } 92 | # 93 | # Checking if remote branch exist on the defined remote 94 | # 95 | 96 | (git branch -r | grep -q "$remote/""$branch""$") \ 97 | && { printf "Remote branch : [%s]\n" $branch; } \ 98 | || { printf "Sorry this branch doesn't exist: [%s]\n Valid branches are:\n" $branch; \ 99 | git branch -r | grep $remote | sed 's/"$remote"\///'; exit 1; } 100 | # 101 | # Checking for Python version number to select the right python script to use 102 | # 103 | command -v "$python" >/dev/null 2>&1 || { printf >&2 "Script requires python but it's not installed. Aborting."; \ 104 | printf "Please download latest version and install it from: https://www.python.org/\n"; exit 1; } 105 | printf "Python used [%s]: " "$python" 106 | "$python" --version 107 | # 108 | # Checking for gettext component 109 | # 110 | command -v xgettext --version >/dev/null 2>&1 || { printf "Please install gettext package on your system. Aborting.\n"; exit 1; } 111 | command -v msguniq --version >/dev/null 2>&1 || { printf "Please install gettext package on your system. Aborting.\n"; exit 1; } 112 | # 113 | # On Mac OSX find option are specific 114 | # 115 | if [[ "$OSTYPE" == "darwin"* ]] 116 | then 117 | # Mac OSX 118 | printf "Script running on Mac OSX [%s]\n" "$OSTYPE" 119 | findoptions=" -s -X " 120 | fi 121 | # 122 | # Script only run with sed but on some distro normal sed is already sed so checking it. 123 | # 124 | sed --version 2> /dev/null | grep -q "GNU" 125 | if [ $? -eq 0 ]; then 126 | localgsed="sed" 127 | else 128 | "$localgsed" --version | grep -q "GNU" 129 | if [ $? -eq 0 ]; then 130 | printf "GNU sed found: [%s]\n" $localgsed 131 | fi 132 | fi 133 | # 134 | # Needed when run in git-bash for Windows 135 | # 136 | export PYTHONIOENCODING=utf-8 137 | # 138 | # To fix the LF (Linux, Mac) and CRLF (Windows) conflict 139 | # 140 | git config core.eol lf 141 | git config core.autocrlf input 142 | git config core.safecrlf true 143 | # 144 | # Git commands to sync with origin and create the branch MyTranslation to work on. 145 | # 146 | git pull 147 | # 148 | # Retrieve all existing languages to update 149 | # 150 | printf "Po files update/creation from script starting.\n" 151 | languages=($(ls *.po | tr "\n" " " | sed 's/.po//g')) 152 | 153 | # If you want to define the language locally in this script uncomment and defined languages 154 | #languages=("ar" "bg" "ca" "cs" "da" "de" "el" "en" "es" "et" "fa" "fi" "fr" "fy" "he" "hk" "hr" "hu" "id" "is" "it" "ku" "lt" "lv" "nl" "nb" "nn" "pl" "pt" "pt_BR" "ro" "ru" "sk" "sl" "sr" "sv" "th" "tr" "uk" "zh") 155 | 156 | printf "Creating temporary file %s-py.pot\n" $plugin 157 | find $findoptions .. -name "*.py" -exec xgettext --no-wrap -L Python --from-code=UTF-8 -kpgettext:1c,2 --add-comments="TRANSLATORS:" -d enigma2 -s -o "$plugin"-py.pot {} \+ 158 | "$localgsed" --in-place "$plugin"-py.pot --expression=s/CHARSET/UTF-8/ 159 | printf "Merging pot files to create: %s.pot\n" "$plugin" 160 | cat "$plugin"-py.pot | msguniq --no-wrap -o "$plugin".pot - 161 | OLDIFS=$IFS 162 | IFS=" " 163 | for lang in "${languages[@]}" ; do 164 | if [ -f $lang.po ]; then \ 165 | printf "Updating existing translation file %s.po\n" $lang 166 | msgmerge --backup=none --no-wrap -s -U $lang.po "$plugin".pot && touch $lang.po; \ 167 | msgattrib --no-wrap --no-obsolete $lang.po -o $lang.po; \ 168 | msgfmt -o $lang.mo $lang.po; \ 169 | else \ 170 | printf "New file created: %s.po, please add it to github before commit\n" $lang 171 | msginit -l $lang.po -o $lang.po -i "$plugin".pot --no-translator; \ 172 | msgfmt -o $lang.mo $lang.po; \ 173 | fi 174 | done 175 | if [ $delete -eq 1 ]; then \ 176 | rm "$plugin"-py.pot 177 | fi 178 | IFS=$OLDIFS 179 | printf "Po files update/creation from script finished!\n" 180 | printf "Edit with PoEdit the po file that you want to translate located in:\n\n" 181 | command -v cygpath > /dev/null && { cygpath -w "$PWD"; } || { pwd; } 182 | printf "\n\n" 183 | printf "PoEdit: https://poedit.net/\n" 184 | printf "IMPORTANT: in PoEdit go into Files-Preferences menu select the advanced tab\n" 185 | printf " 1) select Unix(recommended) for carriage return\n" 186 | printf " 2) unselect wrap text\n" 187 | printf " 3) unselect keep original file format\n" 188 | printf "You only need to do this once in PoEdit.\n\n" 189 | printf "Please read the translators wiki page:\n" 190 | printf "\nhttps://wiki.openpli.org/Information_for_Translators\n" 191 | rm -rf *.mo 192 | chmod 644 *.po 193 | -------------------------------------------------------------------------------- /po/xml2po.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | from __future__ import print_function 4 | try: 5 | import builtins 6 | except ImportError: 7 | import __builtin__ as builtins 8 | 9 | import sys 10 | import os 11 | import re 12 | from xml.sax import make_parser 13 | from xml.sax.handler import ContentHandler, property_lexical_handler 14 | 15 | try: 16 | from _xmlplus.sax.saxlib import LexicalHandler 17 | no_comments = False 18 | except ImportError: 19 | class LexicalHandler: 20 | def __init__(self): 21 | pass 22 | 23 | no_comments = True 24 | 25 | 26 | class parseXML(ContentHandler, LexicalHandler): 27 | def __init__(self, attrlist): 28 | self.isPointsElement, self.isReboundsElement = 0, 0 29 | self.attrlist = attrlist 30 | self.last_comment = None 31 | self.ishex = re.compile('#[0-9a-fA-F]+\Z') 32 | 33 | def comment(self, comment): 34 | if "TRANSLATORS:" in comment: 35 | self.last_comment = comment 36 | 37 | def startElement(self, name, attrs): 38 | for x in ["text", "title", "value", "caption", "summary", "description"]: 39 | try: 40 | k = builtins.str(attrs[x]) 41 | if k.strip() != "" and not self.ishex.match(k): 42 | attrlist.add((k, self.last_comment)) 43 | self.last_comment = None 44 | except KeyError: 45 | pass 46 | 47 | 48 | parser = make_parser() 49 | 50 | attrlist = set() 51 | 52 | contentHandler = parseXML(attrlist) 53 | parser.setContentHandler(contentHandler) 54 | if not no_comments: 55 | parser.setProperty(property_lexical_handler, contentHandler) 56 | 57 | for arg in sys.argv[1:]: 58 | if os.path.isdir(arg): 59 | for file in os.listdir(arg): 60 | if file.endswith(".xml"): 61 | parser.parse(os.path.join(arg, file)) 62 | else: 63 | parser.parse(arg) 64 | 65 | attrlist = list(attrlist) 66 | attrlist.sort(key=lambda a: a[0]) 67 | 68 | for (k, c) in attrlist: 69 | print() 70 | print('#: ' + arg) 71 | k.replace("\\n", "\"\n\"") 72 | if c: 73 | for l in c.split('\n'): 74 | print("#. ", l) 75 | print('msgid "' + builtins.str(k) + '"') 76 | print('msgstr ""') 77 | 78 | attrlist = set() 79 | -------------------------------------------------------------------------------- /src/H9SDmanager.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, absolute_import 2 | 3 | from boxbranding import getMachineBuild 4 | from Components.ActionMap import ActionMap 5 | from Components.ChoiceList import ChoiceList, ChoiceEntryComponent 6 | from Components.config import config 7 | from Components.Label import Label 8 | from Components.Sources.StaticText import StaticText 9 | from Components.SystemInfo import SystemInfo 10 | from Screens.Console import Console 11 | from Screens.Screen import Screen 12 | from Screens.MessageBox import MessageBox 13 | from Screens.Standby import TryQuitMainloop 14 | from Tools.BoundFunction import boundFunction 15 | 16 | 17 | class H9SDmanager(Screen): 18 | 19 | skin = """ 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | """ 35 | 36 | def __init__(self, session): 37 | Screen.__init__(self, session) 38 | self.skinName = "H9SDmanager" 39 | self.setTitle(_("H9 SDcard manager")) 40 | self["labe14"] = StaticText(_("Press appropiate Init to move Nand root to SDcard or USB.")) 41 | self["key_red"] = StaticText(_("Reboot")) 42 | self["key_green"] = StaticText(_("Init SDcard")) 43 | self["key_yellow"] = StaticText(_("Init USB/SDA1")) 44 | self["actions"] = ActionMap(["OkCancelActions", "ColorActions"], 45 | { 46 | "red": self.reboot, 47 | "green": self.SDInit, 48 | "yellow": self.USBInit, 49 | "ok": boundFunction(self.close, None), 50 | "cancel": boundFunction(self.close, None), 51 | }, -1) 52 | self.onLayoutFinish.append(self.layoutFinished) 53 | 54 | def layoutFinished(self): 55 | self.setTitle(_("H9 SDcard manager")) 56 | 57 | def SDInit(self): 58 | if SystemInfo["HasH9SD"]: 59 | self.TITLE = _("Init Zgemma H9 SDCARD - please reboot after use.") 60 | cmdlist = [] 61 | cmdlist.append("opkg update") 62 | cmdlist.append("opkg install rsync") 63 | cmdlist.append("umount /dev/mmcblk0p1") 64 | cmdlist.append("dd if=/dev/zero of=/dev/mmcblk0p1 bs=1M count=150") 65 | cmdlist.append("mkfs.ext4 -L 'H9-ROOTFS' /dev/mmcblk0p1") 66 | # cmdlist.append("parted -s /dev/mmcblk0 rm 1") 67 | # cmdlist.append("parted -s /dev/mmcblk0 mklabel gpt") 68 | # cmdlist.append("parted -s /dev/mmcblk0 mkpart rootfs2 ext4 0% 100%") 69 | cmdlist.append("mkdir /tmp/mmc") 70 | cmdlist.append("mount /dev/mmcblk0p1 /tmp/mmc") 71 | cmdlist.append("mkdir /tmp/root") 72 | cmdlist.append("mount --bind / /tmp/root") 73 | cmdlist.append("rsync -aAX /tmp/root/ /tmp/mmc/") 74 | cmdlist.append("umount /tmp/root") 75 | cmdlist.append("umount /tmp/mmc") 76 | cmdlist.append("rmdir /tmp/root") 77 | cmdlist.append("rmdir /tmp/mmc") 78 | self.session.open(Console, title=self.TITLE, cmdlist=cmdlist, closeOnSuccess=True) 79 | else: 80 | self.close() 81 | 82 | def reboot(self): 83 | self.session.open(TryQuitMainloop, 2) 84 | 85 | def USBInit(self): 86 | self.TITLE = _("Init Zgemma H9 USB/SDA1 - please reboot after use.") 87 | cmdlist = [] 88 | cmdlist.append("opkg update") 89 | cmdlist.append("opkg install rsync") 90 | cmdlist.append("umount /dev/mmcblk0p1") 91 | cmdlist.append("dd if=/dev/zero of=/dev/sda1 bs=1M count=150") 92 | cmdlist.append("mkfs.ext4 -L 'H9-ROOTFS' /dev/sda1") 93 | # cmdlist.append("mkfs.ext4 -L 'rootfs2' /dev/sda1") 94 | cmdlist.append("mkdir /tmp/mmc") 95 | cmdlist.append("mount /dev/mmcblk0p1 /tmp/mmc") 96 | cmdlist.append("mkdir /tmp/root") 97 | cmdlist.append("mount --bind / /tmp/root") 98 | cmdlist.append("rsync -aAX /tmp/root/ /tmp/mmc/") 99 | cmdlist.append("umount /tmp/root") 100 | cmdlist.append("umount /tmp/mmc") 101 | cmdlist.append("rmdir /tmp/root") 102 | cmdlist.append("rmdir /tmp/mmc") 103 | self.session.open(Console, title=self.TITLE, cmdlist=cmdlist, closeOnSuccess=True) 104 | -------------------------------------------------------------------------------- /src/IPKInstaller.py: -------------------------------------------------------------------------------- 1 | from os import listdir, path 2 | 3 | from . import _ 4 | 5 | from Components.ActionMap import ActionMap 6 | from Components.Button import Button 7 | from Components.config import config 8 | from Components.Ipkg import IpkgComponent 9 | from Components.Label import Label 10 | from Components.MenuList import MenuList 11 | from Components.SelectionList import SelectionList 12 | from Components.Sources.StaticText import StaticText 13 | from Screens.Console import Console 14 | from Screens.Ipkg import Ipkg 15 | from Screens.MessageBox import MessageBox 16 | from Screens.Screen import Screen 17 | from Screens.Standby import TryQuitMainloop 18 | 19 | 20 | class VIXIPKInstaller(Screen): 21 | skin = [""" 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | self["list"].instance.setItemHeight(%d) 33 | 34 | """, 35 | 560, 400, # screen 36 | 0, 0, 140, 40, #colors 37 | 140, 0, 140, 40, 38 | 280, 0, 140, 40, 39 | 0, 0, 140, 40, 20, 40 | 140, 0, 140, 40, 20, 41 | 280, 0, 140, 40, 20, 42 | 0, 50, 560, 50, 18, # lab1 43 | 10, 105, 540, 260, 20, # list 44 | 26, 45 | ] 46 | 47 | def __init__(self, session): 48 | Screen.__init__(self, session) 49 | self.setTitle(_("IPK installer")) 50 | 51 | self["lab1"] = Label() 52 | self.defaultDir = "/tmp" 53 | self.onChangedEntry = [] 54 | self["myactions"] = ActionMap(["ColorActions", "OkCancelActions", "DirectionActions", "MenuActions"], 55 | { 56 | "cancel": self.close, 57 | "red": self.close, 58 | "green": self.keyInstall, 59 | "yellow": self.changelocation, 60 | "ok": self.keyInstall, 61 | "menu": self.close, 62 | }, -1) 63 | 64 | self["key_red"] = Button(_("Close")) 65 | self["key_green"] = Button(_("Install")) 66 | self["key_yellow"] = Button() 67 | 68 | self.list = [] 69 | self["list"] = MenuList(self.list) 70 | self.populate_List() 71 | 72 | if not self.selectionChanged in self["list"].onSelectionChanged: 73 | self["list"].onSelectionChanged.append(self.selectionChanged) 74 | 75 | def createSummary(self): 76 | from Screens.PluginBrowser import PluginBrowserSummary 77 | 78 | return PluginBrowserSummary 79 | 80 | def selectionChanged(self): 81 | item = self["list"].getCurrent() 82 | if item: 83 | name = item 84 | desc = "" 85 | else: 86 | name = "" 87 | desc = "" 88 | for cb in self.onChangedEntry: 89 | cb(name, desc) 90 | 91 | def changelocation(self): 92 | if self.defaultDir == "/tmp": 93 | self["key_yellow"].setText(_("Extra IPK's")) 94 | self.defaultDir = config.backupmanager.xtraplugindir.value 95 | if not self.defaultDir: 96 | message = _("It seems you have not setup an extra location. Please set it up in the Backup manager setup menu.") 97 | ybox = self.session.open(MessageBox, message, MessageBox.TYPE_INFO) 98 | ybox.setTitle(_("Change location")) 99 | elif self.defaultDir and not path.exists(self.defaultDir): 100 | message = _("Sorry but that location does not exist or is not setup. Please set it up in the Backup manager setup menu.") 101 | ybox = self.session.open(MessageBox, message, MessageBox.TYPE_INFO) 102 | ybox.setTitle(_("Change location")) 103 | else: 104 | self.populate_List() 105 | else: 106 | self["key_yellow"].setText(_("Temp folder")) 107 | self.defaultDir = "/tmp" 108 | self.populate_List() 109 | 110 | def populate_List(self): 111 | if self.defaultDir == "/tmp": 112 | self["key_yellow"].setText(_("Extra IPK's")) 113 | else: 114 | self["key_yellow"].setText(_("Temp folder")) 115 | 116 | self["lab1"].setText(_("Select a package to install:")) 117 | 118 | del self.list[:] 119 | f = listdir(self.defaultDir) 120 | for line in f: 121 | if line.find(".ipk") != -1: 122 | self.list.append(line) 123 | 124 | if path.ismount("/media/usb"): 125 | f = listdir("/media/usb") 126 | for line in f: 127 | if line.find(".ipk") != -1: 128 | self.list.append(line) 129 | 130 | self.list.sort() 131 | self["list"].l.setList(self.list) 132 | 133 | def keyInstall(self): 134 | message = _("Are you ready to install ?") 135 | ybox = self.session.openWithCallback(self.Install, MessageBox, message, MessageBox.TYPE_YESNO) 136 | ybox.setTitle(_("Install confirmation")) 137 | 138 | def Install(self, answer): 139 | if answer is True: 140 | sel = self["list"].getCurrent() 141 | if sel: 142 | self.defaultDir = self.defaultDir.replace(" ", "%20") 143 | cmd1 = "/usr/bin/opkg install " + path.join(self.defaultDir, sel) 144 | self.session.openWithCallback(self.installFinished(sel), Console, title=_("Installing..."), cmdlist=[cmd1], closeOnSuccess=True) 145 | 146 | def installFinished(self, sel): 147 | message = _("Do you want to restart GUI now ?") 148 | ybox = self.session.openWithCallback(self.restBox, MessageBox, message, MessageBox.TYPE_YESNO) 149 | ybox.setTitle(_("Restart GUI.")) 150 | 151 | def restBox(self, answer): 152 | if answer is True: 153 | self.session.open(TryQuitMainloop, 3) 154 | else: 155 | self.populate_List() 156 | self.close() 157 | 158 | def myclose(self): 159 | self.close() 160 | 161 | 162 | class IpkgInstaller(Screen): 163 | skin = [""" 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | """, 177 | 560, 450, # screen 178 | 0, 0, 140, 40, #colors 179 | 140, 0, 140, 40, 180 | 280, 0, 140, 40, 181 | 420, 0, 140, 40, 182 | 0, 0, 140, 40, 20, 183 | 140, 0, 140, 40, 20, 184 | 280, 0, 140, 40, 20, 185 | 420, 0, 140, 40, 20, 186 | 5, 50, 540, 360, 20, # list 187 | 0, 410, 560, 2, 188 | 5, 420, 550, 30, 22, 189 | ] 190 | 191 | def __init__(self, session, list): 192 | Screen.__init__(self, session) 193 | Screen.setTitle(self, _("IPK installer")) 194 | self.list = SelectionList() 195 | self["list"] = self.list 196 | for listindex in range(len(list)): 197 | if not list[listindex].split("/")[-1].startswith("._"): 198 | self.list.addSelection(list[listindex].split("/")[-1], list[listindex], listindex, False) 199 | 200 | self["key_red"] = StaticText(_("Close")) 201 | self["key_green"] = StaticText(_("Install")) 202 | self["key_yellow"] = StaticText() 203 | self["key_blue"] = StaticText(_("Invert")) 204 | self["introduction"] = StaticText(_("Press OK to toggle the selection.")) 205 | 206 | self["actions"] = ActionMap(["OkCancelActions", "ColorActions"], 207 | { 208 | "ok": self.list.toggleSelection, 209 | "cancel": self.close, 210 | "red": self.close, 211 | "green": self.install, 212 | "blue": self.list.toggleAllSelection 213 | }, -1) 214 | 215 | def install(self): 216 | list = self.list.getSelectionsList() 217 | cmdList = [] 218 | for item in list: 219 | cmdList.append((IpkgComponent.CMD_INSTALL, {"package": item[1]})) 220 | self.session.open(Ipkg, cmdList=cmdList) 221 | -------------------------------------------------------------------------------- /src/LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = images 2 | 3 | installdir = $(libdir)/enigma2/python/Plugins/SystemPlugins/ViX 4 | install_PYTHON = *.py 5 | 6 | install_DATA = *.png \ 7 | *.xml \ 8 | LICENSE \ 9 | maintainer.info 10 | -------------------------------------------------------------------------------- /src/MountManager.py: -------------------------------------------------------------------------------- 1 | import errno 2 | from os import mkdir, path, remove, rename, statvfs, system 3 | import re 4 | from time import sleep 5 | 6 | from boxbranding import getMachineBrand, getMachineName, getMachineBuild 7 | from enigma import eTimer, getDesktop 8 | 9 | from . import _ 10 | 11 | from Components.ActionMap import ActionMap 12 | from Components.Label import Label 13 | from Components.ConfigList import ConfigListScreen 14 | from Components.config import config, getConfigListEntry, ConfigSelection, NoSave 15 | from Components.Console import Console 16 | from Components.Sources.List import List 17 | from Components.Sources.StaticText import StaticText 18 | from Components.SystemInfo import SystemInfo 19 | from Screens.MessageBox import MessageBox 20 | from Screens.Screen import Screen 21 | from Screens.Standby import QUIT_REBOOT, TryQuitMainloop 22 | from Tools.LoadPixmap import LoadPixmap 23 | from Tools.Directories import SCOPE_CURRENT_SKIN, resolveFilename 24 | 25 | blacklistedDisks = [ 26 | 1, # RAM disk (/dev/ram0=0, /dev/initrd=250 [250=Initial RAM disk for old systems, new systems use 0]) 27 | 7, # Loopback devices (/dev/loop0=0) 28 | 31, # ROM/flash memory card (/dev/rom0=0, /dev/rrom0=8, /dev/flash0=16, /dev/rflash0=24 [r=Read Only]) 29 | 240, # ROM/flash memory card (/dev/rom0=0, /dev/rrom0=8, /dev/flash0=16, /dev/rflash0=24 [r=Read Only]) 30 | 253, # LOCAL/EXPERIMENTAL USE 31 | 254, # LOCAL/EXPERIMENTAL USE 32 | 259 # MMC block devices (/dev/mmcblk0=0, /dev/mmcblk0p1=1, /dev/mmcblk1=8) 33 | ] 34 | 35 | 36 | def readFile(filename): 37 | try: 38 | with open(filename, "r") as fd: 39 | data = fd.read().strip() 40 | except (IOError, OSError) as err: 41 | if err.errno != errno.ENOENT: # No such file or directory. 42 | print("[MountManager] Error: Failed to read file! ", err) 43 | data = None 44 | return data 45 | 46 | 47 | def getProcPartitions(partitionList): 48 | partitions = [] 49 | with open("/proc/partitions", "r") as fd: 50 | for line in fd.readlines(): 51 | line = line.strip() 52 | if line == "": # Skip empty lines. 53 | continue 54 | (devmajor, devminor, blocks, device) = line.split() 55 | if devmajor == "major": # Skip label line. 56 | continue 57 | # print("[MountManager] device='%s', devmajor='%s', devminor='%s'." % (device, devmajor, devminor)) 58 | devMajor = int(devmajor) 59 | if devMajor in blacklistedDisks: # Ignore all blacklisted devices. 60 | continue 61 | if devMajor == 179: 62 | if not SystemInfo["HasSDnomount"]: # Only interested in h9/i55/h9combo(+dups) mmc partitions. h9combo(+dups) uses mmcblk1p[0-3]. 63 | continue 64 | if SystemInfo["HasH9SD"]: 65 | if not re.search("mmcblk0p1", device): # h9/i55 only mmcblk0p1 mmc partition 66 | continue 67 | if SystemInfo["HasMMC"]: # With h9/i55 reject mmcblk0p1 mmc partition if root device. 68 | continue 69 | if SystemInfo["HasSDnomount"][0] and not re.search("mmcblk1p[0-3]", device): # h9combo(+dups) uses mmcblk1p[0-3] include 70 | continue 71 | if devMajor == 8: 72 | if not re.search("sd[a-z][1-9]", device): # If storage use partitions only. 73 | continue 74 | if SystemInfo["HasHiSi"] and path.exists("/dev/sda4") and re.search("sd[a][1-4]", device): # Sf8008 using SDcard for slots ---> exclude 75 | continue 76 | if device in partitions: # If device is already in partition list ignore it. 77 | continue 78 | buildPartitionInfo(device, partitionList) 79 | partitions.append(device) 80 | 81 | 82 | def buildPartitionInfo(partition, partitionList): 83 | if re.search("mmcblk[0-1]p[0-3]", partition): 84 | device = re.sub("p[0-9]", "", partition) 85 | else: 86 | device = re.sub("[0-9]", "", partition) 87 | physicalDevice = path.realpath(path.join("/sys/block", device, "device")) 88 | 89 | # print("[MountManager] MachineBuild: %s" % getMachineBuild()) 90 | # print("[MountManager] partition: %s" % partition) 91 | # print("[MountManager] device: %s" % device) 92 | # print("[MountManager] physicalDevice: %s" % physicalDevice) 93 | # print("[MountManager] Type: %s" % SystemInfo["MountManager"]) 94 | 95 | description = readFile(path.join(physicalDevice, "model")) 96 | if description is None: 97 | description = readFile(path.join(physicalDevice, "name")) 98 | if description is None: 99 | description = _("Device %s") % partition 100 | description = str(description).replace("\n", "") 101 | 102 | hotplugBuses = ("usb", "mmc", "ata") 103 | busTranslate = ("usb", "sd", "hdd") 104 | count = -1 105 | for bus in hotplugBuses: 106 | count += 1 107 | if "/%s" % bus in physicalDevice: 108 | break 109 | # print("[MountManager1]bus: %s count : %s" % (bus, count)) 110 | pngType = busTranslate[count] 111 | name = _("%s: " % pngType.upper()) 112 | name += description 113 | 114 | if path.exists(resolveFilename(SCOPE_CURRENT_SKIN, "vixcore/dev_%s.png" % pngType)): 115 | mypixmap = resolveFilename(SCOPE_CURRENT_SKIN, "vixcore/dev_%s.png" % pngType) 116 | else: 117 | mypixmap = "/usr/lib/enigma2/python/Plugins/SystemPlugins/ViX/images/dev_%s.png" % pngType 118 | 119 | description = "" 120 | mediamount = _("None") 121 | _format = _("unavailable") 122 | rw = _("None") 123 | 124 | with open("/proc/mounts", "r") as f: 125 | for line in f.readlines(): 126 | if line.find(partition) != -1: 127 | parts = line.strip().split() 128 | mediamount = parts[1] # media mount e.g. /media/xxxxx 129 | _format = parts[2] # _format e.g. ext4 130 | # Also, map any fuseblk fstype to the real file-system behind it... 131 | # Use blkid to get the info we need.... 132 | # 133 | if _format == 'fuseblk': 134 | import subprocess 135 | res = subprocess.run(['blkid', '-sTYPE', '-ovalue', parts[0]], capture_output=True) 136 | if res.returncode == 0: 137 | _format = res.stdout.decode().strip() 138 | rw = parts[3] # read/write 139 | break 140 | print("[MountManager1][buildPartitionInfo] mediamount", mediamount) 141 | if mediamount == "/" and SystemInfo["HasKexecMultiboot"]: 142 | return 143 | if mediamount == _("None") or mediamount is None: 144 | description = _("Size: ") + _("unavailable") 145 | else: 146 | stat = statvfs(mediamount) 147 | # print("[MountManager1]mediamount: %s" % mediamount) 148 | size = (stat.f_blocks * stat.f_bsize) / (1000 * 1000) # get size in MB 149 | if size < 1: # is condition ever fulfilled? 150 | description = _("Size: unavailable") 151 | if size < 1000: 152 | description = _("Size: %sMB") % str(int(size)) 153 | elif size < 1000 * 1000: 154 | description = _("Size: %sGB") % format(size / 1000, '.2f') 155 | else: 156 | description = _("Size: %sTB") % format(size / (1000 * 1000), '.2f') 157 | 158 | if SystemInfo["MountManager"]: # called by VIXDevicesPanel else DeviceMountSetup 159 | if rw.startswith("rw"): 160 | rw = " R/W" 161 | elif rw.startswith("ro"): 162 | rw = " R/O" 163 | else: 164 | rw = "" 165 | description += "\t" + _("Mount: ") + mediamount + "\n" + _("Device: ") + "/dev/" + partition + "\t" + _("Type: ") + _format + rw 166 | png = LoadPixmap(mypixmap) 167 | partitionInfo = (name, description, png) 168 | else: 169 | Gmedia = [ 170 | ("/media/" + device, "/media/" + device), 171 | ("/media/hdd", "/media/hdd"), 172 | ("/media/hdd2", "/media/hdd2"), 173 | ("/media/hdd3", "/media/hdd3"), 174 | ("/media/usb", "/media/usb"), 175 | ("/media/usb2", "/media/usb2"), 176 | ("/media/usb3", "/media/usb3"), 177 | ("/media/sdcard", "/media/sdcard") 178 | ] 179 | item = NoSave(ConfigSelection(default="/media/%s" % partition, choices=Gmedia)) 180 | if _format == "Linux": 181 | _format = "ext4" 182 | else: 183 | _format = "auto" 184 | item.value = mediamount.strip() 185 | text = name + " " + description + " /dev/" + partition 186 | partitionInfo = getConfigListEntry(text, item, partition, _format) 187 | partitionList.append(partitionInfo) 188 | 189 | 190 | class VIXDevicesPanel(Screen): 191 | skin = [""" 192 | 193 | 194 | 195 | { 196 | "template": 197 | [ 198 | MultiContentEntryText(pos = (%d, %d), size = (%d, %d), font = 0, flags = RT_HALIGN_LEFT | RT_VALIGN_CENTER, text = 0), 199 | MultiContentEntryText(pos = (%d, %d), size = (%d, %d), font = 1, flags = RT_HALIGN_LEFT | RT_VALIGN_TOP, text = 1), 200 | MultiContentEntryPixmapAlphaBlend(pos = (%d, %d), size = (%d, %d), flags = BT_SCALE, png = 2), 201 | ], 202 | "fonts": [gFont("Regular",%d), gFont("Regular",%d)], 203 | "itemHeight": %d 204 | } 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | """, 217 | 560, 495, # screen 218 | 10, 50, 540, 400, # Listbox 219 | 100, 0, 520, 30, 220 | 120, 30, 500, 50, 221 | 10, 0, 80, 80, 222 | 24, 20, # fonts 223 | 80, 224 | 10, 10, 540, 425, 25, 225 | 0, 0, 140, 40, #colors 226 | 140, 0, 140, 40, 227 | 280, 0, 140, 40, 228 | 420, 0, 140, 40, 229 | 0, 0, 140, 40, 20, 230 | 140, 0, 140, 40, 20, 231 | 280, 0, 140, 40, 20, 232 | 420, 0, 140, 40, 20, 233 | ] 234 | 235 | def __init__(self, session): 236 | Screen.__init__(self, session) 237 | self.setTitle(_("Mount manager")) 238 | self["key_red"] = Label("") 239 | self["key_green"] = Label(_("Setup mounts")) 240 | self["key_yellow"] = Label(_("Un-mount")) 241 | self["key_blue"] = Label(_("Mount")) 242 | self["lab1"] = Label(_("Please wait while scanning for devices...")) 243 | self.onChangedEntry = [] 244 | self.partitionlist = [] 245 | self["list"] = List(self.partitionlist) 246 | self["list"].onSelectionChanged.append(self.selectionChanged) 247 | self["actions"] = ActionMap(["WizardActions", "ColorActions", "MenuActions"], { 248 | "back": self.close, 249 | "green": self.setupMounts, 250 | "red": self.saveMounts, 251 | "yellow": self.unmount, 252 | "blue": self.mount, 253 | "menu": self.close 254 | }) 255 | self.Console = Console() 256 | self.activityTimer = eTimer() 257 | self.activityTimer.timeout.get().append(self.findPartitions) 258 | self.setTimer() 259 | 260 | def selectionChanged(self): 261 | # print("[MountManager][selectionChanged] self.partitionList=%s" % self.partitionList) 262 | if len(self.partitionList) == 0: 263 | return 264 | sel = self["list"].getCurrent() # partitionInfo = (name, description, png) 265 | # print("[MountManager][selectionChanged] sel1=%s sel2=%s" % (sel[0], sel[1])) 266 | line = sel[1] 267 | # print("[MountManager1][selectionChanged] line=%s" % line) 268 | if line.find("Mount") >= 0: 269 | if line.find("/media/hdd") < 0: 270 | self["key_red"].setText(_("Use as HDD")) 271 | else: 272 | self["key_red"].setText("") 273 | name = description = "" 274 | if sel: 275 | try: 276 | name = str(sel[0]) 277 | description = str(sel[1].replace("\t", " ")) 278 | except Exception: 279 | pass 280 | for cb in self.onChangedEntry: 281 | cb(name, description) 282 | 283 | def setTimer(self, result=None, retval=None, extra_args=None): 284 | self["lab1"].show() 285 | self.activityTimer.start(10) 286 | 287 | def findPartitions(self): 288 | self.activityTimer.stop() 289 | self.partitionList = [] 290 | SystemInfo["MountManager"] = True 291 | getProcPartitions(self.partitionList) 292 | self["list"].list = self.partitionList 293 | self["lab1"].hide() 294 | 295 | def setupMounts(self): 296 | self.session.openWithCallback(self.setTimer, DeviceMountSetup) # print("[MountManager][setupMounts") 297 | 298 | def unmount(self): 299 | sel = self["list"].getCurrent() 300 | # print("[MountManager][unmount] sel1=%s sel2=%s" % (sel[0], sel[1])) 301 | if sel: 302 | des = sel[1] 303 | des = des.replace("\n", "\t") 304 | parts = des.strip().split("\t") 305 | mountp = parts[1].replace(_("Mount: "), "") 306 | device = parts[2].replace(_("Device: "), "") 307 | # print("[MountManager][unmount] mountp=%s device=%s" % (mountp, device)) 308 | exitStatus = system("umount %s" % mountp) 309 | if exitStatus == 0: 310 | self.session.open(MessageBox, _("Partition: %s Mount: %s unmounted successfully; if all partitions now unmounted you can remove device.") % (device, mountp), MessageBox.TYPE_INFO) 311 | self.setTimer() 312 | else: 313 | self.session.open(MessageBox, _("Cannot unmount partition '%s'. Make sure this partition is not in use. (SWAP, record/timeshift, etc.)") % mountp, MessageBox.TYPE_INFO) 314 | return -1 315 | 316 | def mount(self): 317 | sel = self["list"].getCurrent() 318 | # print("[MountManager][mount] sel1=%s sel2=%s" % (sel[0], sel[1])) 319 | if sel: 320 | des = sel[1] 321 | des = des.replace("\n", "\t") 322 | parts = des.strip().split("\t") 323 | mountp = parts[1].replace(_("Mount: "), "") 324 | device = parts[2].replace(_("Device: "), "") 325 | # print("[MountManager][mount] mountp=%s device=%s" % (mountp, device)) 326 | exitStatus = system("mount %s" % device) 327 | if exitStatus != 0: 328 | self.session.open(MessageBox, _("Mount failed for '%s', error code = '%s'.") % (sel, exitStatus), MessageBox.TYPE_INFO, timeout=10) 329 | self.setTimer() 330 | 331 | def saveMounts(self): 332 | sel = self["list"].getCurrent() 333 | # print("[MountManager][saveMounts] selection=%s" % sel) 334 | if sel: 335 | parts = sel[1].split() 336 | self.device = parts[5] 337 | self.mountp = parts[3] 338 | # print("[MountManager1]saveMounts: device = %s, mountp = %s" %(self.device, self.mountp)) 339 | self.Console.ePopen("umount " + self.device) 340 | if self.mountp.find("/media/hdd") < 0: 341 | self.Console.ePopen("umount /media/hdd") 342 | self.Console.ePopen("/sbin/blkid | grep " + self.device, self.addFstab, [self.device, self.mountp]) 343 | else: 344 | self.session.open(MessageBox, _("This device is already mounted as HDD."), MessageBox.TYPE_INFO, timeout=10, close_on_any_key=True) 345 | 346 | def addFstab(self, result=None, retval=None, extra_args=None): 347 | # print("[MountManager] RESULT:", result) 348 | if result: 349 | self.device = extra_args[0] 350 | self.mountp = extra_args[1] 351 | self.device_uuid = "UUID=" + result.split("UUID=")[1].split(" ")[0].replace('"', '') 352 | # print("[MountManager1][addFstab1]: device = %s, mountp=%s, UUID=%s" %(self.device, self.mountp, self.device_uuid)) 353 | if not path.exists(self.mountp): 354 | mkdir(self.mountp, 0o755) 355 | open("/etc/fstab.tmp", "w").writelines([l for l in open("/etc/fstab").readlines() if "/media/hdd" not in l]) 356 | rename("/etc/fstab.tmp", "/etc/fstab") 357 | open("/etc/fstab.tmp", "w").writelines([l for l in open("/etc/fstab").readlines() if self.device not in l]) 358 | rename("/etc/fstab.tmp", "/etc/fstab") 359 | open("/etc/fstab.tmp", "w").writelines([l for l in open("/etc/fstab").readlines() if self.device_uuid not in l]) 360 | rename("/etc/fstab.tmp", "/etc/fstab") 361 | with open("/etc/fstab", "a") as fd: 362 | line = self.device_uuid + "\t/media/hdd\tauto\tdefaults\t0 0\n" 363 | fd.write(line) 364 | self.Console.ePopen("mount -a", self.setTimer) 365 | 366 | 367 | class DeviceMountSetup(Screen, ConfigListScreen): 368 | skin = [""" 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | """, 377 | 560, 450, # screen 378 | 0, 0, 140, 40, #colors 379 | 140, 0, 140, 40, 380 | 0, 0, 140, 40, 20, 381 | 140, 0, 140, 40, 20, 382 | 0, 50, 560, 275, 26, 20, # config 383 | 0, 365, 560, 20, 18, 384 | ] 385 | 386 | def __init__(self, session): 387 | Screen.__init__(self, session) 388 | self.partitionList = [] 389 | ConfigListScreen.__init__(self, self.partitionList) 390 | self.setTitle(_("Choose where to mount your devices to:")) 391 | self["key_green"] = Label(_("Save")) 392 | self["key_red"] = Label(_("Cancel")) 393 | self["lab1"] = Label() 394 | self["actions"] = ActionMap(["WizardActions", "ColorActions"], { 395 | "red": self.close, 396 | "green": self.saveconfMounts, 397 | "back": self.close 398 | }) 399 | self.Console = Console() 400 | self.activityTimer = eTimer() 401 | self.activityTimer.timeout.get().append(self.findconfPartitions) 402 | self.setconfTimer() 403 | 404 | def setconfTimer(self, result=None, retval=None, extra_args=None): 405 | scanning = _("Please wait while scanning your %s %s devices...") % (getMachineBrand(), getMachineName()) 406 | self["lab1"].setText(scanning) 407 | self.activityTimer.start(10) 408 | 409 | def findconfPartitions(self): 410 | self.activityTimer.stop() 411 | self.partitionList = [] 412 | SystemInfo["MountManager"] = False 413 | getProcPartitions(self.partitionList) 414 | self["config"].list = self.partitionList 415 | self["config"].l.setList(self.partitionList) 416 | self["lab1"].hide() 417 | 418 | def saveconfMounts(self): 419 | for x in self["config"].list: # partitionInfo = getConfigListEntry(text, item, partition, _format) 420 | self.device = x[2] 421 | self.mountp = x[1].value 422 | self.type = x[3] 423 | # print("[MountManager][saveconfMount] mountp=%s device=%s type=%s" % (self.mountp, self.device, self.type)) 424 | self.Console.ePopen("umount %s" % self.device) 425 | self.Console.ePopen("/sbin/blkid | grep " + self.device + " && opkg list-installed ntfs-3g", self.addconfFstab, [self.device, self.mountp]) 426 | message = _("Updating mount locations...") 427 | ybox = self.session.openWithCallback(self.delay, MessageBox, message, type=MessageBox.TYPE_INFO, timeout=5, enable_input=False) 428 | ybox.setTitle(_("Please wait.")) 429 | 430 | def delay(self, val): 431 | message = _("The changes need a system restart to take effect.\nRestart your %s %s now?") % (getMachineBrand(), getMachineName()) 432 | ybox = self.session.openWithCallback(self.restartBox, MessageBox, message, MessageBox.TYPE_YESNO) 433 | ybox.setTitle(_("Restart %s %s.") % (getMachineBrand(), getMachineName())) 434 | 435 | def addconfFstab(self, result=None, retval=None, extra_args=None): 436 | # print("[MountManager] RESULT:", result) 437 | if result: 438 | self.device = extra_args[0] 439 | self.mountp = extra_args[1] 440 | uuid = re.search('UUID=\"([^\"]+)\"', result) 441 | type = re.search('TYPE=\"([^\"]+)\"', result) 442 | if uuid and type: 443 | self.device_uuid = "UUID=" + uuid.group(1) 444 | self.device_type = type.group(1) 445 | # print("[MountManager][addFstab2] device_uuid:%s device_type:%s" % (self.device_uuid, self.device_type)) 446 | if self.device_type.startswith("ext"): 447 | self.device_type = "auto" 448 | elif self.device_type.startswith("ntfs") and result.find("ntfs-3g") != -1: 449 | self.device_type = "ntfs-3g" 450 | elif self.device_type.startswith("ntfs") and result.find("ntfs-3g") == -1: 451 | self.device_type = "ntfs" 452 | if not path.exists(self.mountp): 453 | mkdir(self.mountp, 0o755) 454 | open("/etc/fstab.tmp", "w").writelines([l for l in open("/etc/fstab").readlines() if self.device not in l]) 455 | rename("/etc/fstab.tmp", "/etc/fstab") 456 | open("/etc/fstab.tmp", "w").writelines([l for l in open("/etc/fstab").readlines() if self.device_uuid not in l]) 457 | rename("/etc/fstab.tmp", "/etc/fstab") 458 | with open("/etc/fstab", "a") as fd: 459 | line = self.device_uuid + "\t" + self.mountp + "\t" + self.device_type + "\tdefaults\t0 0\n" 460 | fd.write(line) 461 | 462 | def restartBox(self, answer): 463 | if answer is True: 464 | self.session.open(TryQuitMainloop, QUIT_REBOOT) 465 | else: 466 | self.close() 467 | -------------------------------------------------------------------------------- /src/Multibootmgr.py: -------------------------------------------------------------------------------- 1 | from os import statvfs 2 | from boxbranding import getMachineBuild 3 | from Components.ActionMap import ActionMap 4 | from Components.ChoiceList import ChoiceList, ChoiceEntryComponent 5 | from Components.config import config 6 | from Components.Label import Label 7 | from Components.Sources.StaticText import StaticText 8 | from Components.SystemInfo import SystemInfo 9 | from Screens.Console import Console 10 | from Screens.MessageBox import MessageBox 11 | from Screens.Screen import Screen 12 | from Screens.Standby import TryQuitMainloop 13 | from Tools.BoundFunction import boundFunction 14 | from Tools.Directories import pathExists 15 | from Tools.Multiboot import GetImagelist, GetCurrentImage, GetCurrentImageMode, EmptySlot 16 | 17 | 18 | class MultiBoot(Screen): 19 | 20 | skin = """ 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | """ 36 | 37 | def __init__(self, session): 38 | Screen.__init__(self, session) 39 | self.skinName = "MultiBoot" 40 | self.setTitle(_("Multiboot image manager")) 41 | self.title = screentitle 42 | self["key_red"] = StaticText(_("Cancel")) 43 | self["labe14"] = StaticText(_("Use the cursor keys to select an installed image and then Erase button.")) 44 | self["labe15"] = StaticText(_("Note: slot list does not show current image or empty slots.")) 45 | self["key_green"] = StaticText(_("Erase")) 46 | self["key_yellow"] = StaticText("") 47 | self["config"] = ChoiceList(list=[ChoiceEntryComponent('', ((_("Retrieving image slots - Please wait...")), "Queued"))]) 48 | imagedict = [] 49 | self.getImageList = None 50 | self.startit() 51 | 52 | self["actions"] = ActionMap(["OkCancelActions", "ColorActions", "DirectionActions", "KeyboardInputActions", "MenuActions"], 53 | { 54 | "red": boundFunction(self.close, None), 55 | "green": self.erase, 56 | "yellow": boundFunction(self.close, None), 57 | "ok": self.erase, 58 | "cancel": boundFunction(self.close, None), 59 | "up": self.keyUp, 60 | "down": self.keyDown, 61 | "left": self.keyLeft, 62 | "right": self.keyRight, 63 | "upRepeated": self.keyUp, 64 | "downRepeated": self.keyDown, 65 | "leftRepeated": self.keyLeft, 66 | "rightRepeated": self.keyRight, 67 | "menu": boundFunction(self.close, True), 68 | }, -1) 69 | self.onLayoutFinish.append(self.layoutFinished) 70 | 71 | def layoutFinished(self): 72 | self.setTitle(self.title) 73 | 74 | def startit(self): 75 | self.getImageList = GetImagelist(self.ImageList) 76 | 77 | def ImageList(self, imagedict): 78 | list = [] 79 | mode = GetCurrentImageMode() or 0 80 | currentimageslot = GetCurrentImage() 81 | for x in sorted(imagedict.keys()): 82 | if imagedict[x]["imagename"] != _("Empty slot") and x != currentimageslot: 83 | list.append(ChoiceEntryComponent('', ((_("slot%s - %s ")) % (x, imagedict[x]['imagename']), x))) 84 | self["config"].setList(list) 85 | 86 | def erase(self): 87 | self.currentSelected = self["config"].l.getCurrentSelection() 88 | if self.currentSelected != None: 89 | if self.currentSelected[0][1] != "Queued": 90 | if SystemInfo["HasRootSubdir"]: 91 | message = _("Removal of this slot will not show in %s Gui. Are you sure you want to delete image slot %s ?" % (getMachineBuild(), self.currentSelected[0][1])) 92 | ybox = self.session.openWithCallback(self.doErase, MessageBox, message, MessageBox.TYPE_YESNO, default=True) 93 | ybox.setTitle(_("Remove confirmation")) 94 | else: 95 | message = _("Are you sure you want to delete image slot %s ?" % self.currentSelected[0][1]) 96 | ybox = self.session.openWithCallback(self.doErase, MessageBox, message, MessageBox.TYPE_YESNO, default=True) 97 | ybox.setTitle(_("Remove confirmation")) 98 | 99 | def doErase(self, answer): 100 | if answer is True: 101 | sloterase = EmptySlot(self.currentSelected[0][1], self.startit) 102 | 103 | def selectionChanged(self): 104 | pass 105 | 106 | def keyLeft(self): 107 | self["config"].instance.moveSelection(self["config"].instance.moveUp) 108 | self.selectionChanged() 109 | 110 | def keyRight(self): 111 | self["config"].instance.moveSelection(self["config"].instance.moveDown) 112 | self.selectionChanged() 113 | 114 | def keyUp(self): 115 | self["config"].instance.moveSelection(self["config"].instance.moveUp) 116 | self.selectionChanged() 117 | 118 | def keyDown(self): 119 | self["config"].instance.moveSelection(self["config"].instance.moveDown) 120 | self.selectionChanged() 121 | -------------------------------------------------------------------------------- /src/RestoreWizard.py: -------------------------------------------------------------------------------- 1 | from os import listdir, path, stat 2 | from boxbranding import getMachineBrand, getMachineName, getImageDistro 3 | from . import _ 4 | from .BackupManager import isRestorableSettings, isRestorablePlugins, isRestorableKernel 5 | 6 | from Components.About import about 7 | from Components.config import config, configfile 8 | from Components.Console import Console 9 | from Components.Pixmap import Pixmap 10 | from Components.SystemInfo import SystemInfo 11 | from Screens.MessageBox import MessageBox 12 | from Screens.Rc import Rc 13 | from Screens.WizardLanguage import WizardLanguage 14 | from Tools.Directories import fileExists, fileHas, resolveFilename, SCOPE_PLUGINS 15 | from Tools.Multiboot import bootmviSlot, createInfo 16 | 17 | 18 | class RestoreWizard(WizardLanguage, Rc): 19 | def __init__(self, session): 20 | self.xmlfile = resolveFilename(SCOPE_PLUGINS, "SystemPlugins/ViX/restorewizard.xml") 21 | WizardLanguage.__init__(self, session, showSteps=False, showStepSlider=False) 22 | Rc.__init__(self) 23 | self.session = session 24 | self.skinName = "StartWizard" 25 | self.skin = "StartWizard.skin" 26 | self["wizard"] = Pixmap() 27 | self.selectedAction = None 28 | self.NextStep = None 29 | self.Text = None 30 | self.buildListRef = None 31 | self.didSettingsRestore = False 32 | self.didPluginRestore = False 33 | self.PluginsRestore = False 34 | self.fullbackupfilename = None 35 | self.delaymess = None 36 | self.selectedDevice = None 37 | self.Console = Console() 38 | 39 | def getTranslation(self, text): 40 | return _(text).replace("%s %s", "%s %s" % (getMachineBrand(), getMachineName())) 41 | 42 | def listDevices(self): 43 | devmounts = [] 44 | list = [] 45 | files = [] 46 | mtimes = [] 47 | defaultprefix = getImageDistro()[4:] 48 | 49 | for dir in ["/media/%s/backup" % media for media in listdir("/media/") if path.isdir(path.join("/media/", media))]: 50 | devmounts.append(dir) 51 | if len(devmounts): 52 | for devpath in devmounts: 53 | if path.exists(devpath): 54 | try: 55 | files = listdir(devpath) 56 | except: 57 | files = [] 58 | else: 59 | files = [] 60 | if len(files): 61 | for file in files: 62 | if file.endswith(".tar.gz") and "vix" in file.lower() or file.startswith("%s" % defaultprefix): 63 | mtimes.append((path.join(devpath, file), stat(path.join(devpath, file)).st_mtime)) # (filname, mtime) 64 | for file in [x[0] for x in sorted(mtimes, key=lambda x: x[1], reverse=True)]: # sort by mtime 65 | list.append((file, file)) 66 | return list 67 | 68 | def settingsdeviceSelectionMade(self, index): 69 | self.selectedAction = index 70 | self.settingsdeviceSelect(index) 71 | 72 | def settingsdeviceSelect(self, index): 73 | self.selectedDevice = index 74 | self.fullbackupfilename = index 75 | self.NextStep = "settingrestorestarted" 76 | 77 | def settingsdeviceSelectionMoved(self): 78 | self.settingsdeviceSelect(self.selection) 79 | 80 | def pluginsdeviceSelectionMade(self, index): 81 | self.selectedAction = index 82 | self.pluginsdeviceSelect(index) 83 | 84 | def pluginsdeviceSelect(self, index): 85 | self.selectedDevice = index 86 | self.fullbackupfilename = index 87 | self.NextStep = "plugindetection" 88 | 89 | def pluginsdeviceSelectionMoved(self): 90 | self.pluginsdeviceSelect(self.selection) 91 | 92 | def markDone(self): 93 | pass 94 | 95 | def listAction(self): 96 | list = [(_("OK, to perform a restore"), "settingsquestion"), (_("Exit the restore wizard"), "end")] 97 | return list 98 | 99 | def listAction2(self): 100 | list = [(_("YES, to restore settings"), "settingsrestore"), (_("NO, do not restore settings"), "pluginsquestion")] 101 | return list 102 | 103 | def listAction3(self): 104 | list = [] 105 | if self.didSettingsRestore: 106 | list.append((_("YES, to restore plugins"), "pluginrestore")) 107 | list.append((_("NO, do not restore plugins"), "reboot")) 108 | else: 109 | list.append((_("YES, to restore plugins"), "pluginsrestoredevice")) 110 | list.append((_("NO, do not restore plugins"), "end")) 111 | return list 112 | 113 | def rebootAction(self): 114 | list = [(_("OK"), "reboot")] 115 | return list 116 | 117 | def ActionSelectionMade(self, index): 118 | self.selectedAction = index 119 | self.ActionSelect(index) 120 | 121 | def ActionSelect(self, index): 122 | self.NextStep = index 123 | 124 | def ActionSelectionMoved(self): 125 | self.ActionSelect(self.selection) 126 | 127 | def buildList(self, action): 128 | if self.NextStep == "reboot": 129 | if fileHas("/proc/cmdline", "kexec=1") and config.usage.bootlogo_identify.value: 130 | slot = SystemInfo["MultiBootSlot"] 131 | text = createInfo(slot) 132 | bootmviSlot(text=text, slot=slot) 133 | if self.didSettingsRestore: 134 | self.Console.ePopen("tar -xzvf " + self.fullbackupfilename + " -C /" + " etc/enigma2/settings") 135 | self.Console.ePopen("killall -9 enigma2 && init 6") 136 | elif self.NextStep == "settingsquestion" or self.NextStep == "settingsrestore" or self.NextStep == "pluginsquestion" or self.NextStep == "pluginsrestoredevice" or self.NextStep == "end" or self.NextStep == "noplugins": 137 | self.buildListfinishedCB(False) 138 | elif self.NextStep == "settingrestorestarted": 139 | self.Console.ePopen("tar -xzvf " + self.fullbackupfilename + " -C / tmp/ExtraInstalledPlugins tmp/backupkernelversion tmp/backupimageversion", self.settingsRestore_Started) 140 | self.buildListRef = self.session.openWithCallback(self.buildListfinishedCB, MessageBox, _("Please wait while the system gathers information..."), type=MessageBox.TYPE_INFO, enable_input=False, wizard=True) 141 | self.buildListRef.setTitle(_("Restore wizard")) 142 | elif self.NextStep == "plugindetection": 143 | print("[RestoreWizard] Stage 2: Restoring plugins") 144 | self.Console.ePopen("tar -xzvf " + self.fullbackupfilename + " -C / tmp/ExtraInstalledPlugins tmp/backupkernelversion tmp/backupimageversion", self.pluginsRestore_Started) 145 | self.buildListRef = self.session.openWithCallback(self.buildListfinishedCB, MessageBox, _("Please wait while the system gathers information..."), type=MessageBox.TYPE_INFO, enable_input=False, wizard=True) 146 | self.buildListRef.setTitle(_("Restore wizard")) 147 | elif self.NextStep == "pluginrestore": 148 | if self.feeds == "OK": 149 | print("[RestoreWizard] Stage 6: Feeds OK, Restoring Plugins") 150 | print("[RestoreWizard] Console command: ", "opkg install " + self.pluginslist + " " + self.pluginslist2) 151 | self.Console.ePopen("opkg install " + self.pluginslist + " " + self.pluginslist2, self.pluginsRestore_Finished) 152 | self.buildListRef = self.session.openWithCallback(self.buildListfinishedCB, MessageBox, _("Please wait while plugins restore completes..."), type=MessageBox.TYPE_INFO, enable_input=False, wizard=True) 153 | self.buildListRef.setTitle(_("Restore wizard")) 154 | elif self.feeds == "DOWN": 155 | print("[RestoreWizard] Stage 6: Feeds Down") 156 | self.didPluginRestore = True 157 | self.NextStep = "reboot" 158 | self.buildListRef = self.session.openWithCallback(self.buildListfinishedCB, MessageBox, _("Sorry the feeds are down for maintenance. Please try using Backup manager to restore plugins later."), type=MessageBox.TYPE_INFO, timeout=30, wizard=True) 159 | self.buildListRef.setTitle(_("Restore wizard")) 160 | elif self.feeds == "BAD": 161 | print("[RestoreWizard] Stage 6: No Network") 162 | self.didPluginRestore = True 163 | self.NextStep = "reboot" 164 | self.buildListRef = self.session.openWithCallback(self.buildListfinishedCB, MessageBox, _("Your %s %s is not connected to the Internet. Please try using Backup manager to restore plugins later.") % (getMachineBrand(), getMachineName()), type=MessageBox.TYPE_INFO, timeout=30, wizard=True) 165 | self.buildListRef.setTitle(_("Restore wizard")) 166 | elif self.feeds == "ERROR": 167 | self.NextStep = "pluginrestore" 168 | self.buildListRef = self.session.openWithCallback(self.buildListfinishedCB, MessageBox, _("A background update check is in progress, please try again."), type=MessageBox.TYPE_INFO, timeout=10, wizard=True) 169 | self.buildListRef.setTitle(_("Restore wizard")) 170 | 171 | def buildListfinishedCB(self, data): 172 | # self.buildListRef = None 173 | if data is True: 174 | self.currStep = self.getStepWithID(self.NextStep) 175 | self.afterAsyncCode() 176 | else: 177 | self.currStep = self.getStepWithID(self.NextStep) 178 | self.afterAsyncCode() 179 | 180 | def settingsRestore_Started(self, result, retval, extra_args=None): 181 | self.doRestoreSettings1() 182 | 183 | def doRestoreSettings1(self): 184 | print("[RestoreWizard] Stage 1: Check Version") 185 | if fileExists("/tmp/backupimageversion"): 186 | imageversion = open("/tmp/backupimageversion").read() 187 | print("[RestoreWizard] Backup Image:", imageversion) 188 | print("[RestoreWizard] Current Image:", about.getVersionString()) 189 | if imageversion == about.getVersionString() or isRestorableSettings(imageversion): 190 | print("[RestoreWizard] Stage 1: Image ver OK") 191 | self.doRestoreSettings2() 192 | else: 193 | print("[RestoreWizard] Stage 1: Image ver different") 194 | self.noVersion = self.session.openWithCallback(self.doNoVersion, MessageBox, _("Sorry, but the file is not compatible with this image version."), type=MessageBox.TYPE_INFO, timeout=30, wizard=True) 195 | self.noVersion.setTitle(_("Restore wizard")) 196 | else: 197 | print("[RestoreWizard] Stage 1: No Image ver to check") 198 | self.noVersion = self.session.openWithCallback(self.doNoVersion, MessageBox, _("Sorry, but the file is not compatible with this image version."), type=MessageBox.TYPE_INFO, timeout=30, wizard=True) 199 | self.noVersion.setTitle(_("Restore wizard")) 200 | 201 | def doNoVersion(self, result=None, retval=None, extra_args=None): 202 | self.buildListRef.close(True) 203 | 204 | def doRestoreSettings2(self): 205 | print("[RestoreWizard] Stage 2: Restoring settings") 206 | self.Console.ePopen("tar -xzvf " + self.fullbackupfilename + " -C /", self.settingRestore_Finished) 207 | self.pleaseWait = self.session.open(MessageBox, _("Please wait while settings restore completes..."), type=MessageBox.TYPE_INFO, enable_input=False, wizard=True) 208 | self.pleaseWait.setTitle(_("Restore wizard")) 209 | 210 | def settingRestore_Finished(self, result, retval, extra_args=None): 211 | self.didSettingsRestore = True 212 | network = [x.split(" ")[3] for x in open("/etc/network/interfaces").read().splitlines() if x.startswith("iface eth0")] 213 | self.pleaseWait.close() 214 | self.doRestorePlugins1() 215 | 216 | def pluginsRestore_Started(self, result, retval, extra_args=None): 217 | self.doRestorePlugins1() 218 | 219 | def pluginsRestore_Finished(self, result, retval, extra_args=None): 220 | if result: 221 | print("[RestoreWizard] opkg install result:\n", result) 222 | self.didPluginRestore = True 223 | self.NextStep = "reboot" 224 | self.buildListRef.close(True) 225 | 226 | def doRestorePlugins1(self): 227 | print("[RestoreWizard] Stage 3: Check Kernel") 228 | if fileExists("/tmp/backupkernelversion") and fileExists("/tmp/backupimageversion"): 229 | imageversion = open("/tmp/backupimageversion").read() 230 | kernelversion = open("/tmp/backupkernelversion").read() 231 | print("[RestoreWizard] Backup Image:", imageversion) 232 | print("[RestoreWizard] Current Image:", about.getVersionString()) 233 | print("[RestoreWizard] Backup Kernel:", kernelversion) 234 | print("[RestoreWizard] Current Kernel:", about.getKernelVersionString()) 235 | if isRestorableKernel(kernelversion) and (imageversion == about.getVersionString() or isRestorablePlugins(imageversion)): 236 | print("[RestoreWizard] Stage 3: Kernel and image ver OK") 237 | self.doRestorePluginsTest() 238 | else: 239 | print("[RestoreWizard] Stage 3: Kernel or image ver Different") 240 | if self.didSettingsRestore: 241 | self.NextStep = "reboot" 242 | else: 243 | self.NextStep = "noplugins" 244 | self.buildListRef.close(True) 245 | else: 246 | print("[RestoreWizard] Stage 3: No Kernel to check") 247 | if self.didSettingsRestore: 248 | self.NextStep = "reboot" 249 | else: 250 | self.NextStep = "noplugins" 251 | self.buildListRef.close(True) 252 | 253 | def doRestorePluginsTest(self, result=None, retval=None, extra_args=None): 254 | if self.delaymess: 255 | self.delaymess.close() 256 | print("[RestoreWizard] Stage 4: Feeds Test") 257 | self.Console.ePopen("opkg update", self.doRestorePluginsTestComplete) 258 | 259 | def doRestorePluginsTestComplete(self, result='', retval=None, extra_args=None): 260 | print("[RestoreWizard] Stage 4: Feeds Test Result", result) 261 | if result.find("wget returned 4") != -1: 262 | self.NextStep = "reboot" 263 | self.buildListRef = self.session.openWithCallback(self.buildListfinishedCB, MessageBox, _("Your %s %s is not connected to a network. Please try using the Backup manager to restore plugins later when a network connection is available.") % (getMachineBrand(), getMachineName()), type=MessageBox.TYPE_INFO, timeout=30, wizard=True) 264 | self.buildListRef.setTitle(_("Restore wizard")) 265 | elif result.find("wget returned 8") != -1: 266 | self.NextStep = "reboot" 267 | self.buildListRef = self.session.openWithCallback(self.buildListfinishedCB, MessageBox, _("Your %s %s could not connect to the plugin feeds at this time. Please try using the Backup manager to restore plugins later.") % (getMachineBrand(), getMachineName()), type=MessageBox.TYPE_INFO, timeout=30, wizard=True) 268 | self.buildListRef.setTitle(_("Restore wizard")) 269 | elif result.find("bad address") != -1: 270 | self.NextStep = "reboot" 271 | self.buildListRef = self.session.openWithCallback(self.buildListfinishedCB, MessageBox, _("Your %s %s is not connected to the Internet. Please try using the Backup manager to restore plugins later.") % (getMachineBrand(), getMachineName()), type=MessageBox.TYPE_INFO, timeout=30, wizard=True) 272 | self.buildListRef.setTitle(_("Restore wizard")) 273 | elif result.find("wget returned 1") != -1 or result.find("wget returned 255") != -1 or result.find("404 Not Found") != -1: 274 | self.NextStep = "reboot" 275 | self.buildListRef = self.session.openWithCallback(self.buildListfinishedCB, MessageBox, _("Sorry the feeds are down for maintenance. Please try using the Backup manager to restore plugins later."), type=MessageBox.TYPE_INFO, timeout=30, wizard=True) 276 | self.buildListRef.setTitle(_("Restore wizard")) 277 | elif result.find("Collected errors") != -1: 278 | print("[RestoreWizard] Stage 4: Update is in progress, delaying") 279 | self.delaymess = self.session.openWithCallback(self.doRestorePluginsTest, MessageBox, _("A background update check is in progress, please try again."), type=MessageBox.TYPE_INFO, timeout=10, wizard=True) 280 | self.delaymess.setTitle(_("Restore wizard")) 281 | else: 282 | print("[RestoreWizard] Stage 4: Feeds OK") 283 | self.feeds = "OK" 284 | self.doListPlugins() 285 | 286 | def doListPlugins(self): 287 | print("[RestoreWizard] Stage 4: Feeds Test") 288 | self.Console.ePopen("opkg list-installed", self.doRestorePlugins2) 289 | 290 | def doRestorePlugins2(self, result, retval, extra_args): 291 | print("[RestoreWizard] Stage 5: Build list of plugins to restore") 292 | self.pluginslist = "" 293 | self.pluginslist2 = "" 294 | plugins = [] 295 | if path.exists("/tmp/ExtraInstalledPlugins"): 296 | self.pluginslist = [] 297 | for line in result.split("\n"): 298 | if line: 299 | parts = line.strip().split() 300 | plugins.append(parts[0]) 301 | tmppluginslist = open("/tmp/ExtraInstalledPlugins", "r").readlines() 302 | for line in tmppluginslist: 303 | if line: 304 | parts = line.strip().split() 305 | if len(parts) > 0 and parts[0] not in plugins: 306 | self.pluginslist.append(parts[0]) 307 | 308 | if path.exists("/tmp/3rdPartyPlugins"): 309 | self.pluginslist2 = [] 310 | if path.exists("/tmp/3rdPartyPluginsLocation"): 311 | self.thirdpartyPluginsLocation = open("/tmp/3rdPartyPluginsLocation", "r").readlines() 312 | self.thirdpartyPluginsLocation = "".join(self.thirdpartyPluginsLocation) 313 | self.thirdpartyPluginsLocation = self.thirdpartyPluginsLocation.replace("\n", "") 314 | self.thirdpartyPluginsLocation = self.thirdpartyPluginsLocation.replace(" ", "%20") 315 | self.plugfiles = self.thirdpartyPluginsLocation.split("/", 3) 316 | else: 317 | self.thirdpartyPluginsLocation = " " 318 | 319 | tmppluginslist2 = open("/tmp/3rdPartyPlugins", "r").readlines() 320 | available = None 321 | for line in tmppluginslist2: 322 | if line: 323 | parts = line.strip().split("_") 324 | if parts[0] not in plugins: 325 | ipk = parts[0] 326 | if path.exists(self.thirdpartyPluginsLocation): 327 | available = listdir(self.thirdpartyPluginsLocation) 328 | else: 329 | devmounts = [] 330 | files = [] 331 | self.plugfile = self.plugfiles[3] 332 | for dir in ["/media/%s/%s" % (media, self.plugfile) for media in listdir("/media/") if path.isdir(path.join("/media/", media)) and path.exists("/media/%s/%s" % (media, self.plugfile))]: 333 | if media not in ("autofs", "net"): 334 | devmounts.append(dir) 335 | if len(devmounts): 336 | for x in devmounts: 337 | print("[BackupManager] search dir = %s" % devmounts) 338 | if path.exists(x): 339 | self.thirdpartyPluginsLocation = x 340 | try: 341 | available = listdir(self.thirdpartyPluginsLocation) 342 | break 343 | except: 344 | continue 345 | if available: 346 | for file in available: 347 | if file: 348 | fileparts = file.strip().split("_") 349 | if fileparts[0] == ipk: 350 | self.thirdpartyPluginsLocation = self.thirdpartyPluginsLocation.replace(" ", "%20") 351 | ipk = path.join(self.thirdpartyPluginsLocation, file) 352 | if path.exists(ipk): 353 | self.pluginslist2.append(ipk) 354 | 355 | if len(self.pluginslist) or len(self.pluginslist2): 356 | self.doRestorePluginsQuestion() 357 | else: 358 | if self.didSettingsRestore: 359 | self.NextStep = "reboot" 360 | else: 361 | self.NextStep = "noplugins" 362 | self.buildListRef.close(True) 363 | 364 | def doRestorePluginsQuestion(self): 365 | if len(self.pluginslist) or len(self.pluginslist2): 366 | if len(self.pluginslist): 367 | self.pluginslist = " ".join(self.pluginslist) 368 | else: 369 | self.pluginslist = "" 370 | if len(self.pluginslist2): 371 | self.pluginslist2 = " ".join(self.pluginslist2) 372 | else: 373 | self.pluginslist2 = "" 374 | print("[RestoreWizard] Stage 6: Plugins to restore in feeds", self.pluginslist) 375 | print("[RestoreWizard] Stage 6: Plugins to restore in extra location", self.pluginslist2) 376 | if self.didSettingsRestore: 377 | print("[RestoreWizard] Stage 6: proceed to question") 378 | self.NextStep = "pluginsquestion" 379 | self.buildListRef.close(True) 380 | else: 381 | print("[RestoreWizard] Stage 6: proceed to restore") 382 | self.NextStep = "pluginrestore" 383 | self.buildListRef.close(True) 384 | else: 385 | print("[RestoreWizard] Stage 6: NO Plugins to restore") 386 | if self.didSettingsRestore: 387 | self.NextStep = "reboot" 388 | else: 389 | self.NextStep = "noplugins" 390 | self.buildListRef.close(True) 391 | -------------------------------------------------------------------------------- /src/ScriptRunner.py: -------------------------------------------------------------------------------- 1 | from os import path, mkdir, listdir, rename 2 | 3 | from . import _, PluginLanguageDomain 4 | from Components.ActionMap import ActionMap 5 | from Components.config import config, ConfigSubsection, ConfigYesNo 6 | from Components.PluginComponent import plugins 7 | from Components.Sources.StaticText import StaticText 8 | from .IPKInstaller import IpkgInstaller 9 | from Screens.Console import Console 10 | from Screens.Screen import Screen 11 | from Screens.Setup import Setup 12 | from Tools.Directories import resolveFilename, SCOPE_PLUGINS 13 | 14 | config.scriptrunner = ConfigSubsection() 15 | config.scriptrunner.close = ConfigYesNo(default=False) 16 | config.scriptrunner.showinextensions = ConfigYesNo(default=False) 17 | 18 | 19 | def updateExtensions(configElement): 20 | plugins.clearPluginList() 21 | plugins.readPluginList(resolveFilename(SCOPE_PLUGINS)) 22 | 23 | 24 | config.scriptrunner.showinextensions.addNotifier(updateExtensions, initial_call=False) 25 | 26 | 27 | def ScriptRunnerAutostart(reason, session=None, **kwargs): 28 | pass 29 | 30 | 31 | class VIXScriptRunner(IpkgInstaller): 32 | def __init__(self, session, list=None): 33 | if not list: 34 | list = [] 35 | if path.exists("/usr/scripts") and not path.exists("/usr/script"): 36 | rename("/usr/scripts", "/usr/script") 37 | if not path.exists("/usr/script"): 38 | mkdir("/usr/script", 0o755) 39 | f = listdir("/usr/script") 40 | for line in f: 41 | parts = line.split() 42 | pkg = parts[0] 43 | if pkg.find(".sh") >= 0: 44 | list.append(pkg) 45 | IpkgInstaller.__init__(self, session, list) 46 | self.setTitle(_("Script runner")) 47 | 48 | self.skinName = ["VIXScriptRunner", "IpkgInstaller"] 49 | self["key_green"] = StaticText(_("Run")) 50 | 51 | self["myactions"] = ActionMap(["MenuActions"], 52 | { 53 | "menu": self.createSetup, 54 | }, -1) 55 | 56 | def createSetup(self): 57 | self.session.open(Setup, "vixscriptrunner", "SystemPlugins/ViX", PluginLanguageDomain) 58 | 59 | def install(self): 60 | list = self.list.getSelectionsList() 61 | cmdList = [] 62 | for item in list: 63 | cmdList.append("chmod +x /usr/script/" + item[0] + " && . " + "/usr/script/" + str(item[0])) 64 | if len(cmdList) < 1 and len(self.list.list): 65 | cmdList.append("chmod +x /usr/script/" + self.list.getCurrent()[0][0] + " && . " + "/usr/script/" + str(self.list.getCurrent()[0][0])) 66 | if len(cmdList) > 0: 67 | self.session.open(Console, cmdlist=cmdList, closeOnSuccess=config.scriptrunner.close.value) 68 | -------------------------------------------------------------------------------- /src/SwapManager.py: -------------------------------------------------------------------------------- 1 | from os import system, stat as mystat, path, remove, rename 2 | from glob import glob 3 | import stat 4 | 5 | from enigma import eTimer 6 | 7 | from . import _ 8 | 9 | from Components.config import config, configfile, ConfigSubsection, ConfigYesNo 10 | from Components.ActionMap import ActionMap 11 | from Components.Console import Console 12 | from Components.Harddisk import harddiskmanager, getProcMounts 13 | from Components.Label import Label 14 | from Components.Pixmap import Pixmap 15 | from Components.Sources.StaticText import StaticText 16 | from Screens.ChoiceBox import ChoiceBox 17 | from Screens.MessageBox import MessageBox 18 | from Screens.Screen import Screen 19 | 20 | config.swapmanager = ConfigSubsection() 21 | config.swapmanager.swapautostart = ConfigYesNo(default=False) 22 | 23 | startswap = None 24 | 25 | 26 | def SwapAutostart(reason, session=None, **kwargs): 27 | global startswap 28 | if reason == 0: 29 | print("[SwapManager] autostart", config.swapmanager.swapautostart.value) 30 | if config.swapmanager.swapautostart.value: 31 | print("[SwapManager] autostart") 32 | startswap = StartSwap() 33 | startswap.start() 34 | 35 | 36 | class StartSwap: 37 | def __init__(self): 38 | self.Console = Console() 39 | 40 | def start(self): 41 | self.Console.ePopen("parted -l /dev/sd? | grep swap", self.startSwap2) 42 | 43 | def startSwap2(self, result=None, retval=None, extra_args=None): # lets find swap partitions(normally receiver specific and part of image build or user swap files 44 | swap_Pname = "" 45 | swap_Fname = "" 46 | # print("[SwapManager][StartSwap] Found a SWAP partition:", result) 47 | if result and result.find("swap") > 0: # so much garbage from parted command with eMMC partitions need further exclude. 48 | for line in result.split("\n"): 49 | # print("[SwapManager][StartSwap] grep lines in result", line) 50 | if "swap" not in line: 51 | continue 52 | parts = line.strip().split() 53 | swap_Pname = line.strip().rsplit(" ", 1)[-1] 54 | print("[SwapManager][StartSwap] Found a SWAP partition:", swap_Pname) 55 | devicelist = [] 56 | for p in harddiskmanager.getMountedPartitions(): 57 | d = path.normpath(p.mountpoint) 58 | if (path.exists(p.mountpoint) and p.mountpoint != "/" 59 | and not p.mountpoint.startswith("/media/net/") 60 | and not p.mountpoint.startswith("/media/autofs/")): 61 | devicelist.append((p.description, d)) 62 | if len(devicelist): 63 | for device in devicelist: 64 | for filename in glob(device[1] + "/swap*"): 65 | if path.exists(filename): 66 | swap_Fname = filename 67 | print("[SwapManager][StartSwap] Found a SWAP file on ", swap_Fname) 68 | # basically a swap file if created, is now given higher priority than system swap partition if built in image. 69 | # swapmanager can now set a user swap file to be active, or deleted and if present system swap partition will be used. 70 | # if both are present, then swap file is used ahead of swap partition (based on priority) in /proc/swaps. 71 | f = open("/proc/swaps") 72 | swapfile = f.read() 73 | f.close() 74 | print("[SwapManager][StartSwap] partition/filename", swap_Fname, " ", swap_Pname) 75 | if swap_Fname and swapfile.find(swap_Fname) == -1: 76 | system("swapon -p 10 " + swap_Fname) 77 | print("[SwapManager][StartSwap] SWAP file active on ", swap_Fname) 78 | if swap_Pname and not swap_Fname: 79 | print("[SwapManager][StartSwap] SWAP partition active on ", swap_Pname) 80 | if swap_Pname and swap_Fname: 81 | print("[SwapManager][StartSwap] SWAP file %s chosen before swap partition on %s by priority" % (swap_Fname, swap_Pname)) 82 | 83 | 84 | class VIXSwap(Screen): 85 | skin = [""" 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | """, 106 | 560, 250, # screen 107 | 0, 0, 140, 40, #colors 108 | 140, 0, 140, 40, 109 | 280, 0, 140, 40, 110 | 420, 0, 140, 40, 111 | 0, 0, 140, 40, 20, 112 | 140, 0, 140, 40, 20, 113 | 280, 0, 140, 40, 20, 114 | 420, 0, 140, 40, 20, 115 | 10, 50, 32, 32, # lock off 116 | 10, 50, 32, 32, # lock on 117 | 50, 50, 360, 30, 20, 118 | 10, 100, 150, 30, 20, 119 | 10, 150, 150, 30, 20, 120 | 10, 200, 150, 30, 20, 121 | 160, 100, 220, 30, 20, 122 | 160, 150, 220, 30, 20, 123 | 160, 200, 100, 30, 20, 124 | 160, 200, 100, 30, 20, 125 | ] 126 | 127 | def __init__(self, session): 128 | Screen.__init__(self, session) 129 | self.setTitle(_("SWAP manager")) 130 | 131 | self["lab1"] = Label() 132 | self["autostart_on"] = Pixmap() 133 | self["autostart_off"] = Pixmap() 134 | self["lab2"] = Label(_("SWAP name:")) 135 | self["labplace"] = Label() 136 | self["lab3"] = Label(_("SWAP size:")) 137 | self["labsize"] = Label() 138 | self["lab4"] = Label(_("Status:")) 139 | self["inactive"] = Label(_("Inactive")) 140 | self["active"] = Label(_("Active")) 141 | self["key_red"] = Label(_("Close")) 142 | self["key_green"] = Label(_("Activate")) 143 | self["key_yellow"] = Label(_("Autostart")) 144 | self["key_blue"] = Label(_("Create")) 145 | self["swapname_summary"] = StaticText() 146 | self["swapactive_summary"] = StaticText() 147 | self.Console = Console() 148 | self.swap_name = "" 149 | self.new_name = "" 150 | self.creatingswap = False 151 | self.swap_active = False 152 | self["actions"] = ActionMap(["WizardActions", "ColorActions", "MenuActions"], 153 | { 154 | "back": self.close, 155 | "red": self.close, 156 | "green": self.actDeact, 157 | "yellow": self.autoSsWap, 158 | "blue": self.createDel, 159 | "menu": self.close, 160 | }) 161 | self.activityTimer = eTimer() 162 | self.activityTimer.timeout.get().append(self.getSwapDevice) 163 | self.updateSwap() 164 | 165 | def updateSwap(self, result=None, retval=None, extra_args=None): 166 | self["actions"].setEnabled(False) 167 | self.swap_active = False 168 | self.swap_name = None 169 | self["autostart_on"].hide() 170 | self["autostart_off"].show() 171 | self["active"].hide() 172 | self["inactive"].show() 173 | self["labplace"].hide() 174 | self["labsize"].hide() 175 | self["swapactive_summary"].setText(_("Current status:")) 176 | scanning = _("Wait please while scanning...") 177 | self["lab1"].setText(scanning) 178 | self.activityTimer.start(10) 179 | 180 | def getSwapDevice(self): 181 | self.activityTimer.stop() 182 | if path.exists("/etc/rcS.d/S98SwapManager"): 183 | remove("/etc/rcS.d/S98SwapManager") 184 | config.swapmanager.swapautostart.value = True 185 | config.swapmanager.swapautostart.save() 186 | if path.exists("/tmp/swapdevices.tmp"): 187 | remove("/tmp/swapdevices.tmp") 188 | self.Console.ePopen("parted -l /dev/sd? | grep swap", self.updateSwap2) 189 | 190 | def updateSwap2(self, result=None, retval=None, extra_args=None): 191 | self.swap_active = False 192 | self.swap_Pname = "" 193 | self.Pswapsize = 0 194 | self.swap_Pactive = False 195 | self.swap_Fname = "" 196 | self.Fswapsize = 0 197 | self.swap_Factive = False 198 | self.MMCdevice = False 199 | self.swapdevice = 0 200 | self.swapcount = 0 201 | self.swapsize = 0 202 | if result.find("swap") > 0: # lets find swap partition 203 | for line in result.split("\n"): 204 | if "swap" not in line: # so much garbage from parted command with eMMC partitions need further exclude. 205 | continue 206 | parts = line.strip().split() 207 | # print("[SwapManager][updateSwap2] parts = ", parts, " ", parts[3]) 208 | self.swap_Pname = line.strip().split(" ", 1)[-1] 209 | self.Pswapsize = parts[3] 210 | if self.swap_Pname == "sfdisk:": 211 | self.swap_Pname = "" 212 | f = open("/proc/swaps", "r") 213 | for line2 in f.readlines(): 214 | parts = line2.strip().split() 215 | if line2.find("partition") != -1: 216 | if "mmc" in parts[0]: 217 | self.MMCdevice = True 218 | self.swap_Pname = parts[0] 219 | # self["key_blue"].setText("") 220 | self.swap_name = _("manufacturer defined swap") 221 | self.swap_Pactive = True 222 | f.close() 223 | self["key_blue"].setText(_("Create")) 224 | devicelist = [] # lets find swap file 225 | for p in harddiskmanager.getMountedPartitions(): 226 | d = path.normpath(p.mountpoint) 227 | if path.exists(p.mountpoint) and p.mountpoint != "/" and not p.mountpoint.startswith("/media/net"): 228 | devicelist.append((p.description, d)) 229 | if len(devicelist): 230 | print("[SwapManager][{updateSwap2] devicelist = ", devicelist) 231 | for device in devicelist: 232 | print("[SwapManager][{updateSwap2] device = ", device, device[1]) 233 | for filename in glob(device[1] + "/swap*"): 234 | self.swap_name = self.swap_Fname = filename 235 | self["key_blue"].setText(_("Delete")) 236 | info = mystat(self.swap_name) 237 | self.Fswapsize = info[stat.ST_SIZE] 238 | 239 | if self.swap_Fname: # if swap file 240 | self["labplace"].setText(self.swap_Fname) 241 | self["autostart_on"].hide() 242 | self["autostart_off"].show() 243 | elif self.swap_Pname: # if only swap partition 244 | self["labplace"].setText(self.swap_name) 245 | else: 246 | self["labplace"].setText("") 247 | self["labplace"].show() 248 | 249 | f = open("/proc/swaps", "r") 250 | for line in f.readlines(): 251 | parts = line.strip().split() 252 | if line.find("file") != -1: # if swap file - takes precedence over swap parition 253 | self["autostart_on"].show() 254 | self["autostart_off"].hide() 255 | self.swap_active = self.swap_Factive = True 256 | continue 257 | elif line.find("partition") != -1 and not self.swap_Fname: # only swap partition 258 | self.swap_active = self.swap_Pactive = True 259 | continue 260 | 261 | f.close() 262 | if self.swap_Fname: 263 | self.swapsize = int(self.Fswapsize) 264 | if self.swapsize > 0: 265 | if self.swapsize >= 1024: 266 | self.swapsize = self.swapsize // 1000 267 | if self.swapsize >= 1024: 268 | self.swapsize = self.swapsize // 1000 269 | self.swapsize = str(self.swapsize) + " " + "MB" 270 | else: 271 | self.swapsize = str(self.swapsize) + " " + "KB" 272 | else: 273 | self.swapsize = "" 274 | elif self.swap_Pname: 275 | self.swapsize = self.Pswapsize # picked up from parted command 276 | self["labsize"].setText(self.swapsize) 277 | self["labsize"].show() 278 | 279 | if self.swap_active: 280 | self["inactive"].hide() 281 | self["active"].show() 282 | self["key_green"].setText(_("Deactivate")) 283 | self["swapactive_summary"].setText(_("Current status:") + " " + _("Active")) 284 | else: 285 | self["inactive"].show() 286 | self["active"].hide() 287 | self["key_green"].setText(_("Activate")) 288 | self["swapactive_summary"].setText(_("Current status:") + " " + _("Inactive")) 289 | if self.swap_name == _("manufacturer defined swap"): 290 | self["key_green"].setText("") 291 | scanning = _("manufacturer defined swap enabled at startup") 292 | else: 293 | scanning = _("Enable SWAP at startup") 294 | 295 | if config.swapmanager.swapautostart.value or self.swap_name == _("manufacturer defined swap"): 296 | # if config.swapmanager.swapautostart.value: 297 | self["autostart_off"].hide() 298 | self["autostart_on"].show() 299 | self["key_yellow"].setText("") 300 | else: 301 | config.swapmanager.swapautostart.setValue(False) 302 | config.swapmanager.swapautostart.save() 303 | configfile.save() 304 | self["autostart_on"].hide() 305 | self["autostart_off"].show() 306 | self["lab1"].setText(scanning) 307 | self["lab1"].show() 308 | self["actions"].setEnabled(True) 309 | 310 | name = self["labplace"].text 311 | self["swapname_summary"].setText(name) 312 | 313 | def actDeact(self): 314 | if self.swap_Factive: 315 | self.Console.ePopen("swapoff " + self.swap_Fname, self.updateSwap) 316 | self.swap_Factive = False 317 | else: 318 | if self.swap_Fname: 319 | self.Console.ePopen("swapon -p 10 " + self.swap_Fname, self.updateSwap) 320 | self.swap_Factive = True 321 | config.swapmanager.swapautostart.setValue(True) 322 | config.swapmanager.swapautostart.save() 323 | configfile.save() 324 | else: 325 | mybox = self.session.open(MessageBox, _("SWAP file not found. You have to create the file before you try to activate it."), MessageBox.TYPE_INFO) 326 | mybox.setTitle(_("Info")) 327 | self.updateSwap() 328 | 329 | def createDel(self): 330 | if self.swap_Fname: 331 | if self.swap_Factive: 332 | self.Console.ePopen("swapoff " + self.swap_Fname, self.createDel2) 333 | else: 334 | self.createDel2(None, 0) 335 | else: 336 | self.doCreateSwap() 337 | 338 | def createDel2(self, result, retval, extra_args=None): 339 | if self.swap_name == _("manufacturer defined swap"): 340 | self.updateSwap() 341 | if retval == 0: 342 | # print("[SwapManager][createDel2] delete swap = ", self.swap_Fname) 343 | self.Console.ePopen("rm " + self.swap_Fname, self.createDel3) 344 | 345 | def createDel3(self, result, retval, extra_args=None): 346 | print("[SwapManager][createDel3] delete swap, retval, result", retval, " ", result) 347 | if config.swapmanager.swapautostart.value: 348 | config.swapmanager.swapautostart.setValue(False) 349 | config.swapmanager.swapautostart.save() 350 | configfile.save() 351 | self.updateSwap() 352 | 353 | def doCreateSwap(self): 354 | parts = [] 355 | supported_filesystems = frozenset(("ext4", "ext3")) 356 | candidates = [] 357 | mounts = getProcMounts() 358 | for partition in harddiskmanager.getMountedPartitions(False, mounts): 359 | if partition.filesystem(mounts) in supported_filesystems: 360 | candidates.append((partition.description, partition.mountpoint)) 361 | if len(candidates): 362 | self.session.openWithCallback(self.doCSname, ChoiceBox, title=_("Please select device to use as SWAP file location."), list=candidates) 363 | else: 364 | self.session.open(MessageBox, _("Sorry, no physical devices that supports SWAP attached. Can't create SWAP file on network or fat32 file-systems."), MessageBox.TYPE_INFO, timeout=10) 365 | 366 | def doCSname(self, name): 367 | if name: 368 | self.new_name = name[1] 369 | myoptions = [[_("8 Mb"), "8192"], [_("16 Mb"), "16384"], [_("32 Mb"), "32768"], [_("64 Mb"), "65536"], [_("96 Mb"), "98304"], [_("128 Mb"), "131072"], [_("256 Mb"), "262144"], [_("512 Mb"), "524288"], [_("756 Mb"), "774144"], [_("1024 Mb"), "1048576"]] 370 | self.session.openWithCallback(self.doCSsize, ChoiceBox, title=_("Select the SWAP file size:"), list=myoptions) 371 | 372 | def doCSsize(self, swapsize): 373 | if swapsize: 374 | self["actions"].setEnabled(False) 375 | scanning = _("Wait please while creating SWAP file...") 376 | self["lab1"].setText(scanning) 377 | self["lab1"].show() 378 | swapsize = swapsize[1] 379 | myfile = self.new_name + "swapfile" 380 | self.commands = [] 381 | self.commands.append("dd if=/dev/zero of=" + myfile + " bs=1024 count=" + swapsize + " 2>/dev/null") 382 | self.commands.append("mkswap " + myfile) 383 | self.Console.eBatch(self.commands, self.updateSwap, debug=True) 384 | 385 | def autoSsWap(self): 386 | if self.swap_name: 387 | if config.swapmanager.swapautostart.value: 388 | config.swapmanager.swapautostart.setValue(False) 389 | config.swapmanager.swapautostart.save() 390 | else: 391 | config.swapmanager.swapautostart.setValue(True) 392 | config.swapmanager.swapautostart.save() 393 | configfile.save() 394 | else: 395 | mybox = self.session.open(MessageBox, _("You have to create a SWAP file before trying to activate the autostart."), MessageBox.TYPE_INFO) 396 | mybox.setTitle(_("Info")) 397 | self.updateSwap() 398 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import print_function, absolute_import 3 | import gettext 4 | 5 | from Components.Language import language 6 | from Tools.Directories import resolveFilename, SCOPE_PLUGINS 7 | 8 | 9 | PluginLanguageDomain = "vix" 10 | PluginLanguagePath = "SystemPlugins/ViX/locale" 11 | 12 | 13 | def pluginlanguagedomain(): 14 | return PluginLanguageDomain 15 | 16 | 17 | def localeInit(): 18 | gettext.bindtextdomain(PluginLanguageDomain, resolveFilename(SCOPE_PLUGINS, PluginLanguagePath)) 19 | 20 | 21 | def _(txt): 22 | if gettext.dgettext(PluginLanguageDomain, txt): 23 | return gettext.dgettext(PluginLanguageDomain, txt) 24 | else: 25 | print("[" + PluginLanguageDomain + "] fallback to default translation for " + txt) 26 | return gettext.gettext(txt) 27 | 28 | 29 | language.addCallback(localeInit()) 30 | -------------------------------------------------------------------------------- /src/images/Makefile.am: -------------------------------------------------------------------------------- 1 | installdir = $(libdir)/enigma2/python/Plugins/SystemPlugins/ViX/images 2 | 3 | install_DATA = *.png -------------------------------------------------------------------------------- /src/images/busy1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy1.png -------------------------------------------------------------------------------- /src/images/busy10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy10.png -------------------------------------------------------------------------------- /src/images/busy11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy11.png -------------------------------------------------------------------------------- /src/images/busy12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy12.png -------------------------------------------------------------------------------- /src/images/busy13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy13.png -------------------------------------------------------------------------------- /src/images/busy14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy14.png -------------------------------------------------------------------------------- /src/images/busy15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy15.png -------------------------------------------------------------------------------- /src/images/busy16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy16.png -------------------------------------------------------------------------------- /src/images/busy17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy17.png -------------------------------------------------------------------------------- /src/images/busy18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy18.png -------------------------------------------------------------------------------- /src/images/busy19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy19.png -------------------------------------------------------------------------------- /src/images/busy2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy2.png -------------------------------------------------------------------------------- /src/images/busy20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy20.png -------------------------------------------------------------------------------- /src/images/busy21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy21.png -------------------------------------------------------------------------------- /src/images/busy22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy22.png -------------------------------------------------------------------------------- /src/images/busy23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy23.png -------------------------------------------------------------------------------- /src/images/busy24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy24.png -------------------------------------------------------------------------------- /src/images/busy3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy3.png -------------------------------------------------------------------------------- /src/images/busy4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy4.png -------------------------------------------------------------------------------- /src/images/busy5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy5.png -------------------------------------------------------------------------------- /src/images/busy6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy6.png -------------------------------------------------------------------------------- /src/images/busy7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy7.png -------------------------------------------------------------------------------- /src/images/busy8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy8.png -------------------------------------------------------------------------------- /src/images/busy9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/busy9.png -------------------------------------------------------------------------------- /src/images/dev_cdrom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/dev_cdrom.png -------------------------------------------------------------------------------- /src/images/dev_cf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/dev_cf.png -------------------------------------------------------------------------------- /src/images/dev_hdd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/dev_hdd.png -------------------------------------------------------------------------------- /src/images/dev_mmc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/dev_mmc.png -------------------------------------------------------------------------------- /src/images/dev_sd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/dev_sd.png -------------------------------------------------------------------------------- /src/images/dev_usb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/images/dev_usb.png -------------------------------------------------------------------------------- /src/install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/install.png -------------------------------------------------------------------------------- /src/installable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/installable.png -------------------------------------------------------------------------------- /src/installed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/installed.png -------------------------------------------------------------------------------- /src/maintainer.info: -------------------------------------------------------------------------------- 1 | andyblac@icloud.com 2 | ViX core plugins -------------------------------------------------------------------------------- /src/noprev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/noprev.png -------------------------------------------------------------------------------- /src/plugin.py: -------------------------------------------------------------------------------- 1 | from os import listdir, path, stat 2 | 3 | from . import _ 4 | from Plugins.Plugin import PluginDescriptor 5 | from Components.config import config, ConfigBoolean, configfile 6 | from Components.SystemInfo import SystemInfo 7 | 8 | from .BackupManager import BackupManagerautostart 9 | from .ImageManager import ImageManagerautostart 10 | from .IPKInstaller import IpkgInstaller 11 | from .ScriptRunner import ScriptRunnerAutostart 12 | from .SoftcamManager import SoftcamAutostart 13 | from .SwapManager import SwapAutostart 14 | from .IPKInstaller import IpkgInstaller 15 | 16 | config.misc.restorewizardrun = ConfigBoolean(default=False) 17 | 18 | # On plugin initialisation (called by StartEnigma). language will be assigned as follows if config.misc.firstrun.value: 19 | # Default language en_GB (OpenViX) is set by SetupDevices called by StartEnigma 20 | # If no backup, the languagewizard will be inserted by Plugin into the wizards. 21 | # If backup, then language will be set here from config.osd.language if in backup, else default language 22 | # 23 | 24 | 25 | def setLanguageFromBackup(backupfile): 26 | print("[ViX plugin][setLanguageFromBackup] backupfile", backupfile) 27 | import tarfile 28 | 29 | try: 30 | tar = tarfile.open(backupfile) 31 | member = tar.getmember("etc/enigma2/settings") 32 | except KeyError: 33 | print("[ViX plugin][setLanguageFromBackup] language selected failed") 34 | tar.close() 35 | return 36 | 37 | for line in tar.extractfile(member): 38 | line = line.decode() 39 | if line.startswith("config.osd.language"): 40 | languageToSelect = line.strip().split("=")[1] 41 | print("[ViX plugin][setLanguageFromBackup] language selected", languageToSelect) 42 | from Components.Language import language 43 | language.InitLang() 44 | language.activateLanguage(languageToSelect) 45 | config.misc.languageselected.value = 0 # 0 means found 46 | config.misc.languageselected.save() 47 | break 48 | tar.close() 49 | 50 | 51 | def checkConfigBackup(): 52 | backups = [] 53 | for dir in ["/media/%s/backup" % media for media in listdir("/media/") if path.isdir(path.join("/media/", media))]: 54 | try: 55 | backups += [{"name": f, "mtime": stat(f).st_mtime} for x in listdir(dir) if (f := path.join(dir, x)) and path.isfile(f) and f.endswith(".tar.gz") and "vix" in f.lower()] 56 | except FileNotFoundError: # e.g. /media/autofs/xxx will crash listdir if "xxx" is inactive 57 | pass 58 | if backups: 59 | backupfile = next(iter(sorted(backups, key=lambda x: x["mtime"], reverse=True)))["name"] # select the most recent 60 | print("[RestoreWizard] Backup Image:", backupfile) 61 | return backupfile 62 | return None 63 | 64 | 65 | if config.misc.firstrun.value and not config.misc.restorewizardrun.value: 66 | backupAvailable = checkConfigBackup() 67 | if backupAvailable: 68 | setLanguageFromBackup(backupAvailable) 69 | 70 | 71 | def VIXMenu(session): 72 | from .import ui 73 | return ui.VIXMenu(session) 74 | 75 | 76 | def UpgradeMain(session, **kwargs): 77 | session.open(VIXMenu) 78 | 79 | 80 | def startSetup(menuid): 81 | if menuid != "setup": 82 | return [] 83 | return [(_("ViX"), UpgradeMain, "vix_menu", 1010)] 84 | 85 | 86 | def RestoreWizard(*args, **kwargs): 87 | from .RestoreWizard import RestoreWizard 88 | return RestoreWizard(*args, **kwargs) 89 | 90 | 91 | def LanguageWizard(*args, **kwargs): 92 | from Screens.LanguageSelection import LanguageWizard 93 | return LanguageWizard(*args, **kwargs) 94 | 95 | 96 | def SoftcamManager(session): 97 | from .SoftcamManager import VIXSoftcamManager 98 | return VIXSoftcamManager(session) 99 | 100 | 101 | def SoftcamMenu(session, **kwargs): 102 | session.open(SoftcamManager) 103 | 104 | 105 | def SoftcamSetup(menuid): 106 | if menuid == "cam": 107 | return [(_("Softcam manager"), SoftcamMenu, "softcamsetup", 1005)] 108 | return [] 109 | 110 | 111 | def BackupManager(session): 112 | from .BackupManager import VIXBackupManager 113 | return VIXBackupManager(session) 114 | 115 | 116 | def BackupManagerMenu(session, **kwargs): 117 | session.open(BackupManager) 118 | 119 | 120 | def ImageManager(session): 121 | from .ImageManager import VIXImageManager 122 | return VIXImageManager(session) 123 | 124 | 125 | def ImageManagerMenu(session, **kwargs): 126 | session.open(ImageManager) 127 | 128 | 129 | def ImageManagerStart(menuid, **kwargs): 130 | if menuid == "mainmenu": 131 | return [(_("Image Manager"), ImageManagerMenu, "image_manager", -1)] 132 | return [] 133 | 134 | 135 | def H9SDmanager(session): 136 | from .H9SDmanager import H9SDmanager 137 | return H9SDmanager(session) 138 | 139 | 140 | def H9SDmanagerMenu(session, **kwargs): 141 | session.open(H9SDmanager) 142 | 143 | 144 | def MountManager(session): 145 | from .MountManager import VIXDevicesPanel 146 | return VIXDevicesPanel(session) 147 | 148 | 149 | def MountManagerMenu(session, **kwargs): 150 | session.open(MountManager) 151 | 152 | 153 | def ScriptRunner(session): 154 | from .ScriptRunner import VIXScriptRunner 155 | return VIXScriptRunner(session) 156 | 157 | 158 | def ScriptRunnerMenu(session, **kwargs): 159 | session.open(ScriptRunner) 160 | 161 | 162 | def SwapManager(session): 163 | from .SwapManager import VIXSwap 164 | return VIXSwap(session) 165 | 166 | 167 | def SwapManagerMenu(session, **kwargs): 168 | session.open(SwapManager) 169 | 170 | 171 | def filescan_open(list, session, **kwargs): 172 | filelist = [x.path for x in list] 173 | session.open(IpkgInstaller, filelist) # list 174 | 175 | 176 | def filescan(**kwargs): 177 | from Components.Scanner import Scanner, ScanPath 178 | return Scanner(mimetypes=["application/x-debian-package"], 179 | paths_to_scan=[ 180 | ScanPath(path="ipk", with_subdirs=True), 181 | ScanPath(path="", with_subdirs=False), 182 | ], 183 | name="Ipkg", 184 | description=_("Install extensions."), 185 | openfnc=filescan_open) 186 | 187 | 188 | def Plugins(**kwargs): 189 | if SystemInfo["MultiBootSlot"] == 0: # only in recovery image 190 | plist = [PluginDescriptor(name=_("Image Manager"), where=PluginDescriptor.WHERE_MENU, needsRestart=False, fnc=ImageManagerStart)] 191 | if not config.misc.firstrun.value: 192 | plist.append(PluginDescriptor(name=_("Vu+ ImageManager wizard"), where=PluginDescriptor.WHERE_WIZARD, needsRestart=False, fnc=(30, ImageManager))) 193 | return plist 194 | 195 | plist = [PluginDescriptor(where=PluginDescriptor.WHERE_MENU, needsRestart=False, fnc=startSetup), 196 | PluginDescriptor(name=_("ViX Image Management"), where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=UpgradeMain), 197 | PluginDescriptor(where=PluginDescriptor.WHERE_MENU, fnc=SoftcamSetup)] 198 | if config.softcammanager.showinextensions.value: 199 | plist.append(PluginDescriptor(name=_("Softcam manager"), where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=SoftcamMenu)) 200 | if config.scriptrunner.showinextensions.value: 201 | plist.append(PluginDescriptor(name=_("Script runner"), where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=ScriptRunnerMenu)) 202 | plist.append(PluginDescriptor(where=PluginDescriptor.WHERE_AUTOSTART, fnc=SoftcamAutostart)) 203 | plist.append(PluginDescriptor(where=PluginDescriptor.WHERE_AUTOSTART, fnc=SwapAutostart)) 204 | plist.append(PluginDescriptor(where=PluginDescriptor.WHERE_SESSIONSTART, fnc=ImageManagerautostart)) 205 | plist.append(PluginDescriptor(where=PluginDescriptor.WHERE_SESSIONSTART, fnc=BackupManagerautostart)) 206 | plist.append(PluginDescriptor(where=PluginDescriptor.WHERE_SESSIONSTART, fnc=ScriptRunnerAutostart)) 207 | if config.misc.firstrun.value and not config.misc.restorewizardrun.value: 208 | if backupAvailable: # skip langauge wizard as it was set from the backup 209 | plist.append(PluginDescriptor(name=_("Restore wizard"), where=PluginDescriptor.WHERE_WIZARD, needsRestart=False, fnc=(4, RestoreWizard))) 210 | else: 211 | plist.append(PluginDescriptor(name=_("Language Wizard"), where=PluginDescriptor.WHERE_WIZARD, needsRestart=False, fnc=(1, LanguageWizard))) 212 | plist.append(PluginDescriptor(name=_("Ipkg"), where=PluginDescriptor.WHERE_FILESCAN, needsRestart=False, fnc=filescan)) 213 | plist.append(PluginDescriptor(name=_("ViX Backup manager"), where=PluginDescriptor.WHERE_VIXMENU, fnc=BackupManagerMenu)) 214 | plist.append(PluginDescriptor(name=_("ViX Image manager"), where=PluginDescriptor.WHERE_VIXMENU, fnc=ImageManagerMenu)) 215 | plist.append(PluginDescriptor(name=_("ViX Mount manager"), where=PluginDescriptor.WHERE_VIXMENU, fnc=MountManagerMenu)) 216 | plist.append(PluginDescriptor(name=_("ViX Script runner"), where=PluginDescriptor.WHERE_VIXMENU, fnc=ScriptRunnerMenu)) 217 | plist.append(PluginDescriptor(name=_("ViX SWAP manager"), where=PluginDescriptor.WHERE_VIXMENU, fnc=SwapManagerMenu)) 218 | return plist 219 | -------------------------------------------------------------------------------- /src/remove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/remove.png -------------------------------------------------------------------------------- /src/restorewizard.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | self.clearSelectedKeys() 9 | self.selectKey("OK") 10 | 11 | 12 | self.buildList(self.selectedAction) 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | self.clearSelectedKeys() 22 | self.selectKey("OK") 23 | 24 | 25 | self.buildList(self.selectedAction) 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | self.clearSelectedKeys() 35 | self.selectKey("OK") 36 | 37 | 38 | self.buildList(self.selectedAction) 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | self.clearSelectedKeys() 48 | self.selectKey("OK") 49 | 50 | 51 | self.buildList(self.selectedAction) 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | self.clearSelectedKeys() 61 | self.selectKey("OK") 62 | 63 | 64 | self.buildList(self.selectedAction) 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | self.buildList(None) 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | self.buildList(None) 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /src/setup.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | config.softcammanager.showinextensions 5 | config.softcammanager.softcamtimerenabled 6 | config.softcammanager.softcamtimer 7 | 8 | 9 | config.backupmanager.backuplocation 10 | config.backupmanager.folderprefix 11 | config.backupmanager.showboxname 12 | config.backupmanager.schedule 13 | config.backupmanager.scheduletime 14 | config.backupmanager.repeattype 15 | config.backupmanager.query 16 | config.backupmanager.types_to_prune 17 | 18 | config.backupmanager.number_to_keep 19 | 20 | 21 | 22 | config.imagemanager.autosettingsbackup 23 | config.imagemanager.extensive_location_search 24 | config.imagemanager.backuplocation 25 | config.imagemanager.folderprefix 26 | config.imagemanager.schedule 27 | config.imagemanager.scheduletime 28 | config.imagemanager.repeattype 29 | config.imagemanager.query 30 | config.imagemanager.number_to_keep 31 | config.imagemanager.login_as_ViX_developer 32 | 33 | config.imagemanager.developer_username 34 | config.imagemanager.developer_password 35 | 36 | config.imagemanager.imagefeed_MyBuild 37 | 38 | 39 | config.scriptrunner.close 40 | config.scriptrunner.showinextensions 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/ui.py: -------------------------------------------------------------------------------- 1 | from os import listdir, path, mkdir 2 | 3 | from . import _ 4 | 5 | from Components.ActionMap import NumberActionMap 6 | from Components.config import config 7 | from Components.Sources.StaticText import StaticText 8 | from Components.Sources.List import List 9 | from Components.SystemInfo import SystemInfo 10 | from Screens.ParentalControlSetup import ProtectedScreen 11 | from Screens.Screen import Screen 12 | 13 | 14 | class VIXMenu(Screen, ProtectedScreen): 15 | skin = [""" 16 | 17 | 18 | 19 | 20 | 21 | {"template": [ 22 | MultiContentEntryText(pos = (%d,%d), size = (%d,%d), flags = RT_HALIGN_LEFT, text = 1), # index 0 is the MenuText, 23 | ], 24 | "fonts": [gFont("Regular",%d)], 25 | "itemHeight":%d 26 | } 27 | 28 | 29 | 30 | 31 | {"template": [ 32 | MultiContentEntryText(pos = (%d,%d), size = (%d,%d), flags = RT_HALIGN_CENTER|RT_VALIGN_CENTER|RT_WRAP, text = 2), # index 2 is the Description, 33 | ], 34 | "fonts": [gFont("Regular",%d)], 35 | "itemHeight":%d 36 | } 37 | 38 | 39 | 40 | """, 41 | 610, 410, # screen 42 | 0, 0, 140, 40, # key red image 43 | 0, 0, 140, 40, 20, # key red text 44 | 15, 60, 330, 286, # first menu Listbox 45 | 2, 0, 330, 26, # template one 46 | 22, # fonts 47 | 26, # ItemHeight 48 | 360, 50, 240, 300, # second menu Listbox 49 | 2, 2, 240, 300, # template two 50 | 22, # fonts 51 | 300, # itemHeight 52 | 5, 360, 600, 50, 22, # status 53 | ] 54 | 55 | def __init__(self, session, args=0): 56 | Screen.__init__(self, session) 57 | ProtectedScreen.__init__(self) 58 | self.setTitle(_("ViX")) 59 | self.menu = args 60 | self.list = [] 61 | if self.menu == 0: 62 | self.list.append(("backup-manager", _("Backup manager"), _("Manage settings backup."), None)) 63 | self.list.append(("image-manager", _("Image manager"), _("Backup/Flash/ReBoot system image."), None)) 64 | self.list.append(("ipkg-install", _("Install local extension"), _("Install IPK's from your tmp folder."), None)) 65 | self.list.append(("mount-manager", _("Mount manager"), _("Manage your devices mount points."), None)) 66 | self.list.append(("script-runner", _("Script runner"), _("Run your shell scripts."), None)) 67 | self.list.append(("swap-manager", _("SWAP manager"), _("Create and Manage your SWAP files."), None)) 68 | if SystemInfo["HasH9SD"]: 69 | self.list.append(("H9SDcard manager", _("H9SDcard Manager"), _("Move Nand root to SD card"), None)) 70 | self["menu"] = List(self.list) 71 | self["key_red"] = StaticText(_("Close")) 72 | 73 | self["shortcuts"] = NumberActionMap(["ShortcutActions", "WizardActions", "InfobarEPGActions", "MenuActions", "NumberActions"], 74 | { 75 | "ok": self.go, 76 | "back": self.close, 77 | "red": self.close, 78 | "menu": self.closeRecursive, 79 | "1": self.go, 80 | "2": self.go, 81 | "3": self.go, 82 | "4": self.go, 83 | "5": self.go, 84 | "6": self.go, 85 | "7": self.go, 86 | "8": self.go, 87 | "9": self.go, 88 | }, -1) 89 | self.onLayoutFinish.append(self.layoutFinished) 90 | self.onChangedEntry = [] 91 | self["menu"].onSelectionChanged.append(self.selectionChanged) 92 | 93 | def isProtected(self): 94 | return config.ParentalControl.setuppinactive.value and config.ParentalControl.config_sections.vixmenu.value 95 | 96 | def createSummary(self): 97 | from Screens.PluginBrowser import PluginBrowserSummary 98 | return PluginBrowserSummary 99 | 100 | def selectionChanged(self): 101 | item = self["menu"].getCurrent() 102 | if item: 103 | name = item[1] 104 | desc = item[2] 105 | else: 106 | name = "-" 107 | desc = "" 108 | for cb in self.onChangedEntry: 109 | cb(name, desc) 110 | 111 | def layoutFinished(self): 112 | idx = 0 113 | self["menu"].index = idx 114 | 115 | def go(self, num=None): 116 | if num is not None: 117 | num -= 1 118 | if not num < self["menu"].count(): 119 | return 120 | self["menu"].setIndex(num) 121 | current = self["menu"].getCurrent() 122 | if current: 123 | currentEntry = current[0] 124 | if self.menu == 0: 125 | if currentEntry == "backup-manager": 126 | from .BackupManager import VIXBackupManager 127 | self.session.open(VIXBackupManager) 128 | elif currentEntry == "image-manager": 129 | from .ImageManager import VIXImageManager 130 | self.session.open(VIXImageManager) 131 | elif currentEntry == "H9SDcard manager": 132 | from .H9SDmanager import H9SDmanager 133 | self.session.open(H9SDmanager) 134 | elif currentEntry == "ipkg-install": 135 | from .IPKInstaller import VIXIPKInstaller 136 | self.session.open(VIXIPKInstaller) 137 | elif currentEntry == "mount-manager": 138 | from .MountManager import VIXDevicesPanel 139 | self.session.open(VIXDevicesPanel) 140 | elif currentEntry == "script-runner": 141 | from .ScriptRunner import VIXScriptRunner 142 | self.session.open(VIXScriptRunner, None) 143 | elif currentEntry == "swap-manager": 144 | from .SwapManager import VIXSwap 145 | self.session.open(VIXSwap) 146 | 147 | def closeRecursive(self): 148 | self.close(True) 149 | -------------------------------------------------------------------------------- /src/update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/update.png -------------------------------------------------------------------------------- /src/upgrade.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/upgrade.png -------------------------------------------------------------------------------- /src/upgradeable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenViX/vix-core/11356134d3a0e0bfb92474e6a079dd2818376e09/src/upgradeable.png --------------------------------------------------------------------------------