mirror of
https://github.com/cstaelen/tidarr.git
synced 2026-10-19 01:30:57 -05:00
feat: use tiddl folder templates (#317)
* wip * fix: group constants * feat: add update message modal * feat: add tiddl config in config dialog * fix: preserve cp ownership if puid and pgid are set * feat: move python deps to requirements.txt file * feat: enchance update warn dialog
This commit is contained in:
1 parent
3348b82cdc
commit
cf93f77af6
23 files changed
+228
-91
No files matched your search
@@ -5,10 +5,18 @@ Tidarr notable changes.
|
||||
|
||||
## 📦 0.3.1
|
||||
### 🖍 Changed
|
||||
* [Docker] ⚠️ **Now there is only one docker volume to mount for the music library destination**
|
||||
* [API] ⚠️ **Folder management and format is now handle in `tiddl.json` file.**
|
||||
* [API] Use tiddl `-V` parameters for video DL
|
||||
* [Front] Hide/Show video lists if tiddl `download_video` parameter is false/true.
|
||||
### 🚀 Added
|
||||
* [Front] Add an update warning modal
|
||||
* [Front] Add tiddl config in config dialog
|
||||
### 🐛 Fixed
|
||||
* [Front] Fix skeleton loaders
|
||||
* [API] If PUID and PGID are set, `cp` uses `-rfp` args
|
||||
### 🧹 Renovate
|
||||
* [Chore] Move python deps to `requirements.txt` file
|
||||
|
||||
## 📦 0.3.0
|
||||
### 🚀 Added
|
||||
|
||||
@@ -73,10 +73,7 @@ docker-run: ## Run tidarr docker image
|
||||
--platform=linux/amd64 \
|
||||
-p 8484:8484 \
|
||||
-v ${PWD}/docker/mnt/config/:/home/app/standalone/shared \
|
||||
-v ${PWD}/docker/mnt/download/albums:/home/app/standalone/download/albums \
|
||||
-v ${PWD}/docker/mnt/download/tracks:/home/app/standalone/download/tracks \
|
||||
-v ${PWD}/docker/mnt/download/playlists:/home/app/standalone/download/playlists \
|
||||
-v ${PWD}/docker/mnt/download/videos:/home/app/standalone/download/videos \
|
||||
-v ${PWD}/docker/mnt/library:/home/app/standalone/library/ \
|
||||
-e ENABLE_BEETS=true \
|
||||
-e PUID=501 \
|
||||
-e PGID=501 \
|
||||
|
||||
@@ -18,7 +18,7 @@ Tidarr is a Docker image that provides a web interface to download up to **24-bi
|
||||
- [Getting Started](#getting-started)
|
||||
- [Tidal authentication](#tidal-authentication)
|
||||
- [Options](#options)
|
||||
- [Download options](#download-settings)
|
||||
- [Download settings](#download-settings)
|
||||
- [PUID/PGID](#puid-pgid)
|
||||
- [Password protection](#password-protection)
|
||||
- [Services](#services):
|
||||
@@ -81,10 +81,7 @@ services:
|
||||
- 8484:8484
|
||||
volumes:
|
||||
- /any/folder/to/tidarr/config:/home/app/standalone/shared
|
||||
- /any/folder/to/download/albums:/home/app/standalone/download/albums
|
||||
- /any/folder/to/download/tracks:/home/app/standalone/download/tracks
|
||||
- /any/folder/to/download/playlists:/home/app/standalone/download/playlists
|
||||
- /any/folder/to/download/videos:/home/app/standalone/download/videos
|
||||
- /any/folder/to/library:/home/app/standalone/library
|
||||
restart: 'unless-stopped'
|
||||
```
|
||||
|
||||
@@ -96,10 +93,7 @@ docker run \
|
||||
--name tidarr \
|
||||
-p 8484:8484 \
|
||||
-v /any/folder/to/tidarr/config/:/home/app/standalone/shared \
|
||||
-v /any/folder/to/download/albums:/home/app/standalone/download/albums \
|
||||
-v /any/folder/to/download/tracks:/home/app/standalone/download/tracks \
|
||||
-v /any/folder/to/download/playlists:/home/app/standalone/download/playlists \
|
||||
-v /any/folder/to/download/videos:/home/app/standalone/download/videos \
|
||||
-v /any/folder/to/library:/home/app/standalone/library \
|
||||
cstaelen/tidarr:latest
|
||||
```
|
||||
|
||||
@@ -128,26 +122,30 @@ docker exec tidarr cp -rf /root/tiddl.json /home/app/standalone/shared/tiddl.jso
|
||||
|
||||
-> You can set download options in `/your/docker/path/to/tidarr/config/tiddl.json`.
|
||||
|
||||
/!\ Be ware to set the right folder templates
|
||||
|
||||
See default :
|
||||
|
||||
```json
|
||||
{
|
||||
// More tiddldetails : https://github.com/oskvr37/wiki/Template-formatting
|
||||
"template": {
|
||||
"track": "{artist} - {title}",
|
||||
"video": "{artist} - {title}",
|
||||
"album": "{album_artist}/{album}/{number:02d}. {title}",
|
||||
"playlist": "{playlist}/{playlist_number:02d}. {artist} - {title}"
|
||||
"track": "{artist}/_tracks/{artist} - {title}",
|
||||
"video": "_videos/{artist}/{artist} - {title}",
|
||||
"playlist": "_playlists/{playlist}/{playlist_number:02d}. {artist} - {title}"
|
||||
},
|
||||
"download": {
|
||||
// Default high (16bit 44.1khz), max available: master (24bit 192khz max)
|
||||
// https://github.com/oskvr37/tiddl?tab=readme-ov-file#download-quality
|
||||
"quality": "high",
|
||||
// Should not be changed (otherwise downloads will fail) /!\
|
||||
// Should not be changed (otherwise downloads will fail) /!\
|
||||
"path": "/home/app/standalone/download/incomplete",
|
||||
"threads": 4,
|
||||
"embed_lyrics": false,
|
||||
// Include or not singles while downloading "all releases"
|
||||
"singles_filter": "none", // "none", "only", "include"
|
||||
// Allow video download
|
||||
// Show video content
|
||||
"download_video": true
|
||||
},
|
||||
"cover": {
|
||||
|
||||
+10
-11
@@ -34,18 +34,17 @@ export async function moveAndClean(
|
||||
setPermissions();
|
||||
|
||||
try {
|
||||
let folder = item.type;
|
||||
if (item.type === "artist") {
|
||||
folder = "album";
|
||||
}
|
||||
if (item.type === "mix") {
|
||||
folder = "playlist";
|
||||
}
|
||||
item["output"] = logs(item, `=== Move processed items ===`);
|
||||
const output_move = execSync(
|
||||
`cp -rf ${ROOT_PATH}/download/incomplete/* ${ROOT_PATH}/download/${folder}s/ >/dev/null`,
|
||||
{ encoding: "utf-8" },
|
||||
);
|
||||
|
||||
let args = "-rf";
|
||||
|
||||
if (process.env.PUID && process.env.PGID) {
|
||||
args = "-rfp";
|
||||
}
|
||||
|
||||
const cmd = `cp ${args} ${ROOT_PATH}/download/incomplete/* ${ROOT_PATH}/library >/dev/null`;
|
||||
item["output"] = logs(item, cmd);
|
||||
const output_move = execSync(cmd, { encoding: "utf-8" });
|
||||
item["output"] = logs(
|
||||
item,
|
||||
`- Move complete ${item.type}\r\n${output_move}`,
|
||||
|
||||
@@ -27,11 +27,15 @@ export function tidalDL(id: number, app: Express, onFinish?: () => void) {
|
||||
|
||||
item["output"] = logs(item, `Executing: ${binary} ${args.join(" ")}`);
|
||||
const child = spawn(binary, args);
|
||||
|
||||
const signal = child.signalCode || undefined;
|
||||
child.stdout?.setEncoding("utf8");
|
||||
child.stdout?.on("data", (data) => {
|
||||
child.stdout?.on("data", (data: string) => {
|
||||
item["output"] = logs(item, data.replace(/[\r\n]+/gm, ""));
|
||||
item["process"] = child;
|
||||
if (data.includes("ERROR")) {
|
||||
child.emit("error", new Error(data));
|
||||
child.kill(signal);
|
||||
}
|
||||
app.settings.processingList.actions.updateItem(item);
|
||||
});
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import { DialogHandler } from ".";
|
||||
const TableParameters = ({
|
||||
rows,
|
||||
}: {
|
||||
rows: [string, string | undefined][];
|
||||
rows: [string, string | number | boolean | undefined][];
|
||||
}) => {
|
||||
return (
|
||||
<TableContainer component={Paper}>
|
||||
@@ -32,7 +32,7 @@ const TableParameters = ({
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<strong>Environment vars</strong>
|
||||
<strong>Variable</strong>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<strong>Value</strong>
|
||||
@@ -46,7 +46,7 @@ const TableParameters = ({
|
||||
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
|
||||
>
|
||||
<TableCell>{row?.[0]}</TableCell>
|
||||
<TableCell>{row?.[1]}</TableCell>
|
||||
<TableCell>{row?.[1]?.toString()}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
@@ -61,6 +61,7 @@ export const DialogConfig = () => {
|
||||
config,
|
||||
isUpdateAvailable,
|
||||
releaseData,
|
||||
tiddlConfig,
|
||||
isConfigModalOpen,
|
||||
actions,
|
||||
} = useConfigProvider();
|
||||
@@ -150,30 +151,52 @@ export const DialogConfig = () => {
|
||||
</>
|
||||
)}
|
||||
{currentTab === 2 && (
|
||||
<Box display="flex" justifyContent="center" my={4}>
|
||||
{!tokenMissing ? (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="warning"
|
||||
startIcon={<KeyOff />}
|
||||
onClick={async () => {
|
||||
actions.toggleModal(false);
|
||||
await delete_token();
|
||||
checkAPI();
|
||||
}}
|
||||
>
|
||||
Revoke Tidal token
|
||||
</Button>
|
||||
) : (
|
||||
<Alert
|
||||
color="warning"
|
||||
icon={<Warning sx={{ fontSize: 20 }} />}
|
||||
variant="outlined"
|
||||
>
|
||||
No Tidal token found !
|
||||
</Alert>
|
||||
)}
|
||||
</Box>
|
||||
<>
|
||||
<Box display="flex" justifyContent="center" my={4}>
|
||||
{!tokenMissing ? (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="warning"
|
||||
startIcon={<KeyOff />}
|
||||
onClick={async () => {
|
||||
actions.toggleModal(false);
|
||||
await delete_token();
|
||||
checkAPI();
|
||||
}}
|
||||
>
|
||||
Revoke Tidal token
|
||||
</Button>
|
||||
) : (
|
||||
<Alert
|
||||
color="warning"
|
||||
icon={<Warning sx={{ fontSize: 20 }} />}
|
||||
variant="outlined"
|
||||
>
|
||||
No Tidal token found !
|
||||
</Alert>
|
||||
)}
|
||||
</Box>
|
||||
<Box>
|
||||
<h3>Tiddl template options</h3>
|
||||
{tiddlConfig?.template ? (
|
||||
<TableParameters rows={Object.entries(tiddlConfig.template)} />
|
||||
) : (
|
||||
"Not found."
|
||||
)}
|
||||
<h3>Tiddl download options</h3>
|
||||
{tiddlConfig?.download ? (
|
||||
<TableParameters rows={Object.entries(tiddlConfig.download)} />
|
||||
) : (
|
||||
"Not found."
|
||||
)}
|
||||
<h3>Tiddl cover options</h3>
|
||||
{tiddlConfig?.cover ? (
|
||||
<TableParameters rows={Object.entries(tiddlConfig.cover)} />
|
||||
) : (
|
||||
"Not found."
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</DialogHandler>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import { useState } from "react";
|
||||
import OpenInNewIcon from "@mui/icons-material/OpenInNew";
|
||||
import WarningIcon from "@mui/icons-material/Warning";
|
||||
import { Box, Button, Link } from "@mui/material";
|
||||
import { LOCALSTORAGE_UPDATE_WARNING } from "src/contants";
|
||||
import { useConfigProvider } from "src/provider/ConfigProvider";
|
||||
|
||||
import { DialogHandler } from ".";
|
||||
|
||||
const VERSION_CONCERNED = "0.3.1";
|
||||
|
||||
export const DialogUpdateWarning = () => {
|
||||
const { config } = useConfigProvider();
|
||||
const [show, setShow] = useState(
|
||||
localStorage.getItem(LOCALSTORAGE_UPDATE_WARNING) !== VERSION_CONCERNED,
|
||||
);
|
||||
|
||||
function persistClose() {
|
||||
setShow(false);
|
||||
localStorage.setItem(LOCALSTORAGE_UPDATE_WARNING, VERSION_CONCERNED);
|
||||
}
|
||||
|
||||
return (
|
||||
<DialogHandler
|
||||
open={show && !!config?.TIDARR_VERSION?.includes(VERSION_CONCERNED)}
|
||||
onClose={() => setShow(false)}
|
||||
title={"Folder configuration has changed !"}
|
||||
icon={<WarningIcon color="error" />}
|
||||
maxWidth="sm"
|
||||
buttons={
|
||||
<Button onClick={persistClose} variant="outlined">
|
||||
Don't show again
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<p>
|
||||
Since the latest update, the folder configuration has changed. There is
|
||||
now only a single Docker volume for the music library (destination).
|
||||
</p>
|
||||
<p>
|
||||
To define the subfolders and the destination format for tracks, albums,
|
||||
playlists, etc., you will need to edit the "template" section of the{" "}
|
||||
<strong>
|
||||
<i>tiddl.json</i>
|
||||
</strong>{" "}
|
||||
file.
|
||||
</p>
|
||||
<Box gap={2} display="flex">
|
||||
<Link
|
||||
href="https://github.com/cstaelen/tidarr?tab=readme-ov-file#getting-started"
|
||||
target="_blank"
|
||||
>
|
||||
Read more on Github{` `}
|
||||
<OpenInNewIcon sx={{ fontSize: 15, verticalAlign: "middle" }} />
|
||||
</Link>
|
||||
<Link
|
||||
href="https://github.com/oskvr37/tiddl/wiki/Template-formatting"
|
||||
target="_blank"
|
||||
>
|
||||
Template formatting (tiddl){` `}
|
||||
<OpenInNewIcon sx={{ fontSize: 15, verticalAlign: "middle" }} />
|
||||
</Link>
|
||||
</Box>
|
||||
</DialogHandler>
|
||||
);
|
||||
};
|
||||
@@ -17,6 +17,7 @@ export const DialogHandler = ({
|
||||
open,
|
||||
icon,
|
||||
maxWidth = "xl",
|
||||
buttons,
|
||||
onClose,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
@@ -24,6 +25,7 @@ export const DialogHandler = ({
|
||||
icon: ReactNode;
|
||||
open: boolean;
|
||||
maxWidth?: Breakpoint;
|
||||
buttons?: ReactNode;
|
||||
onClose?: () => void;
|
||||
}) => {
|
||||
const handleClose = () => {
|
||||
@@ -51,7 +53,10 @@ export const DialogHandler = ({
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleClose}>Close</Button>
|
||||
<Button variant="outlined" onClick={handleClose}>
|
||||
Close
|
||||
</Button>
|
||||
{buttons}
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
@@ -10,6 +10,7 @@ import { ProcessingProvider } from "../provider/ProcessingProvider";
|
||||
import { DialogConfig } from "./Dialog/DialogConfig";
|
||||
import { DialogNoAPI } from "./Dialog/DialogNoAPI";
|
||||
import { DialogToken } from "./Dialog/DialogToken";
|
||||
import { DialogUpdateWarning } from "./Dialog/DialogUpdateWarning";
|
||||
import { Footer } from "./Layout/Footer";
|
||||
import { HeaderSearch } from "./Layout/HeaderSearch";
|
||||
import { ProcessingList } from "./Processing/ProcessingList";
|
||||
@@ -58,6 +59,7 @@ function MainLayout({ children }: { children: ReactNode }) {
|
||||
<DialogToken />
|
||||
<DialogNoAPI />
|
||||
<DialogConfig />
|
||||
<DialogUpdateWarning />
|
||||
</ProcessingProvider>
|
||||
</SearchProvider>
|
||||
</main>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Navigate, Outlet, useLocation } from "react-router-dom";
|
||||
|
||||
import {
|
||||
LOCALSTORAGE_REDIRECT_URL,
|
||||
LOCALSTORAGE_TOKEN_KEY,
|
||||
useAuth,
|
||||
} from "../../provider/AuthProvider";
|
||||
} from "src/contants";
|
||||
|
||||
import { useAuth } from "../../provider/AuthProvider";
|
||||
|
||||
import { Loader } from "./Loader";
|
||||
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
export const TIDAL_ITEMS_PER_PAGE = 18;
|
||||
export const TIDAL_API_URL = "https://api.tidal.com";
|
||||
export const TIDAL_ALBUM_URL = "http://www.tidal.com/album/";
|
||||
|
||||
export const LOCALSTORAGE_TOKEN_KEY = "tidarr-token";
|
||||
export const LOCALSTORAGE_REDIRECT_URL = "tidarr-redirect";
|
||||
export const LOCALSTORAGE_UPDATE_WARNING = "tidarr-update-warn";
|
||||
|
||||
export const LOCALSTORAGE_QUALITY_DOWNLOAD = "tidarr-quality-download";
|
||||
export const LOCALSTORAGE_DISPLAY_MODE = "tidarr-display-mode";
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
EventSourcePlus,
|
||||
SseMessage,
|
||||
} from "event-source-plus";
|
||||
import { LOCALSTORAGE_TOKEN_KEY } from "src/provider/AuthProvider";
|
||||
import { LOCALSTORAGE_TOKEN_KEY } from "src/contants";
|
||||
|
||||
import {
|
||||
AuthType,
|
||||
|
||||
@@ -7,13 +7,14 @@ import React, {
|
||||
} from "react";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import { ROUTE_LOGIN } from "src/components/Security/PrivateRoute";
|
||||
import {
|
||||
LOCALSTORAGE_REDIRECT_URL,
|
||||
LOCALSTORAGE_TOKEN_KEY,
|
||||
} from "src/contants";
|
||||
import { ApiReturnType, AuthType, CheckAuthType } from "src/types";
|
||||
|
||||
import { useApiFetcher } from "./ApiFetcherProvider";
|
||||
|
||||
export const LOCALSTORAGE_TOKEN_KEY = "tidarr-token";
|
||||
export const LOCALSTORAGE_REDIRECT_URL = "tidarr-redirect";
|
||||
|
||||
type AuthContextType = {
|
||||
isAuthActive: boolean | undefined;
|
||||
isAccessGranted: boolean;
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import React, { ReactNode, useContext, useEffect, useState } from "react";
|
||||
import {
|
||||
LOCALSTORAGE_DISPLAY_MODE,
|
||||
LOCALSTORAGE_QUALITY_DOWNLOAD,
|
||||
} from "src/contants";
|
||||
|
||||
import {
|
||||
ConfigParametersType,
|
||||
@@ -30,9 +34,6 @@ type ConfigContextType = {
|
||||
};
|
||||
};
|
||||
|
||||
export const LOCALSTORAGE_QUALITY_DOWNLOAD = "tidarr-quality-download";
|
||||
export const LOCALSTORAGE_DISPLAY_MODE = "tidarr-display-mode";
|
||||
|
||||
const ConfigContext = React.createContext<ConfigContextType>(
|
||||
{} as ConfigContextType,
|
||||
);
|
||||
|
||||
+1
-4
@@ -10,10 +10,7 @@ services:
|
||||
- 3000:3000
|
||||
volumes:
|
||||
- ./docker/mnt/config:/home/app/standalone/shared
|
||||
- ./docker/mnt/download/albums:/home/app/standalone/download/albums
|
||||
- ./docker/mnt/download/tracks:/home/app/standalone/download/tracks
|
||||
- ./docker/mnt/download/playlists:/home/app/standalone/download/playlists
|
||||
- ./docker/mnt/download/videos:/home/app/standalone/download/videos
|
||||
- ./docker/mnt/library:/home/app/standalone/library
|
||||
- .:/home/app/build # only used in dev environment
|
||||
restart: 'unless-stopped'
|
||||
environment:
|
||||
|
||||
+4
-10
@@ -1,26 +1,20 @@
|
||||
# syntax = devthefuture/dockerfile-x
|
||||
|
||||
FROM ghcr.io/linuxserver/baseimage-alpine:3.21
|
||||
WORKDIR /home/app/build
|
||||
|
||||
ENV SHELL=bash
|
||||
ARG NODE_ENV
|
||||
ENV NODE_ENV="${NODE_ENV}"
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV ENVIRONMENT="development"
|
||||
|
||||
ENV VERSION="0.0.0-dev"
|
||||
|
||||
RUN apk update && apk upgrade
|
||||
RUN apk add npm nodejs py-pip ffmpeg beets
|
||||
RUN apk add npm nodejs py-pip
|
||||
|
||||
RUN mkdir -p $HOME/.venvs
|
||||
RUN python3 -m venv $HOME/.venvs
|
||||
RUN source $HOME/.venvs/bin/activate
|
||||
ENV PATH="$HOME/.venvs/bin:$PATH"
|
||||
INCLUDE docker/stages/Dockerfile.python
|
||||
|
||||
RUN python -m pip install "tiddl==2.5.1" requests ffmpeg-python mutagen
|
||||
|
||||
RUN mkdir -p /home/app/standalone/shared/beets
|
||||
RUN touch /home/app/standalone/shared/beets/beets-library.blb
|
||||
RUN mkdir -p /home/app/standalone/download/incomplete/
|
||||
RUN npm install -g yarn tsx
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@ ENV VIRTUAL_ENV=/opt/venv
|
||||
RUN python3 -m venv $VIRTUAL_ENV
|
||||
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
|
||||
|
||||
RUN python3 -m pip install "tiddl==2.5.1" requests ffmpeg-python mutagen
|
||||
COPY docker/requirements.txt docker/requirements.txt
|
||||
RUN python -m pip install -r docker/requirements.txt
|
||||
|
||||
INCLUDE docker/stages/Dockerfile.runner
|
||||
|
||||
RUN mkdir -p /home/app/standalone/download/incomplete/
|
||||
|
||||
@@ -9,7 +9,7 @@ INCLUDE docker/stages/Dockerfile.builder
|
||||
## BUILD STANDALONE APP
|
||||
|
||||
FROM ghcr.io/linuxserver/baseimage-alpine:3.21 AS runner
|
||||
RUN apk add npm nodejs py3-pip beets ffmpeg
|
||||
RUN apk add npm nodejs py3-pip
|
||||
|
||||
INCLUDE docker/stages/Dockerfile.python
|
||||
INCLUDE docker/stages/Dockerfile.runner
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
requests
|
||||
ffmpeg-python
|
||||
mutagen
|
||||
|
||||
tiddl == 2.5.1
|
||||
@@ -8,7 +8,10 @@ RUN python3 -m venv $HOME/.venvs
|
||||
RUN source $HOME/.venvs/bin/activate
|
||||
ENV PATH="$HOME/.venvs/bin:$PATH"
|
||||
|
||||
RUN python -m pip install "tiddl==2.5.1" requests ffmpeg-python mutagen
|
||||
RUN apk add ffmpeg beets
|
||||
|
||||
COPY docker/requirements.txt docker/requirements.txt
|
||||
RUN python -m pip install -r docker/requirements.txt
|
||||
|
||||
RUN mkdir -p /home/app/standalone/shared/beets
|
||||
RUN touch /home/app/standalone/shared/beets/beets-library.blb
|
||||
@@ -91,7 +91,7 @@ test("Tidarr config : Should see configuration dialog", async ({ page }) => {
|
||||
];
|
||||
const tableAPIRows = await page
|
||||
.locator("#alert-dialog-description div")
|
||||
.filter({ hasText: "Environment" })
|
||||
.filter({ hasText: "Variable" })
|
||||
.locator("table tbody tr")
|
||||
.all();
|
||||
|
||||
@@ -107,6 +107,14 @@ test("Tidarr config : Should see configuration dialog", async ({ page }) => {
|
||||
await expect(
|
||||
page.getByRole("button", { name: "Revoke Tidal token" }),
|
||||
).toBeInViewport();
|
||||
|
||||
const tableTiddlRows = await page
|
||||
.locator("#alert-dialog-description div")
|
||||
.filter({ hasText: "Variable" })
|
||||
.locator("table tbody tr")
|
||||
.all();
|
||||
|
||||
expect(tableTiddlRows.length).toEqual(13);
|
||||
});
|
||||
|
||||
test("Tidarr config : Should see update button", async ({ page }) => {
|
||||
|
||||
@@ -170,8 +170,24 @@ export async function mockConfigAPI(page: Page) {
|
||||
},
|
||||
download: {
|
||||
quality: "high",
|
||||
path: "/home/app/standalone/download/incomplete",
|
||||
threads: 4,
|
||||
singles_filter: "none",
|
||||
embed_lyrics: false,
|
||||
download_video: true,
|
||||
},
|
||||
cover: {
|
||||
save: true,
|
||||
size: 1280,
|
||||
filename: "cover.jpg",
|
||||
},
|
||||
template: {
|
||||
track: "tracks/{artist}/{artist} - {title}",
|
||||
video: "videos/{artist}/{artist} - {title}",
|
||||
album: "albums/{album_artist}/{year} - {album}/{number:02d}. {title}",
|
||||
playlist:
|
||||
"playlists/{playlist}/{playlist_number:02d}. {artist} - {title}",
|
||||
},
|
||||
},
|
||||
};
|
||||
await route.fulfill({ json });
|
||||
|
||||
+5
-4
@@ -1,15 +1,16 @@
|
||||
{
|
||||
"template": {
|
||||
"track": "{artist} - {title}",
|
||||
"video": "{artist} - {title}",
|
||||
"album": "{album_artist}/{album}/{number:02d}. {title}",
|
||||
"playlist": "{playlist}/{playlist_number:02d}. {artist} - {title}"
|
||||
"track": "tracks/{artist}/{artist} - {title}",
|
||||
"video": "videos/{artist}/{artist} - {title}",
|
||||
"album": "albums/{album_artist}/{year} - {album}/{number:02d}. {title}",
|
||||
"playlist": "playlists/{playlist}/{playlist_number:02d}. {artist} - {title}"
|
||||
},
|
||||
"download": {
|
||||
"quality": "high",
|
||||
"path": "/home/app/standalone/download/incomplete",
|
||||
"threads": 4,
|
||||
"singles_filter": "none",
|
||||
"embed_lyrics": false,
|
||||
"download_video": true
|
||||
},
|
||||
"cover": {
|
||||
|
||||
Reference in new issue
Block a user