mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-06-04 01:40:40 +02:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 52a3bc224a | |||
| 54d67e5216 | |||
| b55d8250cc | |||
| 3a1e9abd68 | |||
| c5ba40a178 | |||
| f0c6dccadb | |||
| e701d1ab6a | |||
| e10c8093c9 | |||
| ef2d736b20 | |||
| f3a453be20 |
@@ -132,6 +132,8 @@ export default {
|
|||||||
ctx.restore()
|
ctx.restore()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const twoColumnWidth = 210
|
||||||
|
|
||||||
ctx.globalAlpha = 1
|
ctx.globalAlpha = 1
|
||||||
ctx.textBaseline = 'middle'
|
ctx.textBaseline = 'middle'
|
||||||
|
|
||||||
@@ -150,12 +152,12 @@ export default {
|
|||||||
|
|
||||||
// Top text
|
// Top text
|
||||||
addText('audiobookshelf', '28px', 'normal', tanColor, '0px', 65, 28)
|
addText('audiobookshelf', '28px', 'normal', tanColor, '0px', 65, 28)
|
||||||
addText(`${this.year} YEAR IN REVIEW`, '18px', 'bold', 'white', '1px', 65, 51)
|
addText(`${this.year} ${this.$strings.StatsYearInReview}`, '18px', 'bold', 'white', '1px', 65, 51,)
|
||||||
|
|
||||||
// Top left box
|
// Top left box
|
||||||
createRoundedRect(50, 100, 340, 160)
|
createRoundedRect(50, 100, 340, 160)
|
||||||
addText(this.yearStats.numBooksFinished, '64px', 'bold', 'white', '0px', 160, 165)
|
addText(this.yearStats.numBooksFinished, '64px', 'bold', 'white', '0px', 160, 165)
|
||||||
addText('books finished', '28px', 'normal', tanColor, '0px', 160, 210)
|
addText(this.$strings.StatsBooksFinished, '28px', 'normal', tanColor, '0px', 160, 210, twoColumnWidth)
|
||||||
const readIconPath = new Path2D()
|
const readIconPath = new Path2D()
|
||||||
readIconPath.addPath(new Path2D('M19 1H5c-1.1 0-1.99.9-1.99 2L3 15.93c0 .69.35 1.3.88 1.66L12 23l8.11-5.41c.53-.36.88-.97.88-1.66L21 3c0-1.1-.9-2-2-2zm-9 15l-5-5 1.41-1.41L10 13.17l7.59-7.59L19 7l-9 9z'), { a: 2, d: 2, e: 100, f: 160 })
|
readIconPath.addPath(new Path2D('M19 1H5c-1.1 0-1.99.9-1.99 2L3 15.93c0 .69.35 1.3.88 1.66L12 23l8.11-5.41c.53-.36.88-.97.88-1.66L21 3c0-1.1-.9-2-2-2zm-9 15l-5-5 1.41-1.41L10 13.17l7.59-7.59L19 7l-9 9z'), { a: 2, d: 2, e: 100, f: 160 })
|
||||||
ctx.fillStyle = '#ffffff'
|
ctx.fillStyle = '#ffffff'
|
||||||
@@ -164,40 +166,40 @@ export default {
|
|||||||
// Box top right
|
// Box top right
|
||||||
createRoundedRect(410, 100, 340, 160)
|
createRoundedRect(410, 100, 340, 160)
|
||||||
addText(this.$elapsedPrettyExtended(this.yearStats.totalListeningTime, true, false), '40px', 'bold', 'white', '0px', 500, 165)
|
addText(this.$elapsedPrettyExtended(this.yearStats.totalListeningTime, true, false), '40px', 'bold', 'white', '0px', 500, 165)
|
||||||
addText('spent listening', '28px', 'normal', tanColor, '0px', 500, 205)
|
addText(this.$strings.StatsSpentListening, '28px', 'normal', tanColor, '0px', 500, 205, twoColumnWidth)
|
||||||
addIcon('watch_later', 'white', '52px', 440, 180)
|
addIcon('watch_later', 'white', '52px', 440, 180)
|
||||||
|
|
||||||
// Box bottom left
|
// Box bottom left
|
||||||
createRoundedRect(50, 280, 340, 160)
|
createRoundedRect(50, 280, 340, 160)
|
||||||
addText(this.yearStats.totalListeningSessions, '64px', 'bold', 'white', '0px', 160, 345)
|
addText(this.yearStats.totalListeningSessions, '64px', 'bold', 'white', '0px', 160, 345)
|
||||||
addText('sessions', '28px', 'normal', tanColor, '1px', 160, 390)
|
addText(this.$strings.StatsSessions, '28px', 'normal', tanColor, '1px', 160, 390, twoColumnWidth)
|
||||||
addIcon('headphones', 'white', '52px', 95, 360)
|
addIcon('headphones', 'white', '52px', 95, 360)
|
||||||
|
|
||||||
// Box bottom right
|
// Box bottom right
|
||||||
createRoundedRect(410, 280, 340, 160)
|
createRoundedRect(410, 280, 340, 160)
|
||||||
addText(this.yearStats.numBooksListened, '64px', 'bold', 'white', '0px', 500, 345)
|
addText(this.yearStats.numBooksListened, '64px', 'bold', 'white', '0px', 500, 345)
|
||||||
addText('books listened to', '28px', 'normal', tanColor, '0px', 500, 390)
|
addText(this.$strings.StatsBooksListenedTo, '28px', 'normal', tanColor, '0px', 500, 390, twoColumnWidth)
|
||||||
addIcon('local_library', 'white', '52px', 440, 360)
|
addIcon('local_library', 'white', '52px', 440, 360)
|
||||||
|
|
||||||
if (!this.variant) {
|
if (!this.variant) {
|
||||||
// Text stats
|
// Text stats
|
||||||
const topNarrator = this.yearStats.mostListenedNarrator
|
const topNarrator = this.yearStats.mostListenedNarrator
|
||||||
if (topNarrator) {
|
if (topNarrator) {
|
||||||
addText('TOP NARRATOR', '24px', 'normal', tanColor, '1px', 70, 520)
|
addText(this.$strings.StatsTopNarrator, '24px', 'normal', tanColor, '1px', 70, 520, 330)
|
||||||
addText(topNarrator.name, '36px', 'bolder', 'white', '0px', 70, 564, 330)
|
addText(topNarrator.name, '36px', 'bolder', 'white', '0px', 70, 564, 330)
|
||||||
addText(this.$elapsedPrettyExtended(topNarrator.time, true, false), '24px', 'lighter', 'white', '1px', 70, 599)
|
addText(this.$elapsedPrettyExtended(topNarrator.time, true, false), '24px', 'lighter', 'white', '1px', 70, 599)
|
||||||
}
|
}
|
||||||
|
|
||||||
const topGenre = this.yearStats.topGenres[0]
|
const topGenre = this.yearStats.topGenres[0]
|
||||||
if (topGenre) {
|
if (topGenre) {
|
||||||
addText('TOP GENRE', '24px', 'normal', tanColor, '1px', 430, 520)
|
addText(this.$strings.StatsTopGenre, '24px', 'normal', tanColor, '1px', 430, 520, 330)
|
||||||
addText(topGenre.genre, '36px', 'bolder', 'white', '0px', 430, 564, 330)
|
addText(topGenre.genre, '36px', 'bolder', 'white', '0px', 430, 564, 330)
|
||||||
addText(this.$elapsedPrettyExtended(topGenre.time, true, false), '24px', 'lighter', 'white', '1px', 430, 599)
|
addText(this.$elapsedPrettyExtended(topGenre.time, true, false), '24px', 'lighter', 'white', '1px', 430, 599)
|
||||||
}
|
}
|
||||||
|
|
||||||
const topAuthor = this.yearStats.topAuthors[0]
|
const topAuthor = this.yearStats.topAuthors[0]
|
||||||
if (topAuthor) {
|
if (topAuthor) {
|
||||||
addText('TOP AUTHOR', '24px', 'normal', tanColor, '1px', 70, 670)
|
addText(this.$strings.StatsTopAuthor, '24px', 'normal', tanColor, '1px', 70, 670, 330)
|
||||||
addText(topAuthor.name, '36px', 'bolder', 'white', '0px', 70, 714, 330)
|
addText(topAuthor.name, '36px', 'bolder', 'white', '0px', 70, 714, 330)
|
||||||
addText(this.$elapsedPrettyExtended(topAuthor.time, true, false), '24px', 'lighter', 'white', '1px', 70, 749)
|
addText(this.$elapsedPrettyExtended(topAuthor.time, true, false), '24px', 'lighter', 'white', '1px', 70, 749)
|
||||||
}
|
}
|
||||||
@@ -205,7 +207,7 @@ export default {
|
|||||||
if (this.yearStats.mostListenedMonth?.time) {
|
if (this.yearStats.mostListenedMonth?.time) {
|
||||||
const jsdate = new Date(this.year, this.yearStats.mostListenedMonth.month, 1)
|
const jsdate = new Date(this.year, this.yearStats.mostListenedMonth.month, 1)
|
||||||
const monthName = this.$formatJsDate(jsdate, 'LLLL')
|
const monthName = this.$formatJsDate(jsdate, 'LLLL')
|
||||||
addText('TOP MONTH', '24px', 'normal', tanColor, '1px', 430, 670)
|
addText(this.$strings.StatsTopMonth, '24px', 'normal', tanColor, '1px', 430, 670, 330)
|
||||||
addText(monthName, '36px', 'bolder', 'white', '0px', 430, 714, 330)
|
addText(monthName, '36px', 'bolder', 'white', '0px', 430, 714, 330)
|
||||||
addText(this.$elapsedPrettyExtended(this.yearStats.mostListenedMonth.time, true, false), '24px', 'lighter', 'white', '1px', 430, 749)
|
addText(this.$elapsedPrettyExtended(this.yearStats.mostListenedMonth.time, true, false), '24px', 'lighter', 'white', '1px', 430, 749)
|
||||||
}
|
}
|
||||||
@@ -214,7 +216,7 @@ export default {
|
|||||||
finishedBookCoverImgs = Object.values(finishedBookCoverImgs)
|
finishedBookCoverImgs = Object.values(finishedBookCoverImgs)
|
||||||
if (finishedBookCoverImgs.length > 0) {
|
if (finishedBookCoverImgs.length > 0) {
|
||||||
ctx.textAlign = 'center'
|
ctx.textAlign = 'center'
|
||||||
addText('Some books finished this year...', '28px', 'normal', tanColor, '0px', canvas.width / 2, 530)
|
addText(this.$strings.StatsBooksFinishedThisYear, '28px', 'normal', tanColor, '0px', canvas.width / 2, 530)
|
||||||
|
|
||||||
for (let i = 0; i < Math.min(5, finishedBookCoverImgs.length); i++) {
|
for (let i = 0; i < Math.min(5, finishedBookCoverImgs.length); i++) {
|
||||||
let imgToAdd = finishedBookCoverImgs[i]
|
let imgToAdd = finishedBookCoverImgs[i]
|
||||||
@@ -224,14 +226,14 @@ export default {
|
|||||||
} else if (this.variant === 2) {
|
} else if (this.variant === 2) {
|
||||||
// Text stats
|
// Text stats
|
||||||
if (this.yearStats.topAuthors.length) {
|
if (this.yearStats.topAuthors.length) {
|
||||||
addText('TOP AUTHORS', '24px', 'normal', tanColor, '1px', 70, 524)
|
addText(this.$strings.StatsTopAuthors, '24px', 'normal', tanColor, '1px', 70, 524)
|
||||||
for (let i = 0; i < this.yearStats.topAuthors.length; i++) {
|
for (let i = 0; i < this.yearStats.topAuthors.length; i++) {
|
||||||
addText(this.yearStats.topAuthors[i].name, '36px', 'bolder', 'white', '0px', 70, 584 + i * 60, 330)
|
addText(this.yearStats.topAuthors[i].name, '36px', 'bolder', 'white', '0px', 70, 584 + i * 60, 330)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.yearStats.topGenres.length) {
|
if (this.yearStats.topGenres.length) {
|
||||||
addText('TOP GENRES', '24px', 'normal', tanColor, '1px', 430, 524)
|
addText(this.$strings.StatsTopGenres, '24px', 'normal', tanColor, '1px', 430, 524)
|
||||||
for (let i = 0; i < this.yearStats.topGenres.length; i++) {
|
for (let i = 0; i < this.yearStats.topGenres.length; i++) {
|
||||||
addText(this.yearStats.topGenres[i].genre, '36px', 'bolder', 'white', '0px', 430, 584 + i * 60, 330)
|
addText(this.yearStats.topGenres[i].genre, '36px', 'bolder', 'white', '0px', 430, 584 + i * 60, 330)
|
||||||
}
|
}
|
||||||
@@ -263,7 +265,7 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
this.$toast.error('Cannot share natively on this device')
|
this.$toast.error(this.$strings.ToastErrorCannotShare)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -123,6 +123,8 @@ export default {
|
|||||||
ctx.restore()
|
ctx.restore()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const threeColumnTextWidth = 200
|
||||||
|
|
||||||
ctx.globalAlpha = 1
|
ctx.globalAlpha = 1
|
||||||
ctx.textBaseline = 'middle'
|
ctx.textBaseline = 'middle'
|
||||||
|
|
||||||
@@ -141,33 +143,33 @@ export default {
|
|||||||
|
|
||||||
// Top text
|
// Top text
|
||||||
addText('audiobookshelf', '28px', 'normal', tanColor, '0px', 65, 28)
|
addText('audiobookshelf', '28px', 'normal', tanColor, '0px', 65, 28)
|
||||||
addText(`${this.year} YEAR IN REVIEW`, '18px', 'bold', 'white', '1px', 65, 51)
|
addText(`${this.year} ${this.$strings.StatsYearInReview}`, '18px', 'bold', 'white', '1px', 65, 51)
|
||||||
|
|
||||||
// Top left box
|
// Top left box
|
||||||
createRoundedRect(40, 100, 230, 100)
|
createRoundedRect(40, 100, 230, 100)
|
||||||
ctx.textAlign = 'center'
|
ctx.textAlign = 'center'
|
||||||
addText(this.yearStats.numBooksAdded, '48px', 'bold', 'white', '0px', 155, 140)
|
addText(this.yearStats.numBooksAdded, '48px', 'bold', 'white', '0px', 155, 140)
|
||||||
addText('books added', '18px', 'normal', tanColor, '0px', 155, 170)
|
addText(this.$strings.StatsBooksAdded, '18px', 'normal', tanColor, '0px', 155, 170, threeColumnTextWidth)
|
||||||
|
|
||||||
// Box top right
|
// Box top right
|
||||||
createRoundedRect(285, 100, 230, 100)
|
createRoundedRect(285, 100, 230, 100)
|
||||||
addText(this.yearStats.numAuthorsAdded, '48px', 'bold', 'white', '0px', 400, 140)
|
addText(this.yearStats.numAuthorsAdded, '48px', 'bold', 'white', '0px', 400, 140)
|
||||||
addText('authors added', '18px', 'normal', tanColor, '0px', 400, 170)
|
addText(this.$strings.StatsAuthorsAdded, '18px', 'normal', tanColor, '0px', 400, 170, threeColumnTextWidth)
|
||||||
|
|
||||||
// Box bottom left
|
// Box bottom left
|
||||||
createRoundedRect(530, 100, 230, 100)
|
createRoundedRect(530, 100, 230, 100)
|
||||||
addText(this.yearStats.numListeningSessions, '48px', 'bold', 'white', '0px', 645, 140)
|
addText(this.yearStats.numListeningSessions, '48px', 'bold', 'white', '0px', 645, 140)
|
||||||
addText('sessions', '18px', 'normal', tanColor, '1px', 645, 170)
|
addText(this.$strings.StatsSessions, '18px', 'normal', tanColor, '1px', 645, 170, threeColumnTextWidth)
|
||||||
|
|
||||||
// Text stats
|
// Text stats
|
||||||
if (this.yearStats.totalBooksAddedSize) {
|
if (this.yearStats.totalBooksAddedSize) {
|
||||||
addText('Your book collection grew to...', '24px', 'normal', tanColor, '0px', canvas.width / 2, 260)
|
addText(this.$strings.StatsCollectionGrewTo, '24px', 'normal', tanColor, '0px', canvas.width / 2, 260)
|
||||||
addText(this.$bytesPretty(this.yearStats.totalBooksSize), '36px', 'bolder', 'white', '0px', canvas.width / 2, 300)
|
addText(this.$bytesPretty(this.yearStats.totalBooksSize), '36px', 'bolder', 'white', '0px', canvas.width / 2, 300)
|
||||||
addText('+' + this.$bytesPretty(this.yearStats.totalBooksAddedSize), '20px', 'lighter', 'white', '0px', canvas.width / 2, 330)
|
addText('+' + this.$bytesPretty(this.yearStats.totalBooksAddedSize), '20px', 'lighter', 'white', '0px', canvas.width / 2, 330)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.yearStats.totalBooksAddedDuration) {
|
if (this.yearStats.totalBooksAddedDuration) {
|
||||||
addText('With a total duration of...', '24px', 'normal', tanColor, '0px', canvas.width / 2, 400)
|
addText(this.$strings.StatsTotalDuration, '24px', 'normal', tanColor, '0px', canvas.width / 2, 400)
|
||||||
addText(this.$elapsedPrettyExtended(this.yearStats.totalBooksDuration, true, false), '36px', 'bolder', 'white', '0px', canvas.width / 2, 440)
|
addText(this.$elapsedPrettyExtended(this.yearStats.totalBooksDuration, true, false), '36px', 'bolder', 'white', '0px', canvas.width / 2, 440)
|
||||||
addText('+' + this.$elapsedPrettyExtended(this.yearStats.totalBooksAddedDuration, true, false), '20px', 'lighter', 'white', '0px', canvas.width / 2, 470)
|
addText('+' + this.$elapsedPrettyExtended(this.yearStats.totalBooksAddedDuration, true, false), '20px', 'lighter', 'white', '0px', canvas.width / 2, 470)
|
||||||
}
|
}
|
||||||
@@ -176,7 +178,7 @@ export default {
|
|||||||
// Bottom images
|
// Bottom images
|
||||||
imgsToAdd = Object.values(imgsToAdd)
|
imgsToAdd = Object.values(imgsToAdd)
|
||||||
if (imgsToAdd.length > 0) {
|
if (imgsToAdd.length > 0) {
|
||||||
addText('Some additions include...', '24px', 'normal', tanColor, '0px', canvas.width / 2, 540)
|
addText(this.$strings.StatsBooksAdditional, '24px', 'normal', tanColor, '0px', canvas.width / 2, 540)
|
||||||
|
|
||||||
for (let i = 0; i < Math.min(5, imgsToAdd.length); i++) {
|
for (let i = 0; i < Math.min(5, imgsToAdd.length); i++) {
|
||||||
let imgToAdd = imgsToAdd[i]
|
let imgToAdd = imgsToAdd[i]
|
||||||
@@ -187,14 +189,14 @@ export default {
|
|||||||
// Text stats
|
// Text stats
|
||||||
ctx.textAlign = 'left'
|
ctx.textAlign = 'left'
|
||||||
if (this.yearStats.topAuthors.length) {
|
if (this.yearStats.topAuthors.length) {
|
||||||
addText('TOP AUTHORS', '24px', 'normal', tanColor, '1px', 70, 549)
|
addText(this.$strings.StatsTopAuthors, '24px', 'normal', tanColor, '1px', 70, 549, 330)
|
||||||
for (let i = 0; i < this.yearStats.topAuthors.length; i++) {
|
for (let i = 0; i < this.yearStats.topAuthors.length; i++) {
|
||||||
addText(this.yearStats.topAuthors[i].name, '36px', 'bolder', 'white', '0px', 70, 609 + i * 60, 330)
|
addText(this.yearStats.topAuthors[i].name, '36px', 'bolder', 'white', '0px', 70, 609 + i * 60, 330)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.yearStats.topNarrators.length) {
|
if (this.yearStats.topNarrators.length) {
|
||||||
addText('TOP NARRATORS', '24px', 'normal', tanColor, '1px', 430, 549)
|
addText(this.$strings.StatsTopNarrators, '24px', 'normal', tanColor, '1px', 430, 549)
|
||||||
for (let i = 0; i < this.yearStats.topNarrators.length; i++) {
|
for (let i = 0; i < this.yearStats.topNarrators.length; i++) {
|
||||||
addText(this.yearStats.topNarrators[i].name, '36px', 'bolder', 'white', '0px', 430, 609 + i * 60, 330)
|
addText(this.yearStats.topNarrators[i].name, '36px', 'bolder', 'white', '0px', 430, 609 + i * 60, 330)
|
||||||
}
|
}
|
||||||
@@ -203,14 +205,14 @@ export default {
|
|||||||
// Text stats
|
// Text stats
|
||||||
ctx.textAlign = 'left'
|
ctx.textAlign = 'left'
|
||||||
if (this.yearStats.topAuthors.length) {
|
if (this.yearStats.topAuthors.length) {
|
||||||
addText('TOP AUTHORS', '24px', 'normal', tanColor, '1px', 70, 549)
|
addText(this.$strings.StatsTopAuthors, '24px', 'normal', tanColor, '1px', 70, 549, 330)
|
||||||
for (let i = 0; i < this.yearStats.topAuthors.length; i++) {
|
for (let i = 0; i < this.yearStats.topAuthors.length; i++) {
|
||||||
addText(this.yearStats.topAuthors[i].name, '36px', 'bolder', 'white', '0px', 70, 609 + i * 60, 330)
|
addText(this.yearStats.topAuthors[i].name, '36px', 'bolder', 'white', '0px', 70, 609 + i * 60, 330)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.yearStats.topGenres.length) {
|
if (this.yearStats.topGenres.length) {
|
||||||
addText('TOP GENRES', '24px', 'normal', tanColor, '1px', 430, 549)
|
addText(this.$strings.StatsTopGenres, '24px', 'normal', tanColor, '1px', 430, 549)
|
||||||
for (let i = 0; i < this.yearStats.topGenres.length; i++) {
|
for (let i = 0; i < this.yearStats.topGenres.length; i++) {
|
||||||
addText(this.yearStats.topGenres[i].genre, '36px', 'bolder', 'white', '0px', 430, 609 + i * 60, 330)
|
addText(this.yearStats.topGenres[i].genre, '36px', 'bolder', 'white', '0px', 430, 609 + i * 60, 330)
|
||||||
}
|
}
|
||||||
@@ -239,7 +241,7 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
this.$toast.error('Cannot share natively on this device')
|
this.$toast.error(this.$strings.ToastErrorCannotShare)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -113,6 +113,8 @@ export default {
|
|||||||
ctx.restore()
|
ctx.restore()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const twoColumnWidth = 180
|
||||||
|
|
||||||
ctx.globalAlpha = 1
|
ctx.globalAlpha = 1
|
||||||
ctx.textBaseline = 'middle'
|
ctx.textBaseline = 'middle'
|
||||||
|
|
||||||
@@ -131,12 +133,12 @@ export default {
|
|||||||
|
|
||||||
// Top text
|
// Top text
|
||||||
addText('audiobookshelf', '28px', 'normal', tanColor, '0px', 65, 28)
|
addText('audiobookshelf', '28px', 'normal', tanColor, '0px', 65, 28)
|
||||||
addText(`${this.year} YEAR IN REVIEW`, '18px', 'bold', 'white', '1px', 65, 51)
|
addText(`${this.year} ${this.$strings.StatsYearInReview}`, '18px', 'bold', 'white', '1px', 65, 51)
|
||||||
|
|
||||||
// Top left box
|
// Top left box
|
||||||
createRoundedRect(15, 75, 280, 110)
|
createRoundedRect(15, 75, 280, 110)
|
||||||
addText(this.yearStats.numBooksFinished, '48px', 'bold', 'white', '0px', 105, 120)
|
addText(this.yearStats.numBooksFinished, '48px', 'bold', 'white', '0px', 105, 120)
|
||||||
addText('books finished', '20px', 'normal', tanColor, '0px', 105, 155)
|
addText(this.$strings.StatsBooksFinished, '20px', 'normal', tanColor, '0px', 105, 155, twoColumnWidth)
|
||||||
const readIconPath = new Path2D()
|
const readIconPath = new Path2D()
|
||||||
readIconPath.addPath(new Path2D('M19 1H5c-1.1 0-1.99.9-1.99 2L3 15.93c0 .69.35 1.3.88 1.66L12 23l8.11-5.41c.53-.36.88-.97.88-1.66L21 3c0-1.1-.9-2-2-2zm-9 15l-5-5 1.41-1.41L10 13.17l7.59-7.59L19 7l-9 9z'), { a: 1.5, d: 1.5, e: 55, f: 115 })
|
readIconPath.addPath(new Path2D('M19 1H5c-1.1 0-1.99.9-1.99 2L3 15.93c0 .69.35 1.3.88 1.66L12 23l8.11-5.41c.53-.36.88-.97.88-1.66L21 3c0-1.1-.9-2-2-2zm-9 15l-5-5 1.41-1.41L10 13.17l7.59-7.59L19 7l-9 9z'), { a: 1.5, d: 1.5, e: 55, f: 115 })
|
||||||
ctx.fillStyle = '#ffffff'
|
ctx.fillStyle = '#ffffff'
|
||||||
@@ -144,7 +146,7 @@ export default {
|
|||||||
|
|
||||||
createRoundedRect(305, 75, 280, 110)
|
createRoundedRect(305, 75, 280, 110)
|
||||||
addText(this.yearStats.numBooksListened, '48px', 'bold', 'white', '0px', 400, 120)
|
addText(this.yearStats.numBooksListened, '48px', 'bold', 'white', '0px', 400, 120)
|
||||||
addText('books listened to', '20px', 'normal', tanColor, '0px', 400, 155)
|
addText(this.$strings.StatsBooksListenedTo, '20px', 'normal', tanColor, '0px', 400, 155, twoColumnWidth)
|
||||||
addIcon('local_library', 'white', '42px', 345, 130)
|
addIcon('local_library', 'white', '42px', 345, 130)
|
||||||
|
|
||||||
this.canvas = canvas
|
this.canvas = canvas
|
||||||
@@ -169,7 +171,7 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
this.$toast.error('Cannot share natively on this device')
|
this.$toast.error(this.$strings.ToastErrorCannotShare)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "audiobookshelf-client",
|
"name": "audiobookshelf-client",
|
||||||
"version": "2.12.2",
|
"version": "2.12.3",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "audiobookshelf-client",
|
"name": "audiobookshelf-client",
|
||||||
"version": "2.12.2",
|
"version": "2.12.3",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxtjs/axios": "^5.13.6",
|
"@nuxtjs/axios": "^5.13.6",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "audiobookshelf-client",
|
"name": "audiobookshelf-client",
|
||||||
"version": "2.12.2",
|
"version": "2.12.3",
|
||||||
"buildNumber": 1,
|
"buildNumber": 1,
|
||||||
"description": "Self-hosted audiobook and podcast client",
|
"description": "Self-hosted audiobook and podcast client",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
|||||||
@@ -771,6 +771,24 @@
|
|||||||
"PlaceholderNewPlaylist": "New playlist name",
|
"PlaceholderNewPlaylist": "New playlist name",
|
||||||
"PlaceholderSearch": "Search..",
|
"PlaceholderSearch": "Search..",
|
||||||
"PlaceholderSearchEpisode": "Search episode..",
|
"PlaceholderSearchEpisode": "Search episode..",
|
||||||
|
"StatsAuthorsAdded": "authors added",
|
||||||
|
"StatsBooksAdded": "books added",
|
||||||
|
"StatsBooksAdditional": "Some additions include…",
|
||||||
|
"StatsBooksFinished": "books finished",
|
||||||
|
"StatsBooksFinishedThisYear": "Some books finished this year…",
|
||||||
|
"StatsBooksListenedTo": "books listened to",
|
||||||
|
"StatsCollectionGrewTo": "Your book collection grew to…",
|
||||||
|
"StatsSessions": "sessions",
|
||||||
|
"StatsSpentListening": "spent listening",
|
||||||
|
"StatsTopAuthor": "TOP AUTHOR",
|
||||||
|
"StatsTopAuthors": "TOP AUTHORS",
|
||||||
|
"StatsTopGenre": "TOP GENRE",
|
||||||
|
"StatsTopGenres": "TOP GENRES",
|
||||||
|
"StatsTopMonth": "TOP MONTH",
|
||||||
|
"StatsTopNarrator": "TOP NARRATOR",
|
||||||
|
"StatsTopNarrators": "TOP NARRATORS",
|
||||||
|
"StatsTotalDuration": "With a total duration of…",
|
||||||
|
"StatsYearInReview": "YEAR IN REVIEW",
|
||||||
"ToastAccountUpdateFailed": "Failed to update account",
|
"ToastAccountUpdateFailed": "Failed to update account",
|
||||||
"ToastAccountUpdateSuccess": "Account updated",
|
"ToastAccountUpdateSuccess": "Account updated",
|
||||||
"ToastAuthorImageRemoveFailed": "Failed to remove image",
|
"ToastAuthorImageRemoveFailed": "Failed to remove image",
|
||||||
@@ -806,6 +824,7 @@
|
|||||||
"ToastCollectionUpdateSuccess": "Collection updated",
|
"ToastCollectionUpdateSuccess": "Collection updated",
|
||||||
"ToastDeleteFileFailed": "Failed to delete file",
|
"ToastDeleteFileFailed": "Failed to delete file",
|
||||||
"ToastDeleteFileSuccess": "File deleted",
|
"ToastDeleteFileSuccess": "File deleted",
|
||||||
|
"ToastErrorCannotShare": "Cannot share natively on this device",
|
||||||
"ToastFailedToLoadData": "Failed to load data",
|
"ToastFailedToLoadData": "Failed to load data",
|
||||||
"ToastItemCoverUpdateFailed": "Failed to update item cover",
|
"ToastItemCoverUpdateFailed": "Failed to update item cover",
|
||||||
"ToastItemCoverUpdateSuccess": "Item cover updated",
|
"ToastItemCoverUpdateSuccess": "Item cover updated",
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "audiobookshelf",
|
"name": "audiobookshelf",
|
||||||
"version": "2.12.2",
|
"version": "2.12.3",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "audiobookshelf",
|
"name": "audiobookshelf",
|
||||||
"version": "2.12.2",
|
"version": "2.12.3",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "audiobookshelf",
|
"name": "audiobookshelf",
|
||||||
"version": "2.12.2",
|
"version": "2.12.3",
|
||||||
"buildNumber": 1,
|
"buildNumber": 1,
|
||||||
"description": "Self-hosted audiobook and podcast server",
|
"description": "Self-hosted audiobook and podcast server",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
|||||||
+4
-5
@@ -207,7 +207,6 @@ class Database {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await this.sequelize.authenticate()
|
await this.sequelize.authenticate()
|
||||||
await this.loadExtensions([process.env.SQLEAN_UNICODE_PATH])
|
|
||||||
Logger.info(`[Database] Db connection was successful`)
|
Logger.info(`[Database] Db connection was successful`)
|
||||||
return true
|
return true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -217,7 +216,7 @@ class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* TODO: Temporarily disabled
|
||||||
* @param {string[]} extensions paths to extension binaries
|
* @param {string[]} extensions paths to extension binaries
|
||||||
*/
|
*/
|
||||||
async loadExtensions(extensions) {
|
async loadExtensions(extensions) {
|
||||||
@@ -827,7 +826,7 @@ class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* TODO: Temporarily unused
|
||||||
* @param {string} value
|
* @param {string} value
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
@@ -836,7 +835,7 @@ class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* TODO: Temporarily unused
|
||||||
* @param {string} query
|
* @param {string} query
|
||||||
* @returns {Promise<string>}
|
* @returns {Promise<string>}
|
||||||
*/
|
*/
|
||||||
@@ -855,7 +854,7 @@ class Database {
|
|||||||
*/
|
*/
|
||||||
matchExpression(column, normalizedQuery) {
|
matchExpression(column, normalizedQuery) {
|
||||||
const normalizedPattern = this.sequelize.escape(`%${normalizedQuery}%`)
|
const normalizedPattern = this.sequelize.escape(`%${normalizedQuery}%`)
|
||||||
const normalizedColumn = this.normalize(column)
|
const normalizedColumn = column
|
||||||
return `${normalizedColumn} LIKE ${normalizedPattern}`
|
return `${normalizedColumn} LIKE ${normalizedPattern}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,21 +113,21 @@ class LibraryItemController {
|
|||||||
Logger.warn('User attempted to download without permission', req.user)
|
Logger.warn('User attempted to download without permission', req.user)
|
||||||
return res.sendStatus(403)
|
return res.sendStatus(403)
|
||||||
}
|
}
|
||||||
|
const libraryItemPath = req.libraryItem.path
|
||||||
|
const itemTitle = req.libraryItem.media.metadata.title
|
||||||
|
|
||||||
// If library item is a single file in root dir then no need to zip
|
// If library item is a single file in root dir then no need to zip
|
||||||
if (req.libraryItem.isFile) {
|
if (req.libraryItem.isFile) {
|
||||||
// Express does not set the correct mimetype for m4b files so use our defined mimetypes if available
|
// Express does not set the correct mimetype for m4b files so use our defined mimetypes if available
|
||||||
const audioMimeType = getAudioMimeTypeFromExtname(Path.extname(req.libraryItem.path))
|
const audioMimeType = getAudioMimeTypeFromExtname(Path.extname(libraryItemPath))
|
||||||
if (audioMimeType) {
|
if (audioMimeType) {
|
||||||
res.setHeader('Content-Type', audioMimeType)
|
res.setHeader('Content-Type', audioMimeType)
|
||||||
}
|
}
|
||||||
|
Logger.info(`[LibraryItemController] User "${req.user.username}" requested download for item "${itemTitle}" at "${libraryItemPath}"`)
|
||||||
res.download(req.libraryItem.path, req.libraryItem.relPath)
|
res.download(libraryItemPath, req.libraryItem.relPath)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const libraryItemPath = req.libraryItem.path
|
|
||||||
const itemTitle = req.libraryItem.media.metadata.title
|
|
||||||
Logger.info(`[LibraryItemController] User "${req.user.username}" requested download for item "${itemTitle}" at "${libraryItemPath}"`)
|
Logger.info(`[LibraryItemController] User "${req.user.username}" requested download for item "${itemTitle}" at "${libraryItemPath}"`)
|
||||||
const filename = `${itemTitle}.zip`
|
const filename = `${itemTitle}.zip`
|
||||||
zipHelpers.zipDirectoryPipe(libraryItemPath, filename, res)
|
zipHelpers.zipDirectoryPipe(libraryItemPath, filename, res)
|
||||||
@@ -715,7 +715,7 @@ class LibraryItemController {
|
|||||||
return res.sendStatus(403)
|
return res.sendStatus(403)
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.info(`[LibraryItemController] User "${req.user.username}" requested file download at "${libraryFile.metadata.path}"`)
|
Logger.info(`[LibraryItemController] User "${req.user.username}" requested download for item "${req.libraryItem.media.metadata.title}" file at "${libraryFile.metadata.path}"`)
|
||||||
|
|
||||||
if (global.XAccel) {
|
if (global.XAccel) {
|
||||||
const encodedURI = encodeUriPath(global.XAccel + libraryFile.metadata.path)
|
const encodedURI = encodeUriPath(global.XAccel + libraryFile.metadata.path)
|
||||||
|
|||||||
@@ -263,8 +263,9 @@ module.exports.sqlean = sqlean // for testing
|
|||||||
class BinaryManager {
|
class BinaryManager {
|
||||||
defaultRequiredBinaries = [
|
defaultRequiredBinaries = [
|
||||||
new Binary('ffmpeg', 'executable', 'FFMPEG_PATH', ['5.1'], ffbinaries), // ffmpeg executable
|
new Binary('ffmpeg', 'executable', 'FFMPEG_PATH', ['5.1'], ffbinaries), // ffmpeg executable
|
||||||
new Binary('ffprobe', 'executable', 'FFPROBE_PATH', ['5.1'], ffbinaries), // ffprobe executable
|
new Binary('ffprobe', 'executable', 'FFPROBE_PATH', ['5.1'], ffbinaries) // ffprobe executable
|
||||||
new Binary('unicode', 'library', 'SQLEAN_UNICODE_PATH', ['0.24.2'], sqlean) // sqlean unicode extension
|
// TODO: Temporarily disabled due to db corruption issues
|
||||||
|
// new Binary('unicode', 'library', 'SQLEAN_UNICODE_PATH', ['0.24.2'], sqlean) // sqlean unicode extension
|
||||||
]
|
]
|
||||||
|
|
||||||
constructor(requiredBinaries = this.defaultRequiredBinaries) {
|
constructor(requiredBinaries = this.defaultRequiredBinaries) {
|
||||||
|
|||||||
@@ -975,7 +975,7 @@ module.exports = {
|
|||||||
async search(oldUser, oldLibrary, query, limit, offset) {
|
async search(oldUser, oldLibrary, query, limit, offset) {
|
||||||
const userPermissionBookWhere = this.getUserPermissionBookWhereQuery(oldUser)
|
const userPermissionBookWhere = this.getUserPermissionBookWhereQuery(oldUser)
|
||||||
|
|
||||||
const normalizedQuery = await Database.getNormalizedQuery(query)
|
const normalizedQuery = query
|
||||||
|
|
||||||
const matchTitle = Database.matchExpression('title', normalizedQuery)
|
const matchTitle = Database.matchExpression('title', normalizedQuery)
|
||||||
const matchSubtitle = Database.matchExpression('subtitle', normalizedQuery)
|
const matchSubtitle = Database.matchExpression('subtitle', normalizedQuery)
|
||||||
|
|||||||
@@ -314,7 +314,7 @@ module.exports = {
|
|||||||
async search(oldUser, oldLibrary, query, limit, offset) {
|
async search(oldUser, oldLibrary, query, limit, offset) {
|
||||||
const userPermissionPodcastWhere = this.getUserPermissionPodcastWhereQuery(oldUser)
|
const userPermissionPodcastWhere = this.getUserPermissionPodcastWhereQuery(oldUser)
|
||||||
|
|
||||||
const normalizedQuery = await Database.getNormalizedQuery(query)
|
const normalizedQuery = query
|
||||||
const matchTitle = Database.matchExpression('title', normalizedQuery)
|
const matchTitle = Database.matchExpression('title', normalizedQuery)
|
||||||
const matchAuthor = Database.matchExpression('author', normalizedQuery)
|
const matchAuthor = Database.matchExpression('author', normalizedQuery)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user