Files
lidarr/src/Lidarr.Api.V1/Tracks/TrackResource.cs
T
shaunrd0 b258e7e41b Per-track monitoring for Lidarr — track-level Monitored flag + UI + stats fixes
Squashes 8 fork commits onto upstream/develop as a single diff for cleaner
cross-fork comparison. Original history preserved on pre-squash-backup tag
locally.

Motivation
──────────
Upstream Lidarr only supports per-album monitoring. This fork adds a
per-track Monitored bit, exposed via the API, so a companion tool
(musicseerr fork: shaunrd0/musicseerr) can selectively grab a single
track from an album without flipping the whole album's monitor state.

Changes
───────

• Track POCO + persistence
  src/NzbDrone.Core/Music/Model/Track.cs gains a Monitored property
  (default true — new tracks start monitored, matching the historical
  behavior where every track on a monitored album was implicitly
  in-scope). AlbumRepository / TrackRepository / TrackService updated
  to read, write, and bulk-update the field.

• UI: per-track monitor toggle on album details page
  Adds a checkbox column in the Album Details track list so users can
  flip individual tracks Monitored/Unmonitored without using the API.

• UI: Tracks column on Wanted/Missing and Wanted/CutoffUnmet
  Adds an explicit "Tracks" column to those views so the row shows
  monitored-tracks / total-tracks for each album, making partial-album
  state visible at a glance.

• Stats correctness
  TrackCount on album-level stats is now gated by Tracks.Monitored
  (an album where every track is unmonitored should not be counted as
  having wanted/missing tracks). Wanted Tracks denominator switched
  from monitored-track-count to total trackCount, matching the
  semantics of the new column.

• Build: custom Dockerfile.fork for local image
  Multi-stage build producing a hotio-compatible runtime image,
  targeting linux-musl-x64 (hotio's base is Alpine). Used by the
  lidarr-personal and lidarr-shared instances; lidarr (the public
  stock instance on gnat:8686) continues running upstream hotio.

Compatibility
─────────────
The schema migration is purely additive (one new BOOLEAN column with a
true default), so a fork → upstream rollback works without data loss —
the column simply becomes dead weight on disk.
2026-05-30 00:01:33 +00:00

79 lines
2.5 KiB
C#

using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using Lidarr.Api.V1.Artist;
using Lidarr.Api.V1.TrackFiles;
using Lidarr.Http.REST;
using NzbDrone.Core.Music;
using Swashbuckle.AspNetCore.Annotations;
namespace Lidarr.Api.V1.Tracks
{
public class TrackResource : RestResource
{
public int ArtistId { get; set; }
public string ForeignTrackId { get; set; }
public string ForeignRecordingId { get; set; }
public int TrackFileId { get; set; }
public int AlbumId { get; set; }
public bool Explicit { get; set; }
public int AbsoluteTrackNumber { get; set; }
public string TrackNumber { get; set; }
public string Title { get; set; }
public int Duration { get; set; }
public TrackFileResource TrackFile { get; set; }
public int MediumNumber { get; set; }
public bool HasFile { get; set; }
public bool Monitored { get; set; }
public ArtistResource Artist { get; set; }
public Ratings Ratings { get; set; }
// Hiding this so people don't think its usable (only used to set the initial state)
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
[SwaggerIgnore]
public bool Grabbed { get; set; }
}
public static class TrackResourceMapper
{
public static TrackResource ToResource(this Track model)
{
if (model == null)
{
return null;
}
return new TrackResource
{
Id = model.Id,
ArtistId = model.Artist.Value.Id,
ForeignTrackId = model.ForeignTrackId,
ForeignRecordingId = model.ForeignRecordingId,
TrackFileId = model.TrackFileId,
AlbumId = model.AlbumId,
Explicit = model.Explicit,
AbsoluteTrackNumber = model.AbsoluteTrackNumber,
TrackNumber = model.TrackNumber,
Title = model.Title,
Duration = model.Duration,
MediumNumber = model.MediumNumber,
HasFile = model.HasFile,
Monitored = model.Monitored,
Ratings = model.Ratings,
};
}
public static List<TrackResource> ToResource(this IEnumerable<Track> models)
{
if (models == null)
{
return null;
}
return models.Select(ToResource).ToList();
}
}
}