├── 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 |
--------------------------------------------------------------------------------