Skip to content

Commit

Permalink
Add #16 Download all files as zip
Browse files Browse the repository at this point in the history
  • Loading branch information
alangrainger committed Nov 15, 2024
1 parent 4a54641 commit 9e1970a
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 16 deletions.
4 changes: 3 additions & 1 deletion app/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
},
"singleImageGallery": false,
"singleItemAutoOpen": true,
"downloadOriginalPhoto": true
"downloadOriginalPhoto": true,
"showGalleryTitle": false,
"allowDownloadAll": false
},
"lightGallery": {
"controls": true,
Expand Down
18 changes: 10 additions & 8 deletions app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "immich-public-proxy",
"version": "1.4.2",
"version": "1.4.3",
"scripts": {
"dev": "ts-node src/index.ts",
"build": "npx tsc",
Expand All @@ -16,20 +16,22 @@
},
"main": "dist/index.js",
"dependencies": {
"express": "^4.21.1",
"dotenv": "^16.4.5",
"archiver": "^7.0.1",
"dayjs": "^1.11.13",
"dotenv": "^16.4.5",
"ejs": "^3.1.10",
"typescript": "^5.6.2",
"tslib": "^2.8.1"
"express": "^4.21.1",
"tslib": "^2.8.1",
"typescript": "^5.6.2"
},
"devDependencies": {
"ts-node": "^10.9.2",
"@types/node": "^16.18.111",
"@types/archiver": "^6.0.3",
"@types/express": "^4.17.21",
"@types/node": "^16.18.111",
"@typescript-eslint/eslint-plugin": "5.29.0",
"@typescript-eslint/parser": "5.29.0",
"eslint": "^8.49.0",
"eslint-config-standard": "^17.1.0"
"eslint-config-standard": "^17.1.0",
"ts-node": "^10.9.2"
}
}
1 change: 1 addition & 0 deletions app/public/images/download-all.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 20 additions & 1 deletion app/public/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ html {
margin: 4px;
}

@media (max-width:800px) {
@media (max-width: 800px) {
#lightgallery a {
width: calc(100vw / 3 - 10px);
height: calc(100vw / 3 - 10px);
overflow: hidden;
}

#lightgallery img {
width: 100%;
height: 100%;
Expand All @@ -42,10 +43,28 @@ html {
background-repeat: no-repeat;
opacity: 0.5;
}

#lightgallery a:has(.play-icon):hover .play-icon {
opacity: 1;
}

#password {
color: white;
}

#header {
font-family: system-ui, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, Helvetica, Arial, "Helvetica Neue", sans-serif;
color: white;
width: 100%;
display: flex;
}

#header h1 {
font-size: 18pt;
font-weight: bold;
margin: 4px;
}

#download-all {
margin: auto 4px auto auto;
}
8 changes: 6 additions & 2 deletions app/src/immich.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,12 @@ class Immich {
return
}

// Everything is ok - output the link page
if (link.assets.length === 1) {
// Everything is ok - output the shared link data

if (request.mode === 'download' && getConfigOption('ipp.allowDownloadAll', false)) {
// Download all assets as a zip file
await render.downloadAll(res, link)
} else if (link.assets.length === 1) {
// This is an individual item (not a gallery)
log('Serving link ' + request.key)
const asset = link.assets[0]
Expand Down
5 changes: 3 additions & 2 deletions app/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ app.use(express.static('public', { setHeaders: addResponseHeaders }))
/*
* [ROUTE] This is the main URL that someone would visit if they are opening a shared link
*/
app.get('/share/:key', async (req, res) => {
app.get('/share/:key/:mode?', async (req, res) => {
await immich.handleShareRequest({
key: req.params.key
key: req.params.key,
mode: req.params.mode
}, res)
})

Expand Down
25 changes: 25 additions & 0 deletions app/src/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import immich from './immich'
import { Response } from 'express-serve-static-core'
import { Asset, AssetType, ImageSize, IncomingShareRequest, SharedLink } from './types'
import { getConfigOption } from './functions'
import archiver from 'archiver'

class Render {
lgConfig
Expand Down Expand Up @@ -111,6 +112,9 @@ class Render {
items,
openItem,
title: this.title(share),
path: '/share/' + share.key,
showDownload: getConfigOption('ipp.allowDownloadAll', true),
showTitle: getConfigOption('ipp.showGalleryTitle', true),
lgConfig: getConfigOption('lightGallery', {})
})
}
Expand All @@ -121,6 +125,27 @@ class Render {
title (share: SharedLink) {
return share.description || share?.album?.albumName || ''
}

/**
* Download all assets as a zip file
*/
async downloadAll (res: Response, share: SharedLink) {
res.setHeader('Content-Type', 'application/zip')
const title = this.title(share).replace(/[^\w .-]/g, '') + '.zip'
res.setHeader('Content-Disposition', `attachment; filename="${title}"`)
const archive = archiver('zip', { zlib: { level: 9 } })
archive.pipe(res)
for (const asset of share.assets) {
const url = immich.buildUrl(immich.apiUrl() + '/assets/' + encodeURIComponent(asset.id) + '/original', {
key: asset.key,
password: asset.password
})
const data = await fetch(url)
archive.append(Buffer.from(await data.arrayBuffer()), { name: asset.originalFileName || asset.id })
}
await archive.finalize()
res.end()
}
}

const render = new Render()
Expand Down
2 changes: 2 additions & 0 deletions app/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export enum AssetType {
export interface Asset {
id: string;
key: string;
originalFileName?: string;
password?: string;
type: AssetType;
isTrashed: boolean;
Expand Down Expand Up @@ -39,6 +40,7 @@ export enum ImageSize {
export interface IncomingShareRequest {
key: string;
password?: string;
mode?: string;
size?: ImageSize;
range?: string;
}
17 changes: 15 additions & 2 deletions app/views/gallery.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@
<link type="text/css" rel="stylesheet" href="/lightgallery-bundle.min.css"/>
</head>
<body>
<div id="header">
<% if (showTitle) { %>
<h1><%- title || 'Gallery' %></h1>
<% } %>
<% if (showDownload) { %>
<div id="download-all">
<a href="<%- path %>/download" title="Download all"><img src="/images/download-all.svg" height="24" width="24" alt="Download all"></a>
</div>
<% } %>
</div>
<div id="lightgallery">
<% items.forEach(item => {
if (item.video) { %>
Expand All @@ -15,8 +25,11 @@
<div class="play-icon"></div>
</a>
<% } else { %>
<a href="<%- item.previewUrl %>"<% if (item.downloadUrl) { %>
data-download-url="<%- item.downloadUrl %>"<% } %>>
<a href="<%- item.previewUrl %>"
<% if (item.downloadUrl) { %>
data-download-url="<%- item.downloadUrl %>"
<% } %>
>
<img alt="" src="<%- item.thumbnailUrl %>"/>
</a>
<% }
Expand Down

0 comments on commit 9e1970a

Please sign in to comment.