diff --git a/cmd/wire_gen.go b/cmd/wire_gen.go
index e462c1102..cf8a4e69b 100644
--- a/cmd/wire_gen.go
+++ b/cmd/wire_gen.go
@@ -52,8 +52,9 @@ func CreateSubsonicAPIRouter() (*subsonic.Router, error) {
 	transcoderTranscoder := transcoder.New()
 	transcodingCache := core.NewTranscodingCache()
 	mediaStreamer := core.NewMediaStreamer(dataStore, transcoderTranscoder, transcodingCache)
+	archiver := core.NewArchiver(dataStore)
 	players := engine.NewPlayers(dataStore)
-	router := subsonic.New(browser, artwork, listGenerator, users, playlists, scrobbler, search, mediaStreamer, players, dataStore)
+	router := subsonic.New(browser, artwork, listGenerator, users, playlists, scrobbler, search, mediaStreamer, archiver, players, dataStore)
 	return router, nil
 }
 
diff --git a/core/archiver.go b/core/archiver.go
new file mode 100644
index 000000000..0d3823e1d
--- /dev/null
+++ b/core/archiver.go
@@ -0,0 +1,89 @@
+package core
+
+import (
+	"archive/zip"
+	"context"
+	"fmt"
+	"io"
+	"os"
+	"path/filepath"
+
+	"github.com/Masterminds/squirrel"
+	"github.com/deluan/navidrome/log"
+	"github.com/deluan/navidrome/model"
+)
+
+type Archiver interface {
+	Zip(ctx context.Context, id string, w io.Writer) error
+}
+
+func NewArchiver(ds model.DataStore) Archiver {
+	return &archiver{ds: ds}
+}
+
+type archiver struct {
+	ds model.DataStore
+}
+
+func (a *archiver) Zip(ctx context.Context, id string, out io.Writer) error {
+	mfs, err := a.loadTracks(ctx, id)
+	if err != nil {
+		log.Error(ctx, "Error loading media", "id", id, err)
+		return err
+	}
+	z := zip.NewWriter(out)
+	for _, mf := range mfs {
+		_ = a.addFileToZip(ctx, z, mf)
+	}
+	err = z.Close()
+	if err != nil {
+		log.Error(ctx, "Error closing zip file", "id", id, err)
+	}
+	return err
+}
+
+func (a *archiver) addFileToZip(ctx context.Context, z *zip.Writer, mf model.MediaFile) error {
+	_, file := filepath.Split(mf.Path)
+	w, err := z.Create(fmt.Sprintf("%s/%s", mf.Album, file))
+	if err != nil {
+		log.Error(ctx, "Error creating zip entry", "file", mf.Path, err)
+		return err
+	}
+	f, err := os.Open(mf.Path)
+	defer func() { _ = f.Close() }()
+	if err != nil {
+		log.Error(ctx, "Error opening file for zipping", "file", mf.Path, err)
+		return err
+	}
+	_, err = io.Copy(w, f)
+	if err != nil {
+		log.Error(ctx, "Error zipping file", "file", mf.Path, err)
+		return err
+	}
+	return nil
+}
+
+func (a *archiver) loadTracks(ctx context.Context, id string) (model.MediaFiles, error) {
+	exist, err := a.ds.Album(ctx).Exists(id)
+	if err != nil {
+		return nil, err
+	}
+	if exist {
+		return a.ds.MediaFile(ctx).FindByAlbum(id)
+	}
+	exist, err = a.ds.Artist(ctx).Exists(id)
+	if err != nil {
+		return nil, err
+	}
+	if exist {
+		return a.ds.MediaFile(ctx).GetAll(model.QueryOptions{
+			Sort:    "album",
+			Filters: squirrel.Eq{"album_artist_id": id},
+		})
+	}
+	mf, err := a.ds.MediaFile(ctx).Get(id)
+	if err != nil {
+		return nil, err
+	}
+	return model.MediaFiles{*mf}, nil
+}
diff --git a/core/wire_providers.go b/core/wire_providers.go
index 45c94da79..9fdb9dbbf 100644
--- a/core/wire_providers.go
+++ b/core/wire_providers.go
@@ -10,5 +10,6 @@ var Set = wire.NewSet(
 	NewMediaStreamer,
 	NewTranscodingCache,
 	NewImageCache,
+	NewArchiver,
 	transcoder.New,
 )
diff --git a/server/subsonic/api.go b/server/subsonic/api.go
index ad01afb53..4db481381 100644
--- a/server/subsonic/api.go
+++ b/server/subsonic/api.go
@@ -31,6 +31,7 @@ type Router struct {
 	Search        engine.Search
 	Users         engine.Users
 	Streamer      core.MediaStreamer
+	Archiver      core.Archiver
 	Players       engine.Players
 	DataStore     model.DataStore
 
@@ -39,10 +40,10 @@ type Router struct {
 
 func New(browser engine.Browser, artwork core.Artwork, listGenerator engine.ListGenerator, users engine.Users,
 	playlists engine.Playlists, scrobbler engine.Scrobbler, search engine.Search,
-	streamer core.MediaStreamer, players engine.Players, ds model.DataStore) *Router {
+	streamer core.MediaStreamer, archiver core.Archiver, players engine.Players, ds model.DataStore) *Router {
 	r := &Router{Browser: browser, Artwork: artwork, ListGenerator: listGenerator, Playlists: playlists,
-		Scrobbler: scrobbler, Search: search, Users: users, Streamer: streamer, Players: players,
-		DataStore: ds}
+		Scrobbler: scrobbler, Search: search, Users: users, Streamer: streamer, Archiver: archiver,
+		Players: players, DataStore: ds}
 	r.mux = r.routes()
 	return r
 }
diff --git a/server/subsonic/stream.go b/server/subsonic/stream.go
index bc25bb405..9afc3c334 100644
--- a/server/subsonic/stream.go
+++ b/server/subsonic/stream.go
@@ -7,16 +7,19 @@ import (
 
 	"github.com/deluan/navidrome/core"
 	"github.com/deluan/navidrome/log"
+	"github.com/deluan/navidrome/model"
 	"github.com/deluan/navidrome/server/subsonic/responses"
 	"github.com/deluan/navidrome/utils"
 )
 
 type StreamController struct {
 	streamer core.MediaStreamer
+	archiver core.Archiver
+	ds       model.DataStore
 }
 
-func NewStreamController(streamer core.MediaStreamer) *StreamController {
-	return &StreamController{streamer: streamer}
+func NewStreamController(streamer core.MediaStreamer, archiver core.Archiver, ds model.DataStore) *StreamController {
+	return &StreamController{streamer: streamer, archiver: archiver, ds: ds}
 }
 
 func (c *StreamController) Stream(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
@@ -73,11 +76,25 @@ func (c *StreamController) Download(w http.ResponseWriter, r *http.Request) (*re
 		return nil, err
 	}
 
-	stream, err := c.streamer.NewStream(r.Context(), id, "raw", 0)
+	isTrack, err := c.ds.MediaFile(r.Context()).Exists(id)
 	if err != nil {
 		return nil, err
 	}
 
-	http.ServeContent(w, r, stream.Name(), stream.ModTime(), stream)
+	if isTrack {
+		stream, err := c.streamer.NewStream(r.Context(), id, "raw", 0)
+		if err != nil {
+			return nil, err
+		}
+
+		http.ServeContent(w, r, stream.Name(), stream.ModTime(), stream)
+	} else {
+		w.Header().Set("Content-Type", "application/zip")
+		err := c.archiver.Zip(r.Context(), id, w)
+
+		if err != nil {
+			return nil, err
+		}
+	}
 	return nil, nil
 }
diff --git a/server/subsonic/wire_gen.go b/server/subsonic/wire_gen.go
index 82b2b413f..9357f014f 100644
--- a/server/subsonic/wire_gen.go
+++ b/server/subsonic/wire_gen.go
@@ -60,7 +60,9 @@ func initMediaRetrievalController(router *Router) *MediaRetrievalController {
 
 func initStreamController(router *Router) *StreamController {
 	mediaStreamer := router.Streamer
-	streamController := NewStreamController(mediaStreamer)
+	archiver := router.Archiver
+	dataStore := router.DataStore
+	streamController := NewStreamController(mediaStreamer, archiver, dataStore)
 	return streamController
 }
 
@@ -82,5 +84,6 @@ var allProviders = wire.NewSet(
 	NewUsersController,
 	NewMediaRetrievalController,
 	NewStreamController,
-	NewBookmarksController, wire.FieldsOf(new(*Router), "Browser", "Artwork", "ListGenerator", "Playlists", "Scrobbler", "Search", "Streamer", "DataStore"),
+	NewBookmarksController, wire.FieldsOf(new(*Router), "Browser", "Artwork", "ListGenerator", "Playlists", "Scrobbler",
+		"Search", "Streamer", "Archiver", "DataStore"),
 )
diff --git a/server/subsonic/wire_injectors.go b/server/subsonic/wire_injectors.go
index 3673eed3e..a533ef786 100644
--- a/server/subsonic/wire_injectors.go
+++ b/server/subsonic/wire_injectors.go
@@ -17,7 +17,8 @@ var allProviders = wire.NewSet(
 	NewMediaRetrievalController,
 	NewStreamController,
 	NewBookmarksController,
-	wire.FieldsOf(new(*Router), "Browser", "Artwork", "ListGenerator", "Playlists", "Scrobbler", "Search", "Streamer", "DataStore"),
+	wire.FieldsOf(new(*Router), "Browser", "Artwork", "ListGenerator", "Playlists", "Scrobbler",
+		"Search", "Streamer", "Archiver", "DataStore"),
 )
 
 func initSystemController(router *Router) *SystemController {