├── LICENSE ├── README.md ├── addon.xml ├── changelog.txt ├── default.py ├── icon.png ├── loading.gif └── resources ├── language ├── English │ ├── strings.po │ └── strings.xml └── German │ ├── strings.po │ └── strings.xml └── settings.xml /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Noesis Labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # script.securitycam 2 | 3 | This a rework of the kodi 'Security Cam Overlay' addon originally developed by Ryan Melena Noesis. 4 | 5 | The main change is that the addon is now capable of handling up to 4 camera feeds simultaneously. Each feed is updated in a seperate thread which should add to the addon's performance. 6 | 7 | Though the addon would technically support even more feeds, it is restricted by the arrangement of the feeds in the (single) window. The arrangement currently supports only a 4 item geometry: horizontal, vertical or square (2x2). 8 | 9 | The addon feeds each require a source providing snapshots in jpeg format. This can either be a http URL or a file source (new). You should adjust the refresh interval in accordance with the source's capapility to update its output. 10 | 11 | If you want the addon execution triggered by email (this is how I get notified exclusively of a motion detected by my cam), you may also want to look at my other project 'Kodi-Email-Alert'. 12 | 13 | Alternatively, with a PIR motion detection device sending on 433 Mhz and 433 Mhz receiver module in a raspberry pi you can kick-off the addon almost instantly on motion detected. Have a look at my project 'Kodi-RF-Alert' if you're interested. 14 | 15 | Since release 1.3.0 the addon can be called with an optional argument 'streamid' set to the index (=1,..,4) of the cam feed as configured in the addon settings. When, for example, a kodi json-rpc call for method='Addons.ExecuteAddon' is used to trigger the addon, the cam feed to be displayed can be set with params={'addonid' :'script.securitycam', 'params': {'streamid': str(index)}}. 16 | -------------------------------------------------------------------------------- /addon.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | executable 11 | 12 | 13 | all 14 | A script to support the overlay of multiple security camera image feeds. 15 | Up to 4 camera image feeds can be displayed simultaneously in one overlay. The image urls must provide a JPEG output and are queried at an adjustable interval for updates. The feeds can be arranged in various patterns. 16 | Skript zur simultanen Anzeige mehrerer Security-Kamera Feeds (Bildersequenz). 17 | Bis zu 4 Kamera Feeds lassen sich gleichzeitig darstellen. Die Bilderquellen müssen die Ausgabe im JPEG-Format unterstützen und werden zur Aktualisierung in einstellbaren Intervallen parallel abgefragt. Die Feeds können in verschiedenen Anordnungen gruppiert werden. 18 | 19 | icon.png 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /changelog.txt: -------------------------------------------------------------------------------- 1 | v3.0.1 2 | - Version updated 3 | 4 | v1.6.1 5 | - Code cleanup 6 | 7 | v1.6.0 8 | - Adaptions to make code run with both Matrix and Leia. Remove xbmc.python version from addon.xml (workaround to support Matrix and Leia with one release) 9 | 10 | v1.5.1 11 | - Code cleanup and cosmetics 12 | 13 | v1.5.0 14 | - Added string.po language files as an alternative to strings.xml which will be deprecated in kodi v19 15 | 16 | v1.4.3 17 | - (Re-)Adapted xbmc.python version for Leia in addon.xml. 18 | - Corrected change log 19 | 20 | v1.4.2 21 | - Allow duration, width, height, and url with user and password as additional parameters in jsonrpc call (all but duration require to include streamid) 22 | - Attempt to pass actions to background window while Cam Overlay is active (experimental) 23 | - Merged python 3 compatible code into master 24 | 25 | v1.4.1 26 | - First attempt to make the code run under python 2 and python 3. 27 | - Added missing dependency on script.mdoule.requests in addon.xml 28 | 29 | v1.4.0 30 | - Added capability to capture snapshots from rtsp stream (requires ffmpeg) 31 | 32 | v1.3.5 33 | - Handling of feeds with different Aspect Ratio is now a configurable option 34 | 35 | v1.3.0 36 | - Setting of streamid parameter in jsonrpc call allows selection of camera stream to be displayed by index 37 | 38 | v1.2.2 39 | - Keep last pic if update fails 40 | 41 | v1.2.1 42 | - Another fix for URLs which require authentication 43 | 44 | v1.2 45 | - Fixed authentication handling 46 | 47 | v1.0.3 --> v1.1 48 | - Support copy file for non-http URLs 49 | - add setting to invidually activate configured cams 50 | 51 | v1.0.2 52 | - Items in settings dialog rearranged 53 | - Extended description text 54 | 55 | v1.0.1 56 | - Reintroduced animation 57 | - Added more alignemnt patterns 58 | - Fixes 59 | 60 | v1.0 61 | - Code reworked. Parts removed, e.g. animation, placeholder support 62 | - Authenticatin handling once again changed (still untested) 63 | - Now supports overlay of up to 4 camera image feeds simultaneously 64 | - Image updates are run as threads to increase performance 65 | 66 | v0.0.9 67 | - Change image update method to prevent caching (should eliminate issue some users were seeing where image didn't update properly) 68 | - Eliminate dependency on urllib (now uses urllib2 exclusively) 69 | - Bumped python import version to 2.14.0 (per http://wiki.xbmc.org/index.php?title=Addon.xml#addon_attribute) 70 | 71 | v0.0.7 72 | - Fixed boolean bug that caused script to crash 73 | 74 | v0.0.6 75 | - Bump xmbc.python requirement from version 2.0 to version 2.1 76 | - Remove script.module.simplejson requirement 77 | - Add argument to ControlImage.setImage method to prevent image caching (http://mirrors.xbmc.org/docs/python-docs/13.0-gotham/xbmcgui.html#ControlImage) 78 | 79 | v0.0.5 80 | - Fixed bug causing cam image to download indefinitely when "Enable auto-close after duration" was set to disabled. 81 | - Added debug logging 82 | 83 | v0.0.4 84 | - Added support for url placeholders which can be passed to the Add-On (see http://wiki.xbmc.org/index.php?title=HOW-TO:Write_Python_Scripts#Passing_Arguments_to_a_Script) ex. http://localhost/{0}/{1}.jpg?size={2} 85 | 86 | v0.0.3 87 | - Added new method for authentication handling, previous method was failing 88 | 89 | v0.0.2 90 | - Added option to disable auto-close 91 | 92 | v0.0.1 93 | - Initial version 94 | -------------------------------------------------------------------------------- /default.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # Source: https://github.com/RyanMelenaNoesis/XbmcSecurityCamOverlayAddOn" 6 | # and kodi forum discussion: https://forum.kodi.tv/showthread.php?tid=182540 7 | # 8 | # JSONRPC Call to trigger this script: 9 | # 10 | # curl -s -u : -H "Content-Type: application/json" -X POST -d '{"jsonrpc":"2.0","method":"Addons.ExecuteAddon","params":{"addonid":"script.securitycam"},"id":1}' http://:/jsonrpc 11 | # curl -X POST -H "Content-Type: application/json' -i http://:/jsonrpc --data '{ "jsonrpc": "2.0", "method": "Addons.ExecuteAddon", "params": { "wait": false, "addonid": "script.securitycam", "params": { "streamid": "1"} }, "id": 1 }' 12 | # 13 | 14 | # Import the modules 15 | import os, time, random, string, sys, platform 16 | import xbmc, xbmcaddon, xbmcgui, xbmcvfs 17 | import requests, subprocess 18 | from requests.auth import HTTPBasicAuth, HTTPDigestAuth 19 | from threading import Thread 20 | 21 | try: 22 | from urllib.request import build_opener, HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, HTTPDigestAuthHandler, Request 23 | except ImportError: 24 | from urllib2 import build_opener, HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, HTTPDigestAuthHandler, Request 25 | 26 | if sys.version_info.major < 3: 27 | INFO = xbmc.LOGNOTICE 28 | from xbmc import translatePath 29 | else: 30 | INFO = xbmc.LOGINFO 31 | from xbmcvfs import translatePath 32 | DEBUG = xbmc.LOGDEBUG 33 | 34 | # Constants 35 | ACTION_PREVIOUS_MENU = 10 36 | ACTION_STOP = 13 37 | ACTION_NAV_BACK = 92 38 | ACTION_BACKSPACE = 110 39 | 40 | MAXCAMS = 4 41 | 42 | # Set plugin variables 43 | __addon__ = xbmcaddon.Addon() 44 | __addon_id__ = __addon__.getAddonInfo('id') 45 | __addon_path__ = __addon__.getAddonInfo('path') 46 | __profile__ = __addon__.getAddonInfo('profile') 47 | __icon__ = os.path.join(__addon_path__, 'icon.png') 48 | __loading__ = os.path.join(__addon_path__, 'loading.gif') 49 | 50 | # Get settings 51 | SETTINGS = { 52 | 'width': int(float(__addon__.getSetting('width'))), 53 | 'height': int(float(__addon__.getSetting('height'))), 54 | 'interval': int(float(__addon__.getSetting('interval'))), 55 | 'autoClose': bool(__addon__.getSetting('autoClose') == 'true'), 56 | 'duration': int(float(__addon__.getSetting('duration')) * 1000), 57 | 'alignment': int(float(__addon__.getSetting('alignment'))), 58 | 'padding': int(float(__addon__.getSetting('padding'))), 59 | 'animate': bool(__addon__.getSetting('animate') == 'true'), 60 | 'aspectRatio': int(float(__addon__.getSetting('aspectRatio'))) 61 | } 62 | 63 | CAMERAS = [] 64 | 65 | streamid = 0 66 | streamurl = None 67 | streamusr = None 68 | streampwd = None 69 | streamwd = 0 70 | streamht = 0 71 | 72 | ffmpeg_exec = 'ffmpeg.exe' if platform.system() == 'Windows' else 'ffmpeg' 73 | 74 | if len(sys.argv) > 1: 75 | for i in range (1, len(sys.argv)): 76 | try: 77 | if sys.argv[i].split('=')[0] == 'streamid': 78 | streamid = int(sys.argv[i].split('=')[1]) 79 | # break here, or keep on searching for other arguments 80 | #break 81 | if sys.argv[i].split('=')[0] == 'user': 82 | streamusr = sys.argv[i].split('=')[1] 83 | if sys.argv[i].split('=')[0] == 'password': 84 | streampwd = sys.argv[i].split('=')[1] 85 | if sys.argv[i].split('=')[0] == 'url': 86 | streamurl = sys.argv[i].split('=')[1] 87 | if sys.argv[i].split('=')[0] == 'width': 88 | streamwd = int(sys.argv[i].split('=')[1]) 89 | if sys.argv[i].split('=')[0] == 'height': 90 | streamht = int(sys.argv[i].split('=')[1]) 91 | if sys.argv[i].split('=')[0] == 'duration': 92 | SETTINGS['duration'] = int(sys.argv[i].split('=')[1]) 93 | except: 94 | continue 95 | 96 | if streamid in range(1, MAXCAMS + 1) and (streamurl or __addon__.getSetting('url{:d}'.format(streamid))): 97 | cam = { 98 | 'url': streamurl or __addon__.getSetting('url{:d}'.format(streamid)), 99 | 'username': streamusr or __addon__.getSetting('username{:d}'.format(streamid)), 100 | 'password': streampwd or __addon__.getSetting('password{:d}'.format(streamid)) 101 | } 102 | CAMERAS.append(cam) 103 | if streamwd > 0: SETTINGS['width'] = streamwd 104 | if streamht > 0: SETTINGS['height'] = streamht 105 | else: 106 | for i in range(MAXCAMS): 107 | if __addon__.getSetting('active{:d}'.format(i + 1)) == 'true': 108 | cam = { 109 | 'url': __addon__.getSetting('url{:d}'.format(i + 1)), 110 | 'username': __addon__.getSetting('username{:d}'.format(i + 1)), 111 | 'password': __addon__.getSetting('password{:d}'.format(i + 1)) 112 | } 113 | CAMERAS.append(cam) 114 | 115 | # Utils 116 | def log(message,loglevel=INFO): 117 | xbmc.log(msg='[{}] {}'.format(__addon_id__, message), level=loglevel) 118 | 119 | 120 | def which(pgm): 121 | for path in os.getenv('PATH').split(os.path.pathsep): 122 | p = os.path.join(path, pgm) 123 | if os.path.exists(p) and os.access(p, os.X_OK): 124 | return p 125 | 126 | return None 127 | 128 | # Classes 129 | class CamPreviewDialog(xbmcgui.WindowDialog): 130 | def __init__(self, cameras): 131 | self.total = len(cameras) 132 | self.cams = cameras 133 | 134 | passwd_mgr = HTTPPasswordMgrWithDefaultRealm() 135 | self.opener = build_opener() 136 | 137 | for i in range(self.total): 138 | if self.cams[i]['username'] and self.cams[i]['password']: 139 | passwd_mgr.add_password(None, self.cams[i]['url'], self.cams[i]['username'], self.cams[i]['password']) 140 | self.opener.add_handler(HTTPBasicAuthHandler(passwd_mgr)) 141 | self.opener.add_handler(HTTPDigestAuthHandler(passwd_mgr)) 142 | 143 | randomname = ''.join([random.choice(string.ascii_letters + string.digits) for n in range(32)]) 144 | self.cams[i]['tmpdir'] = os.path.join(__profile__, randomname) 145 | if not xbmcvfs.exists(self.cams[i]['tmpdir']): 146 | xbmcvfs.mkdir(self.cams[i]['tmpdir']) 147 | 148 | x, y, w, h = self.coordinates(i) 149 | self.cams[i]['control'] = xbmcgui.ControlImage(x, y, w, h, __loading__, aspectRatio = SETTINGS['aspectRatio']) 150 | self.addControl(self.cams[i]['control']) 151 | 152 | if SETTINGS['animate']: 153 | if SETTINGS['alignment'] in [0, 4, 6, 8, 9]: 154 | direction = 1 155 | else: 156 | direction = -1 157 | self.cams[i]['control'].setAnimations([('WindowOpen', 'effect=slide start=%d time=1000 tween=cubic easing=in'%(w*direction),), ('WindowClose', 'effect=slide end=%d time=1000 tween=cubic easing=in'%(w*direction),)]) 158 | 159 | 160 | def coordinates(self, position): 161 | WIDTH = 1280 162 | HEIGHT = 720 163 | 164 | w = SETTINGS['width'] 165 | h = SETTINGS['height'] 166 | p = SETTINGS['padding'] 167 | 168 | alignment = SETTINGS['alignment'] 169 | 170 | if alignment == 0: # vertical right, top to bottom 171 | x = int(WIDTH - (w + p)) 172 | y = int(p + position * (h + p)) 173 | if alignment == 1: # vertical left, top to bottom 174 | x = int(p) 175 | y = int(p + position * (h + p)) 176 | if alignment == 2: # horizontal top, left to right 177 | x = int(p + position * (w + p)) 178 | y = int(p) 179 | if alignment == 3: # horizontal bottom, left to right 180 | x = int(p + position * (w + p)) 181 | y = int(HEIGHT - (h + p)) 182 | if alignment == 4: # square right 183 | x = int(WIDTH - (2 - position%2) * (w + p)) 184 | y = int(p + position/2 * (h + p)) 185 | if alignment == 5: # square left 186 | x = int(p + position%2 * (w + p)) 187 | y = int(p + position/2 * (h + p)) 188 | if alignment == 6: # vertical right, bottom to top 189 | x = int(WIDTH - (w + p)) 190 | y = int(HEIGHT - (position + 1) * (h + p)) 191 | if alignment == 7: # vertical left, bottom to top 192 | x = int(p) 193 | y = int(HEIGHT - (position + 1) * (h + p)) 194 | if alignment == 8: # horizontal top, right to left 195 | x = int(WIDTH - (position + 1) * (w + p)) 196 | y = int(p) 197 | if alignment == 9: # horizontal bottom, right to left 198 | x = int(WIDTH - (position + 1) * (w + p)) 199 | y = int(HEIGHT - (h + p)) 200 | 201 | return x, y, w, h 202 | 203 | 204 | def start(self): 205 | self.show() 206 | self.isRunning = True 207 | 208 | for i in range(self.total): 209 | Thread(target=self.update, args=(self.cams[i],)).start() 210 | 211 | startTime = time.time() 212 | while(not SETTINGS['autoClose'] or (time.time() - startTime) * 1000 <= SETTINGS['duration']): 213 | if not self.isRunning: 214 | break 215 | xbmc.sleep(500) 216 | 217 | self.isRunning = False 218 | 219 | self.close() 220 | self.cleanup() 221 | 222 | 223 | def update(self, cam): 224 | request = Request(cam['url']) 225 | index = 1 226 | 227 | type = cam['url'][:4] 228 | 229 | if type == 'rtsp': 230 | if not which(ffmpeg_exec): 231 | log('Error: {} not installed. Can\'t process rtsp input format.'.format(ffmpeg_exec)) 232 | #self.isRunning = False 233 | self.stop() 234 | return 235 | 236 | if cam['username'] and cam['password']: 237 | input = 'rtsp://{}:{}@{}'.format(cam['username'], cam['password'], cam['url'][7:]) 238 | else: 239 | input = cam['url'] 240 | 241 | output = os.path.join(cam['tmpdir'], 'snapshot_%06d.jpg') 242 | command = [ffmpeg_exec, 243 | '-nostdin', 244 | '-rtsp_transport', 'tcp', 245 | '-i', input, 246 | '-an', 247 | '-f', 'image2', 248 | '-vf', 'fps=fps='+str(int(1000.0/SETTINGS['interval'])), 249 | '-q:v', '10', 250 | '-s', str(SETTINGS['width'])+'x'+str(SETTINGS['height']), 251 | '-vcodec', 'mjpeg', 252 | translatePath(output)] 253 | p = subprocess.Popen(command) 254 | 255 | while(self.isRunning): 256 | snapshot = os.path.join(cam['tmpdir'], 'snapshot_{:06d}.jpg'.format(index)) 257 | index += 1 258 | 259 | try: 260 | if type == 'http': 261 | imgData = self.opener.open(request).read() 262 | 263 | if imgData: 264 | file = xbmcvfs.File(snapshot, 'wb') 265 | file.write(bytearray(imgData)) 266 | file.close() 267 | 268 | elif type == 'rtsp': 269 | while(self.isRunning): 270 | if xbmcvfs.exists(snapshot): 271 | break 272 | xbmc.sleep(10) 273 | 274 | elif xbmcvfs.exists(cam['url']): 275 | xbmcvfs.copy(cam['url'], snapshot) 276 | 277 | except Exception as e: 278 | log(str(e)) 279 | #snapshot = __loading__ 280 | snapshot = None 281 | 282 | #if snapshot and xbmcvfs.exists(snapshot): 283 | if snapshot: 284 | cam['control'].setImage(snapshot, False) 285 | 286 | if type != 'rtsp': 287 | xbmc.sleep(SETTINGS['interval']) 288 | 289 | if type == 'rtsp' and p.pid: 290 | p.terminate() 291 | 292 | 293 | def cleanup(self): 294 | for i in range(self.total): 295 | files = xbmcvfs.listdir(self.cams[i]['tmpdir'])[1] 296 | for file in files: 297 | xbmcvfs.delete(os.path.join(self.cams[i]['tmpdir'], file)) 298 | xbmcvfs.rmdir(self.cams[i]['tmpdir']) 299 | 300 | 301 | def onAction(self, action): 302 | if action in (ACTION_PREVIOUS_MENU, ACTION_STOP, ACTION_BACKSPACE, ACTION_NAV_BACK): 303 | self.stop() 304 | 305 | 306 | def stop(self): 307 | self.isRunning = False 308 | 309 | 310 | if __name__ == '__main__': 311 | if streamid > 0: 312 | log('Addon called with streamid={}'.format(streamid)) 313 | if streamurl: 314 | log('and url={}'.format(streamurl)) 315 | 316 | camPreview = CamPreviewDialog(CAMERAS) 317 | camPreview.start() 318 | 319 | del camPreview 320 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Paulemann/script.securitycam/23419f1d7fb1072ff15cac23afb2e6b60bbe5d1c/icon.png -------------------------------------------------------------------------------- /loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Paulemann/script.securitycam/23419f1d7fb1072ff15cac23afb2e6b60bbe5d1c/loading.gif -------------------------------------------------------------------------------- /resources/language/English/strings.po: -------------------------------------------------------------------------------- 1 | # Kodi Media Center language file 2 | # Addon Name: Security Cam Overlay 3 | # Addon id: script.securitycam 4 | # Addon Provider: Paulemann 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Kodi-Addons\n" 8 | "Report-Msgid-Bugs-To: alanwww1@kodi.org\n" 9 | "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" 10 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 11 | "Last-Translator: Paulemann\n" 12 | "Language-Team: English\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: 8bit\n" 16 | "Language: en\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1)\n" 18 | 19 | msgctxt "#32000" 20 | msgid "Camera 1" 21 | msgstr "" 22 | 23 | msgctxt "#32001" 24 | msgid "Active:" 25 | msgstr "" 26 | 27 | msgctxt "#32002" 28 | msgid "Image Url:" 29 | msgstr "" 30 | 31 | msgctxt "#32003" 32 | msgid "Username:" 33 | msgstr "" 34 | 35 | msgctxt "#32004" 36 | msgid "Password:" 37 | msgstr "" 38 | 39 | msgctxt "#32010" 40 | msgid "Camera 2" 41 | msgstr "" 42 | 43 | msgctxt "#32020" 44 | msgid "Camera 3" 45 | msgstr "" 46 | 47 | msgctxt "#32030" 48 | msgid "Camera 4" 49 | msgstr "" 50 | 51 | msgctxt "#32040" 52 | msgid "Camera 5" 53 | msgstr "" 54 | 55 | msgctxt "#32050" 56 | msgid "Camera 6" 57 | msgstr "" 58 | 59 | msgctxt "#32060" 60 | msgid "Camera 7" 61 | msgstr "" 62 | 63 | msgctxt "#32070" 64 | msgid "Camera 8" 65 | msgstr "" 66 | 67 | msgctxt "#33000" 68 | msgid "Behavior" 69 | msgstr "" 70 | 71 | msgctxt "#33001" 72 | msgid "Window Width:" 73 | msgstr "" 74 | 75 | msgctxt "#33002" 76 | msgid "Window Height:" 77 | msgstr "" 78 | 79 | msgctxt "#33003" 80 | msgid "Refresh Interval (in milliseconds):" 81 | msgstr "" 82 | 83 | msgctxt "#33004" 84 | msgid "Enable auto-close after duration:" 85 | msgstr "" 86 | 87 | msgctxt "#33005" 88 | msgid "Duration (in seconds):" 89 | msgstr "" 90 | 91 | msgctxt "#33006" 92 | msgid "Alignment:" 93 | msgstr "" 94 | 95 | msgctxt "#33007" 96 | msgid "Padding (in Pixels):" 97 | msgstr "" 98 | 99 | msgctxt "#33008" 100 | msgid "Animate on open/close:" 101 | msgstr "" 102 | 103 | msgctxt "#33009" 104 | msgid "Aspect Ratio:" 105 | msgstr "" 106 | 107 | msgctxt "#34000" 108 | msgid "Vertical: right, from top to bottom" 109 | msgstr "" 110 | 111 | msgctxt "#34001" 112 | msgid "Vertical: left, from top to bottom" 113 | msgstr "" 114 | 115 | msgctxt "#34002" 116 | msgid "Horizontal: top, from left to right" 117 | msgstr "" 118 | 119 | msgctxt "#34003" 120 | msgid "Horizontal: bottom, from left to right" 121 | msgstr "" 122 | 123 | msgctxt "#34004" 124 | msgid "Square: right top" 125 | msgstr "" 126 | 127 | msgctxt "#34005" 128 | msgid "Square: left top" 129 | msgstr "" 130 | 131 | msgctxt "#34006" 132 | msgid "Vertical: right, from bottom to top" 133 | msgstr "" 134 | 135 | msgctxt "#34007" 136 | msgid "Vertical: left, from bottom to top" 137 | msgstr "" 138 | 139 | msgctxt "#34008" 140 | msgid "Horizontal: top, from right to left" 141 | msgstr "" 142 | 143 | msgctxt "#34009" 144 | msgid "Horizontal: bottom, from right to left" 145 | msgstr "" 146 | 147 | msgctxt "#34010" 148 | msgid "Square: right bottom" 149 | msgstr "" 150 | 151 | msgctxt "#34011" 152 | msgid "Square: left bottom" 153 | msgstr "" 154 | 155 | msgctxt "#34020" 156 | msgid "Stretch" 157 | msgstr "" 158 | 159 | msgctxt "#34021" 160 | msgid "Scale up (crops)" 161 | msgstr "" 162 | 163 | msgctxt "#34022" 164 | msgid "Scale down (black bars)" 165 | msgstr "" 166 | -------------------------------------------------------------------------------- /resources/language/English/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Camera 1 4 | Active: 5 | Image Url: 6 | Username: 7 | Password: 8 | 9 | Camera 2 10 | Camera 3 11 | Camera 4 12 | Camera 5 13 | Camera 6 14 | Camera 7 15 | Camera 8 16 | 17 | Behavior 18 | 19 | Window Width: 20 | Window Height: 21 | Refresh Interval (in milliseconds): 22 | 23 | Enable auto-close after duration: 24 | Duration (in seconds): 25 | 26 | Alignment: 27 | Padding (in Pixels): 28 | Animate on open/close: 29 | Aspect Ratio: 30 | 31 | Vertical: right, from top to bottom 32 | Vertical: left, from top to bottom 33 | Horizontal: top, from left to right 34 | Horizontal: bottom, from left to right 35 | Square: right top 36 | Square: left top 37 | Vertical: right, from bottom to top 38 | Vertical: left, from bottom to top 39 | Horizontal: top, from right to left 40 | Horizontal: bottom, from right to left 41 | Square: right bottom 42 | Square: left bottom 43 | 44 | Stretch 45 | Scale up (crops) 46 | Scale down (black bars) 47 | 48 | -------------------------------------------------------------------------------- /resources/language/German/strings.po: -------------------------------------------------------------------------------- 1 | # Kodi Media Center language file 2 | # Addon Name: Security Cam Overlay 3 | # Addon id: script.securitycam 4 | # Addon Provider: Paulemann 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Kodi-Addons\n" 8 | "Report-Msgid-Bugs-To: alanwww1@kodi.org\n" 9 | "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" 10 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 11 | "Last-Translator: Paulemann\n" 12 | "Language-Team: German\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: 8bit\n" 16 | "Language: de\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1)\n" 18 | 19 | msgctxt "#32000" 20 | msgid "Camera 1" 21 | msgstr "Kamera 1" 22 | 23 | msgctxt "#32001" 24 | msgid "Active:" 25 | msgstr "Aktiv:" 26 | 27 | msgctxt "#32002" 28 | msgid "Image Url:" 29 | msgstr "Url:" 30 | 31 | msgctxt "#32003" 32 | msgid "Username:" 33 | msgstr "Benutzername:" 34 | 35 | msgctxt "#32004" 36 | msgid "Password:" 37 | msgstr "Passwort:" 38 | 39 | msgctxt "#32010" 40 | msgid "Camera 2" 41 | msgstr "Kamera 2" 42 | 43 | msgctxt "#32020" 44 | msgid "Camera 3" 45 | msgstr "Kamera 3" 46 | 47 | msgctxt "#32030" 48 | msgid "Camera 4" 49 | msgstr "Kamera 4" 50 | 51 | msgctxt "#32040" 52 | msgid "Camera 5" 53 | msgstr "Kamera 5" 54 | 55 | msgctxt "#32050" 56 | msgid "Camera 6" 57 | msgstr "Kamera 6" 58 | 59 | msgctxt "#32060" 60 | msgid "Camera 7" 61 | msgstr "Kamera 7" 62 | 63 | msgctxt "#32070" 64 | msgid "Camera 8" 65 | msgstr "Kamera 8" 66 | 67 | msgctxt "#33000" 68 | msgid "Behavior" 69 | msgstr "Anzeige" 70 | 71 | msgctxt "#33001" 72 | msgid "Window Width:" 73 | msgstr "Fensterbreite:" 74 | 75 | msgctxt "#33002" 76 | msgid "Window Height:" 77 | msgstr "Fensterhöhe:" 78 | 79 | msgctxt "#33003" 80 | msgid "Refresh Interval (in milliseconds):" 81 | msgstr "Aktualisierungsinterval (in msec):" 82 | 83 | msgctxt "#33004" 84 | msgid "Enable auto-close after duration:" 85 | msgstr "Automatisches Schließen:" 86 | 87 | msgctxt "#33005" 88 | msgid "Duration (in seconds):" 89 | msgstr "Anzeigedauer (in Sekunden):" 90 | 91 | msgctxt "#33006" 92 | msgid "Alignment:" 93 | msgstr "Ausrichtung:" 94 | 95 | msgctxt "#33007" 96 | msgid "Padding (in Pixels):" 97 | msgstr "Zwischenraum (in Pixeln):" 98 | 99 | msgctxt "#33008" 100 | msgid "Animate on open/close:" 101 | msgstr "Animation beim Öffnen/Schließen:" 102 | 103 | msgctxt "#33009" 104 | msgid "Aspect Ratio:" 105 | msgstr "Bildformat-Anpassung:" 106 | 107 | msgctxt "#34000" 108 | msgid "Vertical: right, from top to bottom" 109 | msgstr "Vertikal: rechts, von oben nach unten" 110 | 111 | msgctxt "#34001" 112 | msgid "Vertical: left, from top to bottom" 113 | msgstr "Vertikal: links, von oben nach unten" 114 | 115 | msgctxt "#34002" 116 | msgid "Horizontal: top, from left to right" 117 | msgstr "Horizontal: oben, von links nach rechts" 118 | 119 | msgctxt "#34003" 120 | msgid "Horizontal: bottom, from left to right" 121 | msgstr "Horizontal: unten, von links nach rechts" 122 | 123 | msgctxt "#34004" 124 | msgid "Square: right top" 125 | msgstr "Quadrat: rechts oben" 126 | 127 | msgctxt "#34005" 128 | msgid "Square: left top" 129 | msgstr "Quadrat: links oben" 130 | 131 | msgctxt "#34006" 132 | msgid "Vertical: right, from bottom to top" 133 | msgstr "Vertikal: rechts, von unten nach oben" 134 | 135 | msgctxt "#34007" 136 | msgid "Vertical: left, from bottom to top" 137 | msgstr "Vertikal: links, von unten nach oben" 138 | 139 | msgctxt "#34008" 140 | msgid "Horizontal: top, from right to left" 141 | msgstr "Horizontal: oben, von rechts nach links" 142 | 143 | msgctxt "#34009" 144 | msgid "Horizontal: bottom, from right to left" 145 | msgstr "Horizontal: unten, von rechts nach links" 146 | 147 | msgctxt "#34010" 148 | msgid "Square: right bottom" 149 | msgstr "Quadrat: rechts unten" 150 | 151 | msgctxt "#34011" 152 | msgid "Square: left bottom" 153 | msgstr "Quadrat: links unten" 154 | 155 | msgctxt "#34020" 156 | msgid "Stretch" 157 | msgstr "Strecken" 158 | 159 | msgctxt "#34021" 160 | msgid "Scale up (crops)" 161 | msgstr "Vergrößern (Abschneiden)" 162 | 163 | msgctxt "#34022" 164 | msgid "Scale down (black bars)" 165 | msgstr "Verkleinern (Schw. Balken)" 166 | -------------------------------------------------------------------------------- /resources/language/German/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Kamera 1 4 | Aktiv: 5 | Url: 6 | Benutzername: 7 | Passwort: 8 | 9 | Kamera 2 10 | Kamera 3 11 | Kamera 4 12 | Kamera 5 13 | Kamera 6 14 | Kamera 7 15 | Kamera 8 16 | 17 | Anzeige 18 | 19 | Fensterbreite: 20 | Fensterhöhe: 21 | Aktualisierungsinterval (in msec): 22 | Automatisches Schließen: 23 | Anzeigedauer (in Sekunden): 24 | Ausrichtung: 25 | Zwischenraum (in Pixeln): 26 | Animation beim Öffnen/Schließen: 27 | Bildformat-Anpassung: 28 | 29 | Vertikal: rechts, von oben nach unten 30 | Vertikal: links, von oben nach unten 31 | Horizontal: oben, von links nach rechts 32 | Horizontal: unten, von links nach rechts 33 | Quadrat: rechts oben 34 | Quadrat: links oben 35 | Vertikal: rechts, von unten nach oben 36 | Vertikal: links, von unten nach oben 37 | Horizontal: oben, von rechts nach links 38 | Horizontal: unten, von rechts nach links 39 | Quadrat: rechts unten 40 | Quadrat: links unten 41 | 42 | Strecken 43 | Vergrößern (Abschneiden) 44 | Verkleinern (Schw. Balken) 45 | 46 | -------------------------------------------------------------------------------- /resources/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | --------------------------------------------------------------------------------