merge branch 'develop' into 'main' - Release v1.7.28
This commit is contained in:
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
|
||||
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.7.28] - 2026-05-27
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Missing Sonarr Link on TV Requests (Issue #58)** — Resolved a bug where the Sonarr deep-link button was missing on TV request cards while Radarr links correctly appeared on movie request cards. Added support for all camelCase TVDB ID variants (`tvDbId`, `tvdbId`, `theTvdbId`, `theTvDbId`, `TvDbId`, `TheTvDbId`) on both backend link decoration (`server/utils/ombiHelpers.js`) and frontend rendering (`client/src/ui/requests.js`). Added a dedicated integration test to safeguard links decoration for TV requests.
|
||||
|
||||
## [1.7.27] - 2026-05-27
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -194,7 +194,7 @@ function createRequestCard(request) {
|
||||
const actions = document.createElement('span');
|
||||
actions.className = 'service-icons-container';
|
||||
|
||||
const id = request.theTvDbId || request.theMovieDbId || request.theTvdbId || request.theTmdbId || request.TvDbId || request.TheTvDbId || request.imdbId || request.ImdbId;
|
||||
const id = request.theTvDbId || request.theTvdbId || request.tvDbId || request.tvdbId || request.TvDbId || request.TheTvDbId || request.theMovieDbId || request.theTmdbId || request.imdbId || request.ImdbId;
|
||||
if (state.ombiBaseUrl && id) {
|
||||
const ombiLink = document.createElement('a');
|
||||
ombiLink.className = 'ombi-link';
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "sofarr",
|
||||
"version": "1.7.27",
|
||||
"version": "1.7.28",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "sofarr",
|
||||
"version": "1.7.27",
|
||||
"version": "1.7.28",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^1.6.0",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sofarr",
|
||||
"version": "1.7.27",
|
||||
"version": "1.7.28",
|
||||
"description": "A personal media download dashboard that shows your downloads 'so far' while you relax on the sofa waiting for your *arr services to finish",
|
||||
"main": "server/index.js",
|
||||
"scripts": {
|
||||
|
||||
+1
-1
@@ -133,7 +133,7 @@ function createApp({ skipRateLimits = false } = {}) {
|
||||
* version:
|
||||
* type: string
|
||||
* description: sofarr version
|
||||
* example: "1.7.27"
|
||||
* example: "1.7.28"
|
||||
* x-code-samples:
|
||||
* - lang: curl
|
||||
* label: cURL
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ info:
|
||||
|
||||
## SSE Streaming
|
||||
Real-time updates are available via Server-Sent Events at GET /api/dashboard/stream.
|
||||
version: 1.7.27
|
||||
version: 1.7.28
|
||||
contact:
|
||||
name: sofarr
|
||||
license:
|
||||
|
||||
@@ -115,10 +115,10 @@ async function decorateRequestsWithArrLinks(requests, isAdmin) {
|
||||
requests.forEach(req => {
|
||||
// Determine if it's TV or Movie. Often `mediaType` is set, or `type === 'Tv'`
|
||||
// Fallback to checking for TV specific IDs.
|
||||
const isTv = req.mediaType === 'tv' || req.type === 'Tv' || req.tvDbId || req.theTvDbId;
|
||||
const isTv = req.mediaType === 'tv' || req.type === 'Tv' || req.tvDbId || req.tvdbId || req.theTvDbId || req.theTvdbId || req.TvDbId || req.TheTvDbId;
|
||||
|
||||
if (isTv) {
|
||||
const tvdbId = req.theTvDbId || req.theMovieDbId || req.theTvdbId || req.theTmdbId || req.TvDbId || req.TheTvDbId;
|
||||
const tvdbId = req.theTvDbId || req.theTvdbId || req.tvDbId || req.tvdbId || req.TvDbId || req.TheTvDbId || req.theMovieDbId || req.theTmdbId;
|
||||
if (!tvdbId) return;
|
||||
|
||||
for (const instData of sonarrData) {
|
||||
|
||||
@@ -233,6 +233,48 @@ describe('GET /api/ombi/requests', () => {
|
||||
expect(res.body.requests.movie[0].requestedUser.userName).toBe('adminuser');
|
||||
});
|
||||
|
||||
it('decorates TV requests with Sonarr links using tvDbId camelCase property (Issue #58)', async () => {
|
||||
// 1. Setup mock instance config
|
||||
process.env.SONARR_INSTANCES = JSON.stringify([
|
||||
{ id: 'sonarr-1', name: 'Test Sonarr', url: 'https://sonarr.test', apiKey: 'sonarr-key' }
|
||||
]);
|
||||
|
||||
// Reset and re-initialize retrievers registry to pick up the Sonarr instance
|
||||
arrRetrieverRegistry.retrievers.clear();
|
||||
arrRetrieverRegistry.initialized = false;
|
||||
|
||||
// 2. Setup mock Ombi TV request carrying `tvDbId` instead of `theTvDbId`
|
||||
const tvRequestsWithTvDbId = [
|
||||
{ id: 4, title: 'Superman Show', requestedUser: { userName: 'adminuser' }, requestedByAlias: 'adminuser', type: 'tv', tvDbId: '101' }
|
||||
];
|
||||
|
||||
nock.cleanAll();
|
||||
setupOmbiRequestMocks(OMBI_REQUESTS.movie, tvRequestsWithTvDbId);
|
||||
|
||||
// 3. Mock Sonarr API series call returning the series with matching tvdbId and titleSlug
|
||||
nock('https://sonarr.test')
|
||||
.get('/api/v3/series')
|
||||
.reply(200, [
|
||||
{ tvdbId: 101, title: 'Superman Show', titleSlug: 'superman-show' }
|
||||
]);
|
||||
|
||||
const { cookies } = await authenticateUser(app, 'AdminUser', true);
|
||||
|
||||
const res = await request(app)
|
||||
.get('/api/ombi/requests?showAll=true')
|
||||
.set('Cookie', cookies)
|
||||
.expect(200);
|
||||
|
||||
// 4. Assert decoration succeeded
|
||||
const supermanShow = res.body.requests.tv.find(r => r.id === 4);
|
||||
expect(supermanShow).toBeDefined();
|
||||
expect(supermanShow.arrLink).toBe('https://sonarr.test/series/superman-show');
|
||||
expect(supermanShow.arrType).toBe('sonarr');
|
||||
|
||||
// Clean up
|
||||
delete process.env.SONARR_INSTANCES;
|
||||
});
|
||||
|
||||
it('handles case-insensitive username matching', async () => {
|
||||
const requestsWithMixedCase = [
|
||||
{ id: 1, title: 'Test Movie', requestedUser: { userName: 'TestUser' }, requestedByAlias: 'TestUser', type: 'movie' },
|
||||
|
||||
Reference in New Issue
Block a user