From bbb35b236f5832a6736b70484560aa963d43c666 Mon Sep 17 00:00:00 2001
From: ajnart
Date: Tue, 24 May 2022 20:13:01 +0200
Subject: [PATCH 01/18] :lipstick: Change the way the footer is displayed
---
src/components/layout/Footer.tsx | 3 ---
1 file changed, 3 deletions(-)
diff --git a/src/components/layout/Footer.tsx b/src/components/layout/Footer.tsx
index 11580978109..7a8df4be8f0 100644
--- a/src/components/layout/Footer.tsx
+++ b/src/components/layout/Footer.tsx
@@ -81,9 +81,6 @@ export function Footer({ links }: FooterCenteredProps) {
background: 'none',
border: 'none',
clear: 'both',
- position: 'fixed',
- bottom: '0',
- left: '0',
}}
>
From 802f7fd6c77a49f34147099fb936556da504cb54 Mon Sep 17 00:00:00 2001
From: ajnart
Date: Tue, 24 May 2022 20:14:07 +0200
Subject: [PATCH 02/18] :technologist: Added strings as an option type for
modules
---
src/components/modules/moduleWrapper.tsx | 44 ++++++++++++++++++++++--
src/components/modules/modules.tsx | 2 +-
2 files changed, 42 insertions(+), 4 deletions(-)
diff --git a/src/components/modules/moduleWrapper.tsx b/src/components/modules/moduleWrapper.tsx
index dada02e2e2d..0afc0dc8504 100644
--- a/src/components/modules/moduleWrapper.tsx
+++ b/src/components/modules/moduleWrapper.tsx
@@ -1,4 +1,4 @@
-import { Card, Menu, Switch, useMantineTheme } from '@mantine/core';
+import { Button, Card, Group, Menu, Switch, TextInput, useMantineTheme } from '@mantine/core';
import { useConfig } from '../../tools/state';
import { IModule } from './modules';
@@ -19,13 +19,51 @@ export function ModuleWrapper(props: any) {
types.forEach((type, index) => {
const optionName = `${module.title}.${keys[index]}`;
const moduleInConfig = config.modules?.[module.title];
+ if (type === 'string') {
+ items.push(
+
+ );
+ }
// TODO: Add support for other types
if (type === 'boolean') {
items.push(
{
@@ -59,7 +97,7 @@ export function ModuleWrapper(props: any) {
{module.options && (
Date: Tue, 24 May 2022 20:14:26 +0200
Subject: [PATCH 03/18] :bug: Fixing issues with weahter module
---
.../modules/weather/WeatherModule.tsx | 35 +++++++++++--------
1 file changed, 21 insertions(+), 14 deletions(-)
diff --git a/src/components/modules/weather/WeatherModule.tsx b/src/components/modules/weather/WeatherModule.tsx
index dd08d3e7944..167c58464a4 100644
--- a/src/components/modules/weather/WeatherModule.tsx
+++ b/src/components/modules/weather/WeatherModule.tsx
@@ -27,6 +27,10 @@ export const WeatherModule: IModule = {
name: 'Display in Fahrenheit',
value: false,
},
+ location: {
+ name: 'Current location',
+ value: '',
+ },
},
};
@@ -128,27 +132,30 @@ export function WeatherIcon(props: any) {
export default function WeatherComponent(props: any) {
// Get location from browser
- const [location, setLocation] = useState({ lat: 0, lng: 0 });
const { config } = useConfig();
const [weather, setWeather] = useState({} as WeatherResponse);
+ const cityInput: string =
+ (config?.modules?.[WeatherModule.title]?.options?.location?.value as string) ?? '';
const isFahrenheit: boolean =
- config?.modules?.[WeatherModule.title]?.options?.freedomunit?.value ?? false;
-
- if ('geolocation' in navigator && location.lat === 0 && location.lng === 0) {
- navigator.geolocation.getCurrentPosition((position) => {
- setLocation({ lat: position.coords.latitude, lng: position.coords.longitude });
- });
- }
+ (config?.modules?.[WeatherModule.title]?.options?.freedomunit?.value as boolean) ?? false;
useEffect(() => {
axios
- .get(
- `https://api.open-meteo.com/v1/forecast?latitude=${location.lat}&longitude=${location.lng}&daily=weathercode,temperature_2m_max,temperature_2m_min¤t_weather=true&timezone=Europe%2FLondon`
- )
- .then((res) => {
- setWeather(res.data);
+ .get(`https://geocoding-api.open-meteo.com/v1/search?name=${cityInput}`)
+ .then((response) => {
+ // Check if results exists
+ const { latitude, longitude } = response.data.results
+ ? response.data.results[0]
+ : { latitude: 0, longitude: 0 };
+ axios
+ .get(
+ `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&daily=weathercode,temperature_2m_max,temperature_2m_min¤t_weather=true&timezone=Europe%2FLondon`
+ )
+ .then((res) => {
+ setWeather(res.data);
+ });
});
- }, []);
+ }, [cityInput]);
if (!weather.current_weather) {
return null;
}
From 4984866fb36e20467c9cf007e2dd3959af83b550 Mon Sep 17 00:00:00 2001
From: ajnart
Date: Tue, 24 May 2022 20:15:07 +0200
Subject: [PATCH 04/18] :rotating_light: Linting and add icons
Adds future support for self hosted icons
---
public/icons/.gitkeep | 0
src/components/AppShelf/AppShelfItem.tsx | 10 +---------
2 files changed, 1 insertion(+), 9 deletions(-)
create mode 100644 public/icons/.gitkeep
diff --git a/public/icons/.gitkeep b/public/icons/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/src/components/AppShelf/AppShelfItem.tsx b/src/components/AppShelf/AppShelfItem.tsx
index 585f14bd117..047caa9dcd7 100644
--- a/src/components/AppShelf/AppShelfItem.tsx
+++ b/src/components/AppShelf/AppShelfItem.tsx
@@ -1,12 +1,4 @@
-import {
- Text,
- Card,
- Anchor,
- AspectRatio,
- Image,
- Center,
- createStyles,
-} from '@mantine/core';
+import { Text, Card, Anchor, AspectRatio, Image, Center, createStyles } from '@mantine/core';
import { motion } from 'framer-motion';
import { useState } from 'react';
import { useSortable } from '@dnd-kit/sortable';
From fd73c7f70dfa025270a293753f6c1584da05ca29 Mon Sep 17 00:00:00 2001
From: WalkxCode
Date: Tue, 24 May 2022 21:21:20 +0200
Subject: [PATCH 05/18] :memo: (README): Updates documentation & Move to Wiki
---
README.md | 259 +++++++++++++++++++++++++-----------------------------
1 file changed, 122 insertions(+), 137 deletions(-)
diff --git a/README.md b/README.md
index 3c1ad52c057..229963003e4 100644
--- a/README.md
+++ b/README.md
@@ -1,69 +1,95 @@
-Homarr
-
-
- Don't forget to star the repo if you enjoy the Homarr project!
-
-
-
-
-
-
-
-
-
-
-
+
+
+Homarr
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Join the discord! — Don't forget to star the repo if you are enjoying the project!
+
+ Demo ↗️ • Install ➡️
+
-
-
- A homepage for your server.
-
- Demo ↗️ • Install ➡️
-
-
- Join the discord!
-
-
-
-
-
+---
+
+
+
+
+Homarr is a simple and lightweight homepage for your server, that helps you easily access all of your services in one place.
+
+It integrates with the services you use to display information on the homepage (E.g. Show upcoming Sonarr/Radarr releases).
+
+For a full list of integrations look at: [wiki/integrations](#).
+
+If you have any questions about Homarr or want to share information with us, please go to one of the following places:
+
+- [Github Discussions](https://github.com/ajnart/homarr/discussions)
+- [Discord Server](https://discord.gg/aCsmEV5RgA)
-# 📃 Table of Contents
-- [📃 Table of Contents](#-table-of-contents)
-- [🚀 Getting Started](#-getting-started)
- - [ℹ️ About](#ℹ️-about)
- - [💥 Known Issues](#-known-issues)
- - [⚡ Installation](#-installation)
- - [🐳 Deploying from Docker Image](#-deploying-from-docker-image)
- - [🛠️ Building from Source](#%EF%B8%8F-building-from-source)
- - [📖 Guides](#-guides)
- - [🔁 Drag and Drop (Rearrange)](#-drag-and-drop-rearrange)
- - [🔧 Configuration](#-configuration)
- - [🧩 Integrations](#--integrations)
- - [🧑🤝🧑 Multiple Configs](#-multiple-configs)
- - [🐻 Icons](#-icons)
- - [📊 Modules](#-modules)
- - [🔍 Search Bar](#-search-bar)
+*Before you file an [issue](https://github.com/ajnart/homarr/issues/new/choose), make sure you have the read [known issues](#-known-issues) section.*
+
+**For more information, [read the wiki!](https://github.com/ajnart/homarr/wiki)**
+
+
+ Table of Contents
+
+
+- [✨ Features](#-features)
+- [👀 Preview](#-preview)
+- [💥 Known Issues](#-known-issues)
+- [🚀 Installation](#-installation)
+ - [🐳 Deploying from Docker Image](#-deploying-from-docker-image)
+ - [🛠️ Building from Source](#️-building-from-source)
- [💖 Contributing](#-contributing)
- - [🍏 Request Icons](#-request-icons)
+- [📜 License](#-license)
+
+
-
-# 🚀 Getting Started
+---
-## ℹ️ About
+## ✨ Features
+- Integrates with services you use.
+- Search the web direcetly from your homepage.
+- Real-time status indicator for every service.
+- Automatically finds icons while you type the name of a serivce.
+- Widgets that can display all types of information.
+- Easy deployment with Docker.
+- Very light-weight and fast.
+- Free and Open-Source.
+- And more...
-Homarr is a simple and lightweight homepage for your server, that helps you easily access all of your services in one place.
-
**[⤴️ Back to Top](#-table-of-contents)**
+---
+
+## 👀 Preview
+
+
+**[⤴️ Back to Top](#-table-of-contents)**
+
+---
+
## 💥 Known Issues
- Posters on the Calendar get blocked by adblockers. (IMDb posters)
**[⤴️ Back to Top](#-table-of-contents)**
-## ⚡ Installation
+---
+## 🚀 Installation
### 🐳 Deploying from Docker Image
> Supported architectures: x86-64, ARM, ARM64
@@ -72,16 +98,16 @@ _Requirements_:
**Standard Docker Install**
```sh
-docker run --name homarr -p 7575:7575 -v /data/docker/homarr:/app/data/configs -d ghcr.io/ajnart/homarr:latest
+docker run --name homarr --restart unless-stopped -p 7575:7575 -v /data/docker/homarr:/app/data/configs -d ghcr.io/ajnart/homarr:latest
```
**Docker Compose**
```yml
---
version: '3'
-#--------------------------------------------------------------------------------------------#
-# Homarr - A homepage for your server. #
-#--------------------------------------------------------------------------------------------#
+#---------------------------------------------------------------------#
+# Homarr - A homepage for your server. #
+#---------------------------------------------------------------------#
services:
homarr:
container_name: homarr
@@ -93,7 +119,13 @@ services:
- '7575:7575'
```
-***Getting EACCESS errors in the logs? Try running `sudo chmod 775 /directory-you-mounted-to`!***
+```sh
+docker compose up -d
+```
+
+*Getting EACCESS errors in the logs? Try running `sudo chmod 777 /directory-you-mounted-to`!*
+
+**[⤴️ Back to Top](#-table-of-contents)**
### 🛠️ Building from Source
@@ -110,101 +142,54 @@ _Requirements_:
- Start the NextJS web server: ``yarn start``
- *Note: If you want to update the code in real time, launch with ``yarn dev``*
-## 📖 Guides
-
-### 🔁 Drag and Drop (Rearrange)
-You can rearrange items by Drag and Dropping them to any position. To Drag an Drop, click and hold an icon for 250ms and then drag it to the desired position.
-
-## 🔧 Configuration
-
-### 🧩 Integrations
-
-Homarr natively integrates with your services. Here is a list of all supported services.
-
-**Emby**
-*The Emby integration is still in development.*
-
-**Lidarr**
-*The Lidarr integration is still in development.*
-
-**Sonarr**
-*Sonarr needs an API key.*
-Make a new API key in `Advanced > Security > Create new API key`
-**Current integration:** Upcoming media is displayed in the **Calendar** module.
-
-**Plex**
-*The Plex integration is still in development.*
-
-**Radarr**
-*Radarr needs an API key.*
-Make a new API key in `Advanced > Security > Create new API key`
-**Current integration:** Upcoming media is displayed in the **Calendar** module.
-
-**qBittorent**
-*The qBittorent integration is still in development.*
-
**[⤴️ Back to Top](#-table-of-contents)**
-### 🧑🤝🧑 Multiple Configs
-
-Homarr allows the usage of multiple configs. You can add a new config in two ways.
-
-**Drag-and-Drop**
-1. Download your config from the Homarr settings.
-2. Change the name of the `.json` file and the name in the `.json` file to any name you want *(just make sure it's different)*.
-3. Drag-and-Drop the file into the Homarr tab in your browser.
-4. Change the config in settings.
-
-**Using a filebrowser**
-1. Locate your mounted `default.json` file.
-2. Duplicate your `default.json` file.
-3. Change the name of the `.json` file and the name in the `.json` file to any name you want *(just make sure it's different)*.
-4. Refresh the Homarr tab in your browser.
-5. Change the config in settings.
-
-**[⤴️ Back to Top](#-table-of-contents)**
-
-### 🐻 Icons
+---
-The icons used in Homarr are automatically requested from the [dashboard-icons](https://github.com/walkxhub/dashboard-icons) repo.
+## 💖 Contributing
+**Please read our [Contribution Guidelines](/CONTRIBUTING.md)**
-Icons are requested in the following way:
-`Grab name > Replace ' ' with '-' > .toLower() > https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/{name}.png`
+All contributions are highly appreciated.
**[⤴️ Back to Top](#-table-of-contents)**
-### 📊 Modules
-
-Modules are blocks shown on the sides of the Homarr dashboard that display information. They can be enabled in settings.
-
-**Clock Module**
-The Clock Module will display your current time and date.
-
-**Calendar Module**
-The Calendar Module uses [integrations](#--integrations-1) to display new content.
-
-**Weather Module**
-The Weather Module uses your devices location to display the current, highest, and lowest temperature.
-
-**[⤴️ Back to Top](#-table-of-contents)**
+---
-### 🔍 Search Bar
-The Search Bar will open any Search Query after the Query URL you've specified in settings.
+## 📜 License
+Homarr is Licensed under [MIT](https://en.wikipedia.org/wiki/MIT_License)
-*(Eg. `https://www.google.com/search?q=*Your Query will be inserted here*`)*
+```txt
+Copyright © 2022 Thomas "ajnart" Camlong
-**[⤴️ Back to Top](#-table-of-contents)**
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
-# 💖 Contributing
-**Please read our [Contribution Guidelines](/CONTRIBUTING.md)**
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
-All contributions are highly appreciated.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+```
**[⤴️ Back to Top](#-table-of-contents)**
-## 🍏 Request Icons
+---
-The icons used in Homarr are automatically requested from the [dashboard-icons](https://github.com/walkxhub/dashboard-icons) repo. You can make a icon request by creating an [issue](https://github.com/walkxhub/dashboard-icons/issues/new/choose).
-
-**[⤴️ Back to Top](#-table-of-contents)**
+
+ Thank you for visiting! For more information read the wiki!
+
+
+
+
+
+
From fd65dc89439727ce7ffad86750cb53c82bf1eb58 Mon Sep 17 00:00:00 2001
From: Bjorn Lammers
Date: Tue, 24 May 2022 21:26:10 +0200
Subject: [PATCH 06/18] =?UTF-8?q?=F0=9F=93=9D=20Fix=20some=20bugs?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 28 +++++++++++++++-------------
1 file changed, 15 insertions(+), 13 deletions(-)
diff --git a/README.md b/README.md
index 229963003e4..557163d9bc0 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,9 @@
-
+
-Homarr
+Homarr
+
@@ -13,14 +14,15 @@
-
+
-
+
Join the discord! — Don't forget to star the repo if you are enjoying the project!
-
+
+
Demo ↗️ • Install ➡️
-
+
---
@@ -71,21 +73,21 @@ If you have any questions about Homarr or want to share information with us, ple
- Free and Open-Source.
- And more...
-**[⤴️ Back to Top](#-table-of-contents)**
+**[⤴️ Back to Top](#homarr)**
---
## 👀 Preview
-**[⤴️ Back to Top](#-table-of-contents)**
+**[⤴️ Back to Top](#homarr)**
---
## 💥 Known Issues
- Posters on the Calendar get blocked by adblockers. (IMDb posters)
-**[⤴️ Back to Top](#-table-of-contents)**
+**[⤴️ Back to Top](#homarr)**
---
@@ -125,7 +127,7 @@ docker compose up -d
*Getting EACCESS errors in the logs? Try running `sudo chmod 777 /directory-you-mounted-to`!*
-**[⤴️ Back to Top](#-table-of-contents)**
+**[⤴️ Back to Top](#homarr)**
### 🛠️ Building from Source
@@ -142,7 +144,7 @@ _Requirements_:
- Start the NextJS web server: ``yarn start``
- *Note: If you want to update the code in real time, launch with ``yarn dev``*
-**[⤴️ Back to Top](#-table-of-contents)**
+**[⤴️ Back to Top](#homarr)**
---
@@ -151,7 +153,7 @@ _Requirements_:
All contributions are highly appreciated.
-**[⤴️ Back to Top](#-table-of-contents)**
+**[⤴️ Back to Top](#homarr)**
---
@@ -181,7 +183,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
-**[⤴️ Back to Top](#-table-of-contents)**
+**[⤴️ Back to Top](#homarr)**
---
From 3249d766b32e4f33688fe04b8a57f2548e46880c Mon Sep 17 00:00:00 2001
From: Bjorn Lammers
Date: Tue, 24 May 2022 21:39:34 +0200
Subject: [PATCH 07/18] =?UTF-8?q?=F0=9F=93=9D=20Add=20"Read=20the=20Wiki"?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 557163d9bc0..421bad80eb6 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@
Join the discord! — Don't forget to star the repo if you are enjoying the project!
- Demo ↗️ • Install ➡️
+ Demo ↗️ • Install ➡️ • Read the Wiki 📄
---
From 364055b9b6e2d2f9c166be2e05e8d0bee9a645ee Mon Sep 17 00:00:00 2001
From: Bjorn Lammers
Date: Tue, 24 May 2022 21:48:06 +0200
Subject: [PATCH 08/18] =?UTF-8?q?=F0=9F=93=9D=20Oopsie=20doopsie=20hehe?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 421bad80eb6..5a45f143821 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@
Join the discord! — Don't forget to star the repo if you are enjoying the project!
- Demo ↗️ • Install ➡️ • Read the Wiki 📄
+ Demo ↗️ • Install ➡️ • Read the Wiki 📄
---
From f029483f1e69435ff4316a5a3f461392d35967e2 Mon Sep 17 00:00:00 2001
From: Bjorn Lammers
Date: Tue, 24 May 2022 21:50:57 +0200
Subject: [PATCH 09/18] =?UTF-8?q?=F0=9F=94=96=20Update=20version=20to=20v0?=
=?UTF-8?q?.5.1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
data/constants.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/data/constants.ts b/data/constants.ts
index 9c979351842..0e475f1c61c 100644
--- a/data/constants.ts
+++ b/data/constants.ts
@@ -1,2 +1,2 @@
export const REPO_URL = 'ajnart/homarr';
-export const CURRENT_VERSION = 'v0.5.0';
+export const CURRENT_VERSION = 'v0.5.1';
From 09dd5d79076a1d019b18101558b87afd6ea47087 Mon Sep 17 00:00:00 2001
From: Bjorn Lammers
Date: Tue, 24 May 2022 21:51:08 +0200
Subject: [PATCH 10/18] =?UTF-8?q?=F0=9F=94=96=20Update=20version=20to=20v0?=
=?UTF-8?q?.5.1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 59d5c5b6d6f..203aa805db8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "homarr",
- "version": "0.5.0",
+ "version": "0.5.1",
"private": "false",
"description": "Homarr - A homepage for your server.",
"repository": {
From a89b0746ba0a076e7dfa6c4b51840d2b093da90e Mon Sep 17 00:00:00 2001
From: ajnart
Date: Tue, 24 May 2022 22:55:10 +0200
Subject: [PATCH 11/18] :lipstick: Make the settings menu a drawer instead
---
src/components/Settings/SettingsMenu.tsx | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/src/components/Settings/SettingsMenu.tsx b/src/components/Settings/SettingsMenu.tsx
index ed7c425c6fa..4693e65f7f2 100644
--- a/src/components/Settings/SettingsMenu.tsx
+++ b/src/components/Settings/SettingsMenu.tsx
@@ -1,12 +1,12 @@
import {
ActionIcon,
Group,
- Modal,
Title,
Text,
Tooltip,
SegmentedControl,
TextInput,
+ Drawer,
} from '@mantine/core';
import { useColorScheme } from '@mantine/hooks';
import { useState } from 'react';
@@ -100,15 +100,16 @@ export function SettingsMenuButton(props: any) {
const [opened, setOpened] = useState(false);
return (
<>
- Settings}
opened={props.opened || opened}
onClose={() => setOpened(false)}
>
-
+
Date: Tue, 24 May 2022 22:55:28 +0200
Subject: [PATCH 12/18] :sparkles: Add a way to delete a config via the API
---
src/pages/api/configs/[slug].ts | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/src/pages/api/configs/[slug].ts b/src/pages/api/configs/[slug].ts
index dfa8ed8d0af..6f71ca2576a 100644
--- a/src/pages/api/configs/[slug].ts
+++ b/src/pages/api/configs/[slug].ts
@@ -51,6 +51,9 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method === 'PUT') {
return Put(req, res);
}
+ if (req.method === 'DELETE') {
+ return Delete(req, res);
+ }
if (req.method === 'GET') {
return Get(req, res);
}
@@ -59,3 +62,28 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {
message: 'Method not allowed',
});
};
+
+function Delete(req: NextApiRequest, res: NextApiResponse) {
+ // Get the slug of the request
+ const { slug } = req.query as { slug: string };
+ if (!slug) {
+ return res.status(400).json({
+ message: 'Wrong request',
+ });
+ }
+ // Loop over all the files in the /data/configs directory
+ const files = fs.readdirSync('data/configs');
+ // Strip the .json extension from the file name
+ const configs = files.map((file) => file.replace('.json', ''));
+ // If the target is not in the list of files, return an error
+ if (!configs.includes(slug)) {
+ return res.status(404).json({
+ message: 'Target not found',
+ });
+ }
+ // Delete the file
+ fs.unlinkSync(path.join('data/configs', `${slug}.json`));
+ return res.status(200).json({
+ message: 'Configuration deleted with success',
+ });
+}
From 9eef4988e7f32a8523c820f2abcc181f4d9a5728 Mon Sep 17 00:00:00 2001
From: ajnart
Date: Tue, 24 May 2022 22:55:47 +0200
Subject: [PATCH 13/18] :sparkles: Add a way to save a config and delete it
---
src/components/Config/ConfigChanger.tsx | 2 +-
src/components/Config/SaveConfig.tsx | 90 +++++++++++++++++++++++--
2 files changed, 85 insertions(+), 7 deletions(-)
diff --git a/src/components/Config/ConfigChanger.tsx b/src/components/Config/ConfigChanger.tsx
index 5b883680336..8d4be4db805 100644
--- a/src/components/Config/ConfigChanger.tsx
+++ b/src/components/Config/ConfigChanger.tsx
@@ -9,7 +9,7 @@ export default function ConfigChanger() {
useEffect(() => {
getConfigs().then((configs) => setConfigList(configs));
// setConfig(initialConfig);
- }, [config]);
+ }, [config.name]);
// If configlist is empty, return a loading indicator
if (configList.length === 0) {
return (
diff --git a/src/components/Config/SaveConfig.tsx b/src/components/Config/SaveConfig.tsx
index cfaf196d47c..a0b7997e7ca 100644
--- a/src/components/Config/SaveConfig.tsx
+++ b/src/components/Config/SaveConfig.tsx
@@ -1,18 +1,96 @@
-import { Button } from '@mantine/core';
+import { Button, Group, Modal, TextInput } from '@mantine/core';
+import { useForm } from '@mantine/form';
+import { showNotification } from '@mantine/notifications';
+import axios from 'axios';
import fileDownload from 'js-file-download';
-import { Download } from 'tabler-icons-react';
+import { useState } from 'react';
+import { Check, Download, Plus, Trash, X } from 'tabler-icons-react';
import { useConfig } from '../../tools/state';
export default function SaveConfigComponent(props: any) {
- const { config } = useConfig();
+ const [opened, setOpened] = useState(false);
+ const { config, setConfig } = useConfig();
+ const form = useForm({
+ initialValues: {
+ configName: config.name,
+ },
+ });
function onClick(e: any) {
if (config) {
fileDownload(JSON.stringify(config, null, '\t'), `${config.name}.json`);
}
}
return (
- } variant="outline" onClick={onClick}>
- Download your config
-
+
+ setOpened(false)}
+ title="Choose the name of your new config"
+ >
+
+
+ } variant="outline" onClick={onClick}>
+ Download your config
+
+ }
+ variant="outline"
+ onClick={() => {
+ axios
+ .delete(`/api/configs/${config.name}`)
+ .then(() => {
+ showNotification({
+ title: 'Config deleted',
+ icon: ,
+ color: 'green',
+ autoClose: 1500,
+ radius: 'md',
+ message: 'Config deleted',
+ });
+ })
+ .catch(() => {
+ showNotification({
+ title: 'Config delete failed',
+ icon: ,
+ color: 'red',
+ autoClose: 1500,
+ radius: 'md',
+ message: 'Config delete failed',
+ });
+ });
+ setConfig({ ...config, name: 'default' });
+ }}
+ >
+ Delete current config
+
+ } variant="outline" onClick={() => setOpened(true)}>
+ Save a copy of your config
+
+
);
}
From 4f68f7e3957c1dc0c6ce286c60cff6f63c593125 Mon Sep 17 00:00:00 2001
From: ajnart
Date: Tue, 24 May 2022 23:02:27 +0200
Subject: [PATCH 14/18] :sparkles: Add a keybind to open settings, CTRL + L
---
src/components/Settings/SettingsMenu.tsx | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/components/Settings/SettingsMenu.tsx b/src/components/Settings/SettingsMenu.tsx
index 4693e65f7f2..d0d57fd5af9 100644
--- a/src/components/Settings/SettingsMenu.tsx
+++ b/src/components/Settings/SettingsMenu.tsx
@@ -8,7 +8,7 @@ import {
TextInput,
Drawer,
} from '@mantine/core';
-import { useColorScheme } from '@mantine/hooks';
+import { useColorScheme, useHotkeys } from '@mantine/hooks';
import { useState } from 'react';
import { Settings as SettingsIcon } from 'tabler-icons-react';
import { useConfig } from '../../tools/state';
@@ -97,6 +97,8 @@ function SettingsMenu(props: any) {
}
export function SettingsMenuButton(props: any) {
+ useHotkeys([['ctrl+L', () => setOpened(!opened)]]);
+
const [opened, setOpened] = useState(false);
return (
<>
From 2cb6781a94147292ff596b1275330300d7682217 Mon Sep 17 00:00:00 2001
From: ajnart
Date: Wed, 25 May 2022 10:50:57 +0200
Subject: [PATCH 15/18] :sparkles: Lidarr and Readarr integrations
---
src/components/AppShelf/AddAppShelfItem.tsx | 5 +-
.../modules/calendar/CalendarModule.tsx | 149 +++++++++++++++++-
.../modules/calendar/MediaDisplay.tsx | 86 ++++++++--
src/tools/types.ts | 2 +
4 files changed, 218 insertions(+), 24 deletions(-)
diff --git a/src/components/AppShelf/AddAppShelfItem.tsx b/src/components/AppShelf/AddAppShelfItem.tsx
index c061930e901..871e45f051e 100644
--- a/src/components/AppShelf/AddAppShelfItem.tsx
+++ b/src/components/AppShelf/AddAppShelfItem.tsx
@@ -173,7 +173,10 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } &
{...form.getInputProps('type')}
/>
- {(form.values.type === 'Sonarr' || form.values.type === 'Radarr') && (
+ {(form.values.type === 'Sonarr' ||
+ form.values.type === 'Radarr' ||
+ form.values.type === 'Lidarr' ||
+ form.values.type === 'Readarr') && (
{
// Filter only sonarr and radarr services
const filtered = config.services.filter(
- (service) => service.type === 'Sonarr' || service.type === 'Radarr'
+ (service) =>
+ service.type === 'Sonarr' ||
+ service.type === 'Radarr' ||
+ service.type === 'Lidarr' ||
+ service.type === 'Readarr'
);
// Get the url and apiKey for all Sonarr and Radarr services
const sonarrService = filtered.filter((service) => service.type === 'Sonarr').at(0);
const radarrService = filtered.filter((service) => service.type === 'Radarr').at(0);
+ const lidarrService = filtered.filter((service) => service.type === 'Lidarr').at(0);
+ const readarrService = filtered.filter((service) => service.type === 'Readarr').at(0);
const nextMonth = new Date(new Date().setMonth(new Date().getMonth() + 2)).toISOString();
if (sonarrService && sonarrService.apiKey) {
const baseUrl = new URL(sonarrService.url).origin;
@@ -69,6 +82,44 @@ export default function CalendarComponent(props: any) {
}
);
}
+ if (lidarrService && lidarrService.apiKey) {
+ const baseUrl = new URL(lidarrService.url).origin;
+ fetch(`${baseUrl}/api/v1/calendar?apikey=${lidarrService?.apiKey}&end=${nextMonth}`).then(
+ (response) => {
+ response.ok &&
+ response.json().then((data) => {
+ setLidarrMedias(data);
+ showNotification({
+ title: 'Lidarr',
+ icon: ,
+ color: 'green',
+ autoClose: 1500,
+ radius: 'md',
+ message: `Loaded ${data.length} releases`,
+ });
+ });
+ }
+ );
+ }
+ if (readarrService && readarrService.apiKey) {
+ const baseUrl = new URL(readarrService.url).origin;
+ fetch(`${baseUrl}/api/v1/calendar?apikey=${readarrService?.apiKey}&end=${nextMonth}`).then(
+ (response) => {
+ response.ok &&
+ response.json().then((data) => {
+ setReadarrMedias(data);
+ showNotification({
+ title: 'Readarr',
+ icon: ,
+ color: 'green',
+ autoClose: 1500,
+ radius: 'md',
+ message: `Loaded ${data.length} releases`,
+ });
+ });
+ }
+ );
+ }
}, [config.services]);
if (sonarrMedias === undefined && radarrMedias === undefined) {
@@ -82,6 +133,8 @@ export default function CalendarComponent(props: any) {
renderdate={renderdate}
sonarrmedias={sonarrMedias}
radarrmedias={radarrMedias}
+ lidarrmedias={lidarrMedias}
+ readarrmedias={readarrMedias}
/>
)}
/>
@@ -93,12 +146,25 @@ function DayComponent(props: any) {
renderdate,
sonarrmedias,
radarrmedias,
- }: { renderdate: Date; sonarrmedias: []; radarrmedias: [] } = props;
+ lidarrmedias,
+ readarrmedias,
+ }: { renderdate: Date; sonarrmedias: []; radarrmedias: []; lidarrmedias: []; readarrmedias: [] } =
+ props;
const [opened, setOpened] = useState(false);
const theme = useMantineTheme();
const day = renderdate.getDate();
- // Itterate over the medias and filter the ones that are on the same day
+
+ const readarrFiltered = readarrmedias.filter((media: any) => {
+ const mediaDate = new Date(media.releaseDate);
+ return mediaDate.getDate() === day;
+ });
+
+ const lidarrFiltered = lidarrmedias.filter((media: any) => {
+ const date = new Date(media.releaseDate);
+ // Return true if the date is renerdate without counting hours and minutes
+ return date.getDate() === day && date.getMonth() === renderdate.getMonth();
+ });
const sonarrFiltered = sonarrmedias.filter((media: any) => {
const date = new Date(media.airDate);
// Return true if the date is renerdate without counting hours and minutes
@@ -109,7 +175,12 @@ function DayComponent(props: any) {
// Return true if the date is renerdate without counting hours and minutes
return date.getDate() === day && date.getMonth() === renderdate.getMonth();
});
- if (sonarrFiltered.length === 0 && radarrFiltered.length === 0) {
+ if (
+ sonarrFiltered.length === 0 &&
+ radarrFiltered.length === 0 &&
+ lidarrFiltered.length === 0 &&
+ readarrFiltered.length === 0
+ ) {
return {day}
;
}
@@ -119,8 +190,58 @@ function DayComponent(props: any) {
setOpened(true);
}}
>
- {radarrFiltered.length > 0 && }
- {sonarrFiltered.length > 0 && }
+ {readarrFiltered.length > 0 && (
+
+ )}
+ {radarrFiltered.length > 0 && (
+
+ )}
+ {sonarrFiltered.length > 0 && (
+
+ )}
+ {lidarrFiltered.length > 0 && (
+
+ )}
}
))}
+ {lidarrFiltered.map((media: any, index: number) => (
+
+
+ {index < lidarrFiltered.length - 1 && }
+
+ ))}
+ {readarrFiltered.map((media: any, index: number) => (
+
+
+ {index < readarrFiltered.length - 1 && }
+
+ ))}
diff --git a/src/components/modules/calendar/MediaDisplay.tsx b/src/components/modules/calendar/MediaDisplay.tsx
index ccf6745b360..a3e5a5674af 100644
--- a/src/components/modules/calendar/MediaDisplay.tsx
+++ b/src/components/modules/calendar/MediaDisplay.tsx
@@ -3,9 +3,10 @@ import { Link } from 'tabler-icons-react';
export interface IMedia {
overview: string;
- imdbId: any;
+ imdbId?: any;
+ artist?: string;
title: string;
- poster: string;
+ poster?: string;
genres: string[];
seasonNumber?: number;
episodeNumber?: number;
@@ -15,14 +16,17 @@ function MediaDisplay(props: { media: IMedia }) {
const { media }: { media: IMedia } = props;
return (
-
+ {media.poster && (
+
+ )}
+
({
@@ -32,12 +36,28 @@ function MediaDisplay(props: { media: IMedia }) {
{media.title}
-
-
-
-
-
+ {media.imdbId && (
+
+
+
+
+
+ )}
+ {media.artist && (
+
+ New album from {media.artist}
+
+ )}
{media.episodeNumber && media.seasonNumber && (
image.coverType === 'poster');
+ // Return a movie poster containting the title and the description
+ return (
+
+ );
+}
+
+export function LidarrMediaDisplay(props: any) {
+ const { media }: { media: any } = props;
+ // Find a poster CoverType
+ const poster = media.artist.images.find((image: any) => image.coverType === 'poster');
+ // Return a movie poster containting the title and the description
+ return (
+
+ );
+}
+
export function RadarrMediaDisplay(props: any) {
const { media }: { media: any } = props;
// Find a poster CoverType
diff --git a/src/tools/types.ts b/src/tools/types.ts
index 92a56f8fc2e..71a0b3feafe 100644
--- a/src/tools/types.ts
+++ b/src/tools/types.ts
@@ -27,6 +27,7 @@ export const ServiceTypeList = [
'Lidarr',
'Plex',
'Radarr',
+ 'Readarr',
'Sonarr',
'qBittorrent',
];
@@ -36,6 +37,7 @@ export type ServiceType =
| 'Lidarr'
| 'Plex'
| 'Radarr'
+ | 'Readarr'
| 'Sonarr'
| 'qBittorrent';
From af83695d819808449e5781055284dbbd93467202 Mon Sep 17 00:00:00 2001
From: ajnart
Date: Wed, 25 May 2022 13:12:12 +0200
Subject: [PATCH 16/18] :building_construction: Make the max notifications to 4
---
src/pages/_app.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx
index 610e21ad4c0..2a37b40085c 100644
--- a/src/pages/_app.tsx
+++ b/src/pages/_app.tsx
@@ -38,7 +38,7 @@ export default function App(props: AppProps & { colorScheme: ColorScheme }) {
withGlobalStyles
withNormalizeCSS
>
-
+
From fbaaa389c2858b9020f5ffd51fbf6c199fe1f483 Mon Sep 17 00:00:00 2001
From: ajnart
Date: Wed, 25 May 2022 13:13:17 +0200
Subject: [PATCH 17/18] :bug: Fix Readarr date match
---
src/components/modules/calendar/CalendarModule.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/components/modules/calendar/CalendarModule.tsx b/src/components/modules/calendar/CalendarModule.tsx
index 91689af255e..39350e6718c 100644
--- a/src/components/modules/calendar/CalendarModule.tsx
+++ b/src/components/modules/calendar/CalendarModule.tsx
@@ -156,8 +156,8 @@ function DayComponent(props: any) {
const day = renderdate.getDate();
const readarrFiltered = readarrmedias.filter((media: any) => {
- const mediaDate = new Date(media.releaseDate);
- return mediaDate.getDate() === day;
+ const date = new Date(media.releaseDate);
+ return date.getDate() === day && date.getMonth() === renderdate.getMonth();
});
const lidarrFiltered = lidarrmedias.filter((media: any) => {
From 3f2aa50f85bc220f5569bc04467ff568337d230f Mon Sep 17 00:00:00 2001
From: ajnart
Date: Wed, 25 May 2022 13:13:36 +0200
Subject: [PATCH 18/18] :sparkles: Totally rework how the media previews work!
---
.../modules/calendar/MediaDisplay.tsx | 113 ++++++++++--------
1 file changed, 65 insertions(+), 48 deletions(-)
diff --git a/src/components/modules/calendar/MediaDisplay.tsx b/src/components/modules/calendar/MediaDisplay.tsx
index a3e5a5674af..e3a4a90b704 100644
--- a/src/components/modules/calendar/MediaDisplay.tsx
+++ b/src/components/modules/calendar/MediaDisplay.tsx
@@ -1,5 +1,7 @@
-import { Stack, Image, Group, Title, Badge, Text, ActionIcon, Anchor } from '@mantine/core';
+import { Image, Group, Title, Badge, Text, ActionIcon, Anchor, ScrollArea } from '@mantine/core';
import { Link } from 'tabler-icons-react';
+import { useConfig } from '../../../tools/state';
+import { serviceItem } from '../../../tools/types';
export interface IMedia {
overview: string;
@@ -15,39 +17,34 @@ export interface IMedia {
function MediaDisplay(props: { media: IMedia }) {
const { media }: { media: IMedia } = props;
return (
-
- {media.poster && (
-
- )}
-
- ({
- height: 400,
- })}
- >
-
-
- {media.title}
- {media.imdbId && (
-
-
-
-
-
- )}
-
+
+
+ {media.poster && (
+
+ )}
+
+ {media.title}
+ {media.imdbId && (
+
+
+
+
+
+ )}
{media.artist && (
- New album from {media.artist}
+ New release from {media.artist}
)}
{media.episodeNumber && media.seasonNumber && (
@@ -68,31 +65,42 @@ function MediaDisplay(props: { media: IMedia }) {
Season {media.seasonNumber} episode {media.episodeNumber}
)}
-
- {media.overview}
-
- {/*Add the genres at the bottom of the poster*/}
-
- {media.genres.map((genre: string, i: number) => (
- {genre}
- ))}
+
+ {media.overview}
+
+ {media.genres.map((genre: string, i: number) => (
+
+ {genre}
+
+ ))}
+
-
+
);
}
export function ReadarrMediaDisplay(props: any) {
const { media }: { media: any } = props;
+ const { config } = useConfig();
+ // Find lidarr in services
+ const readarr = config.services.find((service: serviceItem) => service.type === 'Readarr');
// Find a poster CoverType
- const poster = media.author.images.find((image: any) => image.coverType === 'poster');
+ const poster = media.images.find((image: any) => image.coverType === 'cover');
+ if (!readarr) {
+ return null;
+ }
+ const baseUrl = new URL(readarr.url).origin;
+ // Remove '/' from the end of the lidarr url
+ console.log(poster);
+ const fullLink = `${baseUrl}${poster.url}`;
// Return a movie poster containting the title and the description
return (
service.type === 'Lidarr');
// Find a poster CoverType
- const poster = media.artist.images.find((image: any) => image.coverType === 'poster');
+ const poster = media.images.find((image: any) => image.coverType === 'cover');
+ if (!lidarr) {
+ return null;
+ }
+ const baseUrl = new URL(lidarr.url).origin;
+ // Remove '/' from the end of the lidarr url
+ const fullLink = `${baseUrl}${poster.url}`;
// Return a movie poster containting the title and the description
return (