Ability to cache subsonic artist images locally on disk (#61)

This commit is contained in:
Simon J
2021-10-03 16:36:50 +11:00
committed by GitHub
parent da1860d556
commit d7a7747fab
9 changed files with 404 additions and 142 deletions

View File

@@ -1,5 +1,8 @@
import { Md5 } from "ts-md5/dist/md5";
import { v4 as uuid } from "uuid";
import tmp from "tmp";
import fse from "fs-extra";
import path from "path";
import {
isDodgyImage,
@@ -11,6 +14,7 @@ import {
appendMimeTypeToClientFor,
asURLSearchParams,
splitCoverArtId,
cachingImageFetcher,
} from "../src/subsonic";
import encryption from "../src/encryption";
@@ -158,6 +162,74 @@ describe("asURLSearchParams", () => {
});
});
describe("cachingImageFetcher", () => {
const delegate = jest.fn();
const url = "http://test.example.com/someimage.jpg";
beforeEach(() => {
jest.clearAllMocks();
jest.resetAllMocks();
});
describe("when there is no image in the cache", () => {
it.only("should fetch the image from the source and then cache and return it", async () => {
const dir = tmp.dirSync();
const cacheFile = path.join(dir.name, `${Md5.hashStr(url)}.png`);
const jpgImage = Buffer.from("jpg-image", 'utf-8');
const pngImage = Buffer.from("png-image", 'utf-8');
delegate.mockResolvedValue({ contentType: "image/jpeg", data: jpgImage });
const png = jest.fn();
(sharp as unknown as jest.Mock).mockReturnValue({ png });
png.mockReturnValue({
toBuffer: () => Promise.resolve(pngImage),
});
const result = await cachingImageFetcher(dir.name, delegate)(url);
expect(result!.contentType).toEqual("image/png");
expect(result!.data).toEqual(pngImage);
expect(delegate).toHaveBeenCalledWith(url);
expect(fse.existsSync(cacheFile)).toEqual(true);
expect(fse.readFileSync(cacheFile)).toEqual(pngImage);
});
});
describe("when the image is already in the cache", () => {
it.only("should fetch the image from the cache and return it", async () => {
const dir = tmp.dirSync();
const cacheFile = path.join(dir.name, `${Md5.hashStr(url)}.png`);
const data = Buffer.from("foobar2", "utf-8");
fse.writeFileSync(cacheFile, data);
const result = await cachingImageFetcher(dir.name, delegate)(url);
expect(result!.contentType).toEqual("image/png");
expect(result!.data).toEqual(data);
expect(delegate).not.toHaveBeenCalled();
});
});
describe("when the delegate returns undefined", () => {
it.only("should return undefined", async () => {
const dir = tmp.dirSync();
const cacheFile = path.join(dir.name, `${Md5.hashStr(url)}.png`);
delegate.mockResolvedValue(undefined);
const result = await cachingImageFetcher(dir.name, delegate)(url);
expect(result).toBeUndefined();
expect(delegate).toHaveBeenCalledWith(url);
expect(fse.existsSync(cacheFile)).toEqual(false);
});
});
});
const ok = (data: string) => ({
status: 200,
data,