diff --git a/README.md b/README.md
index 6928271a58..5b97d7f6e0 100644
--- a/README.md
+++ b/README.md
@@ -52,6 +52,7 @@ If you have a spare domain name you can configure applications to be accessible
* [Heimdall](https://heimdall.site/) - Home server dashboard
* [Home Assistant](https://www.home-assistant.io) - Open source home automation
* [Homebridge](https://github.com/nfarina/homebridge) - Emulate the iOS HomeKit API
+* [iSpy Agent DVR](https://www.ispyconnect.com/) - iSpy - Agent DVR is a video surveillance software
* [Jackett](https://github.com/Jackett/Jackett) - API Support for your favorite torrent trackers
* [Jellyfin](https://jellyfin.github.io) - The Free Software Media System
* [Joomla](https://www.joomla.org/) - Open source content management system
diff --git a/group_vars/all.yml b/group_vars/all.yml
index ed164f3f23..503bb6b0ba 100644
--- a/group_vars/all.yml
+++ b/group_vars/all.yml
@@ -183,6 +183,14 @@ samba_shares:
browseable: yes
path: "{{ code_root }}"
+ - name: recordings
+ comment: "Recordings"
+ guest_ok: yes
+ public: yes
+ writable: yes
+ browseable: yes
+ path: "{{ recordings_root }}"
+
###
### NFS
###
diff --git a/nas.yml b/nas.yml
index 90ce437e6f..9f2bf36956 100644
--- a/nas.yml
+++ b/nas.yml
@@ -193,6 +193,10 @@
tags:
- homepage
+ - role: ispyagentdvr
+ tags:
+ - ispyagentdvr
+
- role: jackett
tags:
- jackett
diff --git a/roles/ispyagentdvr/defaults/main.yml b/roles/ispyagentdvr/defaults/main.yml
new file mode 100644
index 0000000000..e2f51723e5
--- /dev/null
+++ b/roles/ispyagentdvr/defaults/main.yml
@@ -0,0 +1,23 @@
+---
+ispyagentdvr_enabled: false
+ispyagentdvr_available_externally: false
+
+# directories
+ispyagentdvr_config_directory: "{{ docker_home }}/ispyagentdvr/config"
+ispyagentdvr_commands_directory: "{{ docker_home }}/ispyagentdvr/commands"
+ispyagentdvr_recordings_directory: "{{ downloads_root }}/recordings"
+
+# uid / gid
+ispyagentdvr_user_id: "1000"
+ispyagentdvr_group_id: "1000"
+
+# network
+ispyagentdvr_webui_port: "8097"
+ispyagentdvr_turn_port: "3478"
+ispyagentdvr_agentdvr_host_port: "50000-50010"
+
+# specs
+ispyagentdvr_memory: 1g
+
+# docker
+ispyagentdvr_container_name: ispyagentdvr
diff --git a/roles/ispyagentdvr/docs/ispyagentdvr.md b/roles/ispyagentdvr/docs/ispyagentdvr.md
new file mode 100644
index 0000000000..aaffb18e2d
--- /dev/null
+++ b/roles/ispyagentdvr/docs/ispyagentdvr.md
@@ -0,0 +1,11 @@
+# iSpyAgentDVR
+
+Homepage:
+
+iSpy - Agent DVR is a video surveillance software.
+
+## Usage
+
+Set `ispyagentdvr_enabled: true` in your `inventories//nas.yml` file.
+
+The iSpyAgentDVR UI can be accessed from .
diff --git a/roles/ispyagentdvr/molecule/default/molecule.yml b/roles/ispyagentdvr/molecule/default/molecule.yml
new file mode 100644
index 0000000000..02d56c99f9
--- /dev/null
+++ b/roles/ispyagentdvr/molecule/default/molecule.yml
@@ -0,0 +1,6 @@
+---
+provisioner:
+ inventory:
+ group_vars:
+ all:
+ ispyagentdvr_enabled: true
diff --git a/roles/ispyagentdvr/molecule/default/side_effect.yml b/roles/ispyagentdvr/molecule/default/side_effect.yml
new file mode 100644
index 0000000000..103aaceeb4
--- /dev/null
+++ b/roles/ispyagentdvr/molecule/default/side_effect.yml
@@ -0,0 +1,10 @@
+---
+- name: Stop
+ hosts: all
+ become: true
+ tasks:
+ - name: "Include {{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }} role"
+ ansible.builtin.include_role:
+ name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}"
+ vars:
+ ispyagentdvr_enabled: false
diff --git a/roles/ispyagentdvr/molecule/default/verify.yml b/roles/ispyagentdvr/molecule/default/verify.yml
new file mode 100644
index 0000000000..dc259e6547
--- /dev/null
+++ b/roles/ispyagentdvr/molecule/default/verify.yml
@@ -0,0 +1,18 @@
+---
+- name: Verify
+ hosts: all
+ gather_facts: false
+ tasks:
+ - ansible.builtin.include_vars:
+ file: ../../defaults/main.yml
+
+ - name: Get container state
+ community.docker.docker_container_info:
+ name: "{{ ispyagentdvr_container_name }}"
+ register: result
+
+ - name: Check iSpyAgentDVR is running
+ ansible.builtin.assert:
+ that:
+ - result.container['State']['Status'] == "running"
+ - result.container['State']['Restarting'] == false
diff --git a/roles/ispyagentdvr/molecule/default/verify_stopped.yml b/roles/ispyagentdvr/molecule/default/verify_stopped.yml
new file mode 100644
index 0000000000..cdb410bd57
--- /dev/null
+++ b/roles/ispyagentdvr/molecule/default/verify_stopped.yml
@@ -0,0 +1,18 @@
+---
+- name: Verify
+ hosts: all
+ gather_facts: false
+ tasks:
+ - ansible.builtin.include_vars:
+ file: ../../defaults/main.yml
+
+ - name: Try and stop and remove iSpyAgentDVR
+ community.docker.docker_container:
+ name: "{{ ispyagentdvr_container_name }}"
+ state: absent
+ register: result
+
+ - name: Check iSpyAgentDVR is stopped
+ ansible.builtin.assert:
+ that:
+ - not result.changed
diff --git a/roles/ispyagentdvr/tasks/main.yml b/roles/ispyagentdvr/tasks/main.yml
new file mode 100644
index 0000000000..d928ab7d8b
--- /dev/null
+++ b/roles/ispyagentdvr/tasks/main.yml
@@ -0,0 +1,54 @@
+---
+- name: Start iSpyAgentDVR
+ block:
+ - name: Create iSpyAgentDVR Directories
+ ansible.builtin.file:
+ path: "{{ item }}"
+ state: directory
+ # mode: 0755
+ with_items:
+ - "{{ ispyagentdvr_config_directory }}"
+ - "{{ ispyagentdvr_commands_directory }}"
+ - "{{ ispyagentdvr_recordings_directory }}"
+
+ - name: Create iSpyAgentDVR Docker Container
+ community.docker.docker_container:
+ name: "{{ ispyagentdvr_container_name }}"
+ image: mekayelanik/ispyagentdvr:latest
+ pull: true
+ volumes:
+ - "{{ ispyagentdvr_config_directory }}:/AgentDVR/Media/XML:rw"
+ - "{{ ispyagentdvr_commands_directory }}:/AgentDVR/Commands:rw"
+ - "{{ ispyagentdvr_recordings_directory }}:/AgentDVR/Media/WebServerRoot/Media:rw"
+ ports:
+ - "{{ ispyagentdvr_webui_port }}:8090"
+ - "{{ ispyagentdvr_turn_port }}:3478/udp"
+ - "{{ ispyagentdvr_agentdvr_host_port }}:50000-50010/udp"
+ env:
+ TZ: "{{ ansible_nas_timezone }}"
+ WEBUI_PORT: "{{ ispyagentdvr_webui_port }}"
+ PUID: "{{ ispyagentdvr_user_id }}"
+ PGID: "{{ ispyagentdvr_group_id }}"
+ restart_policy: unless-stopped
+ memory: "{{ ispyagentdvr_memory }}"
+ labels:
+ traefik.enable: "{{ ispyagentdvr_available_externally | string }}"
+ traefik.http.routers.ispyagentdvr.rule: "Host(`ispyagentdvr.{{ ansible_nas_domain }}`)"
+ traefik.http.routers.ispyagentdvr.tls.certresolver: "letsencrypt"
+ traefik.http.routers.ispyagentdvr.tls.domains[0].main: "{{ ansible_nas_domain }}"
+ traefik.http.routers.ispyagentdvr.tls.domains[0].sans: "*.{{ ansible_nas_domain }}"
+ traefik.http.services.ispyagentdvr.loadbalancer.server.port: "8097"
+ homepage.group: Monitoring
+ homepage.name: iSpy AgentDVR
+ homepage.icon: ispy.png
+ homepage.href: "http://{{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'] }}:{{ ispyagentdvr_webui_port }}"
+ homepage.description: Camera Monitoring software
+ when: ispyagentdvr_enabled is true
+
+- name: Stop iSpyAgentDVR
+ block:
+ - name: Stop iSpyAgentDVR
+ community.docker.docker_container:
+ name: "{{ ispyagentdvr_container_name }}"
+ state: absent
+ when: ispyagentdvr_enabled is false