├── .github └── workflows │ └── sync-addon-metadata-translations.yml ├── COPYING ├── LICENSES ├── GPL-2.0-only.txt └── GPL-2.0-or-later.txt ├── Makefile ├── addon.xml ├── default.py ├── requirements-dev.txt ├── resources ├── __init__.py ├── language │ ├── resource.language.af_za │ │ └── strings.po │ ├── resource.language.am_et │ │ └── strings.po │ ├── resource.language.ar_sa │ │ └── strings.po │ ├── resource.language.ast_es │ │ └── strings.po │ ├── resource.language.az_az │ │ └── strings.po │ ├── resource.language.be_by │ │ └── strings.po │ ├── resource.language.bg_bg │ │ └── strings.po │ ├── resource.language.bs_ba │ │ └── strings.po │ ├── resource.language.ca_es │ │ └── strings.po │ ├── resource.language.cs_cz │ │ └── strings.po │ ├── resource.language.cy_gb │ │ └── strings.po │ ├── resource.language.da_dk │ │ └── strings.po │ ├── resource.language.de_de │ │ └── strings.po │ ├── resource.language.el_gr │ │ └── strings.po │ ├── resource.language.en_au │ │ └── strings.po │ ├── resource.language.en_gb │ │ └── strings.po │ ├── resource.language.en_nz │ │ └── strings.po │ ├── resource.language.en_us │ │ └── strings.po │ ├── resource.language.eo │ │ └── strings.po │ ├── resource.language.es_ar │ │ └── strings.po │ ├── resource.language.es_es │ │ └── strings.po │ ├── resource.language.es_mx │ │ └── strings.po │ ├── resource.language.et_ee │ │ └── strings.po │ ├── resource.language.eu_es │ │ └── strings.po │ ├── resource.language.fa_af │ │ └── strings.po │ ├── resource.language.fa_ir │ │ └── strings.po │ ├── resource.language.fi_fi │ │ └── strings.po │ ├── resource.language.fo_fo │ │ └── strings.po │ ├── resource.language.fr_ca │ │ └── strings.po │ ├── resource.language.fr_fr │ │ └── strings.po │ ├── resource.language.gl_es │ │ └── strings.po │ ├── resource.language.he_il │ │ └── strings.po │ ├── resource.language.hi_in │ │ └── strings.po │ ├── resource.language.hr_hr │ │ └── strings.po │ ├── resource.language.hu_hu │ │ └── strings.po │ ├── resource.language.hy_am │ │ └── strings.po │ ├── resource.language.id_id │ │ └── strings.po │ ├── resource.language.is_is │ │ └── strings.po │ ├── resource.language.it_it │ │ └── strings.po │ ├── resource.language.ja_jp │ │ └── strings.po │ ├── resource.language.kn_in │ │ └── strings.po │ ├── resource.language.ko_kr │ │ └── strings.po │ ├── resource.language.lt_lt │ │ └── strings.po │ ├── resource.language.lv_lv │ │ └── strings.po │ ├── resource.language.mi │ │ └── strings.po │ ├── resource.language.mk_mk │ │ └── strings.po │ ├── resource.language.ml_in │ │ └── strings.po │ ├── resource.language.mn_mn │ │ └── strings.po │ ├── resource.language.ms_my │ │ └── strings.po │ ├── resource.language.mt_mt │ │ └── strings.po │ ├── resource.language.my_mm │ │ └── strings.po │ ├── resource.language.nb_no │ │ └── strings.po │ ├── resource.language.nl_nl │ │ └── strings.po │ ├── resource.language.os_os │ │ └── strings.po │ ├── resource.language.pl_pl │ │ └── strings.po │ ├── resource.language.pt_br │ │ └── strings.po │ ├── resource.language.pt_pt │ │ └── strings.po │ ├── resource.language.ro_ro │ │ └── strings.po │ ├── resource.language.ru_ru │ │ └── strings.po │ ├── resource.language.si_lk │ │ └── strings.po │ ├── resource.language.sk_sk │ │ └── strings.po │ ├── resource.language.sl_si │ │ └── strings.po │ ├── resource.language.sq_al │ │ └── strings.po │ ├── resource.language.sr_rs │ │ └── strings.po │ ├── resource.language.sr_rs@latin │ │ └── strings.po │ ├── resource.language.sv_se │ │ └── strings.po │ ├── resource.language.szl │ │ └── strings.po │ ├── resource.language.ta_in │ │ └── strings.po │ ├── resource.language.tg_tj │ │ └── strings.po │ ├── resource.language.th_th │ │ └── strings.po │ ├── resource.language.tr_tr │ │ └── strings.po │ ├── resource.language.uk_ua │ │ └── strings.po │ ├── resource.language.uz_uz │ │ └── strings.po │ ├── resource.language.vi_vn │ │ └── strings.po │ ├── resource.language.zh_cn │ │ └── strings.po │ └── resource.language.zh_tw │ │ └── strings.po ├── lib │ ├── config.py │ ├── dbus_bluez.py │ ├── dbus_connman.py │ ├── dbus_obex.py │ ├── dbus_utils.py │ ├── debug_utils.py │ ├── defaults.py │ ├── hostname.py │ ├── log.py │ ├── modules.py │ ├── modules │ │ ├── about.py │ │ ├── bluetooth.py │ │ ├── connman.py │ │ ├── services.py │ │ ├── system.py │ │ └── updates.py │ ├── oe.py │ ├── oeWindows.py │ ├── os_tools.py │ ├── regdomain.py │ ├── timezone.py │ └── ui_tools.py └── skins │ └── Default │ ├── 1080i │ ├── service-LibreELEC-Settings-getPasskey.xml │ ├── service-LibreELEC-Settings-mainWindow.xml │ └── service-LibreELEC-Settings-wizard.xml │ └── media │ ├── arrowdown.png │ ├── bg.jpg │ ├── black-back.png │ ├── bt-audio-card.png │ ├── bt-camera-photo.png │ ├── bt-camera-video.png │ ├── bt-computer.png │ ├── bt-input-gaming.png │ ├── bt-input-keyboard.png │ ├── bt-input-mouse.png │ ├── bt-input-tablet.png │ ├── bt-modem.png │ ├── bt-phone.png │ ├── bt-printer.png │ ├── bt.png │ ├── button-fo.png │ ├── button-nofo.png │ ├── connected.png │ ├── dialog-bg-solid.png │ ├── dialog-bg.png │ ├── do.png │ ├── eth.png │ ├── fanart.png │ ├── favorite.png │ ├── floor.png │ ├── focus.png │ ├── icon.png │ ├── icon_button_back.png │ ├── key.png │ ├── logo.png │ ├── radio-button-off.png │ ├── radio-button-on.png │ ├── separator-grey.png │ ├── separator.png │ ├── unlock.png │ ├── vpn.png │ ├── wizard.png │ └── wlan.png ├── service.py └── syspath.py /.github/workflows/sync-addon-metadata-translations.yml: -------------------------------------------------------------------------------- 1 | name: Sync addon metadata translations 2 | 3 | on: 4 | push: 5 | branches: [ master, main ] 6 | paths: 7 | - '**addon.xml' 8 | - '**resource.language.**strings.po' 9 | 10 | jobs: 11 | default: 12 | if: github.repository == 'LibreELEC/service.libreelec.settings' 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | 17 | - name: Checkout repository 18 | uses: actions/checkout@v2 19 | with: 20 | path: project 21 | 22 | - name: Set up Python 23 | uses: actions/setup-python@v2 24 | with: 25 | python-version: '3.9' 26 | 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | python -m pip install git+https://github.com/xbmc/sync_addon_metadata_translations.git 31 | 32 | - name: Run sync-addon-metadata-translations 33 | run: | 34 | sync-addon-metadata-translations 35 | working-directory: ./project 36 | 37 | - name: Create PR for sync-addon-metadata-translations changes 38 | uses: peter-evans/create-pull-request@v3.10.0 39 | with: 40 | commit-message: Sync of addon metadata translations 41 | title: Sync of addon metadata translations 42 | body: Sync of addon metadata translations triggered by ${{ github.sha }} 43 | branch: amt-sync 44 | delete-branch: true 45 | path: ./project -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | This program is provided under a mix of the following licenses: 2 | 3 | SPDX-License-Identifier: GPL-2.0-only 4 | SPDX-License-Identifier: GPL-2.0-or-later 5 | 6 | Being under the terms of the GNU General Public License version 2 only, or 7 | the GNU General Public License version 2 or later. Licensing varies by 8 | individual file. Check each file for specifics. 9 | 10 | Copies of the licenses are available in: 11 | 12 | LICENSES/GPL-2.0-only.txt 13 | LICENSES/GPL-2.0-or-later.txt 14 | 15 | Unless expressly stated otherwise, all code submitted to the project is 16 | licensed under GPL-2.0-only, and copyright is donated to the project. 17 | -------------------------------------------------------------------------------- /LICENSES/GPL-2.0-only.txt: -------------------------------------------------------------------------------- 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 | 7 | Everyone is permitted to copy and distribute verbatim copies of this license 8 | document, but changing it is not allowed. 9 | 10 | Preamble 11 | 12 | The licenses for most software are designed to take away your freedom to share 13 | and change it. By contrast, the GNU General Public License is intended to 14 | guarantee your freedom to share and change free software--to make sure the 15 | software is free for all its users. This General Public License applies to 16 | most of the Free Software Foundation's software and to any other program whose 17 | authors commit to using it. (Some other Free Software Foundation software 18 | is covered by the GNU Lesser General Public License instead.) You can apply 19 | it to your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not price. Our 22 | General Public Licenses are designed to make sure that you have the freedom 23 | to distribute copies of free software (and charge for this service if you 24 | wish), that you receive source code or can get it if you want it, that you 25 | can change the software or use pieces of it in new free programs; and that 26 | you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid anyone to 29 | deny you these rights or to ask you to surrender the rights. These restrictions 30 | translate to certain responsibilities for you if you distribute copies of 31 | the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether gratis or 34 | for a fee, you must give the recipients all the rights that you have. You 35 | must make sure that they, too, receive or can get the source code. And you 36 | must show them these terms so they know their rights. 37 | 38 | We protect your rights with two steps: (1) copyright the software, and (2) 39 | offer you this license which gives you legal permission to copy, distribute 40 | and/or modify the software. 41 | 42 | Also, for each author's protection and ours, we want to make certain that 43 | everyone understands that there is no warranty for this free software. If 44 | the software is modified by someone else and passed on, we want its recipients 45 | to know that what they have is not the original, so that any problems introduced 46 | by others will not reflect on the original authors' reputations. 47 | 48 | Finally, any free program is threatened constantly by software patents. We 49 | wish to avoid the danger that redistributors of a free program will individually 50 | obtain patent licenses, in effect making the program proprietary. To prevent 51 | this, we have made it clear that any patent must be licensed for everyone's 52 | free use or not licensed at all. 53 | 54 | The precise terms and conditions for copying, distribution and modification 55 | follow. 56 | 57 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 58 | 59 | 0. This License applies to any program or other work which contains a notice 60 | placed by the copyright holder saying it may be distributed under the terms 61 | of this General Public License. The "Program", below, refers to any such program 62 | or work, and a "work based on the Program" means either the Program or any 63 | derivative work under copyright law: that is to say, a work containing the 64 | Program or a portion of it, either verbatim or with modifications and/or translated 65 | into another language. (Hereinafter, translation is included without limitation 66 | in the term "modification".) Each licensee is addressed as "you". 67 | 68 | Activities other than copying, distribution and modification are not covered 69 | by this License; they are outside its scope. The act of running the Program 70 | is not restricted, and the output from the Program is covered only if its 71 | contents constitute a work based on the Program (independent of having been 72 | made by running the Program). Whether that is true depends on what the Program 73 | does. 74 | 75 | 1. You may copy and distribute verbatim copies of the Program's source code 76 | as you receive it, in any medium, provided that you conspicuously and appropriately 77 | publish on each copy an appropriate copyright notice and disclaimer of warranty; 78 | keep intact all the notices that refer to this License and to the absence 79 | of any warranty; and give any other recipients of the Program a copy of this 80 | License along with the Program. 81 | 82 | You may charge a fee for the physical act of transferring a copy, and you 83 | may at your option offer warranty protection in exchange for a fee. 84 | 85 | 2. You may modify your copy or copies of the Program or any portion of it, 86 | thus forming a work based on the Program, and copy and distribute such modifications 87 | or work under the terms of Section 1 above, provided that you also meet all 88 | of these conditions: 89 | 90 | a) You must cause the modified files to carry prominent notices stating that 91 | you changed the files and the date of any change. 92 | 93 | b) You must cause any work that you distribute or publish, that in whole or 94 | in part contains or is derived from the Program or any part thereof, to be 95 | licensed as a whole at no charge to all third parties under the terms of this 96 | License. 97 | 98 | c) If the modified program normally reads commands interactively when run, 99 | you must cause it, when started running for such interactive use in the most 100 | ordinary way, to print or display an announcement including an appropriate 101 | copyright notice and a notice that there is no warranty (or else, saying that 102 | you provide a warranty) and that users may redistribute the program under 103 | these conditions, and telling the user how to view a copy of this License. 104 | (Exception: if the Program itself is interactive but does not normally print 105 | such an announcement, your work based on the Program is not required to print 106 | an announcement.) 107 | 108 | These requirements apply to the modified work as a whole. If identifiable 109 | sections of that work are not derived from the Program, and can be reasonably 110 | considered independent and separate works in themselves, then this License, 111 | and its terms, do not apply to those sections when you distribute them as 112 | separate works. But when you distribute the same sections as part of a whole 113 | which is a work based on the Program, the distribution of the whole must be 114 | on the terms of this License, whose permissions for other licensees extend 115 | to the entire whole, and thus to each and every part regardless of who wrote 116 | it. 117 | 118 | Thus, it is not the intent of this section to claim rights or contest your 119 | rights to work written entirely by you; rather, the intent is to exercise 120 | the right to control the distribution of derivative or collective works based 121 | on the Program. 122 | 123 | In addition, mere aggregation of another work not based on the Program with 124 | the Program (or with a work based on the Program) on a volume of a storage 125 | or distribution medium does not bring the other work under the scope of this 126 | License. 127 | 128 | 3. You may copy and distribute the Program (or a work based on it, under Section 129 | 2) in object code or executable form under the terms of Sections 1 and 2 above 130 | provided that you also do one of the following: 131 | 132 | a) Accompany it with the complete corresponding machine-readable source code, 133 | which must be distributed under the terms of Sections 1 and 2 above on a medium 134 | customarily used for software interchange; or, 135 | 136 | b) Accompany it with a written offer, valid for at least three years, to give 137 | any third party, for a charge no more than your cost of physically performing 138 | source distribution, a complete machine-readable copy of the corresponding 139 | source code, to be distributed under the terms of Sections 1 and 2 above on 140 | a medium customarily used for software interchange; or, 141 | 142 | c) Accompany it with the information you received as to the offer to distribute 143 | corresponding source code. (This alternative is allowed only for noncommercial 144 | distribution and only if you received the program in object code or executable 145 | form with such an offer, in accord with Subsection b above.) 146 | 147 | The source code for a work means the preferred form of the work for making 148 | modifications to it. For an executable work, complete source code means all 149 | the source code for all modules it contains, plus any associated interface 150 | definition files, plus the scripts used to control compilation and installation 151 | of the executable. However, as a special exception, the source code distributed 152 | need not include anything that is normally distributed (in either source or 153 | binary form) with the major components (compiler, kernel, and so on) of the 154 | operating system on which the executable runs, unless that component itself 155 | accompanies the executable. 156 | 157 | If distribution of executable or object code is made by offering access to 158 | copy from a designated place, then offering equivalent access to copy the 159 | source code from the same place counts as distribution of the source code, 160 | even though third parties are not compelled to copy the source along with 161 | the object code. 162 | 163 | 4. You may not copy, modify, sublicense, or distribute the Program except 164 | as expressly provided under this License. Any attempt otherwise to copy, modify, 165 | sublicense or distribute the Program is void, and will automatically terminate 166 | your rights under this License. However, parties who have received copies, 167 | or rights, from you under this License will not have their licenses terminated 168 | so long as such parties remain in full compliance. 169 | 170 | 5. You are not required to accept this License, since you have not signed 171 | it. However, nothing else grants you permission to modify or distribute the 172 | Program or its derivative works. These actions are prohibited by law if you 173 | do not accept this License. Therefore, by modifying or distributing the Program 174 | (or any work based on the Program), you indicate your acceptance of this License 175 | to do so, and all its terms and conditions for copying, distributing or modifying 176 | the Program or works based on it. 177 | 178 | 6. Each time you redistribute the Program (or any work based on the Program), 179 | the recipient automatically receives a license from the original licensor 180 | to copy, distribute or modify the Program subject to these terms and conditions. 181 | You may not impose any further restrictions on the recipients' exercise of 182 | the rights granted herein. You are not responsible for enforcing compliance 183 | by third parties to this License. 184 | 185 | 7. If, as a consequence of a court judgment or allegation of patent infringement 186 | or for any other reason (not limited to patent issues), conditions are imposed 187 | on you (whether by court order, agreement or otherwise) that contradict the 188 | conditions of this License, they do not excuse you from the conditions of 189 | this License. If you cannot distribute so as to satisfy simultaneously your 190 | obligations under this License and any other pertinent obligations, then as 191 | a consequence you may not distribute the Program at all. For example, if a 192 | patent license would not permit royalty-free redistribution of the Program 193 | by all those who receive copies directly or indirectly through you, then the 194 | only way you could satisfy both it and this License would be to refrain entirely 195 | from distribution of the Program. 196 | 197 | If any portion of this section is held invalid or unenforceable under any 198 | particular circumstance, the balance of the section is intended to apply and 199 | the section as a whole is intended to apply in other circumstances. 200 | 201 | It is not the purpose of this section to induce you to infringe any patents 202 | or other property right claims or to contest validity of any such claims; 203 | this section has the sole purpose of protecting the integrity of the free 204 | software distribution system, which is implemented by public license practices. 205 | Many people have made generous contributions to the wide range of software 206 | distributed through that system in reliance on consistent application of that 207 | system; it is up to the author/donor to decide if he or she is willing to 208 | distribute software through any other system and a licensee cannot impose 209 | that choice. 210 | 211 | This section is intended to make thoroughly clear what is believed to be a 212 | consequence of the rest of this License. 213 | 214 | 8. If the distribution and/or use of the Program is restricted in certain 215 | countries either by patents or by copyrighted interfaces, the original copyright 216 | holder who places the Program under this License may add an explicit geographical 217 | distribution limitation excluding those countries, so that distribution is 218 | permitted only in or among countries not thus excluded. In such case, this 219 | License incorporates the limitation as if written in the body of this License. 220 | 221 | 9. The Free Software Foundation may publish revised and/or new versions of 222 | the General Public License from time to time. Such new versions will be similar 223 | in spirit to the present version, but may differ in detail to address new 224 | problems or concerns. 225 | 226 | Each version is given a distinguishing version number. If the Program specifies 227 | a version number of this License which applies to it and "any later version", 228 | you have the option of following the terms and conditions either of that version 229 | or of any later version published by the Free Software Foundation. If the 230 | Program does not specify a version number of this License, you may choose 231 | any version ever published by the Free Software Foundation. 232 | 233 | 10. If you wish to incorporate parts of the Program into other free programs 234 | whose distribution conditions are different, write to the author to ask for 235 | permission. For software which is copyrighted by the Free Software Foundation, 236 | write to the Free Software Foundation; we sometimes make exceptions for this. 237 | Our decision will be guided by the two goals of preserving the free status 238 | of all derivatives of our free software and of promoting the sharing and reuse 239 | of software generally. 240 | 241 | NO WARRANTY 242 | 243 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR 244 | THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE 245 | STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM 246 | "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, 247 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 248 | FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE 249 | OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME 250 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 251 | 252 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 253 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE 254 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 255 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE 256 | OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA 257 | OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES 258 | OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH 259 | HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 260 | 261 | END OF TERMS AND CONDITIONS 262 | 263 | How to Apply These Terms to Your New Programs 264 | 265 | If you develop a new program, and you want it to be of the greatest possible 266 | use to the public, the best way to achieve this is to make it free software 267 | which everyone can redistribute and change under these terms. 268 | 269 | To do so, attach the following notices to the program. It is safest to attach 270 | them to the start of each source file to most effectively convey the exclusion 271 | of warranty; and each file should have at least the "copyright" line and a 272 | pointer to where the full notice is found. 273 | 274 | one line to give the program's name and an idea of what it does. Copyright 275 | (C) yyyy name of author 276 | 277 | This program is free software; you can redistribute it and/or modify it under 278 | the terms of the GNU General Public License as published by the Free Software 279 | Foundation; either version 2 of the License, or (at your option) any later 280 | version. 281 | 282 | This program is distributed in the hope that it will be useful, but WITHOUT 283 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 284 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 285 | 286 | You should have received a copy of the GNU General Public License along with 287 | this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 288 | Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how 289 | to contact you by electronic and paper mail. 290 | 291 | If the program is interactive, make it output a short notice like this when 292 | it starts in an interactive mode: 293 | 294 | Gnomovision version 69, Copyright (C) year name of author Gnomovision comes 295 | with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, 296 | and you are welcome to redistribute it under certain conditions; type `show 297 | c' for details. 298 | 299 | The hypothetical commands `show w' and `show c' should show the appropriate 300 | parts of the General Public License. Of course, the commands you use may be 301 | called something other than `show w' and `show c'; they could even be mouse-clicks 302 | or menu items--whatever suits your program. 303 | 304 | You should also get your employer (if you work as a programmer) or your school, 305 | if any, to sign a "copyright disclaimer" for the program, if necessary. Here 306 | is a sample; alter the names: 307 | 308 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' 309 | (which makes passes at compilers) written by James Hacker. 310 | 311 | signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice 312 | -------------------------------------------------------------------------------- /LICENSES/GPL-2.0-or-later.txt: -------------------------------------------------------------------------------- 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 | 7 | Everyone is permitted to copy and distribute verbatim copies of this license 8 | document, but changing it is not allowed. 9 | 10 | Preamble 11 | 12 | The licenses for most software are designed to take away your freedom to share 13 | and change it. By contrast, the GNU General Public License is intended to 14 | guarantee your freedom to share and change free software--to make sure the 15 | software is free for all its users. This General Public License applies to 16 | most of the Free Software Foundation's software and to any other program whose 17 | authors commit to using it. (Some other Free Software Foundation software 18 | is covered by the GNU Lesser General Public License instead.) You can apply 19 | it to your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not price. Our 22 | General Public Licenses are designed to make sure that you have the freedom 23 | to distribute copies of free software (and charge for this service if you 24 | wish), that you receive source code or can get it if you want it, that you 25 | can change the software or use pieces of it in new free programs; and that 26 | you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid anyone to 29 | deny you these rights or to ask you to surrender the rights. These restrictions 30 | translate to certain responsibilities for you if you distribute copies of 31 | the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether gratis or 34 | for a fee, you must give the recipients all the rights that you have. You 35 | must make sure that they, too, receive or can get the source code. And you 36 | must show them these terms so they know their rights. 37 | 38 | We protect your rights with two steps: (1) copyright the software, and (2) 39 | offer you this license which gives you legal permission to copy, distribute 40 | and/or modify the software. 41 | 42 | Also, for each author's protection and ours, we want to make certain that 43 | everyone understands that there is no warranty for this free software. If 44 | the software is modified by someone else and passed on, we want its recipients 45 | to know that what they have is not the original, so that any problems introduced 46 | by others will not reflect on the original authors' reputations. 47 | 48 | Finally, any free program is threatened constantly by software patents. We 49 | wish to avoid the danger that redistributors of a free program will individually 50 | obtain patent licenses, in effect making the program proprietary. To prevent 51 | this, we have made it clear that any patent must be licensed for everyone's 52 | free use or not licensed at all. 53 | 54 | The precise terms and conditions for copying, distribution and modification 55 | follow. 56 | 57 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 58 | 59 | 0. This License applies to any program or other work which contains a notice 60 | placed by the copyright holder saying it may be distributed under the terms 61 | of this General Public License. The "Program", below, refers to any such program 62 | or work, and a "work based on the Program" means either the Program or any 63 | derivative work under copyright law: that is to say, a work containing the 64 | Program or a portion of it, either verbatim or with modifications and/or translated 65 | into another language. (Hereinafter, translation is included without limitation 66 | in the term "modification".) Each licensee is addressed as "you". 67 | 68 | Activities other than copying, distribution and modification are not covered 69 | by this License; they are outside its scope. The act of running the Program 70 | is not restricted, and the output from the Program is covered only if its 71 | contents constitute a work based on the Program (independent of having been 72 | made by running the Program). Whether that is true depends on what the Program 73 | does. 74 | 75 | 1. You may copy and distribute verbatim copies of the Program's source code 76 | as you receive it, in any medium, provided that you conspicuously and appropriately 77 | publish on each copy an appropriate copyright notice and disclaimer of warranty; 78 | keep intact all the notices that refer to this License and to the absence 79 | of any warranty; and give any other recipients of the Program a copy of this 80 | License along with the Program. 81 | 82 | You may charge a fee for the physical act of transferring a copy, and you 83 | may at your option offer warranty protection in exchange for a fee. 84 | 85 | 2. You may modify your copy or copies of the Program or any portion of it, 86 | thus forming a work based on the Program, and copy and distribute such modifications 87 | or work under the terms of Section 1 above, provided that you also meet all 88 | of these conditions: 89 | 90 | a) You must cause the modified files to carry prominent notices stating that 91 | you changed the files and the date of any change. 92 | 93 | b) You must cause any work that you distribute or publish, that in whole or 94 | in part contains or is derived from the Program or any part thereof, to be 95 | licensed as a whole at no charge to all third parties under the terms of this 96 | License. 97 | 98 | c) If the modified program normally reads commands interactively when run, 99 | you must cause it, when started running for such interactive use in the most 100 | ordinary way, to print or display an announcement including an appropriate 101 | copyright notice and a notice that there is no warranty (or else, saying that 102 | you provide a warranty) and that users may redistribute the program under 103 | these conditions, and telling the user how to view a copy of this License. 104 | (Exception: if the Program itself is interactive but does not normally print 105 | such an announcement, your work based on the Program is not required to print 106 | an announcement.) 107 | 108 | These requirements apply to the modified work as a whole. If identifiable 109 | sections of that work are not derived from the Program, and can be reasonably 110 | considered independent and separate works in themselves, then this License, 111 | and its terms, do not apply to those sections when you distribute them as 112 | separate works. But when you distribute the same sections as part of a whole 113 | which is a work based on the Program, the distribution of the whole must be 114 | on the terms of this License, whose permissions for other licensees extend 115 | to the entire whole, and thus to each and every part regardless of who wrote 116 | it. 117 | 118 | Thus, it is not the intent of this section to claim rights or contest your 119 | rights to work written entirely by you; rather, the intent is to exercise 120 | the right to control the distribution of derivative or collective works based 121 | on the Program. 122 | 123 | In addition, mere aggregation of another work not based on the Program with 124 | the Program (or with a work based on the Program) on a volume of a storage 125 | or distribution medium does not bring the other work under the scope of this 126 | License. 127 | 128 | 3. You may copy and distribute the Program (or a work based on it, under Section 129 | 2) in object code or executable form under the terms of Sections 1 and 2 above 130 | provided that you also do one of the following: 131 | 132 | a) Accompany it with the complete corresponding machine-readable source code, 133 | which must be distributed under the terms of Sections 1 and 2 above on a medium 134 | customarily used for software interchange; or, 135 | 136 | b) Accompany it with a written offer, valid for at least three years, to give 137 | any third party, for a charge no more than your cost of physically performing 138 | source distribution, a complete machine-readable copy of the corresponding 139 | source code, to be distributed under the terms of Sections 1 and 2 above on 140 | a medium customarily used for software interchange; or, 141 | 142 | c) Accompany it with the information you received as to the offer to distribute 143 | corresponding source code. (This alternative is allowed only for noncommercial 144 | distribution and only if you received the program in object code or executable 145 | form with such an offer, in accord with Subsection b above.) 146 | 147 | The source code for a work means the preferred form of the work for making 148 | modifications to it. For an executable work, complete source code means all 149 | the source code for all modules it contains, plus any associated interface 150 | definition files, plus the scripts used to control compilation and installation 151 | of the executable. However, as a special exception, the source code distributed 152 | need not include anything that is normally distributed (in either source or 153 | binary form) with the major components (compiler, kernel, and so on) of the 154 | operating system on which the executable runs, unless that component itself 155 | accompanies the executable. 156 | 157 | If distribution of executable or object code is made by offering access to 158 | copy from a designated place, then offering equivalent access to copy the 159 | source code from the same place counts as distribution of the source code, 160 | even though third parties are not compelled to copy the source along with 161 | the object code. 162 | 163 | 4. You may not copy, modify, sublicense, or distribute the Program except 164 | as expressly provided under this License. Any attempt otherwise to copy, modify, 165 | sublicense or distribute the Program is void, and will automatically terminate 166 | your rights under this License. However, parties who have received copies, 167 | or rights, from you under this License will not have their licenses terminated 168 | so long as such parties remain in full compliance. 169 | 170 | 5. You are not required to accept this License, since you have not signed 171 | it. However, nothing else grants you permission to modify or distribute the 172 | Program or its derivative works. These actions are prohibited by law if you 173 | do not accept this License. Therefore, by modifying or distributing the Program 174 | (or any work based on the Program), you indicate your acceptance of this License 175 | to do so, and all its terms and conditions for copying, distributing or modifying 176 | the Program or works based on it. 177 | 178 | 6. Each time you redistribute the Program (or any work based on the Program), 179 | the recipient automatically receives a license from the original licensor 180 | to copy, distribute or modify the Program subject to these terms and conditions. 181 | You may not impose any further restrictions on the recipients' exercise of 182 | the rights granted herein. You are not responsible for enforcing compliance 183 | by third parties to this License. 184 | 185 | 7. If, as a consequence of a court judgment or allegation of patent infringement 186 | or for any other reason (not limited to patent issues), conditions are imposed 187 | on you (whether by court order, agreement or otherwise) that contradict the 188 | conditions of this License, they do not excuse you from the conditions of 189 | this License. If you cannot distribute so as to satisfy simultaneously your 190 | obligations under this License and any other pertinent obligations, then as 191 | a consequence you may not distribute the Program at all. For example, if a 192 | patent license would not permit royalty-free redistribution of the Program 193 | by all those who receive copies directly or indirectly through you, then the 194 | only way you could satisfy both it and this License would be to refrain entirely 195 | from distribution of the Program. 196 | 197 | If any portion of this section is held invalid or unenforceable under any 198 | particular circumstance, the balance of the section is intended to apply and 199 | the section as a whole is intended to apply in other circumstances. 200 | 201 | It is not the purpose of this section to induce you to infringe any patents 202 | or other property right claims or to contest validity of any such claims; 203 | this section has the sole purpose of protecting the integrity of the free 204 | software distribution system, which is implemented by public license practices. 205 | Many people have made generous contributions to the wide range of software 206 | distributed through that system in reliance on consistent application of that 207 | system; it is up to the author/donor to decide if he or she is willing to 208 | distribute software through any other system and a licensee cannot impose 209 | that choice. 210 | 211 | This section is intended to make thoroughly clear what is believed to be a 212 | consequence of the rest of this License. 213 | 214 | 8. If the distribution and/or use of the Program is restricted in certain 215 | countries either by patents or by copyrighted interfaces, the original copyright 216 | holder who places the Program under this License may add an explicit geographical 217 | distribution limitation excluding those countries, so that distribution is 218 | permitted only in or among countries not thus excluded. In such case, this 219 | License incorporates the limitation as if written in the body of this License. 220 | 221 | 9. The Free Software Foundation may publish revised and/or new versions of 222 | the General Public License from time to time. Such new versions will be similar 223 | in spirit to the present version, but may differ in detail to address new 224 | problems or concerns. 225 | 226 | Each version is given a distinguishing version number. If the Program specifies 227 | a version number of this License which applies to it and "any later version", 228 | you have the option of following the terms and conditions either of that version 229 | or of any later version published by the Free Software Foundation. If the 230 | Program does not specify a version number of this License, you may choose 231 | any version ever published by the Free Software Foundation. 232 | 233 | 10. If you wish to incorporate parts of the Program into other free programs 234 | whose distribution conditions are different, write to the author to ask for 235 | permission. For software which is copyrighted by the Free Software Foundation, 236 | write to the Free Software Foundation; we sometimes make exceptions for this. 237 | Our decision will be guided by the two goals of preserving the free status 238 | of all derivatives of our free software and of promoting the sharing and reuse 239 | of software generally. 240 | 241 | NO WARRANTY 242 | 243 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR 244 | THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE 245 | STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM 246 | "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, 247 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 248 | FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE 249 | OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME 250 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 251 | 252 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 253 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE 254 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 255 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE 256 | OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA 257 | OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES 258 | OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH 259 | HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 260 | 261 | END OF TERMS AND CONDITIONS 262 | 263 | How to Apply These Terms to Your New Programs 264 | 265 | If you develop a new program, and you want it to be of the greatest possible 266 | use to the public, the best way to achieve this is to make it free software 267 | which everyone can redistribute and change under these terms. 268 | 269 | To do so, attach the following notices to the program. It is safest to attach 270 | them to the start of each source file to most effectively convey the exclusion 271 | of warranty; and each file should have at least the "copyright" line and a 272 | pointer to where the full notice is found. 273 | 274 | one line to give the program's name and an idea of what it does. Copyright 275 | (C) yyyy name of author 276 | 277 | This program is free software; you can redistribute it and/or modify it under 278 | the terms of the GNU General Public License as published by the Free Software 279 | Foundation; either version 2 of the License, or (at your option) any later 280 | version. 281 | 282 | This program is distributed in the hope that it will be useful, but WITHOUT 283 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 284 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 285 | 286 | You should have received a copy of the GNU General Public License along with 287 | this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 288 | Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how 289 | to contact you by electronic and paper mail. 290 | 291 | If the program is interactive, make it output a short notice like this when 292 | it starts in an interactive mode: 293 | 294 | Gnomovision version 69, Copyright (C) year name of author Gnomovision comes 295 | with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, 296 | and you are welcome to redistribute it under certain conditions; type `show 297 | c' for details. 298 | 299 | The hypothetical commands `show w' and `show c' should show the appropriate 300 | parts of the General Public License. Of course, the commands you use may be 301 | called something other than `show w' and `show c'; they could even be mouse-clicks 302 | or menu items--whatever suits your program. 303 | 304 | You should also get your employer (if you work as a programmer) or your school, 305 | if any, to sign a "copyright disclaimer" for the program, if necessary. Here 306 | is a sample; alter the names: 307 | 308 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' 309 | (which makes passes at compilers) written by James Hacker. 310 | 311 | signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice 312 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-or-later 2 | # Copyright (C) 2009-2013 Stephan Raue (stephan@openelec.tv) 3 | # Copyright (C) 2013 Lutz Fiebach (lufie@openelec.tv) 4 | # Copyright (C) 2020-present Team LibreELEC (https://libreelec.tv) 5 | 6 | ADDON_NAME := service.libreelec.settings 7 | ADDON_VERSION := 0.0.0 8 | DISTRONAME := LibreELEC 9 | ROOT_PASSWORD := libreelec 10 | 11 | SHELL := /bin/bash 12 | BUILDDIR := build 13 | DATADIR := /usr/share/kodi 14 | ADDONDIR := $(DATADIR)/addons 15 | 16 | ################################################################################ 17 | 18 | all: $(BUILDDIR)/$(ADDON_NAME) 19 | 20 | addon: $(BUILDDIR)/$(ADDON_NAME)-$(ADDON_VERSION).zip 21 | 22 | install: $(BUILDDIR)/$(ADDON_NAME) 23 | mkdir -p $(DESTDIR)$(ADDONDIR) 24 | cp -R $(BUILDDIR)/$(ADDON_NAME) $(DESTDIR)$(ADDONDIR) 25 | 26 | clean: 27 | rm -rf $(BUILDDIR) 28 | 29 | uninstall: 30 | rm -rf $(DESTDIR)$(ADDONDIR)/$(ADDON_NAME) 31 | 32 | $(BUILDDIR)/$(ADDON_NAME): 33 | mkdir -p $(BUILDDIR)/$(ADDON_NAME) 34 | cp -R resources $(BUILDDIR)/$(ADDON_NAME) 35 | cp COPYING $(BUILDDIR)/$(ADDON_NAME) 36 | cp addon.xml $(BUILDDIR)/$(ADDON_NAME) 37 | cp *.py $(BUILDDIR)/$(ADDON_NAME) 38 | sed -e "s,@ADDONNAME@,$(ADDON_NAME),g" \ 39 | -e "s,@ADDONVERSION@,$(ADDON_VERSION),g" \ 40 | -e "s,@DISTRONAME@,$(DISTRONAME),g" \ 41 | -i $(BUILDDIR)/$(ADDON_NAME)/addon.xml 42 | sed -e "s,@DISTRONAME@,$(DISTRONAME),g" \ 43 | -e "s,@ROOT_PASSWORD@,$(ROOT_PASSWORD),g" \ 44 | -i $(BUILDDIR)/$(ADDON_NAME)/resources/language/*/*.po 45 | 46 | $(BUILDDIR)/$(ADDON_NAME)-$(ADDON_VERSION).zip: $(BUILDDIR)/$(ADDON_NAME) 47 | cd $(BUILDDIR); zip -r $(ADDON_NAME)-$(ADDON_VERSION).zip $(ADDON_NAME) 48 | -------------------------------------------------------------------------------- /addon.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | all 13 | 14 | resources/skins/Default/media/icon.png 15 | resources/skins/Default/media/fanart.png 16 | 17 | 18 | https://github.com/LibreELEC/service.libreelec.settings/commits 19 | 20 | @DISTRONAME@ Konfigurasie 21 | Настройки на @DISTRONAME@ 22 | Configuració de @DISTRONAME@ 23 | @DISTRONAME@ Nastavení 24 | @DISTRONAME@ konfiguration 25 | @DISTRONAME@-Konfiguration 26 | Διαμόρφωση του @DISTRONAME@ 27 | @DISTRONAME@ Configuration 28 | @DISTRONAME@ Configuration 29 | @DISTRONAME@ Configuration 30 | @DISTRONAME@ Configuration 31 | Configuración de @DISTRONAME@ 32 | Configuración de @DISTRONAME@ 33 | @DISTRONAME@ Seadistus 34 | @DISTRONAME@-asetukset 35 | Configuration @DISTRONAME@ 36 | Configuration d'@DISTRONAME@ 37 | Configuración do @DISTRONAME@ 38 | הגדרות @DISTRONAME@ 39 | @DISTRONAME@ podešavanje 40 | @DISTRONAME@ konfiguráció 41 | Impostazioni @DISTRONAME@ 42 | @DISTRONAME@設定 43 | @DISTRONAME@ 설정 44 | @DISTRONAME@ konfigūravimas 45 | @DISTRONAME@ Configuratie 46 | @DISTRONAME@-konfigurasjon 47 | Konfiguracja @DISTRONAME@ 48 | Configuração @DISTRONAME@ 49 | Configuração do @DISTRONAME@ 50 | Configurare @DISTRONAME@ 51 | Конфигурация @DISTRONAME@ 52 | Konfigurácia @DISTRONAME@ 53 | Prilagoditev @DISTRONAME@ 54 | @DISTRONAME@-konfiguration 55 | @DISTRONAME@ Yapılandırması 56 | @DISTRONAME@ Cấu hình 57 | @DISTRONAME@ 设置 58 | Ползвайте добавката за да управлявате операционната система, в основата на @DISTRONAME@. Чрез добавката можете да настроите всичко и така не се налага да се притеснявате какво работи под Kodi.[CR][CR]От добавката можете да променяте настройките на мрежовите връзки, клавиатурната подредба и да включвате/изключвате отдалечения достъп посредством Samba или SSH. 59 | Utilitza aquest complement per configurar el sistema operatiu subjacent del sistema @DISTRONAME@. Des d'aquí es pot gestionar tot el sistema, de manera que no s'ha de preocupar sobre el que s'executa sota el Kodi.[CR][CR]Des d'aquí es pot canviar la connectivitat de la xarxa, la distribució del teclat i idioma i activar / desactivar l'accés remot inclòs samba i SSH. 60 | Použijte toto rozšíření pro správu vašeho @DISTRONAME@ systému. Odtud můžete spravovat celý systém, takže nemusíte mít strach o to, co běží pod kapotou. [CR] [CR] Odtud můžete změnit připojení k síti, rozložení klávesnice, jazyk a povolit nebo zakázat vzdálený přístup, včetně Samby a SSH. 61 | Brug denne addon til at styre dit @DISTRONAME@ systems underliggende operativsystem. Herfra kan du styre hele systemet, så du ikke behøver at bekymre dig om, hvad der kører under motorhjelmen. [CR] [CR] Herfra kan du ændre din netværksforbindelse, dit tastaturlayout og sprog og aktivere/deaktivere fjernadgang, herunder Samba og SSH. 62 | Verwenden Sie dieses Addon, um das Betriebssystem, auf dem @DISTRONAME@ läuft, zu administrieren. Von hier aus können Sie das gesamte System verwalten ohne sich Gedanken machen zu müssen, was genau im System abläuft.[CR][CR]Hier haben Sie die Möglichkeit Netzwerkeinstellungen, Tastaturlayouts oder Systemdienste, wie SSH oder Samba, zu konfigurieren. 63 | Χρησιμοποιήστε αυτό το πρόσθετο για να διαχειριστείτε το λειτουργικό σύστημα @DISTRONAME@. Από εδώ μπορείτε να διαχειριστείτε ολόκληρο το σύστημα ούτως ώστε να μη χρειάζεται να ανησυχείτε σχετικά με το τι τρέχει στο υπόβαθρο.[CR][CR]Μπορείτε, επίσης, να αλλάξετε τη συνδεσιμότητα του δικτύου σας, τη διάταξη του πληκτρολογίου σας και τη γλώσσα, καθώς και να ενεργοποιήσετε/απενεργοποιήσετε την απομακρυσμένη πρόσβαση μέσω Samba και SSH. 64 | Use this addon to manage your @DISTRONAME@ system's underlying operating system. From here you can manage the entire system so you don't have to worry about what runs under the hood.[CR][CR]From here you can change your network connectivity, your keyboard layout and language and enable/disable remote access including Samba and SSH. 65 | Use this addon to manage your @DISTRONAME@ system's underlying operating system. From here you can manage the entire system so you don't have to worry about what runs under the hood.[CR][CR]From here you can change your network connectivity, your keyboard layout and language and enable/disable remote access including Samba and SSH. 66 | Use this addon to manage your @DISTRONAME@ system's underlying operating system. From here you can manage the entire system so you don't have to worry about what runs under the hood.[CR][CR]From here you can change your network connectivity, your keyboard layout and language and enable/disable remote access including Samba and SSH. 67 | Use this addon to manage your @DISTRONAME@ system's underlying operating system. From here you can manage the entire system so you don't have to worry about what runs under the hood.[CR][CR]From here you can change your network connectivity, your keyboard layout and language and enable/disable remote access including Samba and SSH. 68 | Usa este add-on para administrar el sistema @DISTRONAME@. Desde aquí podrás controlar el sistema al completo (Conexiones de red, teclado, acceso remoto, samba y SSH) 69 | Usá esta extensión para administrar el sistema operativo subyacente a tu sistema @DISTRONAME@. Desde acá podés administrar el sistema en su totalidad sin tener que preocuparte por lo que pasa debajo del capó.[CR][CR]Desde acá podés cambiar la conexión de red, la distribución del teclado y el lenguaje y habilitar/deshabilitar el acceso remoto, incluyendo Samba y SSH. 70 | Usa este complemento para configurar el sistema operativo de tu @DISTRONAME@. Desde aquí puedes administrar todo el sistema fácilmente.[CR][CR]Desde aquí puedes cambiar la configuración de red, distribución del teclado, lenguaje del sistema y configurar Samba y SSH para controlar el acceso remoto al sistema. 71 | See lisa on @DISTRONAME@ operatsioonisüsteemi häälestamiseks. Siit saad hallata kogu süsteemi, ilma et oleks vaja kaane alla piiluda.[CR][CR] Siit saab seada võrguühenduse parameetreid, klaviatuuripaigutust, keelt ning lubada/keelata kaugligipääsu, sealhulgas Samba ja SSH. 72 | Käytä tätä lisäosaa muokataksesi @DISTRONAME@in alla olevaa käyttöjärjestelmää. Täältä voit muokata koko järjestelmää välittämättä siitä, mitä konepellin alta löytyy.[CR][CR]Voit muokata verkkoasetuksia, näppäimistön tyyppiä, kieliasetuksia sekä yhteysasetuksia, mukaanlukien Samba ja SSH. 73 | Utiliser cette extension pour configurer le système d'exploitation @DISTRONAME@. Il est possible de gérer l'intégralité du système simplement sans se soucier des détails techniques.[CR][CR]Il est possible de modifier la connexion au réseau, la disposition du clavier ou la langue du système. Il est aussi possible d'activer les accès distants comme Samba ou SSH. 74 | Utilisez cet addiciel pour gérer le système d'exploitation sous-jacent de votre système @DISTRONAME@. D'ici vous pouvez gérer votre système au complet sans vous soucier de ce qui tourne sous la capot.[CR][CR]D'ici vous pouvez changer vos paramètres de connexion réseau, l'agencement et la langue de votre clavier et activer/désactiver l'accès distant incluant Samba et SSH. 75 | בעזרת הרחבה זו ניתן לנהל את מערכת ההפעלה של @DISTRONAME@. מכאן ניתן לנהל כל אחד ממאפייני המערכת כך שאין צורך לדאוג מה קורה מתחת לממשק המשתמש.[CR][CR]ניתן לשנות הגדרות רשת, מערך מקלדת ושפה וגם להפעיל/לנטרל שירותי גישה מרחוק כולל Samba ו-SSH. 76 | Koristite ovaj dodatak za upravljanje @DISTRONAME@ operativnim sustavom. Odavdje možete upravljati cijelim sustavom stoga se ne morate brinuti što je pokrenuto u pozadini.[CR][CR]Odavdje možete promijeniti mrežno povezivenje, vaš raspored tipkovnice i jezik, omogućiti/onemogućiti udaljeni pristup uključujući Sambu i SSH. 77 | Ez a kiegészítő vezérli az @DISTRONAME@ rendszer alatti operációs rendszer beállításait. Innen kezelheti a teljes rendszert, tehát nem szükséges aggódnia mi van a motorháztető alatt. [CR][CR] Innen állíthatóak be a hálózati kapcsolatok, billentyűzet kiosztás és nyelvezet valamint a távoli hozzáférések engedélyezése/letiltása beleértve a Samba-t és SSH-t is. 78 | Usa questo addon per gestire le impostazioni del sistema operativo di @DISTRONAME@. Da qui puoi gestire l'intero sistema senza necessità di una conoscenza avanzata del software sottostante.[CR][CR]Puoi modificare le connessioni di rete, il layout e la lingua della tastiera così come abilitare/disabilitare l'accesso remoto tramite Samba e SSH 79 | このアドオンで@DISTRONAME@システムのオペレーティング・システムを管理します。ここからシステム全体を管理できますので、バックグラウンドで何が動作しているかなどを気にしなくてかまいません。[CR][CR]ネットワーク接続、キーボードのレイアウトや言語、SambaとSSHを含むリモートアクセスの有効・無効などの設定を行います。 80 | Naudokite šį priedą operacines sistemos, kuris pagrįsta jūsų @DISTRONAME@ sistema, tvarkymui. Čia jūs galite valdyta visą sistemą, kad jums netektų rūpintis kas vyksta po gaubtu..[CR][CR]Čia galite pakeisti tinklo nustatymus, klaviatūros išdėstymą ir kalbą bei įjungti/išjungti nuotolinę prieigą, įskaitant Samba ir SSH. 81 | Gebruik deze addon om het onderliggende @DISTRONAME@ systeem in te stellen. Vanaf hier kunt u het gehele systeem aanpassen, zodat u zich geen zorgen hoeft te maken over wat er onderhuids gebeurt.[CR][CR]Vanaf hier kunt u de netwerkverbindingen aanpassen, uw toetsenbord indeling en -taal, alsook een externe verbinding inclusief Samba en SSH in- of uitschakelen. 82 | Bruk dette tillegget for å konfigurere @DISTRONAME@s underliggende operativsystem. Herfra kan du konfigurere hele systemet slik at du ikke trenger å bekymre deg om hva som skjer under panseret.[CR][CR]Du kan endre nettverksinnstillinger, tastatur, språk og aktivere eller deaktivere nettverkstilgang slik som Samba og SSH. 83 | Za pomocą tej wtyczki możesz zarządzać systemem @DISTRONAME@. Z tego miejsca masz kontrolę nad całym systemem.[CR][CR] Zarządzasz połączeniem sieciowym, układem klawiatury i językiem systemu oraz możesz włączyć lub wyłączyć dostęp zdalny, w tym Sambę i SSH. 84 | Utilize este addon para gerir o sistema operativo que está na base do @DISTRONAME@. Aqui pode gerir todo o sistema, para não ter de se preocupar com aquilo que se passa nos bastidores.[CR][CR] Daqui, pode alterar as suas ligações de rede, disposição do teclado e idiomas. Também pode activar/desactivar acesso remoto, incluindo Samba e SSH. 85 | Utilize este addon para gerenciar o sistema operacional do @DISTRONAME@. Aqui você pode gerenciar todo o sistema, para não ter de se preocupar com aquilo que se passa nos bastidores.[CR][CR] Daqui, você pode alterar as suas conexões de rede, configurações do teclado e idiomas. Também pode ativar/desativar acesso remoto, incluindo compartilhamento de rede do Windows e SSH. 86 | Данное дополнение предназначено для управления системными настройками @DISTRONAME@. С помощью удобного интерфейса Вы можете управлять настройками системы (включая низкоуровневые настройки ОС) через удобный интерфейс.[CR][CR]Здесь Вы также сможете изменить способ соединения с сетью, настроить раскладку клавиатуры, выбрать язык интерфейса, а также включить удаленный доступ по SSH и/или Samba. 87 | Tento doplnok slúži na správu operačného systému, na ktorom beží @DISTRONAME@. Odtiaľto je možné spravovať celý systém bez nutnosti znalostí o tom, čo beží pod kapotou.[CR][CR]Je tu možné meniť sieťové nastavenia, rozloženie a jazyk klávesnice alebo povoliť/zakázať vzdialený prístup prostredníctvom Samby a SSH. 88 | S tem dodatkom upravljate operacijski sistem, ki deluje pod pokrovom sistema @DISTRONAME@. Odtod lahko upravljate s celotnim sistemom, tako da je skrb o tem, kar teče pod pokrovom, odveč.[CR][CR]Odtod lahko spremenite svojo omrežno povezljivost, postavitev tipkovnice in jezik ter omogočite/onemogočite oddaljeni dostop, vključno s Sambo in SSH. 89 | Använd detta tillägg för att hantera ditt @DISTRONAME@-systems underliggande operativ. Härifrån kan du hantera hela systemet så du inte behöver oroa dig för vad som körs under skalet.[CR][CR]Härifrån kan du ändra din nätverksanslutning, tangentbordslayout och språk, samt aktivera/inaktivera fjärråtkomst inklusive Samba och SSH. 90 | Bu eklentiyi kullanarak @DISTRONAME@ sisteminizin temelindeki işletim sistemini yönetin. Buradan tüm sistemi yönetebilirsiniz ve bu yüzden sistemin altında neler olduğu hakkında endişelenmenize gerek yok.[CR][CR]Buradan ağ bağlantısı, klavye düzeni ve dilini değiştirip, Samba ve SSH gibi uzaktan erişimi etkinleştirip/devre dışı bırakabilirsiniz. 91 | Sử dụng addon này để quản lý hệ thống điều hành @DISTRONAME@ cơ bản của bạn.&amp;#10;Từ đây bạn có thể quản lý toàn bộ hệ thống, do đó bạn không cần phải lo lắng về những gì đang chạy . [CR][CR] Từ đây bạn có thể thay đổi kết nối mạng của bạn, bố trí bàn phím của bạn và ngôn ngữ và cho phép / vô hiệu hóa truy cập từ xa bao gồm Samba và SSH. 92 | 使用此插件管理你的 @DISTRONAME@ 系统的底层操作系统。它使你可以在无需深入了解的情况下管理整个系统。[CR][CR]在这里,你可以修改网络设置、键盘布局、语言以及启用/禁用 Samba 和 SSH 等远程访问功能。 93 | 94 | 95 | -------------------------------------------------------------------------------- /default.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-or-later 2 | # Copyright (C) 2009-2013 Stephan Raue (stephan@openelec.tv) 3 | # Copyright (C) 2013 Lutz Fiebach (lufie@openelec.tv) 4 | # Copyright (C) 2019-present Team LibreELEC (https://libreelec.tv) 5 | 6 | import socket 7 | 8 | import xbmc 9 | import xbmcaddon 10 | 11 | 12 | __scriptid__ = 'service.libreelec.settings' 13 | __addon__ = xbmcaddon.Addon(id=__scriptid__) 14 | __cwd__ = __addon__.getAddonInfo('path') 15 | __media__ = f'{__cwd__}/resources/skins/Default/media' 16 | _ = __addon__.getLocalizedString 17 | 18 | try: 19 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 20 | sock.connect('/var/run/service.libreelec.settings.sock') 21 | sock.send(bytes('openConfigurationWindow', 'utf-8')) 22 | sock.close() 23 | except Exception as e: 24 | xbmc.executebuiltin(f'Notification("LibreELEC", "{_(32390)}", 5000, "{__media__}/icon.png"') 25 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | Kodistubs 2 | DBussy 3 | -------------------------------------------------------------------------------- /resources/__init__.py: -------------------------------------------------------------------------------- 1 | # Dummy file to make this directory a package. 2 | -------------------------------------------------------------------------------- /resources/language/resource.language.en_gb/strings.po: -------------------------------------------------------------------------------- 1 | # Kodi Media Center language file 2 | # Addon Name: @DISTRONAME@ Configuration 3 | # Addon id: service.libreelec.settings 4 | # Addon Provider: LibreELEC 5 | msgid "" 6 | msgstr "" 7 | "MIME-Version: 1.0\n" 8 | "Content-Type: text/plain; charset=UTF-8\n" 9 | "Content-Transfer-Encoding: 8bit\n" 10 | 11 | msgctxt "#600" 12 | msgid "@DISTRONAME@ Settings" 13 | msgstr "" 14 | 15 | msgctxt "#601" 16 | msgid "Interface" 17 | msgstr "" 18 | 19 | msgctxt "#602" 20 | msgid "Address" 21 | msgstr "" 22 | 23 | msgctxt "#603" 24 | msgid "Type" 25 | msgstr "" 26 | 27 | msgctxt "#604" 28 | msgid "State" 29 | msgstr "" 30 | 31 | msgctxt "#605" 32 | msgid "Address" 33 | msgstr "" 34 | 35 | msgctxt "#606" 36 | msgid "Distribution: " 37 | msgstr "" 38 | 39 | msgctxt "#607" 40 | msgid "Version: " 41 | msgstr "" 42 | 43 | msgctxt "#608" 44 | msgid "Architecture: " 45 | msgstr "" 46 | 47 | msgctxt "#609" 48 | msgid "LibreELEC is created by a global team of Linux and Kodi enthusiasts who contribute time and effort to manage the project, write code, and provide support. We have fun making it. We hope you have fun using it!" 49 | msgstr "" 50 | 51 | msgctxt "#610" 52 | msgid "[B]Support[/B] is provided via our forum:[CR]https://forum.libreelec.tv[CR][CR][B]Documentation[/B] can be read in our wiki:[CR]https://libreelec.wiki[CR][CR][B]Code[/B] can be found on GitHub:[CR]https://github.com/LibreELEC[CR][CR][B]Donations[/B] towards project expenses are accepted via OpenCollective:[CR]https://opencollective.com/libreelec/donate" 53 | msgstr "" 54 | 55 | msgctxt "#700" 56 | msgid "Configure a hostname, keyboard type, perform a reset, update, or backups and restores" 57 | msgstr "" 58 | 59 | msgctxt "#701" 60 | msgid "Configure network startup options, NTP and VPNs" 61 | msgstr "" 62 | 63 | msgctxt "#702" 64 | msgid "Manage the Ethernet, Wireless and VPN connections available to the system" 65 | msgstr "" 66 | 67 | msgctxt "#703" 68 | msgid "Configure system services like Samba, SSH, Cron, Avahi and Syslog" 69 | msgstr "" 70 | 71 | msgctxt "#704" 72 | msgid "Configure and pair Bluetooth devices" 73 | msgstr "" 74 | 75 | msgctxt "#705" 76 | msgid "Useful information like version and architecture details, support and documentation links, and how to donate and support the project." 77 | msgstr "" 78 | 79 | msgctxt "#707" 80 | msgid "Configure updates" 81 | msgstr "" 82 | 83 | msgctxt "#709" 84 | msgid "Select the timezone for this device." 85 | msgstr "" 86 | 87 | msgctxt "#710" 88 | msgid "Configure the Hostname of your @DISTRONAME@ system. This will be used for the HTPC \\\\hostname in Windows and SMB server name in the Mac OS Finder" 89 | msgstr "" 90 | 91 | msgctxt "#711" 92 | msgid "Configure the 1st keyboard software layout" 93 | msgstr "" 94 | 95 | msgctxt "#712" 96 | msgid "Configure the 2nd keyboard software layout" 97 | msgstr "" 98 | 99 | msgctxt "#713" 100 | msgid "Configure the physical keyboard type" 101 | msgstr "" 102 | 103 | msgctxt "#714" 104 | msgid "@DISTRONAME@ can be configured for automatic or manual updates. Automatic updates are available on stable releases and stable release candidates. Automatic update self-disables on beta and development builds" 105 | msgstr "" 106 | 107 | msgctxt "#715" 108 | msgid "Set to ON and an on-screen notification will be displayed when a new update is available" 109 | msgstr "" 110 | 111 | msgctxt "#718" 112 | msgid "Send boot config files from /flash, kodi.log, kernel log and Samba configs to http://ix.io, and display the short URL" 113 | msgstr "" 114 | 115 | msgctxt "#719" 116 | msgid "Send boot config files from /flash, kodi_crash.log, kernel log and Samba configs to http://ix.io, and display the short URL" 117 | msgstr "" 118 | 119 | msgctxt "#720" 120 | msgid "Set to ON to enable the Bluetooth service" 121 | msgstr "" 122 | 123 | msgctxt "#722" 124 | msgid "Create a tar archive containing all @DISTRONAME@ and Kodi configuration settings, databases and thumbnail content. Backup files will be stored in /storage/backup/" 125 | msgstr "" 126 | 127 | msgctxt "#723" 128 | msgid "Restore a tar archive backup previously created using the backup option above" 129 | msgstr "" 130 | 131 | msgctxt "#724" 132 | msgid "[COLOR FF00FF00]SOFT RESET[/COLOR]\nPermanently reset @DISTRONAME@ configuration to defaults (resets also Kodi to default including your library. your media - music/videos/etc are not touched)" 133 | msgstr "" 134 | 135 | msgctxt "#725" 136 | msgid "[COLOR FFFF0000]HARD RESET[/COLOR]\nPermanently reset @DISTRONAME@ and Kodi configuration to defaults (everything in your storage partition will be deleted)" 137 | msgstr "" 138 | 139 | msgctxt "#726" 140 | msgid "Enable or disable support for Wireless (WLAN) networks" 141 | msgstr "" 142 | 143 | msgctxt "#727" 144 | msgid "Enable a 'tethered' Wireless Access Point. This requires your wireless card (and driver) to support bridging and access point mode. Not all cards are capable." 145 | msgstr "" 146 | 147 | msgctxt "#728" 148 | msgid "Configure the Access Point SSID" 149 | msgstr "" 150 | 151 | msgctxt "#729" 152 | msgid "Configure the Access Point Passphrase" 153 | msgstr "" 154 | 155 | msgctxt "#730" 156 | msgid "Enable or disable support for Wired (Ethernet) networks" 157 | msgstr "" 158 | 159 | msgctxt "#732" 160 | msgid "Configure the 1st time (NTP) server" 161 | msgstr "" 162 | 163 | msgctxt "#733" 164 | msgid "Configure the 2nd time (NTP) server" 165 | msgstr "" 166 | 167 | msgctxt "#734" 168 | msgid "Configure the 3rd time (NTP) server" 169 | msgstr "" 170 | 171 | msgctxt "#736" 172 | msgid "Set to ON to delay Kodi startup until the network is available. Use this if the OS is booting into Kodi before the network is up and central MySQL databases are accessible." 173 | msgstr "" 174 | 175 | msgctxt "#737" 176 | msgid "Time in seconds to wait for an established network connection." 177 | msgstr "" 178 | 179 | msgctxt "#738" 180 | msgid "Set to ON to enable the embedded Samba (SMB) filesharing service" 181 | msgstr "" 182 | 183 | msgctxt "#739" 184 | msgid "Set to ON to require username/password access to local Samba fileshares" 185 | msgstr "" 186 | 187 | msgctxt "#740" 188 | msgid "Set the username for Samba sharing" 189 | msgstr "" 190 | 191 | msgctxt "#741" 192 | msgid "Set the password for Samba sharing" 193 | msgstr "" 194 | 195 | msgctxt "#742" 196 | msgid "Set to ON to enable the embedded SSH server. The SSH console can be accessed with username 'root' and password '@ROOT_PASSWORD@'." 197 | msgstr "" 198 | 199 | msgctxt "#743" 200 | msgid "Set to ON if you have copied private SSH keys to your HTPC and would like to improve security by disabling SSH username and password authentication." 201 | msgstr "" 202 | 203 | msgctxt "#744" 204 | msgid "Set to ON to enable the Avahi (Zeroconf/Bonjour) network discovery service" 205 | msgstr "" 206 | 207 | msgctxt "#745" 208 | msgid "Set to ON to enable the cron daemon" 209 | msgstr "" 210 | 211 | msgctxt "#746" 212 | msgid "Set the password for SSH" 213 | msgstr "" 214 | 215 | msgctxt "#747" 216 | msgid "Enable a PIN for @DISTRONAME@ Settings access" 217 | msgstr "" 218 | 219 | msgctxt "#748" 220 | msgid "Change the PIN Lock for @DISTRONAME@ Settings access" 221 | msgstr "" 222 | 223 | msgctxt "#749" 224 | msgid "Overrride the Wireless Regulatory Domain so radio properties (channels and transmit power) comply with local laws or match the country configuration of your router." 225 | msgstr "" 226 | 227 | msgctxt "#750" 228 | msgid "750" 229 | msgstr "" 230 | 231 | msgctxt "#751" 232 | msgid "With OBEX filetransfer you can send files, for example movies or pictures over bluetooth to @DISTRONAME@." 233 | msgstr "" 234 | 235 | msgctxt "#752" 236 | msgid "Here you can set the folder location where OBEX file transfer should store incoming files" 237 | msgstr "" 238 | 239 | msgctxt "#753" 240 | msgid "Configure the 1st keyboard software Variant" 241 | msgstr "" 242 | 243 | msgctxt "#754" 244 | msgid "Configure the 2nd keyboard software Variant" 245 | msgstr "" 246 | 247 | msgctxt "#755" 248 | msgid "Auto share external drives / partitions" 249 | msgstr "" 250 | 251 | msgctxt "#756" 252 | msgid "Disable older SMB protocols by specifying the minimum supported protocol." 253 | msgstr "" 254 | 255 | msgctxt "#757" 256 | msgid "Disable more recent SMB protocols for backward compatability with legacy clients." 257 | msgstr "" 258 | 259 | msgctxt "#758" 260 | msgid "NetBIOS group to which the server belongs. Default is WORKGROUP." 261 | msgstr "" 262 | 263 | msgctxt "#760" 264 | msgid "Select an update channel" 265 | msgstr "" 266 | 267 | msgctxt "#761" 268 | msgid "Enable to allow entering a custom update url" 269 | msgstr "" 270 | 271 | msgctxt "#762" 272 | msgid "Enter a custom update url (url should be the root of the update files)" 273 | msgstr "" 274 | 275 | msgctxt "#770" 276 | msgid "Select an available version" 277 | msgstr "" 278 | 279 | msgctxt "#771" 280 | msgid "The firewall blocks unwanted network traffic. Home (default) allows traffic from private network ranges (192.168.x.x, 172.16.x.x and 10.x.x.x) only. Public blocks all traffic from all networks. Custom uses rules in /storage/.config/iptables. Off disables the firewall." 281 | msgstr "" 282 | 283 | msgctxt "#772" 284 | msgid "Checking for a new version will submit the installed Distribution, Project, CPU Arch and Version to the update server. This data is also used to report basic statistics for the project. To continue checking for updates while opting-out of stats reporting, disable this option." 285 | msgstr "" 286 | 287 | msgctxt "#773" 288 | msgid "Set the idle timeout (in minutes) before devices will be disconnected (defaults to 0 for no timeout). Applies to devices where \"Enable Standby\" has been activated in the LibreELEC device connection context menu." 289 | msgstr "" 290 | 291 | msgctxt "#32001" 292 | msgid "Services" 293 | msgstr "" 294 | 295 | msgctxt "#32002" 296 | msgid "System" 297 | msgstr "" 298 | 299 | msgctxt "#32003" 300 | msgid "Interface" 301 | msgstr "" 302 | 303 | msgctxt "#32005" 304 | msgid "Updates" 305 | msgstr "" 306 | 307 | msgctxt "#32009" 308 | msgid "Keyboard" 309 | msgstr "" 310 | 311 | msgctxt "#32010" 312 | msgid "Keyboard Layout" 313 | msgstr "" 314 | 315 | msgctxt "#32012" 316 | msgid "Select Action" 317 | msgstr "" 318 | 319 | msgctxt "#32013" 320 | msgid "OS Updates" 321 | msgstr "" 322 | 323 | msgctxt "#32014" 324 | msgid "Automatic Updates" 325 | msgstr "" 326 | 327 | msgctxt "#32015" 328 | msgid "Update Channel" 329 | msgstr "" 330 | 331 | msgctxt "#32016" 332 | msgid "Show Custom Channels" 333 | msgstr "" 334 | 335 | msgctxt "#32017" 336 | msgid " - Custom Channel 1" 337 | msgstr "" 338 | 339 | msgctxt "#32018" 340 | msgid " - Custom Channel 2" 341 | msgstr "" 342 | 343 | msgctxt "#32019" 344 | msgid " - Custom Channel 3" 345 | msgstr "" 346 | 347 | msgctxt "#32020" 348 | msgid "Available Versions" 349 | msgstr "" 350 | 351 | msgctxt "#32021" 352 | msgid "Submit Statistics" 353 | msgstr "" 354 | 355 | msgctxt "#32022" 356 | msgid "Firmware Updates" 357 | msgstr "" 358 | 359 | msgctxt "#32023" 360 | msgid "Updating will occur once the system is rebooted." 361 | msgstr "" 362 | 363 | msgctxt "#32024" 364 | msgid "Bootloader EEPROM" 365 | msgstr "" 366 | 367 | msgctxt "#32025" 368 | msgid "Update the Raspberry Pi Bootloader EEPROM to the latest version. This option is automatically disabled after rebooting. Reboot the system to apply the update." 369 | msgstr "" 370 | 371 | msgctxt "#32026" 372 | msgid "VIA USB3 Firmware" 373 | msgstr "" 374 | 375 | msgctxt "#32027" 376 | msgid "Update the Raspberry Pi VIA USB3 firmware to the latest version. This option is automatically disabled after rebooting. Reboot the system to apply the update." 377 | msgstr "" 378 | 379 | msgctxt "#32028" 380 | msgid "update from %s to %s" 381 | msgstr "" 382 | 383 | msgctxt "#32029" 384 | msgid "up to date: %s" 385 | msgstr "" 386 | 387 | msgctxt "#32100" 388 | msgid "Connections" 389 | msgstr "" 390 | 391 | msgctxt "#32101" 392 | msgid "Network" 393 | msgstr "" 394 | 395 | msgctxt "#32102" 396 | msgid "Wireless Networks" 397 | msgstr "" 398 | 399 | msgctxt "#32103" 400 | msgid "Wired Networks" 401 | msgstr "" 402 | 403 | msgctxt "#32105" 404 | msgid "Active" 405 | msgstr "" 406 | 407 | msgctxt "#32106" 408 | msgid "Username" 409 | msgstr "" 410 | 411 | msgctxt "#32107" 412 | msgid "Passphrase" 413 | msgstr "" 414 | 415 | msgctxt "#32108" 416 | msgid "Enable 'tethered' Wireless Access Point" 417 | msgstr "" 418 | 419 | msgctxt "#32109" 420 | msgid "Connect Automatically" 421 | msgstr "" 422 | 423 | msgctxt "#32110" 424 | msgid "Connection" 425 | msgstr "" 426 | 427 | msgctxt "#32111" 428 | msgid "IPv4" 429 | msgstr "" 430 | 431 | msgctxt "#32112" 432 | msgid "IPv6" 433 | msgstr "" 434 | 435 | msgctxt "#32113" 436 | msgid "IP Address Method" 437 | msgstr "" 438 | 439 | msgctxt "#32114" 440 | msgid "IP Address" 441 | msgstr "" 442 | 443 | msgctxt "#32115" 444 | msgid "Subnet Mask" 445 | msgstr "" 446 | 447 | msgctxt "#32116" 448 | msgid "Default Gateway" 449 | msgstr "" 450 | 451 | msgctxt "#32117" 452 | msgid "Prefix Length" 453 | msgstr "" 454 | 455 | msgctxt "#32118" 456 | msgid "Privacy" 457 | msgstr "" 458 | 459 | msgctxt "#32119" 460 | msgid "DNS Servers" 461 | msgstr "" 462 | 463 | msgctxt "#32120" 464 | msgid "Nameserver #1" 465 | msgstr "" 466 | 467 | msgctxt "#32121" 468 | msgid "Nameserver #2" 469 | msgstr "" 470 | 471 | msgctxt "#32122" 472 | msgid "Nameserver #3" 473 | msgstr "" 474 | 475 | msgctxt "#32123" 476 | msgid "NTP Servers" 477 | msgstr "" 478 | 479 | msgctxt "#32124" 480 | msgid "Timeserver #1" 481 | msgstr "" 482 | 483 | msgctxt "#32125" 484 | msgid "Timeserver #2" 485 | msgstr "" 486 | 487 | msgctxt "#32126" 488 | msgid "Timeserver #3" 489 | msgstr "" 490 | 491 | msgctxt "#32127" 492 | msgid "DNS Domains" 493 | msgstr "" 494 | 495 | msgctxt "#32128" 496 | msgid "Domain #1" 497 | msgstr "" 498 | 499 | msgctxt "#32129" 500 | msgid "Domain #2" 501 | msgstr "" 502 | 503 | msgctxt "#32130" 504 | msgid "Domain #3" 505 | msgstr "" 506 | 507 | msgctxt "#32140" 508 | msgid "Save" 509 | msgstr "" 510 | 511 | msgctxt "#32141" 512 | msgid "Delete" 513 | msgstr "" 514 | 515 | msgctxt "#32142" 516 | msgid "Refresh" 517 | msgstr "" 518 | 519 | msgctxt "#32143" 520 | msgid "Disconnect" 521 | msgstr "" 522 | 523 | msgctxt "#32144" 524 | msgid "Connect" 525 | msgstr "" 526 | 527 | msgctxt "#32145" 528 | msgid "Pair" 529 | msgstr "" 530 | 531 | msgctxt "#32146" 532 | msgid "Hidden Network Name" 533 | msgstr "" 534 | 535 | msgctxt "#32147" 536 | msgid "Wireless Network Passphrase" 537 | msgstr "" 538 | 539 | msgctxt "#32148" 540 | msgid "Wireless Network Username" 541 | msgstr "" 542 | 543 | msgctxt "#32150" 544 | msgid "Edit" 545 | msgstr "" 546 | 547 | msgctxt "#32180" 548 | msgid "Would you like to update @DISTRONAME@ now?" 549 | msgstr "" 550 | 551 | msgctxt "#32181" 552 | msgid "Filename" 553 | msgstr "" 554 | 555 | msgctxt "#32182" 556 | msgid "Download speed" 557 | msgstr "" 558 | 559 | msgctxt "#32183" 560 | msgid "Time remaining" 561 | msgstr "" 562 | 563 | msgctxt "#32184" 564 | msgid "Extract file" 565 | msgstr "" 566 | 567 | msgctxt "#32185" 568 | msgid "Extract speed" 569 | msgstr "" 570 | 571 | msgctxt "#32186" 572 | msgid "Initialize Archive File" 573 | msgstr "" 574 | 575 | msgctxt "#32187" 576 | msgid "New Version" 577 | msgstr "" 578 | 579 | msgctxt "#32188" 580 | msgid "Current Version" 581 | msgstr "" 582 | 583 | msgctxt "#32189" 584 | msgid "OS Configuration" 585 | msgstr "" 586 | 587 | msgctxt "#32190" 588 | msgid "System Name" 589 | msgstr "" 590 | 591 | msgctxt "#32191" 592 | msgid "Invalid URL" 593 | msgstr "" 594 | 595 | msgctxt "#32192" 596 | msgid "PIN lock" 597 | msgstr "" 598 | 599 | msgctxt "#32193" 600 | msgid "Enable @DISTRONAME@ Settings PIN Lock" 601 | msgstr "" 602 | 603 | msgctxt "#32194" 604 | msgid "Change @DISTRONAME@ Settings PIN Lock" 605 | msgstr "" 606 | 607 | msgctxt "#32196" 608 | msgid "About" 609 | msgstr "" 610 | 611 | msgctxt "#32198" 612 | msgid "SSID" 613 | msgstr "" 614 | 615 | msgctxt "#32200" 616 | msgid "Samba" 617 | msgstr "" 618 | 619 | msgctxt "#32201" 620 | msgid "SSH" 621 | msgstr "" 622 | 623 | msgctxt "#32202" 624 | msgid "Use Samba Password Authentication" 625 | msgstr "" 626 | 627 | msgctxt "#32203" 628 | msgid "Disable SSH Password" 629 | msgstr "" 630 | 631 | msgctxt "#32204" 632 | msgid "Enable Samba" 633 | msgstr "" 634 | 635 | msgctxt "#32205" 636 | msgid "Enable SSH" 637 | msgstr "" 638 | 639 | msgctxt "#32206" 640 | msgid "Enable Avahi (Zeroconf)" 641 | msgstr "" 642 | 643 | msgctxt "#32207" 644 | msgid "Avahi" 645 | msgstr "" 646 | 647 | msgctxt "#32208" 648 | msgid "Hidden Wlan" 649 | msgstr "" 650 | 651 | msgctxt "#32209" 652 | msgid "SSH Password" 653 | msgstr "" 654 | 655 | msgctxt "#32210" 656 | msgid "The default SSH password is widely known and considered insecure.[CR][CR]Setting a personal password is recommended." 657 | msgstr "" 658 | 659 | msgctxt "#32212" 660 | msgid "Cancel" 661 | msgstr "" 662 | 663 | msgctxt "#32213" 664 | msgid "Keep Existing" 665 | msgstr "" 666 | 667 | msgctxt "#32214" 668 | msgid "Set Password" 669 | msgstr "" 670 | 671 | msgctxt "#32215" 672 | msgid "Workgroup name" 673 | msgstr "" 674 | 675 | msgctxt "#32216" 676 | msgid "Auto-Share External Drives" 677 | msgstr "" 678 | 679 | msgctxt "#32217" 680 | msgid "Minimum supported protocol" 681 | msgstr "" 682 | 683 | msgctxt "#32218" 684 | msgid "Maximum supported protocol" 685 | msgstr "" 686 | 687 | msgctxt "#32220" 688 | msgid "Bad password!" 689 | msgstr "" 690 | 691 | msgctxt "#32221" 692 | msgid "The entered password is too weak.[CR]SSH password is unchanged." 693 | msgstr "" 694 | 695 | msgctxt "#32222" 696 | msgid "Password changed!" 697 | msgstr "" 698 | 699 | msgctxt "#32223" 700 | msgid "The SSH password has been successfully changed." 701 | msgstr "" 702 | 703 | msgctxt "#32224" 704 | msgid "Unknown error!" 705 | msgstr "" 706 | 707 | msgctxt "#32225" 708 | msgid "There was an error during the process.[CR]SSH password is unchanged." 709 | msgstr "" 710 | 711 | msgctxt "#32226" 712 | msgid "Enter new 4 digit PIN" 713 | msgstr "" 714 | 715 | msgctxt "#32227" 716 | msgid "Re-enter PIN" 717 | msgstr "" 718 | 719 | msgctxt "#32228" 720 | msgid "Error - PIN codes did not match!" 721 | msgstr "" 722 | 723 | msgctxt "#32229" 724 | msgid "@DISTRONAME@ Settings PIN lock not set.[CR][CR]Please try again." 725 | msgstr "" 726 | 727 | msgctxt "#32230" 728 | msgid "@DISTRONAME@ Settings PIN lock set." 729 | msgstr "" 730 | 731 | msgctxt "#32231" 732 | msgid "Your @DISTRONAME@ Settings PIN lock has been set to:" 733 | msgstr "" 734 | 735 | msgctxt "#32232" 736 | msgid "Error - PIN code not 4 digits!" 737 | msgstr "" 738 | 739 | msgctxt "#32233" 740 | msgid "@DISTRONAME@ Settings Locked[CR]Enter 4 digit PIN" 741 | msgstr "" 742 | 743 | msgctxt "#32234" 744 | msgid "Incorrect PIN!" 745 | msgstr "" 746 | 747 | msgctxt "#32235" 748 | msgid " attempts remaining." 749 | msgstr "" 750 | 751 | msgctxt "#32236" 752 | msgid "You have entered an incorrect PIN too many times.[CR][CR]You will need to wait 5 minutes before trying again." 753 | msgstr "" 754 | 755 | msgctxt "#32237" 756 | msgid "@DISTRONAME@ Settings Locked!" 757 | msgstr "" 758 | 759 | msgctxt "#32238" 760 | msgid "Time remaining before PIN can be entered: %02d:%02d[CR][CR]Too many previous failed attempts." 761 | msgstr "" 762 | 763 | msgctxt "#32240" 764 | msgid "Wireless Regulatory Domain" 765 | msgstr "" 766 | 767 | msgctxt "#32300" 768 | msgid "Welcome to @DISTRONAME@" 769 | msgstr "" 770 | 771 | msgctxt "#32301" 772 | msgid "Welcome" 773 | msgstr "" 774 | 775 | msgctxt "#32302" 776 | msgid "This wizard will guide you through the process of setting up your new @DISTRONAME@ installation - setting your location, timezone and connecting you to the internet.[CR][CR]These settings can be changed later by navigating to Programs > @DISTRONAME@ Settings." 777 | msgstr "" 778 | 779 | msgctxt "#32303" 780 | msgid "Next" 781 | msgstr "" 782 | 783 | msgctxt "#32304" 784 | msgid "To be identified easily on the network, your new @DISTRONAME@ machine needs a name.[CR][CR]Try to choose something meaningful - like the room it's in - so you'll know what it is when you see it on the network.[CR][CR]This name is used when configuring network services, like file sharing using samba." 785 | msgstr "" 786 | 787 | msgctxt "#32305" 788 | msgid "Networking" 789 | msgstr "" 790 | 791 | msgctxt "#32306" 792 | msgid "In order to download backdrops, banners and thumbnails for your movies and TV shows and to stream online content from sites like YouTube, @DISTRONAME@ needs to be connected the Internet.[CR][CR]An Internet connection is also required for @DISTRONAME@ to automatically update itself." 793 | msgstr "" 794 | 795 | msgctxt "#32307" 796 | msgid "Previous" 797 | msgstr "" 798 | 799 | msgctxt "#32308" 800 | msgid "Hostname:" 801 | msgstr "" 802 | 803 | msgctxt "#32309" 804 | msgid "The following Networks are currently available:" 805 | msgstr "" 806 | 807 | msgctxt "#32310" 808 | msgid "Language:" 809 | msgstr "" 810 | 811 | msgctxt "#32311" 812 | msgid "Sharing and Remote Access" 813 | msgstr "" 814 | 815 | msgctxt "#32312" 816 | msgid "@DISTRONAME@ also supports SSH for remote access. This is for advanced users who wish to interact with @DISTRONAME@'s underlying operating system. The default user is [COLOR blue]root[/COLOR] and the default password is [COLOR blue]@ROOT_PASSWORD@[/COLOR]." 817 | msgstr "" 818 | 819 | msgctxt "#32313" 820 | msgid "In order to share your files between your computers @DISTRONAME@ has incorporated a samba server. This samba server can be integrated into your local network by accessing it in a familiar way with Finder or Windows Explorer." 821 | msgstr "" 822 | 823 | msgctxt "#32316" 824 | msgid "Configure Services:" 825 | msgstr "" 826 | 827 | msgctxt "#32317" 828 | msgid "Thank you" 829 | msgstr "" 830 | 831 | msgctxt "#32318" 832 | msgid "Your @DISTRONAME@ installation is now complete and you are about to enter Kodi where you can set up your media libraries. For help with this, there is a guide available at wiki.kodi.tv[CR][CR]@DISTRONAME@ is developed by a dedicated team of developers who work purely in their spare time. Even as a volunteer project, we still have to pay for our internet bandwidth and development resources so if you find @DISTRONAME@ useful we will always appreciate a donation of any amount.[CR][CR]Finally, we hope you enjoy using @DISTRONAME@ as much as we've enjoyed building it. If you need any more help, you can find links to our support forum along with latest news at our website." 833 | msgstr "" 834 | 835 | msgctxt "#32319" 836 | msgid "Cron" 837 | msgstr "" 838 | 839 | msgctxt "#32320" 840 | msgid "Enable Cron" 841 | msgstr "" 842 | 843 | msgctxt "#32323" 844 | msgid "Reset to Defaults" 845 | msgstr "" 846 | 847 | msgctxt "#32324" 848 | msgid "Reset System Settings to defaults" 849 | msgstr "" 850 | 851 | msgctxt "#32325" 852 | msgid "Reset @DISTRONAME@ to defaults" 853 | msgstr "" 854 | 855 | msgctxt "#32326" 856 | msgid "Are you sure?" 857 | msgstr "" 858 | 859 | msgctxt "#32328" 860 | msgid "The system must reboot." 861 | msgstr "" 862 | 863 | msgctxt "#32329" 864 | msgid "Rebooting in %d seconds" 865 | msgstr "" 866 | 867 | msgctxt "#32330" 868 | msgid "Keyboard Type" 869 | msgstr "" 870 | 871 | msgctxt "#32331" 872 | msgid "Bluetooth" 873 | msgstr "" 874 | 875 | msgctxt "#32333" 876 | msgid "Connected: " 877 | msgstr "" 878 | 879 | msgctxt "#32334" 880 | msgid "Yes" 881 | msgstr "" 882 | 883 | msgctxt "#32335" 884 | msgid "No" 885 | msgstr "" 886 | 887 | msgctxt "#32338" 888 | msgid "No Bluetooth adapter found." 889 | msgstr "" 890 | 891 | msgctxt "#32339" 892 | msgid "No Bluetooth device found. Please put your Bluetooth device into discovery mode[CR]and start the scan" 893 | msgstr "" 894 | 895 | msgctxt "#32343" 896 | msgid "Enter the following Key on your Device." 897 | msgstr "" 898 | 899 | msgctxt "#32344" 900 | msgid "Enable Bluetooth" 901 | msgstr "" 902 | 903 | msgctxt "#32346" 904 | msgid "Bluetooth is disabled" 905 | msgstr "" 906 | 907 | msgctxt "#32358" 908 | msgid "Trust and Connect" 909 | msgstr "" 910 | 911 | msgctxt "#32362" 912 | msgid "Check for updates now:" 913 | msgstr "" 914 | 915 | msgctxt "#32363" 916 | msgid "@DISTRONAME@" 917 | msgstr "" 918 | 919 | msgctxt "#32364" 920 | msgid "Update available" 921 | msgstr "" 922 | 923 | msgctxt "#32365" 924 | msgid "Show Update Notifications" 925 | msgstr "" 926 | 927 | msgctxt "#32366" 928 | msgid "Update Download Completed" 929 | msgstr "" 930 | 931 | msgctxt "#32367" 932 | msgid "Update Extract Complete" 933 | msgstr "" 934 | 935 | msgctxt "#32368" 936 | msgid "Advanced Network Settings" 937 | msgstr "" 938 | 939 | msgctxt "#32369" 940 | msgid "Wait for network before starting Kodi" 941 | msgstr "" 942 | 943 | msgctxt "#32370" 944 | msgid "Maximum Wait Time (Sec.)" 945 | msgstr "" 946 | 947 | msgctxt "#32371" 948 | msgid "Backup" 949 | msgstr "" 950 | 951 | msgctxt "#32372" 952 | msgid "Create System and Kodi Backup" 953 | msgstr "" 954 | 955 | msgctxt "#32373" 956 | msgid "Restore Backup" 957 | msgstr "" 958 | 959 | msgctxt "#32374" 960 | msgid "Error: Invalid backup file. Filename must end in: .tar, .tar.gz, .tar.bz2, or .tar.xz" 961 | msgstr "" 962 | 963 | msgctxt "#32375" 964 | msgid "Backup Progress" 965 | msgstr "" 966 | 967 | msgctxt "#32376" 968 | msgid "Submit Log" 969 | msgstr "" 970 | 971 | msgctxt "#32377" 972 | msgid "Upload latest Kodi log and configs, and view the short URL" 973 | msgstr "" 974 | 975 | msgctxt "#32378" 976 | msgid "Upload latest Kodi crash log and configs, and view the short URL" 977 | msgstr "" 978 | 979 | msgctxt "#32379" 980 | msgid "There is not enough free storage space to continue!" 981 | msgstr "" 982 | 983 | msgctxt "#32380" 984 | msgid "Restoring system settings requires a reboot. Are you sure you want to restore?" 985 | msgstr "" 986 | 987 | msgctxt "#32381" 988 | msgid "Accept incoming Bluetooth Filetransfer ?" 989 | msgstr "" 990 | 991 | msgctxt "#32382" 992 | msgid "Speed" 993 | msgstr "" 994 | 995 | msgctxt "#32383" 996 | msgid "Play Files ?" 997 | msgstr "" 998 | 999 | msgctxt "#32384" 1000 | msgid "OBEX Enabled" 1001 | msgstr "" 1002 | 1003 | msgctxt "#32385" 1004 | msgid "OBEX Upload Folder" 1005 | msgstr "" 1006 | 1007 | msgctxt "#32386" 1008 | msgid "Keyboard Layout Variant #1" 1009 | msgstr "" 1010 | 1011 | msgctxt "#32387" 1012 | msgid "Keyboard Layout Variant #2" 1013 | msgstr "" 1014 | 1015 | msgctxt "#32388" 1016 | msgid "Enable Standby" 1017 | msgstr "" 1018 | 1019 | msgctxt "#32389" 1020 | msgid "Disable Standby" 1021 | msgstr "" 1022 | 1023 | msgctxt "#32390" 1024 | msgid "Settings addon is not yet ready, please try again later." 1025 | msgstr "" 1026 | 1027 | msgctxt "#32393" 1028 | msgid "** SAFE MODE! ** SAFE MODE! ** SAFE MODE! **" 1029 | msgstr "" 1030 | 1031 | msgctxt "#32394" 1032 | msgid "@DISTRONAME@ has temporarily started in safe mode due to repeated Kodi crashes.[CR][CR]You may now investigate the cause of the crashes by enabling ssh or Samba.[CR][CR]Your original Kodi installation can be accessed via \"/storage/.kodi.FAILED\" and the Samba \"Kodi-Failed\" share.[CR][CR]Rebooting will return to your original Kodi installation.[CR][CR]Go to https://forum.libreelec.tv if you require further assistance. When posting to the forum include the link to your crash log which can be viewed by clicking the crash log option in Settings > LibreELEC > System > Submit Log." 1033 | msgstr "" 1034 | 1035 | msgctxt "#32395" 1036 | msgid "Firewall" 1037 | msgstr "" 1038 | 1039 | msgctxt "#32396" 1040 | msgid "Custom" 1041 | msgstr "" 1042 | 1043 | msgctxt "#32397" 1044 | msgid "Off" 1045 | msgstr "" 1046 | 1047 | msgctxt "#32398" 1048 | msgid "Home" 1049 | msgstr "" 1050 | 1051 | msgctxt "#32399" 1052 | msgid "Public" 1053 | msgstr "" 1054 | 1055 | msgctxt "#32400" 1056 | msgid "Idle Timeout" 1057 | msgstr "" 1058 | 1059 | msgctxt "#32401" 1060 | msgid "Syncing Disks..." 1061 | msgstr "" 1062 | 1063 | msgctxt "#32402" 1064 | msgid "Backup is incomplete. An error occurred. See Kodi log for details." 1065 | msgstr "" 1066 | 1067 | msgctxt "#32410" 1068 | msgid "Logging" 1069 | msgstr "" 1070 | 1071 | msgctxt "#32411" 1072 | msgid "Use Persistent Logs" 1073 | msgstr "" 1074 | 1075 | msgctxt "#32412" 1076 | msgid "Use Persistent Journal and Log files instead of default volatile ones. Reboot to apply changes." 1077 | msgstr "" 1078 | 1079 | msgctxt "#32413" 1080 | msgid "Journal Size" 1081 | msgstr "" 1082 | 1083 | msgctxt "#32414" 1084 | msgid "Set max. size of persistent journal. Reboot to apply changes." 1085 | msgstr "" 1086 | 1087 | msgctxt "#32415" 1088 | msgid "Disable journald Rate Limit" 1089 | msgstr "" 1090 | 1091 | msgctxt "#32416" 1092 | msgid "Reboot to apply changes." 1093 | msgstr "" 1094 | 1095 | msgctxt "#32417" 1096 | msgid "Sending..." 1097 | msgstr "" 1098 | 1099 | msgctxt "#32418" 1100 | msgid "Log files sent to:" 1101 | msgstr "" 1102 | 1103 | msgctxt "#32419" 1104 | msgid "Error sending log files. Please try again." 1105 | msgstr "" 1106 | 1107 | msgctxt "#32420" 1108 | msgid "Timezone" 1109 | msgstr "" 1110 | -------------------------------------------------------------------------------- /resources/lib/config.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # Copyright (C) 2020-present Team LibreELEC (https://libreelec.tv) 3 | 4 | import os 5 | 6 | import os_tools 7 | 8 | 9 | OS_RELEASE = os_tools.read_shell_settings('/etc/os-release') 10 | 11 | CONFIG_CACHE = os.environ.get('CONFIG_CACHE', '/storage/.cache') 12 | USER_CONFIG = os.environ.get('USER_CONFIG', '/storage/.config') 13 | 14 | HOSTNAME = os.path.join(CONFIG_CACHE, 'hostname') 15 | HOSTS_CONF = os.path.join(USER_CONFIG, 'hosts.conf') 16 | 17 | REGDOMAIN_CONF = os.path.join(CONFIG_CACHE, 'regdomain.conf') 18 | SETREGDOMAIN = '/usr/lib/iw/setregdomain' 19 | 20 | TIMEZONE = os.path.join(CONFIG_CACHE, 'timezone') 21 | -------------------------------------------------------------------------------- /resources/lib/dbus_bluez.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # Copyright (C) 2020-present Team LibreELEC (https://libreelec.tv) 3 | 4 | import dbussy 5 | import ravel 6 | 7 | import dbus_utils 8 | 9 | 10 | BUS_NAME = 'org.bluez' 11 | ERROR_REJECTED = 'org.bluez.Error.Rejected' 12 | INTERFACE_ADAPTER = 'org.bluez.Adapter1' 13 | INTERFACE_AGENT = 'org.bluez.Agent1' 14 | INTERFACE_AGENT_MANAGER = 'org.bluez.AgentManager1' 15 | INTERFACE_DEVICE = 'org.bluez.Device1' 16 | PATH_BLUEZ = '/org/bluez' 17 | PATH_AGENT = '/kodi/agent/bluez' 18 | 19 | 20 | @ravel.interface(ravel.INTERFACE.SERVER, name=INTERFACE_AGENT) 21 | class Agent(dbus_utils.Agent): 22 | 23 | def __init__(self): 24 | super().__init__(BUS_NAME, PATH_AGENT) 25 | 26 | def manager_register_agent(self): 27 | dbus_utils.call_method(BUS_NAME, PATH_BLUEZ, INTERFACE_AGENT_MANAGER, 28 | 'RegisterAgent', PATH_AGENT, 'KeyboardDisplay') 29 | 30 | def manager_unregister_agent(self): 31 | dbus_utils.call_method(BUS_NAME, PATH_BLUEZ, INTERFACE_AGENT_MANAGER, 32 | 'UnregisterAgent', PATH_AGENT) 33 | 34 | @ravel.method( 35 | in_signature='os', 36 | out_signature='', 37 | arg_keys=['device', 'uuid'] 38 | ) 39 | def AuthorizeService(self, device, uuid): 40 | self.authorize_service(device, uuid) 41 | 42 | @ravel.method( 43 | in_signature='', 44 | out_signature='' 45 | ) 46 | def Cancel(self): 47 | self.cancel() 48 | 49 | @ravel.method( 50 | in_signature='ouq', 51 | out_signature='', 52 | arg_keys=['device', 'passkey', 'entered'] 53 | ) 54 | def DisplayPasskey(self, device, passkey, entered): 55 | self.display_passkey(device, passkey, entered) 56 | 57 | @ravel.method( 58 | in_signature='os', 59 | out_signature='', 60 | arg_keys=['device', 'pincode'] 61 | ) 62 | def DisplayPinCode(self, device, pincode): 63 | self.display_pincode(device, pincode) 64 | 65 | @ravel.method( 66 | in_signature='', 67 | out_signature='' 68 | ) 69 | def Release(self): 70 | raise NotImplementedError 71 | 72 | @ravel.method( 73 | in_signature='o', 74 | out_signature='', 75 | arg_keys=['device'] 76 | ) 77 | def RequestAuthorization(self, device): 78 | self.request_authorization(device) 79 | 80 | @ravel.method( 81 | in_signature='ou', 82 | out_signature='', 83 | arg_keys=['device', 'passkey'] 84 | ) 85 | def RequestConfirmation(self, device, passkey): 86 | self.request_confirmation(device, passkey) 87 | 88 | @ravel.method( 89 | in_signature='o', 90 | out_signature='u', 91 | arg_keys=['device'], 92 | result_keyword='reply' 93 | ) 94 | def RequestPasskey(self, device): 95 | passkey = self.request_passkey(device) 96 | reply[0] = (dbussy.DBUS.Signature('u'), passkey) 97 | 98 | @ravel.method( 99 | in_signature='o', 100 | out_signature='s', 101 | arg_keys=['device'], 102 | result_keyword='reply' 103 | ) 104 | def RequestPinCode(self, device, reply): 105 | pincode = self.request_pincode(device) 106 | reply[0] = (dbussy.DBUS.Signature('s'), pincode) 107 | 108 | def reject(self, message): 109 | raise dbussy.DBusError(ERROR_REJECTED, message) 110 | 111 | class Listener(object): 112 | 113 | def __init__(self): 114 | dbus_utils.BUS.listen_objects_added(func=self._on_interfaces_added) 115 | dbus_utils.BUS.listen_objects_removed(func=self._on_interfaces_removed) 116 | dbus_utils.BUS.listen_propchanged( 117 | interface=dbussy.DBUS.INTERFACE_PROPERTIES, 118 | fallback=True, 119 | func=self._on_properties_changed, 120 | path='/') 121 | 122 | @ravel.signal(name='InterfacesAdded', in_signature='oa{sa{sv}}', arg_keys=('path', 'interfaces')) 123 | def _on_interfaces_added(self, path, interfaces): 124 | interfaces = dbus_utils.convert_from_dbussy(interfaces) 125 | self.on_interfaces_added(path, interfaces) 126 | 127 | @ravel.signal(name='InterfacesRemoved', in_signature='oas', arg_keys=('path', 'interfaces')) 128 | def _on_interfaces_removed(self, path, interfaces): 129 | interfaces = dbus_utils.convert_from_dbussy(interfaces) 130 | self.on_interfaces_removed(path, interfaces) 131 | 132 | @ravel.signal(name='PropertiesChanged', in_signature='sa{sv}as', arg_keys=('interface', 'changed', 'invalidated'), path_keyword='path') 133 | def _on_properties_changed(self, interface, changed, invalidated, path): 134 | interface = dbus_utils.convert_from_dbussy(interface) 135 | changed = dbus_utils.convert_from_dbussy(changed) 136 | invalidated = dbus_utils.convert_from_dbussy(invalidated) 137 | self.on_properties_changed(interface, changed, invalidated, path) 138 | 139 | def get_managed_objects(): 140 | return dbus_utils.call_method(BUS_NAME, '/', dbussy.DBUSX.INTERFACE_OBJECT_MANAGER, 'GetManagedObjects') 141 | 142 | 143 | def adapter_get_property(path, name): 144 | return dbus_utils.call_method(BUS_NAME, path, dbussy.DBUS.INTERFACE_PROPERTIES, 'Get', INTERFACE_ADAPTER, name) 145 | 146 | 147 | def adapter_get_powered(path): 148 | return adapter_get_property(path, 'Powered') 149 | 150 | 151 | def adapter_remove_device(path, device): 152 | return dbus_utils.call_method(BUS_NAME, path, INTERFACE_ADAPTER, 'RemoveDevice', device) 153 | 154 | 155 | def adapter_set_property(path, name, value): 156 | return dbus_utils.call_method(BUS_NAME, path, dbussy.DBUS.INTERFACE_PROPERTIES, 'Set', INTERFACE_ADAPTER, name, value) 157 | 158 | 159 | def adapter_set_alias(path, alias): 160 | return adapter_set_property(path, 'Alias', (dbussy.DBUS.Signature('s'), alias)) 161 | 162 | 163 | def adapter_set_powered(path, powered): 164 | return adapter_set_property(path, 'Powered', (dbussy.DBUS.Signature('b'), powered)) 165 | 166 | 167 | def adapter_start_discovery(path): 168 | return dbus_utils.call_method(BUS_NAME, path, INTERFACE_ADAPTER, 'StartDiscovery') 169 | 170 | 171 | def adapter_stop_discovery(path): 172 | return dbus_utils.call_method(BUS_NAME, path, INTERFACE_ADAPTER, 'StopDiscovery') 173 | 174 | 175 | def device_get_property(path, name): 176 | return dbus_utils.call_method(BUS_NAME, path, dbussy.DBUS.INTERFACE_PROPERTIES, 'Get', INTERFACE_DEVICE, name) 177 | 178 | 179 | def device_get_connected(path): 180 | return device_get_property(path, 'Connected') 181 | 182 | 183 | def device_connect(path): 184 | return dbus_utils.run_method(BUS_NAME, path, INTERFACE_DEVICE, 'Connect') 185 | 186 | 187 | def device_disconnect(path): 188 | return dbus_utils.call_method(BUS_NAME, path, INTERFACE_DEVICE, 'Disconnect') 189 | 190 | 191 | def device_pair(path): 192 | return dbus_utils.run_method(BUS_NAME, path, INTERFACE_DEVICE, 'Pair') 193 | 194 | 195 | def device_set_property(path, name, value): 196 | return dbus_utils.call_method(BUS_NAME, path, dbussy.DBUS.INTERFACE_PROPERTIES, 'Set', INTERFACE_DEVICE, name, value) 197 | 198 | 199 | def device_set_trusted(path, trusted): 200 | return device_set_property(path, 'Trusted', (dbussy.DBUS.Signature('b'), trusted)) 201 | 202 | 203 | def find_adapter(): 204 | if system_has_bluez(): 205 | objects = get_managed_objects() 206 | for path, interfaces in objects.items(): 207 | if interfaces.get(INTERFACE_ADAPTER): 208 | return path 209 | 210 | 211 | def find_devices(): 212 | devices = {} 213 | objects = get_managed_objects() 214 | for path, interfaces in objects.items(): 215 | if interfaces.get(INTERFACE_DEVICE): 216 | devices[path] = interfaces[INTERFACE_DEVICE] 217 | return devices 218 | 219 | 220 | def system_has_bluez(): 221 | return BUS_NAME in dbus_utils.list_names() 222 | -------------------------------------------------------------------------------- /resources/lib/dbus_connman.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # Copyright (C) 2020-present Team LibreELEC (https://libreelec.tv) 3 | 4 | import dbussy 5 | import ravel 6 | 7 | import dbus_utils 8 | import log 9 | 10 | 11 | BUS_NAME = 'net.connman' 12 | ERROR_AGENT_CANCELLED = 'net.connman.Agent.Error.Canceled' 13 | INTERFACE_AGENT = 'net.connman.Agent' 14 | INTERFACE_CLOCK = 'net.connman.Clock' 15 | INTERFACE_MANAGER = 'net.connman.Manager' 16 | INTERFACE_SERVICE = 'net.connman.Service' 17 | INTERFACE_TECHNOLOGY = 'net.connman.Technology' 18 | PATH_TECH_ETHERNET = '/net/connman/technology/ethernet' 19 | PATH_TECH_WIFI = '/net/connman/technology/wifi' 20 | PATH_AGENT = '/kodi/agent/connman' 21 | 22 | 23 | @ravel.interface(ravel.INTERFACE.SERVER, name=INTERFACE_AGENT) 24 | class Agent(dbus_utils.Agent): 25 | 26 | @log.log_function() 27 | def __init__(self): 28 | super().__init__(BUS_NAME, PATH_AGENT) 29 | 30 | def manager_register_agent(self): 31 | dbus_utils.call_method( 32 | BUS_NAME, '/', INTERFACE_MANAGER, 'RegisterAgent', PATH_AGENT) 33 | 34 | def manager_unregister_agent(self): 35 | dbus_utils.call_method( 36 | BUS_NAME, '/', INTERFACE_MANAGER, 'UnregisterAgent', PATH_AGENT 37 | ) 38 | 39 | @ravel.method( 40 | in_signature='', 41 | out_signature='' 42 | ) 43 | def Cancel(self): 44 | self.cancel() 45 | 46 | def cancel(self): 47 | pass 48 | 49 | @ravel.method( 50 | in_signature='', 51 | out_signature='' 52 | ) 53 | def Release(self): 54 | pass 55 | 56 | @ravel.method( 57 | in_signature='os', 58 | out_signature='', 59 | arg_keys=['path', 'error'] 60 | ) 61 | async def ReportError(self, path, error): 62 | self.report_error(path, error) 63 | 64 | @ravel.method( 65 | in_signature='os', 66 | out_signature='', 67 | arg_keys=['service', 'url'] 68 | ) 69 | def RequestBrowser(self, path, url): 70 | raise NotImplementedError 71 | 72 | @ravel.method( 73 | in_signature='oa{sv}', 74 | out_signature='a{sv}', 75 | args_keyword='request', 76 | result_keyword='reply' 77 | ) 78 | async def RequestInput(self, request, reply): 79 | request = dbus_utils.convert_from_dbussy(request) 80 | input = self.request_input(*request) 81 | input = {k: (dbussy.DBUS.Signature('s'), v) 82 | for (k, v) in input.items()} 83 | reply[0] = input 84 | 85 | def agent_abort(self): 86 | raise ravel.ErrorReturn(ERROR_AGENT_CANCELLED, 'Input cancelled') 87 | 88 | 89 | class Listener(object): 90 | 91 | def __init__(self): 92 | dbus_utils.BUS.listen_signal( 93 | interface=INTERFACE_MANAGER, 94 | fallback=True, 95 | func=self._on_property_changed, 96 | path='/', 97 | name='PropertyChanged') 98 | dbus_utils.BUS.listen_signal( 99 | interface=INTERFACE_MANAGER, 100 | fallback=True, 101 | func=self._on_services_changed, 102 | path='/', 103 | name='ServicesChanged') 104 | dbus_utils.BUS.listen_signal( 105 | interface=INTERFACE_SERVICE, 106 | fallback=True, 107 | func=self._on_property_changed, 108 | path='/', 109 | name='PropertyChanged') 110 | dbus_utils.BUS.listen_signal( 111 | interface=INTERFACE_TECHNOLOGY, 112 | fallback=True, 113 | func=self._on_technology_changed, 114 | path='/', 115 | name='PropertyChanged') 116 | 117 | @ravel.signal(name='PropertyChanged', in_signature='sv', arg_keys=('name', 'value'), path_keyword='path') 118 | async def _on_property_changed(self, name, value, path): 119 | value = dbus_utils.convert_from_dbussy(value) 120 | await self.on_property_changed(name, value, path) 121 | 122 | @ravel.signal(name='ServicesChanged', in_signature='a(oa{sv})ao', arg_keys=('services', 'removed')) 123 | async def _on_services_changed(self, services, removed): 124 | services = dbus_utils.convert_from_dbussy(services) 125 | removed = dbus_utils.convert_from_dbussy(removed) 126 | await self.on_services_changed(services, removed) 127 | 128 | @ravel.signal(name='PropertyChanged', in_signature='sv', arg_keys=('name', 'value'), path_keyword='path') 129 | async def _on_technology_changed(self, name, value, path): 130 | value = dbus_utils.convert_from_dbussy(value) 131 | await self.on_technology_changed(name, value, path) 132 | 133 | 134 | def clock_get_properties(): 135 | return dbus_utils.call_method(BUS_NAME, '/', INTERFACE_CLOCK, 'GetProperties') 136 | 137 | 138 | def clock_set_timeservers(timeservers): 139 | return dbus_utils.call_method(BUS_NAME, '/', INTERFACE_CLOCK, 'SetProperty', 'Timeservers', (dbussy.DBUS.Signature('as'), timeservers)) 140 | 141 | 142 | def manager_get_properties(): 143 | return dbus_utils.call_method(BUS_NAME, '/', INTERFACE_MANAGER, 'GetProperties') 144 | 145 | 146 | def manager_get_services(): 147 | return dbus_utils.call_method(BUS_NAME, '/', INTERFACE_MANAGER, 'GetServices') 148 | 149 | 150 | def manager_get_technologies(): 151 | return dbus_utils.call_method(BUS_NAME, '/', INTERFACE_MANAGER, 'GetTechnologies') 152 | 153 | 154 | def service_connect(path): 155 | return dbus_utils.run_method(BUS_NAME, path, INTERFACE_SERVICE, 'Connect') 156 | 157 | 158 | def service_disconnect(path): 159 | return dbus_utils.call_method(BUS_NAME, path, INTERFACE_SERVICE, 'Disconnect') 160 | 161 | 162 | def service_get_properties(path): 163 | return dbus_utils.call_method(BUS_NAME, path, INTERFACE_SERVICE, 'GetProperties') 164 | 165 | 166 | def service_remove(path): 167 | return dbus_utils.call_method(BUS_NAME, path, INTERFACE_SERVICE, 'Remove') 168 | 169 | 170 | def service_set_autoconnect(path, autoconnect): 171 | autoconnect = True if autoconnect == '1' else False 172 | return service_set_property(path, 'AutoConnect', (dbussy.DBUS.Signature('b'), autoconnect)) 173 | 174 | 175 | def service_set_domains_configuration(path, domains): 176 | return service_set_property(path, 'Domains.Configuration', (dbussy.DBUS.Signature('as'), domains)) 177 | 178 | 179 | def service_set_ipv4_configuration(path, ipv4): 180 | return service_set_property(path, 'IPv4.Configuration', (dbussy.DBUS.Signature('a{sv}'), {key: (dbussy.DBUS.Signature('s'), value) for key, value in ipv4.items()})) 181 | 182 | 183 | def service_set_ipv6_configuration(path, ipv6): 184 | return service_set_property(path, 'IPv6.Configuration', (dbussy.DBUS.Signature('a{sv}'), {key: (dbussy.DBUS.Signature('y'), int(value)) if key == 'PrefixLength' else (dbussy.DBUS.Signature('s'), value) for key, value in ipv6.items()})) 185 | 186 | 187 | def service_set_nameservers_configuration(path, nameservers): 188 | return service_set_property(path, 'Nameservers.Configuration', (dbussy.DBUS.Signature('as'), nameservers)) 189 | 190 | 191 | def service_set_property(path, name, value): 192 | return dbus_utils.call_method(BUS_NAME, path, INTERFACE_SERVICE, 'SetProperty', name, value) 193 | 194 | 195 | def service_set_timeservers_configuration(path, timeservers): 196 | return service_set_property(path, 'Timeservers.Configuration', (dbussy.DBUS.Signature('as'), timeservers)) 197 | 198 | 199 | def technology_set_powered(path, state): 200 | return technology_set_property(path, 'Powered', (dbussy.DBUS.Signature('b'), state)) 201 | 202 | 203 | def technology_set_property(path, name, value): 204 | return dbus_utils.call_method(BUS_NAME, path, INTERFACE_TECHNOLOGY, 'SetProperty', name, value) 205 | 206 | 207 | def technology_wifi_scan(): 208 | return dbus_utils.call_method(BUS_NAME, PATH_TECH_WIFI, INTERFACE_TECHNOLOGY, 'Scan') 209 | 210 | 211 | def technology_wifi_set_tethering(state): 212 | return technology_set_property(PATH_TECH_WIFI, 'Tethering', (dbussy.DBUS.Signature('b'), state)) 213 | 214 | 215 | def technology_wifi_set_tethering_identifier(identifier): 216 | return technology_set_property(PATH_TECH_WIFI, 'TetheringIdentifier', (dbussy.DBUS.Signature('s'), identifier)) 217 | 218 | 219 | def technology_wifi_set_tethering_passphrase(passphrase): 220 | return technology_set_property(PATH_TECH_WIFI, 'TetheringPassphrase', (dbussy.DBUS.Signature('s'), passphrase)) 221 | -------------------------------------------------------------------------------- /resources/lib/dbus_obex.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # Copyright (C) 2020-present Team LibreELEC (https://libreelec.tv) 3 | 4 | import dbussy 5 | import ravel 6 | 7 | import dbus_utils 8 | 9 | 10 | BUS_NAME = 'org.bluez.obex' 11 | ERROR_REJECTED = 'org.bluez.Error.Rejected' 12 | INTERFACE_AGENT = 'org.bluez.obex.Agent1' 13 | INTERFACE_AGENT_MANAGER = 'org.bluez.obex.AgentManager1' 14 | INTERFACE_TRANSFER = 'org.bluez.obex.Transfer1' 15 | PATH_OBEX = '/org/bluez/obex' 16 | PATH_AGENT = '/kodi/agent/obex' 17 | 18 | @ravel.interface(ravel.INTERFACE.SERVER, name=INTERFACE_AGENT) 19 | class Agent(dbus_utils.Agent): 20 | 21 | def __init__(self): 22 | super().__init__(BUS_NAME, PATH_AGENT) 23 | 24 | def manager_register_agent(self): 25 | dbus_utils.call_method( 26 | BUS_NAME, PATH_OBEX, INTERFACE_AGENT_MANAGER, 'RegisterAgent', PATH_AGENT) 27 | 28 | @ravel.method( 29 | in_signature='', 30 | out_signature='' 31 | ) 32 | def Release(self): 33 | pass 34 | 35 | @ravel.method( 36 | in_signature='o', 37 | out_signature='s', 38 | arg_keys=['path'], 39 | result_keyword='reply' 40 | ) 41 | def AuthorizePush(self, transfer): 42 | name = self.authorize_push(transfer) 43 | reply[0] = (dbussy.DBUS.Signature('s'), name) 44 | 45 | @ravel.method( 46 | in_signature='', 47 | out_signature='' 48 | ) 49 | def Cancel(self): 50 | pass 51 | 52 | def reject(self, message): 53 | raise dbussy.DBusError(ERROR_REJECTED, message) 54 | 55 | class Listener(object): 56 | 57 | def __init__(self): 58 | pass 59 | # dbussy doesn't currenltly support listening for non specific signals 60 | # dbus_utils.BUS.listen_signal( 61 | # interface=INTERFACE_TRANSFER, 62 | # fallback=True, 63 | # func=self._on_transfer_changed, 64 | # path='/') 65 | 66 | # @ravel.signal(name='PropertiesChanged', in_signature='sa{sv}as', arg_keys=('interface', 'changed', 'invalidated'), path_keyword='path', bus_keyword=BUS_NAME) 67 | # async def _on_transfer_changed(self, interface, changed, invalidated, path): 68 | # interface = dbus_utils.convert_from_dbussy(interface) 69 | # changed = dbus_utils.convert_from_dbussy(changed) 70 | # invalidated = dbus_utils.convert_from_dbussy(invalidated) 71 | # await self.on_transfer_changed(interface, changed, invalidated, path) 72 | 73 | def transfer_get_all_properties(path): 74 | return dbus_utils.call_method(BUS_NAME, path, dbussy.DBUS.INTERFACE_PROPERTIES, 'GetAll', INTERFACE_TRANSFER) 75 | -------------------------------------------------------------------------------- /resources/lib/dbus_utils.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # Copyright (C) 2020-present Team LibreELEC (https://libreelec.tv) 3 | 4 | import asyncio 5 | import threading 6 | 7 | import dbussy 8 | import ravel 9 | 10 | import log 11 | 12 | 13 | BUS_NAME = '' 14 | INTERFACE_AGENT = '' 15 | PATH_AGENT = '' 16 | 17 | 18 | class Agent(object): 19 | 20 | def __init__(self, bus_name, path_agent): 21 | self.bus_name = bus_name 22 | self.path_agent = path_agent 23 | if self.bus_name in list_names(): 24 | self.register_agent() 25 | self.watch_name() 26 | 27 | @log.log_function() 28 | def watch_name(self): 29 | BUS.listen_signal( 30 | interface=dbussy.DBUS.SERVICE_DBUS, 31 | fallback=True, 32 | func=self.on_name_owner_changed, 33 | path='/', 34 | name='NameOwnerChanged') 35 | 36 | @ravel.signal(name='NameOwnerChanged', in_signature='sss', arg_keys=('name', 'old_owner', 'new_owner')) 37 | async def on_name_owner_changed(self, name, old_owner, new_owner): 38 | if name == self.bus_name and new_owner != '': 39 | self.register_agent() 40 | 41 | @log.log_function() 42 | def register_agent(self): 43 | BUS.request_name( 44 | self.bus_name, flags=dbussy.DBUS.NAME_FLAG_DO_NOT_QUEUE) 45 | BUS.register( 46 | path=self.path_agent, interface=self, fallback=True) 47 | self.manager_register_agent() 48 | 49 | @log.log_function() 50 | def unregister_agent(self): 51 | self.manager_unregister_agent() 52 | BUS.unregister(path=self.path_agent) 53 | 54 | def manager_register_agent(self): 55 | pass 56 | 57 | def manager_unregister_agent(self): 58 | pass 59 | 60 | 61 | class Bool(int): 62 | 63 | def __new__(cls, value): 64 | return int.__new__(cls, bool(value)) 65 | 66 | def __str__(self): 67 | return '1' if self == True else '0' 68 | 69 | 70 | class LoopThread(threading.Thread): 71 | 72 | def __init__(self, loop): 73 | super().__init__() 74 | self.loop = loop 75 | self.is_stopped = False 76 | 77 | @log.log_function() 78 | async def wait(self): 79 | while not self.is_stopped: 80 | await asyncio.sleep(1) 81 | 82 | @log.log_function() 83 | def run(self): 84 | self.loop.run_until_complete(self.wait()) 85 | self.loop.close() 86 | 87 | @log.log_function() 88 | def stop(self): 89 | self.is_stopped = True 90 | self.join() 91 | 92 | 93 | def list_names(): 94 | return BUS[dbussy.DBUS.SERVICE_DBUS]['/'].get_interface(dbussy.DBUS.INTERFACE_DBUS).ListNames()[0] 95 | 96 | 97 | def convert_from_dbussy(data): 98 | if isinstance(data, bool): 99 | return Bool(data) 100 | if isinstance(data, dict): 101 | return {key: convert_from_dbussy(data[key]) for key in data.keys()} 102 | if isinstance(data, list): 103 | return [convert_from_dbussy(item) for item in data] 104 | if isinstance(data, tuple) and isinstance(data[0], dbussy.DBUS.Signature): 105 | return convert_from_dbussy(data[1]) 106 | return data 107 | 108 | 109 | def call_method(bus_name, path, interface, method_name, *args, **kwargs): 110 | interface = BUS[bus_name][path].get_interface(interface) 111 | method = getattr(interface, method_name) 112 | result = method(*args, **kwargs) 113 | first = next(iter(result or []), None) 114 | return convert_from_dbussy(first) 115 | 116 | 117 | async def call_async_method(bus_name, path, interface, method_name, *args, **kwargs): 118 | interface = await BUS[bus_name][path].get_async_interface(interface) 119 | method = getattr(interface, method_name) 120 | result = await method(*args, **kwargs) 121 | first = next(iter(result or []), None) 122 | return convert_from_dbussy(first) 123 | 124 | 125 | def run_method(bus_name, path, interface, method_name, *args, **kwargs): 126 | future = asyncio.run_coroutine_threadsafe(call_async_method( 127 | bus_name, path, interface, method_name, *args, **kwargs), LOOP) 128 | return future.result() 129 | 130 | 131 | try: 132 | LOOP = asyncio.get_running_loop() 133 | except RuntimeError: 134 | LOOP = asyncio.new_event_loop() 135 | asyncio.set_event_loop(LOOP) 136 | 137 | BUS = ravel.system_bus() 138 | BUS.attach_asyncio(LOOP) 139 | LOOP_THREAD = LoopThread(LOOP) 140 | -------------------------------------------------------------------------------- /resources/lib/debug_utils.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # Copyright (C) 2020-present Team LibreELEC (https://libreelec.tv) 3 | 4 | import inspect 5 | import sys 6 | from contextlib import contextmanager 7 | from platform import uname 8 | from pprint import pformat 9 | from typing import List, Iterable, Tuple, Any, Optional 10 | 11 | import xbmc 12 | 13 | 14 | def _logger(message): 15 | xbmc.log(message, xbmc.LOGERROR) 16 | 17 | 18 | def format_vars(variables: List[Tuple[str, Any]]) -> str: 19 | """ 20 | Format variables dictionary 21 | 22 | :param variables: variables list 23 | :return: formatted string with sorted ``var = val`` pairs 24 | """ 25 | var_list = [(var, val) for var, val in variables 26 | if not (var.startswith('__') and var.endswith('__'))] 27 | var_list.sort(key=lambda i: i[0]) 28 | lines = [] 29 | for var, val in var_list: 30 | lines.append('{} = {}'.format(var, pformat(val, indent=4))) 31 | return '\n'.join(lines) 32 | 33 | 34 | def _format_code_context(code_context: List[str], lineno: int, index: int) -> str: 35 | context = '' 36 | if code_context is not None: 37 | for i, line in enumerate(code_context, lineno - index): 38 | if i == lineno: 39 | context += '{}:>{}'.format(str(i).rjust(5), line) 40 | else: 41 | context += '{}: {}'.format(str(i).rjust(5), line) 42 | return context 43 | 44 | 45 | FRAME_INFO_TEMPLATE = """File: 46 | {file_path}:{lineno} 47 | ---------------------------------------------------------------------------------------------------- 48 | Code context: 49 | {code_context} 50 | ---------------------------------------------------------------------------------------------------- 51 | Local variables: 52 | {local_vars} 53 | ==================================================================================================== 54 | """ 55 | 56 | 57 | def format_frame_info(frame_info: tuple) -> str: 58 | """Get extended information about an execution frame""" 59 | return FRAME_INFO_TEMPLATE.format( 60 | file_path=frame_info[1], 61 | lineno=frame_info[2], 62 | code_context=_format_code_context(frame_info[4], frame_info[2], 63 | frame_info[5]), 64 | local_vars=format_vars(frame_info[0].f_locals.items()) 65 | ) 66 | 67 | 68 | OBJECT_INFO_TEMPLATE = """ 69 | #################################################################################################### 70 | Object info 71 | ---------------------------------------------------------------------------------------------------- 72 | Object : {object} 73 | Object type : {object_type} 74 | Object attributes: 75 | {object_attributes} 76 | ************************************** End of object info ****************************************** 77 | """ 78 | 79 | 80 | def inspect_object(obj): 81 | """Get extended information about an object""" 82 | object_attributes = format_vars(inspect.getmembers(obj)) 83 | object_info_string = OBJECT_INFO_TEMPLATE.format( 84 | object=obj, 85 | object_type=type(obj), 86 | object_attributes=object_attributes 87 | ) 88 | return object_info_string 89 | 90 | 91 | STACK_TRACE_TEMPLATE = """ 92 | #################################################################################################### 93 | Stack Trace 94 | ==================================================================================================== 95 | {stack_trace} 96 | """ 97 | 98 | 99 | def format_stack_trace(frame_stack: Iterable[tuple]) -> str: 100 | """Get extended information about a frame stack""" 101 | stack_trace = '' 102 | for frame_info in frame_stack: 103 | stack_trace += format_frame_info(frame_info) 104 | return STACK_TRACE_TEMPLATE.format(stack_trace=stack_trace) 105 | 106 | 107 | EXCEPTION_TEMPLATE = """ 108 | *********************************** Unhandled exception detected *********************************** 109 | #################################################################################################### 110 | Diagnostic info 111 | ---------------------------------------------------------------------------------------------------- 112 | Exception type : {exc_type} 113 | Exception value : {exc} 114 | System info : {system_info} 115 | Python version : {python_version} 116 | Kodi version : {kodi_version} 117 | sys.argv : {sys_argv} 118 | ---------------------------------------------------------------------------------------------------- 119 | sys.path: 120 | {sys_path} 121 | {stack_trace} 122 | ************************************* End of diagnostic info *************************************** 123 | """ 124 | 125 | 126 | def get_exception_message(exc: Exception, 127 | outer_stack_trace: Optional[List[inspect.FrameInfo]] = None) -> str: 128 | """Get extended information about the currently handled exception""" 129 | stack_trace = inspect.trace(5) 130 | if outer_stack_trace is not None: 131 | stack_trace = outer_stack_trace + stack_trace 132 | stack_trace_string = format_stack_trace(stack_trace) 133 | message = EXCEPTION_TEMPLATE.format( 134 | exc_type=exc.__class__.__name__, 135 | exc=exc, 136 | system_info=uname(), 137 | python_version=sys.version.replace('\n', ' '), 138 | kodi_version=xbmc.getInfoLabel('System.BuildVersion'), 139 | sys_argv=sys.argv, 140 | sys_path=pformat(sys.path), 141 | stack_trace=stack_trace_string 142 | ) 143 | return message 144 | 145 | 146 | @contextmanager 147 | def log_exception(logger_func=_logger): 148 | """ 149 | Diagnostic helper context manager 150 | 151 | It controls execution within its context and writes extended 152 | diagnostic info to the Kodi log if an unhandled exception 153 | happens within the context. The info includes the following items: 154 | 155 | - System info 156 | - Python version 157 | - Kodi version 158 | - Module path. 159 | - Stack trace including: 160 | * File path and line number where the exception happened 161 | * Code fragment where the exception has happened. 162 | * Local variables at the moment of the exception. 163 | 164 | After logging the diagnostic info the exception is re-raised. 165 | 166 | Example:: 167 | 168 | with debug_exception(): 169 | # Some risky code 170 | raise RuntimeError('Fatal error!') 171 | 172 | :param logger_func: logger function that accepts a single argument 173 | that is a log message. 174 | """ 175 | try: 176 | yield 177 | except Exception as exc: 178 | message = get_exception_message(exc) 179 | logger_func(message) 180 | raise exc 181 | -------------------------------------------------------------------------------- /resources/lib/defaults.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-or-later 2 | # Copyright (C) 2009-2013 Stephan Raue (stephan@openelec.tv) 3 | # Copyright (C) 2013 Lutz Fiebach (lufie@openelec.tv) 4 | # Copyright (C) 2020-present Team LibreELEC (https://libreelec.tv) 5 | 6 | import os 7 | 8 | 9 | ################################################################################ 10 | # Base 11 | ################################################################################ 12 | 13 | XBMC_USER_HOME = os.environ.get('XBMC_USER_HOME', '/storage/.kodi') 14 | CONFIG_CACHE = os.environ.get('CONFIG_CACHE', '/storage/.cache') 15 | USER_CONFIG = os.environ.get('USER_CONFIG', '/storage/.config') 16 | 17 | ################################################################################ 18 | # Connamn Module 19 | ################################################################################ 20 | 21 | connman = { 22 | 'CONNMAN_DAEMON': '/usr/sbin/connmand', 23 | 'WAIT_CONF_FILE': f'{CONFIG_CACHE}/libreelec/network_wait', 24 | 'ENABLED': lambda : (True if os.path.exists(connman['CONNMAN_DAEMON']) and not os.path.exists('/dev/.kernel_ipconfig') else False), 25 | } 26 | connman['ENABLED'] = connman['ENABLED']() 27 | 28 | ################################################################################ 29 | # Bluez Module 30 | ################################################################################ 31 | 32 | bluetooth = { 33 | 'BLUETOOTH_DAEMON': '/usr/lib/bluetooth/bluetoothd', 34 | 'OBEX_DAEMON': '/usr/lib/bluetooth/obexd', 35 | 'ENABLED': lambda : (True if os.path.exists(bluetooth['BLUETOOTH_DAEMON']) else False), 36 | 'D_OBEXD_ROOT': '/storage/downloads/', 37 | } 38 | bluetooth['ENABLED'] = bluetooth['ENABLED']() 39 | 40 | ################################################################################ 41 | # Service Module 42 | ################################################################################ 43 | 44 | services = { 45 | 'ENABLED': True, 46 | 'KERNEL_CMD': '/proc/cmdline', 47 | 'SAMBA_NMDB': '/usr/sbin/nmbd', 48 | 'SAMBA_SMDB': '/usr/sbin/smbd', 49 | 'D_SAMBA_WORKGROUP': 'WORKGROUP', 50 | 'D_SAMBA_SECURE': '0', 51 | 'D_SAMBA_USERNAME': 'libreelec', 52 | 'D_SAMBA_PASSWORD': 'libreelec', 53 | 'D_SAMBA_MINPROTOCOL': 'SMB2', 54 | 'D_SAMBA_MAXPROTOCOL': 'SMB3', 55 | 'D_SAMBA_AUTOSHARE': '1', 56 | 'SSH_DAEMON': '/usr/sbin/sshd', 57 | 'OPT_SSH_NOPASSWD': "-o 'PasswordAuthentication no'", 58 | 'D_SSH_DISABLE_PW_AUTH': '0', 59 | 'AVAHI_DAEMON': '/usr/sbin/avahi-daemon', 60 | 'CRON_DAEMON': '/sbin/crond', 61 | } 62 | 63 | system = { 64 | 'ENABLED': True, 65 | 'KERNEL_CMD': '/proc/cmdline', 66 | 'SET_CLOCK_CMD': '/sbin/hwclock --systohc --utc', 67 | 'XBMC_RESET_FILE': f'{CONFIG_CACHE}/reset_soft', 68 | 'LIBREELEC_RESET_FILE': f'{CONFIG_CACHE}/reset_hard', 69 | 'KEYBOARD_INFO': '/usr/share/X11/xkb/rules/base.xml', 70 | 'UDEV_KEYBOARD_INFO': f'{CONFIG_CACHE}/xkb/layout', 71 | 'NOX_KEYBOARD_INFO': '/usr/lib/keymaps', 72 | 'BACKUP_DIRS': [ 73 | XBMC_USER_HOME, 74 | USER_CONFIG, 75 | CONFIG_CACHE, 76 | '/storage/.ssh', 77 | ], 78 | 'BACKUP_FILTER' : [ 79 | f'{XBMC_USER_HOME}/addons/packages', 80 | f'{XBMC_USER_HOME}/addons/temp', 81 | f'{XBMC_USER_HOME}/temp' 82 | ], 83 | 'BACKUP_DESTINATION': '/storage/backup/', 84 | 'RESTORE_DIR': '/storage/.restore/', 85 | 'JOURNALD_CONFIG_FILE': '/storage/.cache/journald.conf.d/00_settings.conf' 86 | } 87 | 88 | updates = { 89 | 'ENABLED': not os.path.exists('/dev/.update_disabled'), 90 | 'UPDATE_REQUEST_URL': 'https://update.libreelec.tv/updates.php', 91 | 'UPDATE_DOWNLOAD_URL': 'http://%s.libreelec.tv/%s', 92 | 'LOCAL_UPDATE_DIR': '/storage/.update/', 93 | } 94 | 95 | about = {'ENABLED': True} 96 | 97 | _services = { 98 | 'sshd': ['sshd.service'], 99 | 'avahi': ['avahi-daemon.service'], 100 | 'samba': ['samba-config.service', 'nmbd.service', 'smbd.service'], 101 | 'bluez': ['bluetooth.service'], 102 | 'obexd': ['obex.service'], 103 | 'crond': ['cron.service'], 104 | 'iptables': ['iptables.service'], 105 | } 106 | -------------------------------------------------------------------------------- /resources/lib/hostname.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # Copyright (C) 2020-present Team LibreELEC (https://libreelec.tv) 3 | 4 | import os 5 | 6 | import config 7 | import os_tools 8 | 9 | 10 | def get_hostname(): 11 | return os_tools.read_shell_setting(config.HOSTNAME, config.OS_RELEASE['NAME']) 12 | 13 | 14 | def set_hostname(hostname): 15 | # network-base.service handles user created persistent settings 16 | current_hostname = get_hostname() 17 | if current_hostname != hostname or not os.path.isfile(config.HOSTNAME): 18 | with open(config.HOSTNAME, mode='w', encoding='utf-8') as out_file: 19 | out_file.write(f'{hostname}\n') 20 | os_tools.execute('systemctl restart network-base') 21 | os_tools.execute('systemctl try-restart avahi-daemon wsdd2') 22 | -------------------------------------------------------------------------------- /resources/lib/log.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # Copyright (C) 2020-present Team LibreELEC (https://libreelec.tv) 3 | 4 | import inspect 5 | import os 6 | import pprint 7 | import sys 8 | from functools import wraps 9 | 10 | from debug_utils import get_exception_message, format_stack_trace, inspect_object 11 | 12 | 13 | DEBUG = 0 14 | INFO = 1 15 | WARNING = 2 16 | ERROR = 3 17 | FATAL = 4 18 | NONE = 5 19 | 20 | _DEFAULT = DEBUG 21 | _HEADER = 'SETTINGS: ' 22 | 23 | 24 | try: 25 | import xbmc 26 | _NO_DEBUG_ENV = os.environ.get('DEBUG', 'no') == 'no' 27 | def _log(message, level=_DEFAULT): 28 | if level == DEBUG and _NO_DEBUG_ENV: 29 | return 30 | xbmc.log(message, level) 31 | except ModuleNotFoundError: 32 | def _log(message, level=_DEFAULT): 33 | print(message) 34 | 35 | 36 | def log(message, level=_DEFAULT): 37 | _log(f'{_HEADER}{sys._getframe().f_back.f_code.co_name} # {message}', level) 38 | 39 | 40 | def log_stack_trace(message='', level=_DEFAULT): 41 | """ 42 | Log extended stack trace from the point of execution to the starting frame 43 | 44 | The stack trace includes code fragments (if available) and local variables for each frame. 45 | """ 46 | message = _HEADER + message 47 | frame_stack = list(reversed(inspect.stack(5)))[:-1] 48 | stack_trace_string = format_stack_trace(frame_stack) 49 | if message: 50 | message += '\n' + stack_trace_string 51 | _log(_HEADER + message, level) 52 | 53 | 54 | def log_object_state(obj, level=_DEFAULT): 55 | """Log all object attributes""" 56 | object_info_string = inspect_object(obj) 57 | _log(_HEADER + object_info_string, level) 58 | 59 | 60 | def log_function(level=_DEFAULT): 61 | def _log_function_1(function): 62 | header = f'{_HEADER}{function.__qualname__} ' 63 | @wraps(function) 64 | def _log_function_2(*args, **kwargs): 65 | try: 66 | _log(f'{header}-', level) 67 | for arg in args: 68 | _log(f'{header}< {pprint.pformat(arg)}', level) 69 | for key, value in kwargs.items(): 70 | _log(f'{header}< {key}={pprint.pformat(value)}', level) 71 | result = function(*args, **kwargs) 72 | _log(f'{header}> {pprint.pformat(result)}', level) 73 | return result 74 | except Exception as e: 75 | _log(f'{header}# {repr(e)}', ERROR) 76 | outer_stack = list(reversed(inspect.stack(5)))[:-1] 77 | exception_message = get_exception_message(e, outer_stack) 78 | _log(header + exception_message, ERROR) 79 | if 'self' in inspect.getfullargspec(function).args and args: 80 | obj = args[0] 81 | object_state = inspect_object(obj) 82 | _log(header + object_state, ERROR) 83 | e.__traceback__ = None 84 | return _log_function_2 85 | return _log_function_1 86 | 87 | 88 | def utf8ify(pstr): 89 | return pstr.encode('utf-8', 'replace').decode('utf-8') 90 | 91 | def asciify(pstr): 92 | return pstr.encode('ascii', 'replace').decode('utf-8') 93 | -------------------------------------------------------------------------------- /resources/lib/modules.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # Copyright (C) 2020-present Team LibreELEC (https://libreelec.tv) 3 | 4 | import defaults 5 | import log 6 | 7 | 8 | class Module(object): 9 | 10 | @log.log_function() 11 | def __init__(self): 12 | name = self.__class__.__name__ 13 | settings = getattr(defaults, name, None) 14 | if settings: 15 | for key, value in settings.items(): 16 | setattr(self, key, value) 17 | log.log(f'{name}.{key}={value}') 18 | 19 | def do_init(self): 20 | pass 21 | 22 | def exit(self): 23 | pass 24 | 25 | def start_service(self): 26 | pass 27 | 28 | def stop_service(self): 29 | pass 30 | -------------------------------------------------------------------------------- /resources/lib/modules/about.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-or-later 2 | # Copyright (C) 2009-2013 Stephan Raue (stephan@openelec.tv) 3 | # Copyright (C) 2013 Lutz Fiebach (lufie@openelec.tv) 4 | # Copyright (C) 2019-present Team LibreELEC (https://libreelec.tv) 5 | 6 | import log 7 | import oe 8 | 9 | 10 | class about: 11 | 12 | ENABLED = False 13 | menu = {'99': { 14 | 'name': 32196, 15 | 'menuLoader': 'menu_loader', 16 | 'listTyp': 'other', 17 | 'InfoText': 705, 18 | }} 19 | 20 | @log.log_function() 21 | def __init__(self, oeMain): 22 | super().__init__() 23 | self.controls = {} 24 | 25 | @log.log_function() 26 | def menu_loader(self, menuItem): 27 | pass 28 | 29 | @log.log_function() 30 | def exit_addon(self): 31 | oe.winOeMain.close() 32 | 33 | @log.log_function() 34 | def init_controls(self): 35 | pass 36 | 37 | @log.log_function() 38 | def exit(self): 39 | for control in self.controls: 40 | try: 41 | oe.winOeMain.removeControl(self.controls[control]) 42 | finally: 43 | self.controls = {} 44 | 45 | @log.log_function() 46 | def do_wizard(self): 47 | oe.winOeMain.set_wizard_title(oe._(32317)) 48 | oe.winOeMain.set_wizard_text(oe._(32318)) 49 | -------------------------------------------------------------------------------- /resources/lib/modules/services.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-or-later 2 | # Copyright (C) 2009-2013 Stephan Raue (stephan@openelec.tv) 3 | # Copyright (C) 2013 Lutz Fiebach (lufie@openelec.tv) 4 | # Copyright (C) 2019-present Team LibreELEC (https://libreelec.tv) 5 | 6 | import os 7 | import shutil 8 | import subprocess 9 | 10 | import xbmcgui 11 | 12 | import log 13 | import modules 14 | import oe 15 | 16 | 17 | xbmcDialog = xbmcgui.Dialog() 18 | 19 | class services(modules.Module): 20 | 21 | ENABLED = False 22 | SAMBA_NMDB = None 23 | SAMBA_SMDB = None 24 | D_SAMBA_SECURE = None 25 | D_SAMBA_WORKGROUP = None 26 | D_SAMBA_USERNAME = None 27 | D_SAMBA_PASSWORD = None 28 | D_SAMBA_MINPROTOCOL = None 29 | D_SAMBA_MAXPROTOCOL = None 30 | D_SAMBA_AUTOSHARE = None 31 | KERNEL_CMD = None 32 | SSH_DAEMON = None 33 | D_SSH_DISABLE_PW_AUTH = None 34 | OPT_SSH_NOPASSWD = None 35 | AVAHI_DAEMON = None 36 | CRON_DAEMON = None 37 | menu = {'7': { 38 | 'name': 32001, 39 | 'menuLoader': 'load_menu', 40 | 'listTyp': 'list', 41 | 'InfoText': 703, 42 | }} 43 | 44 | @log.log_function() 45 | def __init__(self, oeMain): 46 | super().__init__() 47 | self.struct = { 48 | 'samba': { 49 | 'order': 1, 50 | 'name': 32200, 51 | 'not_supported': [], 52 | 'settings': { 53 | 'samba_autostart': { 54 | 'order': 1, 55 | 'name': 32204, 56 | 'value': None, 57 | 'action': 'initialize_samba', 58 | 'type': 'bool', 59 | 'InfoText': 738, 60 | }, 61 | 'samba_workgroup': { 62 | 'order': 2, 63 | 'name': 32215, 64 | 'value': "WORKGROUP", 65 | 'action': 'initialize_samba', 66 | 'type': 'text', 67 | 'parent': { 68 | 'entry': 'samba_autostart', 69 | 'value': ['1'], 70 | }, 71 | 'InfoText': 758, 72 | }, 73 | 'samba_secure': { 74 | 'order': 3, 75 | 'name': 32202, 76 | 'value': None, 77 | 'action': 'initialize_samba', 78 | 'type': 'bool', 79 | 'parent': { 80 | 'entry': 'samba_autostart', 81 | 'value': ['1'], 82 | }, 83 | 'InfoText': 739, 84 | }, 85 | 'samba_username': { 86 | 'order': 4, 87 | 'name': 32106, 88 | 'value': None, 89 | 'action': 'initialize_samba', 90 | 'type': 'text', 91 | 'parent': { 92 | 'entry': 'samba_secure', 93 | 'value': ['1'], 94 | }, 95 | 'InfoText': 740, 96 | }, 97 | 'samba_password': { 98 | 'order': 5, 99 | 'name': 32107, 100 | 'value': None, 101 | 'action': 'initialize_samba', 102 | 'type': 'text', 103 | 'parent': { 104 | 'entry': 'samba_secure', 105 | 'value': ['1'], 106 | }, 107 | 'InfoText': 741, 108 | }, 109 | 'samba_minprotocol': { 110 | 'order': 6, 111 | 'name': 32217, 112 | 'value': 'SMB2', 113 | 'action': 'initialize_samba', 114 | 'type': 'multivalue', 115 | 'values': [ 116 | 'SMB1', 117 | 'SMB2', 118 | 'SMB3', 119 | ], 120 | 'parent': { 121 | 'entry': 'samba_autostart', 122 | 'value': ['1'], 123 | }, 124 | 'InfoText': 756, 125 | }, 126 | 'samba_maxprotocol': { 127 | 'order': 7, 128 | 'name': 32218, 129 | 'value': 'SMB3', 130 | 'action': 'initialize_samba', 131 | 'type': 'multivalue', 132 | 'values': [ 133 | 'SMB1', 134 | 'SMB2', 135 | 'SMB3', 136 | ], 137 | 'parent': { 138 | 'entry': 'samba_autostart', 139 | 'value': ['1'], 140 | }, 141 | 'InfoText': 757, 142 | }, 143 | 'samba_autoshare': { 144 | 'order': 8, 145 | 'name': 32216, 146 | 'value': None, 147 | 'action': 'initialize_samba', 148 | 'type': 'bool', 149 | 'parent': { 150 | 'entry': 'samba_autostart', 151 | 'value': ['1'], 152 | }, 153 | 'InfoText': 755, 154 | }, 155 | }, 156 | }, 157 | 'ssh': { 158 | 'order': 2, 159 | 'name': 32201, 160 | 'not_supported': [], 161 | 'settings': { 162 | 'ssh_autostart': { 163 | 'order': 1, 164 | 'name': 32205, 165 | 'value': None, 166 | 'action': 'initialize_ssh', 167 | 'type': 'bool', 168 | 'InfoText': 742, 169 | }, 170 | 'ssh_secure': { 171 | 'order': 2, 172 | 'name': 32203, 173 | 'value': None, 174 | 'action': 'initialize_ssh', 175 | 'type': 'bool', 176 | 'parent': { 177 | 'entry': 'ssh_autostart', 178 | 'value': ['1'], 179 | }, 180 | 'InfoText': 743, 181 | }, 182 | 'ssh_passwd': { 183 | 'order': 3, 184 | 'name': 32209, 185 | 'value': None, 186 | 'action': 'do_sshpasswd', 187 | 'type': 'button', 188 | 'parent': { 189 | 'entry': 'ssh_secure', 190 | 'value': ['0'], 191 | }, 192 | 'InfoText': 746, 193 | }, 194 | }, 195 | }, 196 | 'avahi': { 197 | 'order': 3, 198 | 'name': 32207, 199 | 'not_supported': [], 200 | 'settings': {'avahi_autostart': { 201 | 'order': 1, 202 | 'name': 32206, 203 | 'value': None, 204 | 'action': 'initialize_avahi', 205 | 'type': 'bool', 206 | 'InfoText': 744, 207 | }}, 208 | }, 209 | 'cron': { 210 | 'order': 4, 211 | 'name': 32319, 212 | 'not_supported': [], 213 | 'settings': {'cron_autostart': { 214 | 'order': 1, 215 | 'name': 32320, 216 | 'value': None, 217 | 'action': 'initialize_cron', 218 | 'type': 'bool', 219 | 'InfoText': 745, 220 | }}, 221 | }, 222 | 'bluez': { 223 | 'order': 6, 224 | 'name': 32331, 225 | 'not_supported': [], 226 | 'settings': { 227 | 'enabled': { 228 | 'order': 1, 229 | 'name': 32344, 230 | 'value': None, 231 | 'action': 'initialize_bluetooth', 232 | 'type': 'bool', 233 | 'InfoText': 720, 234 | }, 235 | 'obex_enabled': { 236 | 'order': 2, 237 | 'name': 32384, 238 | 'value': None, 239 | 'action': 'initialize_obex', 240 | 'type': 'bool', 241 | 'parent': { 242 | 'entry': 'enabled', 243 | 'value': ['1'], 244 | }, 245 | 'InfoText': 751, 246 | }, 247 | 'obex_root': { 248 | 'order': 3, 249 | 'name': 32385, 250 | 'value': None, 251 | 'action': 'initialize_obex', 252 | 'type': 'folder', 253 | 'parent': { 254 | 'entry': 'obex_enabled', 255 | 'value': ['1'], 256 | }, 257 | 'InfoText': 752, 258 | }, 259 | 'idle_timeout': { 260 | 'order': 4, 261 | 'name': 32400, 262 | 'value': None, 263 | 'action': 'idle_timeout', 264 | 'type': 'multivalue', 265 | 'values': [ 266 | '0', 267 | '1', 268 | '3', 269 | '5', 270 | '15', 271 | '30', 272 | '60', 273 | ], 274 | 'parent': { 275 | 'entry': 'enabled', 276 | 'value': ['1'], 277 | }, 278 | 'InfoText': 773, 279 | }, 280 | }, 281 | }, 282 | } 283 | 284 | @log.log_function() 285 | def start_service(self): 286 | self.load_values() 287 | self.initialize_samba(service=1) 288 | self.initialize_ssh(service=1) 289 | self.initialize_avahi(service=1) 290 | self.initialize_cron(service=1) 291 | self.initialize_bluetooth(service=1) 292 | 293 | @log.log_function() 294 | def do_init(self): 295 | self.load_values() 296 | 297 | @log.log_function() 298 | def set_value(self, listItem): 299 | self.struct[listItem.getProperty('category')]['settings'][listItem.getProperty('entry')]['value'] = listItem.getProperty('value') 300 | 301 | @log.log_function() 302 | def load_menu(self, focusItem): 303 | oe.winOeMain.build_menu(self.struct) 304 | 305 | @log.log_function() 306 | def load_values(self): 307 | # SAMBA 308 | if os.path.isfile(self.SAMBA_NMDB) and os.path.isfile(self.SAMBA_SMDB): 309 | self.struct['samba']['settings']['samba_autostart']['value'] = oe.get_service_state('samba') 310 | self.struct['samba']['settings']['samba_workgroup']['value'] = oe.get_service_option('samba', 'SAMBA_WORKGROUP', 311 | self.D_SAMBA_WORKGROUP).replace('"', '') 312 | self.struct['samba']['settings']['samba_secure']['value'] = oe.get_service_option('samba', 'SAMBA_SECURE', 313 | self.D_SAMBA_SECURE).replace('true', '1').replace('false', '0').replace('"', '') 314 | self.struct['samba']['settings']['samba_username']['value'] = self.sh_unesc_str(oe.get_service_option('samba', 'SAMBA_USERNAME', 315 | self.D_SAMBA_USERNAME).replace('"', '')) 316 | self.struct['samba']['settings']['samba_password']['value'] = self.sh_unesc_str(oe.get_service_option('samba', 'SAMBA_PASSWORD', 317 | self.D_SAMBA_PASSWORD).replace('"', '')) 318 | self.struct['samba']['settings']['samba_minprotocol']['value'] = oe.get_service_option('samba', 'SAMBA_MINPROTOCOL', 319 | self.D_SAMBA_MINPROTOCOL).replace('"', '') 320 | self.struct['samba']['settings']['samba_maxprotocol']['value'] = oe.get_service_option('samba', 'SAMBA_MAXPROTOCOL', 321 | self.D_SAMBA_MAXPROTOCOL).replace('"', '') 322 | self.struct['samba']['settings']['samba_autoshare']['value'] = oe.get_service_option('samba', 'SAMBA_AUTOSHARE', 323 | self.D_SAMBA_AUTOSHARE).replace('true', '1').replace('false', '0').replace('"', '') 324 | else: 325 | self.struct['samba']['hidden'] = 'true' 326 | # SSH 327 | if os.path.isfile(self.SSH_DAEMON): 328 | self.struct['ssh']['settings']['ssh_autostart']['value'] = oe.get_service_state('sshd') 329 | self.struct['ssh']['settings']['ssh_secure']['value'] = oe.get_service_option('sshd', 'SSHD_DISABLE_PW_AUTH', 330 | self.D_SSH_DISABLE_PW_AUTH).replace('true', '1').replace('false', '0').replace('"', '') 331 | # hide ssh settings if Kernel Parameter is set 332 | with open(self.KERNEL_CMD, 'r') as cmd_file: 333 | cmd_args = cmd_file.read().split(' ') 334 | if 'ssh' in cmd_args: 335 | self.struct['ssh']['settings']['ssh_autostart']['value'] = '1' 336 | self.struct['ssh']['settings']['ssh_autostart']['hidden'] = 'true' 337 | else: 338 | self.struct['ssh']['hidden'] = 'true' 339 | # AVAHI 340 | if os.path.isfile(self.AVAHI_DAEMON): 341 | self.struct['avahi']['settings']['avahi_autostart']['value'] = oe.get_service_state('avahi') 342 | else: 343 | self.struct['avahi']['hidden'] = 'true' 344 | # CRON 345 | if os.path.isfile(self.CRON_DAEMON): 346 | self.struct['cron']['settings']['cron_autostart']['value'] = oe.get_service_state('crond') 347 | else: 348 | self.struct['cron']['hidden'] = 'true' 349 | # BLUEZ / OBEX 350 | if 'bluetooth' in oe.dictModules: 351 | if os.path.isfile(oe.dictModules['bluetooth'].BLUETOOTH_DAEMON): 352 | self.struct['bluez']['settings']['enabled']['value'] = oe.get_service_state('bluez') 353 | if os.path.isfile(oe.dictModules['bluetooth'].OBEX_DAEMON): 354 | self.struct['bluez']['settings']['obex_enabled']['value'] = oe.get_service_state('obexd') 355 | self.struct['bluez']['settings']['obex_root']['value'] = oe.get_service_option('obexd', 'OBEXD_ROOT', 356 | oe.dictModules['bluetooth'].D_OBEXD_ROOT).replace('"', '') 357 | else: 358 | self.struct['bluez']['settings']['obex_enabled']['hidden'] = True 359 | self.struct['bluez']['settings']['obex_root']['hidden'] = True 360 | 361 | value = oe.read_setting('bluetooth', 'idle_timeout') 362 | if not value: 363 | value = '0' 364 | self.struct['bluez']['settings']['idle_timeout']['value'] = oe.read_setting('bluetooth', 'idle_timeout') 365 | else: 366 | self.struct['bluez']['hidden'] = 'true' 367 | 368 | @log.log_function() 369 | def initialize_samba(self, **kwargs): 370 | if 'listItem' in kwargs: 371 | self.set_value(kwargs['listItem']) 372 | options = {} 373 | if self.struct['samba']['settings']['samba_autostart']['value'] == '1': 374 | state = 1 375 | if 'hidden' in self.struct['samba']['settings']['samba_username']: 376 | del self.struct['samba']['settings']['samba_username']['hidden'] 377 | if 'hidden' in self.struct['samba']['settings']['samba_password']: 378 | del self.struct['samba']['settings']['samba_password']['hidden'] 379 | if self.struct['samba']['settings']['samba_secure']['value'] == '1': 380 | val_secure = 'true' 381 | else: 382 | val_secure = 'false' 383 | if self.struct['samba']['settings']['samba_autoshare']['value'] == '1': 384 | val_autoshare = 'true' 385 | else: 386 | val_autoshare = 'false' 387 | options['SAMBA_WORKGROUP'] = self.struct['samba']['settings']['samba_workgroup']['value'] 388 | options['SAMBA_SECURE'] = val_secure 389 | options['SAMBA_AUTOSHARE'] = val_autoshare 390 | options['SAMBA_MINPROTOCOL'] = self.struct['samba']['settings']['samba_minprotocol']['value'] 391 | options['SAMBA_MAXPROTOCOL'] = self.struct['samba']['settings']['samba_maxprotocol']['value'] 392 | options['SAMBA_USERNAME'] = self.sh_esc_str(self.struct['samba']['settings']['samba_username']['value']) 393 | options['SAMBA_PASSWORD'] = self.sh_esc_str(self.struct['samba']['settings']['samba_password']['value']) 394 | else: 395 | state = 0 396 | self.struct['samba']['settings']['samba_username']['hidden'] = True 397 | self.struct['samba']['settings']['samba_password']['hidden'] = True 398 | oe.set_service('samba', options, state) 399 | 400 | @log.log_function() 401 | def initialize_ssh(self, **kwargs): 402 | if 'listItem' in kwargs: 403 | self.set_value(kwargs['listItem']) 404 | options = {} 405 | if self.struct['ssh']['settings']['ssh_autostart']['value'] == '1': 406 | state = 1 407 | if self.struct['ssh']['settings']['ssh_secure']['value'] == '1': 408 | val = 'true' 409 | options['SSH_ARGS'] = self.OPT_SSH_NOPASSWD 410 | else: 411 | val = 'false' 412 | options['SSH_ARGS'] = '""' 413 | options['SSHD_DISABLE_PW_AUTH'] = val 414 | else: 415 | state = 0 416 | oe.set_service('sshd', options, state) 417 | 418 | @log.log_function() 419 | def initialize_avahi(self, **kwargs): 420 | if 'listItem' in kwargs: 421 | self.set_value(kwargs['listItem']) 422 | options = {} 423 | if self.struct['avahi']['settings']['avahi_autostart']['value'] == '1': 424 | state = 1 425 | else: 426 | state = 0 427 | oe.set_service('avahi', options, state) 428 | 429 | @log.log_function() 430 | def initialize_cron(self, **kwargs): 431 | if 'listItem' in kwargs: 432 | self.set_value(kwargs['listItem']) 433 | options = {} 434 | if self.struct['cron']['settings']['cron_autostart']['value'] == '1': 435 | state = 1 436 | else: 437 | state = 0 438 | oe.set_service('crond', options, state) 439 | 440 | @log.log_function() 441 | def initialize_bluetooth(self, **kwargs): 442 | if 'listItem' in kwargs: 443 | self.set_value(kwargs['listItem']) 444 | options = {} 445 | if self.struct['bluez']['settings']['enabled']['value'] == '1': 446 | state = 1 447 | if 'hidden' in self.struct['bluez']['settings']['obex_enabled']: 448 | del self.struct['bluez']['settings']['obex_enabled']['hidden'] 449 | if 'hidden' in self.struct['bluez']['settings']['obex_root']: 450 | del self.struct['bluez']['settings']['obex_root']['hidden'] 451 | else: 452 | state = 0 453 | self.struct['bluez']['settings']['obex_enabled']['hidden'] = True 454 | self.struct['bluez']['settings']['obex_root']['hidden'] = True 455 | oe.set_service('bluez', options, state) 456 | 457 | @log.log_function() 458 | def initialize_obex(self, **kwargs): 459 | if 'listItem' in kwargs: 460 | self.set_value(kwargs['listItem']) 461 | options = {} 462 | if self.struct['bluez']['settings']['obex_enabled']['value'] == '1': 463 | state = 1 464 | options['OBEXD_ROOT'] = self.struct['bluez']['settings']['obex_root']['value'] 465 | else: 466 | state = 0 467 | oe.set_service('obexd', options, state) 468 | 469 | @log.log_function() 470 | def idle_timeout(self, **kwargs): 471 | if 'listItem' in kwargs: 472 | self.set_value(kwargs['listItem']) 473 | oe.write_setting('bluetooth', 'idle_timeout', self.struct['bluez']['settings']['idle_timeout']['value']) 474 | 475 | @log.log_function() 476 | def do_wizard(self): 477 | oe.winOeMain.set_wizard_title(oe._(32311)) 478 | 479 | # Enable samba 480 | self.struct['samba']['settings']['samba_autostart']['value'] = '1' 481 | self.initialize_samba() 482 | 483 | if hasattr(self, 'samba'): 484 | oe.winOeMain.set_wizard_text(f'{oe._(32313)}[CR][CR]{oe._(32312)}') 485 | else: 486 | oe.winOeMain.set_wizard_text(oe._(32312)) 487 | oe.winOeMain.set_wizard_button_title(oe._(32316)) 488 | self.set_wizard_buttons() 489 | 490 | @log.log_function() 491 | def set_wizard_buttons(self): 492 | if self.struct['ssh']['settings']['ssh_autostart']['value'] == '1': 493 | oe.winOeMain.set_wizard_radiobutton_1(oe._(32201), self, 'wizard_set_ssh', True) 494 | else: 495 | oe.winOeMain.set_wizard_radiobutton_1(oe._(32201), self, 'wizard_set_ssh') 496 | if not 'hidden' in self.struct['samba']: 497 | if self.struct['samba']['settings']['samba_autostart']['value'] == '1': 498 | oe.winOeMain.set_wizard_radiobutton_2(oe._(32200), self, 'wizard_set_samba', True) 499 | else: 500 | oe.winOeMain.set_wizard_radiobutton_2(oe._(32200), self, 'wizard_set_samba') 501 | 502 | @log.log_function() 503 | def wizard_set_ssh(self): 504 | if self.struct['ssh']['settings']['ssh_autostart']['value'] == '1': 505 | self.struct['ssh']['settings']['ssh_autostart']['value'] = '0' 506 | else: 507 | self.struct['ssh']['settings']['ssh_autostart']['value'] = '1' 508 | # ssh button does nothing if "ssh" set on kernel commandline 509 | with open(self.KERNEL_CMD, 'r') as cmd_file: 510 | cmd_args = cmd_file.read().split(' ') 511 | if 'ssh' in cmd_args: 512 | oe.notify('ssh', 'ssh enabled as boot parameter. can not disable') 513 | self.initialize_ssh() 514 | self.load_values() 515 | if self.struct['ssh']['settings']['ssh_autostart']['value'] == '1': 516 | self.wizard_sshpasswd() 517 | self.set_wizard_buttons() 518 | 519 | @log.log_function() 520 | def wizard_set_samba(self): 521 | if self.struct['samba']['settings']['samba_autostart']['value'] == '1': 522 | self.struct['samba']['settings']['samba_autostart']['value'] = '0' 523 | else: 524 | self.struct['samba']['settings']['samba_autostart']['value'] = '1' 525 | self.initialize_samba() 526 | self.load_values() 527 | self.set_wizard_buttons() 528 | 529 | @log.log_function() 530 | def wizard_sshpasswd(self): 531 | SSHresult = False 532 | while SSHresult == False: 533 | changeSSH = xbmcDialog.yesno(oe._(32209), oe._(32210), yeslabel=oe._(32213), nolabel=oe._(32214)) 534 | if changeSSH: 535 | SSHresult = True 536 | else: 537 | changeSSHresult = self.do_sshpasswd() 538 | if changeSSHresult: 539 | SSHresult = True 540 | return 541 | 542 | @log.log_function() 543 | def do_sshpasswd(self, **kwargs): 544 | SSHchange = False 545 | newpwd = xbmcDialog.input(oe._(746)) 546 | if newpwd: 547 | if newpwd == "libreelec": 548 | if os.path.isfile('/storage/.cache/shadow'): 549 | os.remove('/storage/.cache/shadow') 550 | shutil.copy2('/usr/cache/shadow', '/storage/.cache/shadow') 551 | readout3 = "Retype password" 552 | else: 553 | ssh = subprocess.Popen(["passwd"], shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=0) 554 | readout1 = ssh.stdout.readline() 555 | ssh.stdin.write(f'{newpwd}\n') 556 | readout2 = ssh.stdout.readline() 557 | ssh.stdin.write(f'{newpwd}\n') 558 | readout3 = ssh.stdout.readline() 559 | if "Bad password" in readout3: 560 | xbmcDialog.ok(oe._(32220), oe._(32221)) 561 | log.log('Password too weak') 562 | return 563 | elif "Retype password" in readout3: 564 | xbmcDialog.ok(oe._(32222), oe._(32223)) 565 | SSHchange = True 566 | else: 567 | xbmcDialog.ok(oe._(32224), oe._(32225)) 568 | else: 569 | log.log('User cancelled') 570 | return SSHchange 571 | 572 | def sh_esc_str(self, string): 573 | escape = '' 574 | for c in string: 575 | escape += '\\' + c 576 | return escape 577 | 578 | def sh_unesc_str(self, string): 579 | size = len(string) 580 | if size % 2: 581 | return string 582 | if string[0::2] != '\\' * (size // 2): 583 | return string 584 | return string[1::2] 585 | -------------------------------------------------------------------------------- /resources/lib/os_tools.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # Copyright (C) 2020-present Team LibreELEC (https://libreelec.tv) 3 | 4 | '''This module holds support functions for interacting with the underlying OS. 5 | 6 | Support functions are grouped by purpose: 7 | 1. File access: read / write / copy / move / download 8 | 2. System access: executing system commands 9 | ''' 10 | 11 | import os 12 | import subprocess 13 | 14 | import log 15 | 16 | 17 | ### FILE ACCESS ### 18 | def read_shell_setting(file, default=None): 19 | '''Read the first line of a file as the setting''' 20 | setting = default if default else '' 21 | if os.path.isfile(file): 22 | with open(file, mode='r', encoding='utf-8') as data: 23 | setting = data.readline().strip() 24 | # ignore comments 25 | if setting.startswith('#'): 26 | setting = default if default else '' 27 | else: 28 | log.log(f'File not found: {file}', log.DEBUG) 29 | return setting 30 | 31 | 32 | def read_shell_settings(file, defaults=None): 33 | '''Parse settings from text file, placing each value into a dictionary''' 34 | settings = defaults if defaults else {} 35 | if os.path.isfile(file): 36 | with open(file, mode='r', encoding='utf-8') as data: 37 | for line in data: 38 | line = line.strip() 39 | # ignore comments 40 | if not line.startswith('#'): 41 | name, value = line.split('=', 1) 42 | # remove quotes 43 | if value: 44 | value = value.removeprefix('"').removesuffix('"') 45 | settings[name] = value 46 | else: 47 | log.log(f'File not found: {file}', log.DEBUG) 48 | return settings 49 | 50 | 51 | ### SYSTEM ACCESS ### 52 | def execute(command, get_result=False, output_err_msg=True): 53 | '''Run command, waiting for it to finish. Returns: command output, empty string or None''' 54 | log.log(f'Executing command: {command}', log.DEBUG) 55 | try: 56 | cmd_status = subprocess.run(command, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 57 | except subprocess.CalledProcessError as e: 58 | if output_err_msg: 59 | log.log(f'Command failed: {command}', log.ERROR) 60 | log.log(f'Executed command: {e.cmd}', log.DEBUG) 61 | log.log(f'\nSTART COMMAND OUTPUT:\n{e.stdout.decode()}\nEND COMMAND OUTPUT', log.ERROR) 62 | # return empty string if result wanted to match old behaviour 63 | return '' if get_result else None 64 | # return output if requested, otherwise return None 65 | return cmd_status.stdout.decode() if get_result else None 66 | -------------------------------------------------------------------------------- /resources/lib/regdomain.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # Copyright (C) 2020-present Team LibreELEC (https://libreelec.tv) 3 | 4 | import os 5 | 6 | import config 7 | import os_tools 8 | 9 | 10 | REGDOMAIN_DEFAULT = 'NOT SET (DEFAULT)' 11 | REGDOMAIN_LIST = [REGDOMAIN_DEFAULT] + [ 12 | "GLOBAL (00)", 13 | "Afghanistan (AF)", 14 | "Albania (AL)", 15 | "Algeria (DZ)", 16 | "American Samoa (AS)", 17 | "Andorra (AD)", 18 | "Anguilla (AI)", 19 | "Argentina (AR)", 20 | "Armenia (AM)", 21 | "Aruba (AW)", 22 | "Australia (AU)", 23 | "Austria (AT)", 24 | "Azerbaijan (AZ)", 25 | "Bahamas (BS)", 26 | "Bahrain (BH)", 27 | "Bangladesh (BD)", 28 | "Barbados (BB)", 29 | "Belarus (BY)", 30 | "Belgium (BE)", 31 | "Belize (BZ)", 32 | "Bermuda (BM)", 33 | "Bhutan (BT)", 34 | "Bolivia (BO)", 35 | "Bosnia and Herzegovina (BA)", 36 | "Brazil (BR)", 37 | "Brunei Darussalam (BN)", 38 | "Bulgaria (BG)", 39 | "Burkina Faso (BF)", 40 | "Cambodia (KH)", 41 | "Canada (CA)", 42 | "Cayman Islands (KY)", 43 | "Central African Republic (CF)", 44 | "Chad (TD)", 45 | "Chile (CL)", 46 | "China (CN)", 47 | "Christmas Island (CX)", 48 | "Colombia (CO)", 49 | "Costa Rica (CR)", 50 | "Côte d'Ivoire (CI)", 51 | "Croatia (HR)", 52 | "Cuba (CU)", 53 | "Cyprus (CY)", 54 | "Czechia (CZ)", 55 | "Denmark (DK)", 56 | "Dominica (DM)", 57 | "Dominican Republic (DO)", 58 | "Ecuador (EC)", 59 | "Egypt (EG)", 60 | "El Salvador (SV)", 61 | "Estonia (EE)", 62 | "Ethiopia (ET)", 63 | "Finland (FI)", 64 | "France (FR)", 65 | "French Guiana (GF)", 66 | "French Polynesia (PF)", 67 | "Georgia (GE)", 68 | "Germany (DE)", 69 | "Ghana (GH)", 70 | "Greece (GR)", 71 | "Greenland (GL)", 72 | "Grenada (GD)", 73 | "Guadeloupe (GP)", 74 | "Guam (GU)", 75 | "Guatemala (GT)", 76 | "Guyana (GY)", 77 | "Haiti (HT)", 78 | "Honduras (HN)", 79 | "Hong Kong (HK)", 80 | "Hungary (HU)", 81 | "Iceland (IS)", 82 | "India (IN)", 83 | "Indonesia (ID)", 84 | "Iran (IR)", 85 | "Ireland (IE)", 86 | "Israel (IL)", 87 | "Italy (IT)", 88 | "Jamaica (JM)", 89 | "Japan (JP)", 90 | "Jordan (JO)", 91 | "Kazakhstan (KZ)", 92 | "Kenya (KE)", 93 | "Korea (North) (KP)", 94 | "Korea (South) (KR)", 95 | "Kuwait (KW)", 96 | "Latvia (LV)", 97 | "Lebanon (LB)", 98 | "Lesotho (LS)", 99 | "Liechtenstein (LI)", 100 | "Lithuania (LT)", 101 | "Luxembourg (LU)", 102 | "Macao (MO)", 103 | "Malawi (MW)", 104 | "Malaysia (MY)", 105 | "Maldives (MV)", 106 | "Malta (MT)", 107 | "Marshall Islands (MH)", 108 | "Martinique (MQ)", 109 | "Mauritania (MR)", 110 | "Mauritius (MU)", 111 | "Mayotte (YT)", 112 | "Mexico (MX)", 113 | "Micronesia (FM)", 114 | "Moldova (MD)", 115 | "Monaco (MC)", 116 | "Mongolia (MN)", 117 | "Montenegro (ME)", 118 | "Morocco (MA)", 119 | "Nepal (NP)", 120 | "Netherlands (NL)", 121 | "Netherlands Antilles (AN)", 122 | "New Zealand (NZ)", 123 | "Nicaragua (NI)", 124 | "Nigeria (NG)", 125 | "North Macedonia (MK)", 126 | "Northern Mariana Islands (MP)", 127 | "Norway (NO)", 128 | "Oman (OM)", 129 | "Pakistan (PK)", 130 | "Palau (PW)", 131 | "Panama (PA)", 132 | "Papua New Guinea (PG)", 133 | "Paraguay (PY)", 134 | "Peru (PE)", 135 | "Philippines (PH)", 136 | "Poland (PL)", 137 | "Portugal (PT)", 138 | "Puerto Rico (PR)", 139 | "Qatar (QA)", 140 | "Réunion (RE)", 141 | "Romania (RO)", 142 | "Russian Federation (RU)", 143 | "Rwanda (RW)", 144 | "Saint Barthélemy (BL)", 145 | "Saint Kitts and Nevis (KN)", 146 | "Saint Lucia (LC)", 147 | "Saint Martin (MF)", 148 | "Saint Pierre and Miquelon (PM)", 149 | "Saint Vincent and the Grenadines (VC)", 150 | "Samoa (WS)", 151 | "Saudi Arabia (SA)", 152 | "Senegal (SN)", 153 | "Serbia (RS)", 154 | "Singapore (SG)", 155 | "Slovakia (SK)", 156 | "Slovenia (SI)", 157 | "South Africa (ZA)", 158 | "Spain (ES)", 159 | "Sri Lanka (LK)", 160 | "Suriname (SR)", 161 | "Sweden (SE)", 162 | "Switzerland (CH)", 163 | "Syria (SY)", 164 | "Taiwan (TW)", 165 | "Tanzania (TZ)", 166 | "Thailand (TH)", 167 | "Togo (TG)", 168 | "Trinidad and Tobago (TT)", 169 | "Tunisia (TN)", 170 | "Turkey (TR)", 171 | "Turks and Caicos Islands (TC)", 172 | "Uganda (UG)", 173 | "Ukraine (UA)", 174 | "United Arab Emirates (AE)", 175 | "United Kingdom (GB)", 176 | "United States (US)", 177 | "Uraguay (UY)", 178 | "Uzbekistan (UZ)", 179 | "Vanuatu (VU)", 180 | "Venezuela (VE)", 181 | "Vietnam (VN)", 182 | "Virgin Islands (VI)", 183 | "Wallis and Futuna (WF)", 184 | "Yemen (YE)", 185 | "Zimbabwe (ZW)" 186 | ] 187 | 188 | 189 | def get_regdomain(): 190 | if not os.path.isfile(config.REGDOMAIN_CONF): 191 | return REGDOMAIN_DEFAULT 192 | code = f'({open(config.REGDOMAIN_CONF).readline().rstrip()[-2:]})' 193 | regdomain = next((l for l in REGDOMAIN_LIST if code in l), 194 | REGDOMAIN_DEFAULT) 195 | return regdomain 196 | 197 | 198 | def set_regdomain(regdomain): 199 | if regdomain == REGDOMAIN_DEFAULT: 200 | if os.path.isfile(config.REGDOMAIN_CONF): 201 | os.remove(config.REGDOMAIN_CONF) 202 | else: 203 | code = regdomain[-3:-1] 204 | with open(config.REGDOMAIN_CONF, 'w') as file: 205 | file.write(f'REGDOMAIN={code}\n') 206 | os_tools.execute(config.SETREGDOMAIN) 207 | -------------------------------------------------------------------------------- /resources/lib/timezone.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # Copyright (C) 2024-present Team LibreELEC (https://libreelec.tv) 3 | 4 | '''This module holds support functions for interacting with timezones. 5 | ''' 6 | 7 | import os 8 | from urllib.request import urlopen 9 | 10 | import config 11 | import log 12 | import os_tools 13 | 14 | 15 | def get_timezone(): 16 | '''Read timezone setting from file or return default of UTC timezone.''' 17 | return os_tools.read_shell_setting(config.TIMEZONE, default='TIMEZONE=UTC').split('=', 1)[1] 18 | 19 | 20 | def guess_timezone(): 21 | '''Guess the device's timezone based on the public IP of the device.''' 22 | def QueryWebServer(url): 23 | '''Query a webserver for a response.''' 24 | try: 25 | response = urlopen(url) 26 | except Exception as err: 27 | log.log(f'Error querying: {url}', log.ERROR) 28 | log.log(err, log.ERROR) 29 | response = None 30 | return response.read().decode('utf-8').strip() if response else None 31 | 32 | my_ip = QueryWebServer('https://icanhazip.com') 33 | return QueryWebServer(f'https://ipapi.co/{my_ip}/timezone') if my_ip else None 34 | 35 | 36 | def list_timezones(): 37 | '''List timezones available from tzdata.''' 38 | timezones = [] 39 | with open('/usr/share/zoneinfo/tzdata.zi', mode='r', encoding='utf-8') as tz_db: 40 | content = tz_db.read() 41 | for line in content.splitlines(): 42 | # if line starts with Z take second field 43 | if line.startswith('Z'): 44 | timezones.append(line.split(' ')[1]) 45 | # if line starts with L take third field 46 | elif line.startswith('L'): 47 | timezones.append(line.split(' ')[2]) 48 | # sort and return 49 | timezones.sort() 50 | return timezones 51 | 52 | 53 | def set_timezone(timezone): 54 | '''Write new timezone info to .cache file and commit change to system.''' 55 | current_timezone = get_timezone() 56 | if current_timezone != timezone or not os.path.isfile(config.TIMEZONE): 57 | with open(config.TIMEZONE, mode='w', encoding='utf-8') as out_file: 58 | out_file.write(f'TIMEZONE={timezone}\n') 59 | if os.path.isfile(config.TIMEZONE): 60 | os_tools.execute('systemctl restart tz-data') 61 | else: 62 | log.log(f'Failed to write: {config.TIMEZONE}', log.ERROR) 63 | log.log(f'Desired timezone was: {timezone}', log.ERROR) 64 | -------------------------------------------------------------------------------- /resources/lib/ui_tools.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # Copyright (C) 2020-present Team LibreELEC (https://libreelec.tv) 3 | 4 | import xbmcaddon 5 | import xbmcgui 6 | 7 | import log 8 | 9 | 10 | ADDON = xbmcaddon.Addon() 11 | ADDON_ICON = ADDON.getAddonInfo('icon') 12 | ADDON_NAME = ADDON.getAddonInfo('name') 13 | 14 | 15 | @log.log_function() 16 | def notification(message, heading=ADDON_NAME, icon=ADDON_ICON): 17 | xbmcgui.Dialog().notification(heading, message, icon) 18 | -------------------------------------------------------------------------------- /resources/skins/Default/1080i/service-LibreELEC-Settings-getPasskey.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2000 4 | no 5 | 6 | 7 | 650 8 | 390 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 5 19 | 5 20 | 600 21 | 174 22 | dialog-bg-solid.png 23 | 24 | 25 | 17 26 | 16 27 | 28 | 29 | 20 30 | 10 31 | 540 32 | 30 33 | font12 34 | white 35 | 39 | 40 | 10 41 | 50 42 | 560 43 | 30 44 | font23_title 45 | blue 46 | 50 | 51 | 10 52 | 70 53 | 560 54 | 30 55 | font23_title 56 | blue 57 | 61 | 62 | 10 63 | 90 64 | 560 65 | 30 66 | font23_title 67 | blue 68 | 72 | 73 | 10 74 | 140 75 | 555 76 | 12 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /resources/skins/Default/1080i/service-LibreELEC-Settings-wizard.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1500 4 | no 5 | 6 | 7 | 300 8 | 200 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 0 19 | 0 20 | 1320 21 | 680 22 | dialog-bg-solid.png 23 | 24 | 25 | 0 26 | 0 27 | 270 28 | 680 29 | floor.png 30 | 31 | 32 | 2 33 | 2 34 | 270 35 | 676 36 | wizard.png 37 | 38 | 39 | 268 40 | 0 41 | 1052 42 | 50 43 | black-back.png 44 | 45 | 46 | 300 47 | 10 48 | 740 49 | 30 50 | font16caps 51 | 57 | 58 | 268 59 | 576 60 | 1051 61 | 105 62 | black-back.png 63 | 64 | 65 | 269 66 | 575 67 | 1050 68 | 3 69 | separator-grey.png 70 | 71 | 72 | String.IsEmpty(Control.GetLabel(1390)) 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 300 81 | 120 82 | 1 83 | 1 84 | 87 | 88 | 300 89 | 66 90 | 990 91 | 40 92 | font24_title 93 | 98 | 99 | 300 100 | 124 101 | 900 102 | 500 103 | font12 104 | 109 | 110 | 111 | 300 112 | 400 113 | 750 114 | 20 115 | 121 | 122 | 280 123 | 440 124 | 300 125 | 75 126 | 131 | 132 | 570 133 | 440 134 | 300 135 | 75 136 | 141 | 142 | 280 143 | 440 144 | 300 145 | 75 146 | button-fo.png 147 | button-nofo.png 148 | 153 | 154 | 570 155 | 440 156 | 300 157 | 75 158 | button-fo.png 159 | button-nofo.png 160 | 165 | 166 | 167 | !String.IsEmpty(Control.GetLabel(1391)) 168 | 169 | 300 170 | 140 171 | 1 172 | 1 173 | 176 | 177 | 300 178 | 335 179 | 750 180 | 20 181 | 186 | 187 | 278 188 | 370 189 | 1020 190 | 200 191 | dialog-bg.png 192 | 193 | 194 | 195 | 196 | false 197 | 10 198 | 100 199 | 260 200 | 541 201 | 1500 202 | - 203 | 300 204 | 205 | 206 | 207 | 208 | 209 | 210 | 300 211 | 392 212 | 1050 213 | 150 214 | 1500 215 | 1500 216 | 300 217 | 218 | 0 219 | 0 220 | 221 | 0 222 | 2 223 | 974 224 | 48 225 | separator.png 226 | 227 | 228 | 229 | 10 230 | 10 231 | 32 232 | 32 233 | keep 234 | String.IsEqual(ListItem.Property(netType), ethernet) 235 | eth.png 236 | 237 | 238 | 239 | 10 240 | 10 241 | 32 242 | 32 243 | keep 244 | String.IsEqual(ListItem.Property(netType), vpn) 245 | vpn.png 246 | 247 | 248 | 249 | 10 250 | 10 251 | 32 252 | 32 253 | keep 254 | String.IsEqual(ListItem.Property(netType), wifi) 255 | wlan.png 256 | 257 | 258 | 259 | 70 260 | 0 261 | 200 262 | 50 263 | center 264 | font13_title 265 | blue 266 | ListItem.Label 267 | true 268 | 269 | 270 | 271 | 300 272 | 0 273 | 350 274 | 50 275 | center 276 | font12 277 | blue 278 | String.IsEqual(ListItem.Property(State), online) | String.IsEqual(ListItem.Property(State), ready) 279 | 280 | 281 | 282 | 283 | 680 284 | 0 285 | 170 286 | 50 287 | center 288 | font12 289 | blue 290 | 291 | 292 | 293 | 294 | 10 295 | 40 296 | 952 297 | 2 298 | ListItem.Property(Strength) 299 | String.IsEqual(ListItem.Property(netType), wifi) 300 | 301 | 302 | 303 | 304 | separator-grey.png 305 | 306 | 307 | 308 | 930 309 | 15 310 | 20 311 | 20 312 | keep 313 | IntegerGreaterThan(ListItem.Property(Security), 0) 314 | key.png 315 | 316 | 317 | 318 | 0 319 | 0 320 | 321 | 0 322 | 2 323 | 974 324 | 48 325 | separator.png 326 | 327 | 328 | 0 329 | 2 330 | 974 331 | 48 332 | Control.HasFocus(1200) 333 | focus.png 334 | 335 | 336 | 337 | 338 | 339 | 340 | 10 341 | 10 342 | 32 343 | 32 344 | keep 345 | String.IsEqual(ListItem.Property(netType), ethernet) 346 | eth.png 347 | 348 | 349 | 350 | 10 351 | 10 352 | 32 353 | 32 354 | keep 355 | String.IsEqual(ListItem.Property(netType), vpn) 356 | vpn.png 357 | 358 | 359 | 360 | 10 361 | 10 362 | 32 363 | 32 364 | keep 365 | String.IsEqual(ListItem.Property(netType), wifi) 366 | wlan.png 367 | 368 | 369 | 370 | 70 371 | 0 372 | 200 373 | 50 374 | center 375 | font13_title 376 | blue 377 | ListItem.Label 378 | true 379 | 380 | 381 | 382 | 300 383 | 0 384 | 350 385 | 50 386 | center 387 | font12 388 | blue 389 | String.IsEqual(ListItem.Property(State), online) | String.IsEqual(ListItem.Property(State), ready) 390 | 391 | 392 | 393 | 394 | 680 395 | 0 396 | 170 397 | 50 398 | center 399 | font12 400 | blue 401 | 402 | 403 | 404 | 405 | 10 406 | 40 407 | 952 408 | 2 409 | ListItem.Property(Strength) 410 | String.IsEqual(ListItem.Property(netType), wifi) 411 | 412 | 413 | 414 | 415 | separator-grey.png 416 | 417 | 418 | 419 | 930 420 | 15 421 | 20 422 | 20 423 | keep 424 | IntegerGreaterThan(ListItem.Property(Security), 0) 425 | key.png 426 | 427 | 428 | 429 | 430 | 431 | 432 | 970 433 | 586 434 | 300 435 | 75 436 | true 437 | button-fo.png 438 | button-nofo.png 439 | 447 | 448 | 320 449 | 586 450 | 300 451 | 75 452 | true 453 | button-fo.png 454 | button-nofo.png 455 | 463 | 464 | 465 | 466 | -------------------------------------------------------------------------------- /resources/skins/Default/media/arrowdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/arrowdown.png -------------------------------------------------------------------------------- /resources/skins/Default/media/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/bg.jpg -------------------------------------------------------------------------------- /resources/skins/Default/media/black-back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/black-back.png -------------------------------------------------------------------------------- /resources/skins/Default/media/bt-audio-card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/bt-audio-card.png -------------------------------------------------------------------------------- /resources/skins/Default/media/bt-camera-photo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/bt-camera-photo.png -------------------------------------------------------------------------------- /resources/skins/Default/media/bt-camera-video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/bt-camera-video.png -------------------------------------------------------------------------------- /resources/skins/Default/media/bt-computer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/bt-computer.png -------------------------------------------------------------------------------- /resources/skins/Default/media/bt-input-gaming.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/bt-input-gaming.png -------------------------------------------------------------------------------- /resources/skins/Default/media/bt-input-keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/bt-input-keyboard.png -------------------------------------------------------------------------------- /resources/skins/Default/media/bt-input-mouse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/bt-input-mouse.png -------------------------------------------------------------------------------- /resources/skins/Default/media/bt-input-tablet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/bt-input-tablet.png -------------------------------------------------------------------------------- /resources/skins/Default/media/bt-modem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/bt-modem.png -------------------------------------------------------------------------------- /resources/skins/Default/media/bt-phone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/bt-phone.png -------------------------------------------------------------------------------- /resources/skins/Default/media/bt-printer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/bt-printer.png -------------------------------------------------------------------------------- /resources/skins/Default/media/bt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/bt.png -------------------------------------------------------------------------------- /resources/skins/Default/media/button-fo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/button-fo.png -------------------------------------------------------------------------------- /resources/skins/Default/media/button-nofo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/button-nofo.png -------------------------------------------------------------------------------- /resources/skins/Default/media/connected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/connected.png -------------------------------------------------------------------------------- /resources/skins/Default/media/dialog-bg-solid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/dialog-bg-solid.png -------------------------------------------------------------------------------- /resources/skins/Default/media/dialog-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/dialog-bg.png -------------------------------------------------------------------------------- /resources/skins/Default/media/do.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/do.png -------------------------------------------------------------------------------- /resources/skins/Default/media/eth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/eth.png -------------------------------------------------------------------------------- /resources/skins/Default/media/fanart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/fanart.png -------------------------------------------------------------------------------- /resources/skins/Default/media/favorite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/favorite.png -------------------------------------------------------------------------------- /resources/skins/Default/media/floor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/floor.png -------------------------------------------------------------------------------- /resources/skins/Default/media/focus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/focus.png -------------------------------------------------------------------------------- /resources/skins/Default/media/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/icon.png -------------------------------------------------------------------------------- /resources/skins/Default/media/icon_button_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/icon_button_back.png -------------------------------------------------------------------------------- /resources/skins/Default/media/key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/key.png -------------------------------------------------------------------------------- /resources/skins/Default/media/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/logo.png -------------------------------------------------------------------------------- /resources/skins/Default/media/radio-button-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/radio-button-off.png -------------------------------------------------------------------------------- /resources/skins/Default/media/radio-button-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/radio-button-on.png -------------------------------------------------------------------------------- /resources/skins/Default/media/separator-grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/separator-grey.png -------------------------------------------------------------------------------- /resources/skins/Default/media/separator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/separator.png -------------------------------------------------------------------------------- /resources/skins/Default/media/unlock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/unlock.png -------------------------------------------------------------------------------- /resources/skins/Default/media/vpn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/vpn.png -------------------------------------------------------------------------------- /resources/skins/Default/media/wizard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/wizard.png -------------------------------------------------------------------------------- /resources/skins/Default/media/wlan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LibreELEC/service.libreelec.settings/12b47f4ab54fefd755507cf555c33771b8bc7ed3/resources/skins/Default/media/wlan.png -------------------------------------------------------------------------------- /service.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-or-later 2 | # Copyright (C) 2009-2013 Stephan Raue (stephan@openelec.tv) 3 | # Copyright (C) 2013 Lutz Fiebach (lufie@openelec.tv) 4 | # Copyright (C) 2019-present Team LibreELEC (https://libreelec.tv) 5 | 6 | import os 7 | import socket 8 | import threading 9 | 10 | import xbmc 11 | 12 | import syspath 13 | import dbus_utils 14 | import log 15 | import oe 16 | 17 | 18 | class Service_Thread(threading.Thread): 19 | 20 | SOCKET = '/var/run/service.libreelec.settings.sock' 21 | 22 | def __init__(self): 23 | threading.Thread.__init__(self) 24 | self.init() 25 | 26 | @log.log_function() 27 | def init(self): 28 | if os.path.exists(self.SOCKET): 29 | os.remove(self.SOCKET) 30 | self.daemon = True 31 | self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 32 | self.sock.setblocking(1) 33 | self.sock.bind(self.SOCKET) 34 | self.sock.listen(1) 35 | self.stopped = False 36 | 37 | @log.log_function() 38 | def run(self): 39 | if oe.read_setting('libreelec', 'wizard_completed') == None: 40 | threading.Thread(target=oe.openWizard).start() 41 | while self.stopped == False: 42 | log.log(f'Waiting', log.INFO) 43 | conn, addr = self.sock.accept() 44 | message = (conn.recv(1024)).decode('utf-8') 45 | conn.close() 46 | log.log(f'Received {message}', log.INFO) 47 | if message == 'openConfigurationWindow': 48 | if not hasattr(oe, 'winOeMain'): 49 | threading.Thread(target=oe.openConfigurationWindow).start() 50 | else: 51 | if oe.winOeMain.visible != True: 52 | threading.Thread( 53 | target=oe.openConfigurationWindow).start() 54 | if message == 'exit': 55 | self.stopped = True 56 | 57 | @log.log_function() 58 | def stop(self): 59 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 60 | sock.connect(self.SOCKET) 61 | sock.send(bytes('exit', 'utf-8')) 62 | sock.close() 63 | self.join() 64 | self.sock.close() 65 | 66 | 67 | class Monitor(xbmc.Monitor): 68 | 69 | @log.log_function() 70 | def onScreensaverActivated(self): 71 | if oe.read_setting('bluetooth', 'standby'): 72 | threading.Thread(target=oe.standby_devices).start() 73 | 74 | @log.log_function() 75 | def onDPMSActivated(self): 76 | if oe.read_setting('bluetooth', 'standby'): 77 | threading.Thread(target=oe.standby_devices).start() 78 | 79 | @log.log_function() 80 | def run(self): 81 | dbus_utils.LOOP_THREAD.start() 82 | oe.load_modules() 83 | oe.start_service() 84 | service_thread = Service_Thread() 85 | service_thread.start() 86 | while not self.abortRequested(): 87 | if self.waitForAbort(60): 88 | break 89 | if not oe.read_setting('bluetooth', 'standby'): 90 | continue 91 | timeout = oe.read_setting('bluetooth', 'idle_timeout') 92 | if not timeout: 93 | continue 94 | try: 95 | timeout = int(timeout) 96 | except: 97 | continue 98 | if timeout < 1: 99 | continue 100 | if xbmc.getGlobalIdleTime() / 60 >= timeout: 101 | log.log(f'Idle timeout reached', log.DEBUG) 102 | oe.standby_devices() 103 | if hasattr(oe, 'winOeMain') and hasattr(oe.winOeMain, 'visible'): 104 | if oe.winOeMain.visible == True: 105 | oe.winOeMain.close() 106 | oe.stop_service() 107 | service_thread.stop() 108 | dbus_utils.LOOP_THREAD.stop() 109 | 110 | 111 | if __name__ == '__main__': 112 | Monitor().run() 113 | -------------------------------------------------------------------------------- /syspath.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # Copyright (C) 2020-present Team LibreELEC (https://libreelec.tv) 3 | 4 | import os 5 | import sys 6 | 7 | sys.path.append(os.path.join(os.path.dirname(__file__), 'resources', 'lib')) 8 | sys.path.append(os.path.join(os.path.dirname(__file__), 'resources', 'lib', 'modules')) 9 | --------------------------------------------------------------------------------