mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-06-04 18:00:45 +02:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e81b3461b2 | |||
| 9345cb3934 | |||
| eb36a0b3dd | |||
| 7e442ecb3d | |||
| f07c5eb725 | |||
| a486be92cb | |||
| 4d84060036 | |||
| fc503691fe | |||
| c80dd43a3e | |||
| a4a62e0c18 | |||
| 2f98cb9b6d | |||
| 91dc6eebb0 | |||
| d72e0a4418 | |||
| 2c8ebd43cc | |||
| 9f561aa296 | |||
| 930bacd45d |
@@ -6,10 +6,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="px-8 py-6 w-full rounded-lg bg-bg shadow-lg border border-black-300 relative overflow-y-scroll" style="max-height: 80vh">
|
<div class="px-8 py-6 w-full rounded-lg bg-bg shadow-lg border border-black-300 relative overflow-y-scroll" style="max-height: 80vh">
|
||||||
<p class="text-xl font-bold pb-4">
|
<template v-for="release in releasesToShow">
|
||||||
Changelog <a :href="currentTagUrl" target="_blank" class="hover:underline">v{{ currentVersionNumber }}</a> ({{ currentVersionPubDate }})
|
<div :key="release.name">
|
||||||
</p>
|
<p class="text-xl font-bold pb-4">
|
||||||
<div class="custom-text" v-html="compiledMarkedown" />
|
Changelog <a :href="`https://github.com/advplyr/audiobookshelf/releases/tag/${release.name}`" target="_blank" class="hover:underline">{{ release.name }}</a> ({{ $formatDate(release.pubdate, dateFormat) }})
|
||||||
|
</p>
|
||||||
|
<div class="custom-text" v-html="getChangelog(release)" />
|
||||||
|
</div>
|
||||||
|
<div v-if="release !== releasesToShow[releasesToShow.length - 1]" class="border-b border-black-300 my-8" />
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</modals-modal>
|
</modals-modal>
|
||||||
</template>
|
</template>
|
||||||
@@ -37,24 +42,15 @@ export default {
|
|||||||
dateFormat() {
|
dateFormat() {
|
||||||
return this.$store.state.serverSettings.dateFormat
|
return this.$store.state.serverSettings.dateFormat
|
||||||
},
|
},
|
||||||
changelog() {
|
releasesToShow() {
|
||||||
return this.versionData?.currentVersionChangelog || 'No Changelog Available'
|
return this.versionData?.releasesToShow || []
|
||||||
},
|
}
|
||||||
compiledMarkedown() {
|
},
|
||||||
return marked.parse(this.changelog, { gfm: true, breaks: true })
|
methods: {
|
||||||
},
|
getChangelog(release) {
|
||||||
currentVersionPubDate() {
|
return marked.parse(release.changelog || 'No Changelog Available', { gfm: true, breaks: true })
|
||||||
if (!this.versionData?.currentVersionPubDate) return 'Unknown release date'
|
|
||||||
return `${this.$formatDate(this.versionData.currentVersionPubDate, this.dateFormat)}`
|
|
||||||
},
|
|
||||||
currentTagUrl() {
|
|
||||||
return this.versionData?.currentTagUrl
|
|
||||||
},
|
|
||||||
currentVersionNumber() {
|
|
||||||
return this.$config.version
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {},
|
|
||||||
mounted() {}
|
mounted() {}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "audiobookshelf-client",
|
"name": "audiobookshelf-client",
|
||||||
"version": "2.12.1",
|
"version": "2.12.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "audiobookshelf-client",
|
"name": "audiobookshelf-client",
|
||||||
"version": "2.12.1",
|
"version": "2.12.2",
|
||||||
"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.1",
|
"version": "2.12.2",
|
||||||
"buildNumber": 1,
|
"buildNumber": 1,
|
||||||
"description": "Self-hosted audiobook and podcast client",
|
"description": "Self-hosted audiobook and podcast client",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
|||||||
+46
-33
@@ -11,6 +11,7 @@ function parseSemver(ver) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
name: ver,
|
||||||
total,
|
total,
|
||||||
version: groups[2],
|
version: groups[2],
|
||||||
major: Number(groups[3]),
|
major: Number(groups[3]),
|
||||||
@@ -24,49 +25,61 @@ function parseSemver(ver) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getReleases() {
|
||||||
|
return axios
|
||||||
|
.get(`https://api.github.com/repos/advplyr/audiobookshelf/releases`)
|
||||||
|
.then((res) => {
|
||||||
|
return res.data
|
||||||
|
.map((release) => {
|
||||||
|
const tagName = release.tag_name
|
||||||
|
const verObj = parseSemver(tagName)
|
||||||
|
if (verObj) {
|
||||||
|
verObj.pubdate = new Date(release.published_at)
|
||||||
|
verObj.changelog = release.body
|
||||||
|
return verObj
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
.filter((verObj) => verObj)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed to get releases', error)
|
||||||
|
return []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const currentVersion = packagejson.version
|
export const currentVersion = packagejson.version
|
||||||
|
|
||||||
export async function checkForUpdate() {
|
export async function checkForUpdate() {
|
||||||
if (!packagejson.version) {
|
if (!packagejson.version) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
var currVerObj = parseSemver('v' + packagejson.version)
|
|
||||||
if (!currVerObj) {
|
|
||||||
console.error('Invalid version', packagejson.version)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
var largestVer = null
|
|
||||||
await axios.get(`https://api.github.com/repos/advplyr/audiobookshelf/releases`).then((res) => {
|
|
||||||
var releases = res.data
|
|
||||||
if (releases && releases.length) {
|
|
||||||
releases.forEach((release) => {
|
|
||||||
var tagName = release.tag_name
|
|
||||||
var verObj = parseSemver(tagName)
|
|
||||||
if (verObj) {
|
|
||||||
if (!largestVer || largestVer.total < verObj.total) {
|
|
||||||
largestVer = verObj
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verObj.version == currVerObj.version) {
|
const releases = await getReleases()
|
||||||
currVerObj.pubdate = new Date(release.published_at)
|
if (!releases.length) {
|
||||||
currVerObj.changelog = release.body
|
console.error('No releases found')
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (!largestVer) {
|
|
||||||
console.error('No valid version tags to compare with')
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const currentVersion = releases.find((release) => release.version == packagejson.version)
|
||||||
|
if (!currentVersion) {
|
||||||
|
console.error('Current version not found in releases')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const latestVersion = releases[0]
|
||||||
|
const currentVersionMinor = currentVersion.minor
|
||||||
|
const currentVersionMajor = currentVersion.major
|
||||||
|
// Show all releases with the same minor version and lower or equal total version
|
||||||
|
const releasesToShow = releases.filter((release) => {
|
||||||
|
return release.major == currentVersionMajor && release.minor == currentVersionMinor && release.total <= currentVersion.total
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hasUpdate: largestVer.total > currVerObj.total,
|
hasUpdate: latestVersion.total > currentVersion.total,
|
||||||
latestVersion: largestVer.version,
|
latestVersion: latestVersion.version,
|
||||||
githubTagUrl: `https://github.com/advplyr/audiobookshelf/releases/tag/v${largestVer.version}`,
|
githubTagUrl: `https://github.com/advplyr/audiobookshelf/releases/tag/v${latestVersion.version}`,
|
||||||
currentVersion: currVerObj.version,
|
currentVersion: currentVersion.version,
|
||||||
currentTagUrl: `https://github.com/advplyr/audiobookshelf/releases/tag/v${currVerObj.version}`,
|
releasesToShow
|
||||||
currentVersionPubDate: currVerObj.pubdate,
|
|
||||||
currentVersionChangelog: currVerObj.changelog
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-19
@@ -32,33 +32,33 @@ export const state = () => ({
|
|||||||
})
|
})
|
||||||
|
|
||||||
export const getters = {
|
export const getters = {
|
||||||
getServerSetting: state => key => {
|
getServerSetting: (state) => (key) => {
|
||||||
if (!state.serverSettings) return null
|
if (!state.serverSettings) return null
|
||||||
return state.serverSettings[key]
|
return state.serverSettings[key]
|
||||||
},
|
},
|
||||||
getLibraryItemIdStreaming: state => {
|
getLibraryItemIdStreaming: (state) => {
|
||||||
return state.streamLibraryItem?.id || null
|
return state.streamLibraryItem?.id || null
|
||||||
},
|
},
|
||||||
getIsStreamingFromDifferentLibrary: (state, getters, rootState) => {
|
getIsStreamingFromDifferentLibrary: (state, getters, rootState) => {
|
||||||
if (!state.streamLibraryItem) return false
|
if (!state.streamLibraryItem) return false
|
||||||
return state.streamLibraryItem.libraryId !== rootState.libraries.currentLibraryId
|
return state.streamLibraryItem.libraryId !== rootState.libraries.currentLibraryId
|
||||||
},
|
},
|
||||||
getIsMediaStreaming: state => (libraryItemId, episodeId) => {
|
getIsMediaStreaming: (state) => (libraryItemId, episodeId) => {
|
||||||
if (!state.streamLibraryItem) return null
|
if (!state.streamLibraryItem) return null
|
||||||
if (!episodeId) return state.streamLibraryItem.id == libraryItemId
|
if (!episodeId) return state.streamLibraryItem.id == libraryItemId
|
||||||
return state.streamLibraryItem.id == libraryItemId && state.streamEpisodeId == episodeId
|
return state.streamLibraryItem.id == libraryItemId && state.streamEpisodeId == episodeId
|
||||||
},
|
},
|
||||||
getIsMediaQueued: state => (libraryItemId, episodeId) => {
|
getIsMediaQueued: (state) => (libraryItemId, episodeId) => {
|
||||||
return state.playerQueueItems.some(i => {
|
return state.playerQueueItems.some((i) => {
|
||||||
if (!episodeId) return i.libraryItemId === libraryItemId
|
if (!episodeId) return i.libraryItemId === libraryItemId
|
||||||
return i.libraryItemId === libraryItemId && i.episodeId === episodeId
|
return i.libraryItemId === libraryItemId && i.episodeId === episodeId
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
getBookshelfView: state => {
|
getBookshelfView: (state) => {
|
||||||
if (!state.serverSettings || isNaN(state.serverSettings.bookshelfView)) return Constants.BookshelfView.STANDARD
|
if (!state.serverSettings || isNaN(state.serverSettings.bookshelfView)) return Constants.BookshelfView.STANDARD
|
||||||
return state.serverSettings.bookshelfView
|
return state.serverSettings.bookshelfView
|
||||||
},
|
},
|
||||||
getHomeBookshelfView: state => {
|
getHomeBookshelfView: (state) => {
|
||||||
if (!state.serverSettings || isNaN(state.serverSettings.homeBookshelfView)) return Constants.BookshelfView.STANDARD
|
if (!state.serverSettings || isNaN(state.serverSettings.homeBookshelfView)) return Constants.BookshelfView.STANDARD
|
||||||
return state.serverSettings.homeBookshelfView
|
return state.serverSettings.homeBookshelfView
|
||||||
}
|
}
|
||||||
@@ -69,17 +69,20 @@ export const actions = {
|
|||||||
const updatePayload = {
|
const updatePayload = {
|
||||||
...payload
|
...payload
|
||||||
}
|
}
|
||||||
return this.$axios.$patch('/api/settings', updatePayload).then((result) => {
|
return this.$axios
|
||||||
if (result.success) {
|
.$patch('/api/settings', updatePayload)
|
||||||
commit('setServerSettings', result.serverSettings)
|
.then((result) => {
|
||||||
return true
|
if (result.success) {
|
||||||
} else {
|
commit('setServerSettings', result.serverSettings)
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed to update server settings', error)
|
||||||
return false
|
return false
|
||||||
}
|
})
|
||||||
}).catch((error) => {
|
|
||||||
console.error('Failed to update server settings', error)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
checkForUpdate({ commit }) {
|
checkForUpdate({ commit }) {
|
||||||
const VERSION_CHECK_BUFF = 1000 * 60 * 5 // 5 minutes
|
const VERSION_CHECK_BUFF = 1000 * 60 * 5 // 5 minutes
|
||||||
@@ -96,7 +99,7 @@ export const actions = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var shouldCheckForUpdate = Date.now() - Number(lastVerCheck) > VERSION_CHECK_BUFF
|
var shouldCheckForUpdate = Date.now() - Number(lastVerCheck) > VERSION_CHECK_BUFF
|
||||||
if (!shouldCheckForUpdate && savedVersionData && savedVersionData.version !== currentVersion) {
|
if (!shouldCheckForUpdate && savedVersionData && (savedVersionData.version !== currentVersion || !savedVersionData.releasesToShow)) {
|
||||||
// Version mismatch between saved data so check for update anyway
|
// Version mismatch between saved data so check for update anyway
|
||||||
shouldCheckForUpdate = true
|
shouldCheckForUpdate = true
|
||||||
}
|
}
|
||||||
@@ -180,7 +183,7 @@ export const mutations = {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
addItemToQueue(state, item) {
|
addItemToQueue(state, item) {
|
||||||
const exists = state.playerQueueItems.some(i => {
|
const exists = state.playerQueueItems.some((i) => {
|
||||||
if (!i.episodeId) return i.libraryItemId === item.libraryItemId
|
if (!i.episodeId) return i.libraryItemId === item.libraryItemId
|
||||||
return i.libraryItemId === item.libraryItemId && i.episodeId === item.episodeId
|
return i.libraryItemId === item.libraryItemId && i.episodeId === item.episodeId
|
||||||
})
|
})
|
||||||
|
|||||||
+94
-1
@@ -88,6 +88,7 @@
|
|||||||
"ButtonShow": "Näytä",
|
"ButtonShow": "Näytä",
|
||||||
"ButtonStartM4BEncode": "Aloita M4B enkoodaus",
|
"ButtonStartM4BEncode": "Aloita M4B enkoodaus",
|
||||||
"ButtonStartMetadataEmbed": "Aloita metadatan embed",
|
"ButtonStartMetadataEmbed": "Aloita metadatan embed",
|
||||||
|
"ButtonStats": "Tilastot",
|
||||||
"ButtonSubmit": "Lähetä",
|
"ButtonSubmit": "Lähetä",
|
||||||
"ButtonTest": "Testi",
|
"ButtonTest": "Testi",
|
||||||
"ButtonUpload": "Lähetä palvelimelle",
|
"ButtonUpload": "Lähetä palvelimelle",
|
||||||
@@ -120,66 +121,158 @@
|
|||||||
"HeaderDetails": "Yksityiskohdat",
|
"HeaderDetails": "Yksityiskohdat",
|
||||||
"HeaderDownloadQueue": "Latausjono",
|
"HeaderDownloadQueue": "Latausjono",
|
||||||
"HeaderEbookFiles": "E-kirjatiedostot",
|
"HeaderEbookFiles": "E-kirjatiedostot",
|
||||||
|
"HeaderEmail": "Sähköposti",
|
||||||
|
"HeaderEmailSettings": "Sähköpostiasetukset",
|
||||||
"HeaderEpisodes": "Jaksot",
|
"HeaderEpisodes": "Jaksot",
|
||||||
|
"HeaderEreaderDevices": "E-lukijalaitteet",
|
||||||
"HeaderEreaderSettings": "E-lukijan asetukset",
|
"HeaderEreaderSettings": "E-lukijan asetukset",
|
||||||
|
"HeaderFiles": "Tiedostot",
|
||||||
|
"HeaderIgnoredFiles": "Ohitetut tiedostot",
|
||||||
"HeaderLatestEpisodes": "Viimeisimmät jaksot",
|
"HeaderLatestEpisodes": "Viimeisimmät jaksot",
|
||||||
"HeaderLibraries": "Kirjastot",
|
"HeaderLibraries": "Kirjastot",
|
||||||
|
"HeaderLibraryFiles": "Kirjaston tiedostot",
|
||||||
|
"HeaderLibraryStats": "Kirjaston tilastot",
|
||||||
|
"HeaderListeningStats": "Kuuntelutilastot",
|
||||||
|
"HeaderLogs": "Lokit",
|
||||||
|
"HeaderNewAccount": "Uusi tili",
|
||||||
|
"HeaderNewLibrary": "Uusi kirjasto",
|
||||||
|
"HeaderNotifications": "Ilmoitukset",
|
||||||
"HeaderOpenRSSFeed": "Avaa RSS-syöte",
|
"HeaderOpenRSSFeed": "Avaa RSS-syöte",
|
||||||
|
"HeaderOtherFiles": "Muut tiedostot",
|
||||||
|
"HeaderPermissions": "Käyttöoikeudet",
|
||||||
"HeaderPlaylist": "Soittolista",
|
"HeaderPlaylist": "Soittolista",
|
||||||
|
"HeaderPlaylistItems": "Soittolistan kohteet",
|
||||||
"HeaderRSSFeedGeneral": "RSS yksityiskohdat",
|
"HeaderRSSFeedGeneral": "RSS yksityiskohdat",
|
||||||
"HeaderRSSFeedIsOpen": "RSS syöte on avoinna",
|
"HeaderRSSFeedIsOpen": "RSS syöte on avoinna",
|
||||||
|
"HeaderRemoveEpisode": "Poista jakso",
|
||||||
|
"HeaderRemoveEpisodes": "Poista {0} jaksoa",
|
||||||
|
"HeaderSchedule": "Ajoita",
|
||||||
|
"HeaderScheduleLibraryScans": "Ajoita automaattiset kirjastoskannaukset",
|
||||||
|
"HeaderSetBackupSchedule": "Aseta varmuuskopiointiaikataulu",
|
||||||
"HeaderSettings": "Asetukset",
|
"HeaderSettings": "Asetukset",
|
||||||
|
"HeaderSettingsExperimental": "Kokeelliset ominaisuudet",
|
||||||
"HeaderSleepTimer": "Uniajastin",
|
"HeaderSleepTimer": "Uniajastin",
|
||||||
"HeaderStatsMinutesListeningChart": "Kuunteluminuutit (viim. 7 pv)",
|
"HeaderStatsMinutesListeningChart": "Kuunteluminuutit (viim. 7 pv)",
|
||||||
"HeaderStatsRecentSessions": "Viimeaikaiset istunnot",
|
"HeaderStatsRecentSessions": "Viimeaikaiset istunnot",
|
||||||
"HeaderTableOfContents": "Sisällysluettelo",
|
"HeaderTableOfContents": "Sisällysluettelo",
|
||||||
|
"HeaderTools": "Työkalut",
|
||||||
|
"HeaderUsers": "Käyttäjät",
|
||||||
"HeaderYourStats": "Tilastosi",
|
"HeaderYourStats": "Tilastosi",
|
||||||
|
"LabelAccountType": "Tilin tyyppi",
|
||||||
|
"LabelAccountTypeGuest": "Vieras",
|
||||||
|
"LabelAccountTypeUser": "Käyttäjä",
|
||||||
|
"LabelActivity": "Toiminta",
|
||||||
|
"LabelAddToCollection": "Lisää kokoelmaan",
|
||||||
|
"LabelAddToCollectionBatch": "Lisää {0} kirjaa kokoelmaan",
|
||||||
"LabelAddToPlaylist": "Lisää soittolistaan",
|
"LabelAddToPlaylist": "Lisää soittolistaan",
|
||||||
|
"LabelAddToPlaylistBatch": "Lisää {0} kohdetta soittolistaan",
|
||||||
"LabelAdded": "Lisätty",
|
"LabelAdded": "Lisätty",
|
||||||
"LabelAddedAt": "Lisätty listalle",
|
"LabelAddedAt": "Lisätty listalle",
|
||||||
"LabelAll": "Kaikki",
|
"LabelAll": "Kaikki",
|
||||||
|
"LabelAllUsers": "Kaikki käyttäjät",
|
||||||
|
"LabelAllUsersExcludingGuests": "Kaikki käyttäjät vieraita lukuun ottamatta",
|
||||||
|
"LabelAllUsersIncludingGuests": "Kaikki käyttäjät mukaan lukien vieraat",
|
||||||
"LabelAuthor": "Tekijä",
|
"LabelAuthor": "Tekijä",
|
||||||
"LabelAuthorFirstLast": "Tekijä (Etunimi Sukunimi)",
|
"LabelAuthorFirstLast": "Tekijä (Etunimi Sukunimi)",
|
||||||
"LabelAuthorLastFirst": "Tekijä (Sukunimi, Etunimi)",
|
"LabelAuthorLastFirst": "Tekijä (Sukunimi, Etunimi)",
|
||||||
"LabelAuthors": "Tekijät",
|
"LabelAuthors": "Tekijät",
|
||||||
"LabelAutoDownloadEpisodes": "Lataa jaksot automaattisesti",
|
"LabelAutoDownloadEpisodes": "Lataa jaksot automaattisesti",
|
||||||
|
"LabelBackupsEnableAutomaticBackups": "Ota automaattinen varmuuskopiointi käyttöön",
|
||||||
|
"LabelBackupsEnableAutomaticBackupsHelp": "Varmuuskopiot tallennettu kansioon /metadata/backups",
|
||||||
|
"LabelBackupsMaxBackupSize": "Varmuuskopion enimmäiskoko (Gt) (0 rajaton)",
|
||||||
|
"LabelBackupsNumberToKeep": "Säilytettävien varmuuskopioiden määrä",
|
||||||
"LabelBooks": "Kirjat",
|
"LabelBooks": "Kirjat",
|
||||||
|
"LabelButtonText": "Painikkeen teksti",
|
||||||
|
"LabelChangePassword": "Vaihda salasana",
|
||||||
"LabelChapters": "Luvut",
|
"LabelChapters": "Luvut",
|
||||||
|
"LabelClickForMoreInfo": "Napsauta saadaksesi lisätietoja",
|
||||||
"LabelClosePlayer": "Sulje soitin",
|
"LabelClosePlayer": "Sulje soitin",
|
||||||
|
"LabelCodec": "Koodekki",
|
||||||
"LabelCollapseSeries": "Pienennä sarja",
|
"LabelCollapseSeries": "Pienennä sarja",
|
||||||
|
"LabelCollection": "Kokoelma",
|
||||||
|
"LabelCollections": "Kokoelmat",
|
||||||
"LabelComplete": "Valmis",
|
"LabelComplete": "Valmis",
|
||||||
|
"LabelConfirmPassword": "Vahvista salasana",
|
||||||
"LabelContinueListening": "Jatka kuuntelua",
|
"LabelContinueListening": "Jatka kuuntelua",
|
||||||
"LabelContinueReading": "Jatka lukemista",
|
"LabelContinueReading": "Jatka lukemista",
|
||||||
"LabelContinueSeries": "Jatka sarjoja",
|
"LabelContinueSeries": "Jatka sarjoja",
|
||||||
|
"LabelCover": "Kansikuva",
|
||||||
|
"LabelCoverImageURL": "Kansikuvan URL-osoite",
|
||||||
|
"LabelCurrent": "Nykyinen",
|
||||||
"LabelDescription": "Kuvaus",
|
"LabelDescription": "Kuvaus",
|
||||||
|
"LabelDevice": "Laite",
|
||||||
|
"LabelDeviceInfo": "Laitteen tiedot",
|
||||||
"LabelDownload": "Lataa",
|
"LabelDownload": "Lataa",
|
||||||
|
"LabelDownloadNEpisodes": "Lataa {0} jaksoa",
|
||||||
"LabelDuration": "Kesto",
|
"LabelDuration": "Kesto",
|
||||||
"LabelEbook": "E-kirja",
|
"LabelEbook": "E-kirja",
|
||||||
"LabelEbooks": "E-kirjat",
|
"LabelEbooks": "E-kirjat",
|
||||||
|
"LabelEdit": "Muokkaa",
|
||||||
|
"LabelEmail": "Sähköposti",
|
||||||
"LabelEnable": "Ota käyttöön",
|
"LabelEnable": "Ota käyttöön",
|
||||||
|
"LabelEndOfChapter": "Luvun loppu",
|
||||||
|
"LabelEpisode": "Jakso",
|
||||||
"LabelFile": "Tiedosto",
|
"LabelFile": "Tiedosto",
|
||||||
"LabelFileBirthtime": "Tiedoston syntymäaika",
|
"LabelFileBirthtime": "Tiedoston syntymäaika",
|
||||||
"LabelFileModified": "Muutettu tiedosto",
|
"LabelFileModified": "Muutettu tiedosto",
|
||||||
"LabelFilename": "Tiedostonimi",
|
"LabelFilename": "Tiedostonimi",
|
||||||
"LabelFolder": "Kansio",
|
"LabelFolder": "Kansio",
|
||||||
|
"LabelInProgress": "Kesken",
|
||||||
|
"LabelIncomplete": "Keskeneräinen",
|
||||||
"LabelLanguage": "Kieli",
|
"LabelLanguage": "Kieli",
|
||||||
|
"LabelListenAgain": "Kuuntele uudelleen",
|
||||||
|
"LabelMediaType": "Mediatyyppi",
|
||||||
"LabelMore": "Lisää",
|
"LabelMore": "Lisää",
|
||||||
|
"LabelMoreInfo": "Lisätietoja",
|
||||||
|
"LabelName": "Nimi",
|
||||||
"LabelNarrator": "Lukija",
|
"LabelNarrator": "Lukija",
|
||||||
"LabelNarrators": "Lukijat",
|
"LabelNarrators": "Lukijat",
|
||||||
"LabelNewestAuthors": "Uusimmat kirjailijat",
|
"LabelNewestAuthors": "Uusimmat kirjailijat",
|
||||||
"LabelNewestEpisodes": "Uusimmat jaksot",
|
"LabelNewestEpisodes": "Uusimmat jaksot",
|
||||||
"LabelPassword": "Salasana",
|
"LabelPassword": "Salasana",
|
||||||
"LabelPath": "Polku",
|
"LabelPath": "Polku",
|
||||||
|
"LabelPodcast": "Podcast",
|
||||||
|
"LabelPodcasts": "Podcastit",
|
||||||
|
"LabelPublishYear": "Julkaisuvuosi",
|
||||||
|
"LabelRSSFeedPreventIndexing": "Estä indeksointi",
|
||||||
"LabelRead": "Lue",
|
"LabelRead": "Lue",
|
||||||
"LabelReadAgain": "Lue uudelleen",
|
"LabelReadAgain": "Lue uudelleen",
|
||||||
|
"LabelRecentSeries": "Viimeisimmät sarjat",
|
||||||
|
"LabelRecentlyAdded": "Viimeeksi lisätyt",
|
||||||
"LabelSeason": "Kausi",
|
"LabelSeason": "Kausi",
|
||||||
|
"LabelSetEbookAsPrimary": "Aseta ensisijaiseksi",
|
||||||
|
"LabelSetEbookAsSupplementary": "Aseta täydentäväksi",
|
||||||
"LabelShowAll": "Näytä kaikki",
|
"LabelShowAll": "Näytä kaikki",
|
||||||
"LabelSize": "Koko",
|
"LabelSize": "Koko",
|
||||||
"LabelSleepTimer": "Uniajastin",
|
"LabelSleepTimer": "Uniajastin",
|
||||||
|
"LabelStatsDailyAverage": "Päivittäinen keskiarvo",
|
||||||
|
"LabelStatsInARow": "peräjälkeen",
|
||||||
|
"LabelStatsMinutes": "minuuttia",
|
||||||
"LabelTheme": "Teema",
|
"LabelTheme": "Teema",
|
||||||
"LabelThemeDark": "Tumma",
|
"LabelThemeDark": "Tumma",
|
||||||
"LabelThemeLight": "Kirkas",
|
"LabelThemeLight": "Kirkas",
|
||||||
|
"LabelTimeRemaining": "{0} jäljellä",
|
||||||
|
"LabelType": "Tyyppi",
|
||||||
"LabelUser": "Käyttäjä",
|
"LabelUser": "Käyttäjä",
|
||||||
"LabelUsername": "Käyttäjätunnus",
|
"LabelUsername": "Käyttäjätunnus",
|
||||||
"MessageDownloadingEpisode": "Ladataan jaksoa"
|
"LabelYourBookmarks": "Kirjanmerkkisi",
|
||||||
|
"LabelYourProgress": "Edistymisesi",
|
||||||
|
"MessageDownloadingEpisode": "Ladataan jaksoa",
|
||||||
|
"MessageEpisodesQueuedForDownload": "{0} jaksoa on latausjonossa",
|
||||||
|
"MessageFetching": "Haetaan...",
|
||||||
|
"MessageLoading": "Ladataan...",
|
||||||
|
"MessageMarkAsFinished": "Merkitse valmiiksi",
|
||||||
|
"MessageNoBookmarks": "Ei kirjanmerkkejä",
|
||||||
|
"MessageNoItems": "Ei kohteita",
|
||||||
|
"MessageNoItemsFound": "Kohteita ei löytynyt",
|
||||||
|
"MessageNoPodcastsFound": "Podcasteja ei löytynyt",
|
||||||
|
"MessageNoUserPlaylists": "Sinulla ei ole soittolistoja",
|
||||||
|
"MessageReportBugsAndContribute": "Ilmoita virheistä, toivo ominaisuuksia ja osallistu",
|
||||||
|
"ToastBookmarkCreateFailed": "Kirjanmerkin luominen epäonnistui",
|
||||||
|
"ToastBookmarkRemoveFailed": "Kirjanmerkin poistaminen epäonnistui",
|
||||||
|
"ToastBookmarkUpdateFailed": "Kirjanmerkin päivittäminen epäonnistui",
|
||||||
|
"ToastItemMarkedAsFinishedFailed": "Valmiiksi merkitseminen epäonnistui",
|
||||||
|
"ToastPlaylistCreateFailed": "Soittolistan luominen epäonnistui",
|
||||||
|
"ToastPodcastCreateFailed": "Podcastin luominen epäonnistui",
|
||||||
|
"ToastPodcastCreateSuccess": "Podcastin luominen onnistui"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
"ButtonJumpForward": "Avancer",
|
"ButtonJumpForward": "Avancer",
|
||||||
"ButtonLatest": "Dernière version",
|
"ButtonLatest": "Dernière version",
|
||||||
"ButtonLibrary": "Bibliothèque",
|
"ButtonLibrary": "Bibliothèque",
|
||||||
"ButtonLogout": "Me déconnecter",
|
"ButtonLogout": "Déconnexion",
|
||||||
"ButtonLookup": "Chercher",
|
"ButtonLookup": "Chercher",
|
||||||
"ButtonManageTracks": "Gérer les pistes",
|
"ButtonManageTracks": "Gérer les pistes",
|
||||||
"ButtonMapChapterTitles": "Correspondance des titres de chapitres",
|
"ButtonMapChapterTitles": "Correspondance des titres de chapitres",
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
"ButtonPurgeItemsCache": "Purger le cache des éléments",
|
"ButtonPurgeItemsCache": "Purger le cache des éléments",
|
||||||
"ButtonQueueAddItem": "Ajouter à la liste de lecture",
|
"ButtonQueueAddItem": "Ajouter à la liste de lecture",
|
||||||
"ButtonQueueRemoveItem": "Supprimer de la liste de lecture",
|
"ButtonQueueRemoveItem": "Supprimer de la liste de lecture",
|
||||||
"ButtonQuickEmbedMetadata": "Ajoutez rapidement des métadonnées",
|
"ButtonQuickEmbedMetadata": "Ajouter rapidement des métadonnées",
|
||||||
"ButtonQuickMatch": "Recherche rapide",
|
"ButtonQuickMatch": "Recherche rapide",
|
||||||
"ButtonReScan": "Nouvelle analyse",
|
"ButtonReScan": "Nouvelle analyse",
|
||||||
"ButtonRead": "Lire",
|
"ButtonRead": "Lire",
|
||||||
@@ -285,7 +285,7 @@
|
|||||||
"LabelEmail": "Courriel",
|
"LabelEmail": "Courriel",
|
||||||
"LabelEmailSettingsFromAddress": "Expéditeur",
|
"LabelEmailSettingsFromAddress": "Expéditeur",
|
||||||
"LabelEmailSettingsRejectUnauthorized": "Rejeter les certificats non autorisés",
|
"LabelEmailSettingsRejectUnauthorized": "Rejeter les certificats non autorisés",
|
||||||
"LabelEmailSettingsRejectUnauthorizedHelp": "Désactiver la validation du certificat SSL peut exposer votre connexion à des risques de sécurité, tels que des attaques de type « man-in-the-middle ». Ne désactivez cette option que si vous en comprenez les implications et si vous faites confiance au serveur de messagerie auquel vous vous connectez.",
|
"LabelEmailSettingsRejectUnauthorizedHelp": "Désactiver la validation du certificat SSL peut exposer votre connexion à des risques de sécurité, tels que des attaques de type « Attaque de l’homme du milieu ». Ne désactivez cette option que si vous en comprenez les implications et si vous faites confiance au serveur de messagerie auquel vous vous connectez.",
|
||||||
"LabelEmailSettingsSecure": "Sécurisé",
|
"LabelEmailSettingsSecure": "Sécurisé",
|
||||||
"LabelEmailSettingsSecureHelp": "Si vous activez cette option, TLS sera utiliser lors de la connexion au serveur. Sinon, TLS est utilisé uniquement si le serveur supporte l’extension STARTTLS. Dans la plupart des cas, activez l’option, vous vous connecterai sur le port 465. Pour le port 587 ou 25, désactiver l’option. (source : nodemailer.com/smtp/#authentication)",
|
"LabelEmailSettingsSecureHelp": "Si vous activez cette option, TLS sera utiliser lors de la connexion au serveur. Sinon, TLS est utilisé uniquement si le serveur supporte l’extension STARTTLS. Dans la plupart des cas, activez l’option, vous vous connecterai sur le port 465. Pour le port 587 ou 25, désactiver l’option. (source : nodemailer.com/smtp/#authentication)",
|
||||||
"LabelEmailSettingsTestAddress": "Adresse de test",
|
"LabelEmailSettingsTestAddress": "Adresse de test",
|
||||||
@@ -454,7 +454,7 @@
|
|||||||
"LabelRSSFeedSlug": "Balise URL du flux RSS",
|
"LabelRSSFeedSlug": "Balise URL du flux RSS",
|
||||||
"LabelRSSFeedURL": "Adresse du flux RSS",
|
"LabelRSSFeedURL": "Adresse du flux RSS",
|
||||||
"LabelRandomly": "Au hasard",
|
"LabelRandomly": "Au hasard",
|
||||||
"LabelReAddSeriesToContinueListening": "Ajoutez à nouveau la série pour continuer à l’écouter",
|
"LabelReAddSeriesToContinueListening": "Ajouter à nouveau la série pour continuer à l’écouter",
|
||||||
"LabelRead": "Lire",
|
"LabelRead": "Lire",
|
||||||
"LabelReadAgain": "Lire à nouveau",
|
"LabelReadAgain": "Lire à nouveau",
|
||||||
"LabelReadEbookWithoutProgress": "Lire le livre numérique sans sauvegarder la progression",
|
"LabelReadEbookWithoutProgress": "Lire le livre numérique sans sauvegarder la progression",
|
||||||
@@ -616,7 +616,7 @@
|
|||||||
"LabelYearReviewShow": "Afficher le bilan de l’année",
|
"LabelYearReviewShow": "Afficher le bilan de l’année",
|
||||||
"LabelYourAudiobookDuration": "Durée de vos livres audios",
|
"LabelYourAudiobookDuration": "Durée de vos livres audios",
|
||||||
"LabelYourBookmarks": "Vos favoris",
|
"LabelYourBookmarks": "Vos favoris",
|
||||||
"LabelYourPlaylists": "Vos listes de lecture",
|
"LabelYourPlaylists": "Mes listes de lecture",
|
||||||
"LabelYourProgress": "Votre progression",
|
"LabelYourProgress": "Votre progression",
|
||||||
"MessageAddToPlayerQueue": "Ajouter en file d’attente",
|
"MessageAddToPlayerQueue": "Ajouter en file d’attente",
|
||||||
"MessageAppriseDescription": "Nécessite une instance d’<a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">API Apprise</a> pour utiliser cette fonctionnalité ou une api qui prend en charge les mêmes requêtes.<br />L’URL de l’API Apprise doit comprendre le chemin complet pour envoyer la notification. Par exemple, si votre instance écoute sur <code>http://192.168.1.1:8337</code> alors vous devez mettre <code>http://192.168.1.1:8337/notify</code>.",
|
"MessageAppriseDescription": "Nécessite une instance d’<a href=\"https://github.com/caronc/apprise-api\" target=\"_blank\">API Apprise</a> pour utiliser cette fonctionnalité ou une api qui prend en charge les mêmes requêtes.<br />L’URL de l’API Apprise doit comprendre le chemin complet pour envoyer la notification. Par exemple, si votre instance écoute sur <code>http://192.168.1.1:8337</code> alors vous devez mettre <code>http://192.168.1.1:8337/notify</code>.",
|
||||||
|
|||||||
@@ -89,7 +89,7 @@
|
|||||||
"ButtonShow": "显示",
|
"ButtonShow": "显示",
|
||||||
"ButtonStartM4BEncode": "开始 M4B 编码",
|
"ButtonStartM4BEncode": "开始 M4B 编码",
|
||||||
"ButtonStartMetadataEmbed": "开始嵌入元数据",
|
"ButtonStartMetadataEmbed": "开始嵌入元数据",
|
||||||
"ButtonStats": "状态",
|
"ButtonStats": "统计数据",
|
||||||
"ButtonSubmit": "提交",
|
"ButtonSubmit": "提交",
|
||||||
"ButtonTest": "测试",
|
"ButtonTest": "测试",
|
||||||
"ButtonUpload": "上传",
|
"ButtonUpload": "上传",
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "audiobookshelf",
|
"name": "audiobookshelf",
|
||||||
"version": "2.12.1",
|
"version": "2.12.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "audiobookshelf",
|
"name": "audiobookshelf",
|
||||||
"version": "2.12.1",
|
"version": "2.12.2",
|
||||||
"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.1",
|
"version": "2.12.2",
|
||||||
"buildNumber": 1,
|
"buildNumber": 1,
|
||||||
"description": "Self-hosted audiobook and podcast server",
|
"description": "Self-hosted audiobook and podcast server",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
|||||||
+1
-7
@@ -41,7 +41,6 @@ const LibraryScanner = require('./scanner/LibraryScanner')
|
|||||||
//Import the main Passport and Express-Session library
|
//Import the main Passport and Express-Session library
|
||||||
const passport = require('passport')
|
const passport = require('passport')
|
||||||
const expressSession = require('express-session')
|
const expressSession = require('express-session')
|
||||||
const MemoryStore = require('./libs/memorystore')(expressSession)
|
|
||||||
|
|
||||||
class Server {
|
class Server {
|
||||||
constructor(SOURCE, PORT, HOST, CONFIG_PATH, METADATA_PATH, ROUTER_BASE_PATH) {
|
constructor(SOURCE, PORT, HOST, CONFIG_PATH, METADATA_PATH, ROUTER_BASE_PATH) {
|
||||||
@@ -219,12 +218,7 @@ class Server {
|
|||||||
cookie: {
|
cookie: {
|
||||||
// also send the cookie if were are not on https (not every use has https)
|
// also send the cookie if were are not on https (not every use has https)
|
||||||
secure: false
|
secure: false
|
||||||
},
|
}
|
||||||
store: new MemoryStore({
|
|
||||||
checkPeriod: 86400000, // prune expired entries every 24h
|
|
||||||
ttl: 86400000, // 24h
|
|
||||||
max: 1000
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
// init passport.js
|
// init passport.js
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ const Logger = require('../Logger')
|
|||||||
const Database = require('../Database')
|
const Database = require('../Database')
|
||||||
|
|
||||||
class ApiCacheManager {
|
class ApiCacheManager {
|
||||||
|
defaultCacheOptions = { max: 1000, maxSize: 10 * 1000 * 1000, sizeCalculation: (item) => item.body.length + JSON.stringify(item.headers).length }
|
||||||
defaultCacheOptions = { max: 1000, maxSize: 10 * 1000 * 1000, sizeCalculation: item => (item.body.length + JSON.stringify(item.headers).length) }
|
|
||||||
defaultTtlOptions = { ttl: 30 * 60 * 1000 }
|
defaultTtlOptions = { ttl: 30 * 60 * 1000 }
|
||||||
|
|
||||||
constructor(cache = new LRUCache(this.defaultCacheOptions), ttlOptions = this.defaultTtlOptions) {
|
constructor(cache = new LRUCache(this.defaultCacheOptions), ttlOptions = this.defaultTtlOptions) {
|
||||||
@@ -14,7 +13,7 @@ class ApiCacheManager {
|
|||||||
|
|
||||||
init(database = Database) {
|
init(database = Database) {
|
||||||
let hooks = ['afterCreate', 'afterUpdate', 'afterDestroy', 'afterBulkCreate', 'afterBulkUpdate', 'afterBulkDestroy', 'afterUpsert']
|
let hooks = ['afterCreate', 'afterUpdate', 'afterDestroy', 'afterBulkCreate', 'afterBulkUpdate', 'afterBulkDestroy', 'afterUpsert']
|
||||||
hooks.forEach(hook => database.sequelize.addHook(hook, (model) => this.clear(model, hook)))
|
hooks.forEach((hook) => database.sequelize.addHook(hook, (model) => this.clear(model, hook)))
|
||||||
}
|
}
|
||||||
|
|
||||||
clear(model, hook) {
|
clear(model, hook) {
|
||||||
@@ -33,7 +32,16 @@ class ApiCacheManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get middleware() {
|
get middleware() {
|
||||||
|
/**
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
* @param {import('express').NextFunction} next
|
||||||
|
*/
|
||||||
return (req, res, next) => {
|
return (req, res, next) => {
|
||||||
|
if (req.query.sort === 'random') {
|
||||||
|
Logger.debug(`[ApiCacheManager] Skipping cache for random sort`)
|
||||||
|
return next()
|
||||||
|
}
|
||||||
const key = { user: req.user.username, url: req.url }
|
const key = { user: req.user.username, url: req.url }
|
||||||
const stringifiedKey = JSON.stringify(key)
|
const stringifiedKey = JSON.stringify(key)
|
||||||
Logger.debug(`[ApiCacheManager] count: ${this.cache.size} size: ${this.cache.calculatedSize}`)
|
Logger.debug(`[ApiCacheManager] count: ${this.cache.size} size: ${this.cache.calculatedSize}`)
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ module.exports.downloadPodcastEpisode = (podcastEpisodeDownload) => {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
responseType: 'stream',
|
responseType: 'stream',
|
||||||
headers: {
|
headers: {
|
||||||
'User-Agent': 'audiobookshelf (+https://audiobookshelf.org; like iTMS)'
|
'User-Agent': 'audiobookshelf (+https://audiobookshelf.org)'
|
||||||
},
|
},
|
||||||
timeout: 30000
|
timeout: 30000
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const { AudioMimeType } = require('./constants')
|
|||||||
*/
|
*/
|
||||||
const filePathToPOSIX = (path) => {
|
const filePathToPOSIX = (path) => {
|
||||||
if (!global.isWin || !path) return path
|
if (!global.isWin || !path) return path
|
||||||
return path.replace(/\\/g, '/')
|
return path.startsWith('\\\\') ? '\\\\' + path.slice(2).replace(/\\/g, '/') : path.replace(/\\/g, '/')
|
||||||
}
|
}
|
||||||
module.exports.filePathToPOSIX = filePathToPOSIX
|
module.exports.filePathToPOSIX = filePathToPOSIX
|
||||||
|
|
||||||
@@ -169,7 +169,7 @@ async function recurseFiles(path, relPathToReplace = null) {
|
|||||||
extensions: true,
|
extensions: true,
|
||||||
deep: true,
|
deep: true,
|
||||||
realPath: true,
|
realPath: true,
|
||||||
normalizePath: true
|
normalizePath: false
|
||||||
}
|
}
|
||||||
let list = await rra.list(path, options)
|
let list = await rra.list(path, options)
|
||||||
if (list.error) {
|
if (list.error) {
|
||||||
@@ -186,6 +186,8 @@ async function recurseFiles(path, relPathToReplace = null) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
item.fullname = filePathToPOSIX(item.fullname)
|
||||||
|
item.path = filePathToPOSIX(item.path)
|
||||||
const relpath = item.fullname.replace(relPathToReplace, '')
|
const relpath = item.fullname.replace(relPathToReplace, '')
|
||||||
let reldirname = Path.dirname(relpath)
|
let reldirname = Path.dirname(relpath)
|
||||||
if (reldirname === '.') reldirname = ''
|
if (reldirname === '.') reldirname = ''
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ describe('ApiCacheManager', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cache = { get: sinon.stub(), set: sinon.spy() }
|
cache = { get: sinon.stub(), set: sinon.spy() }
|
||||||
req = { user: { username: 'testUser' }, url: '/test-url' }
|
req = { user: { username: 'testUser' }, url: '/test-url', query: {} }
|
||||||
res = { send: sinon.spy(), getHeaders: sinon.stub(), statusCode: 200, status: sinon.spy(), set: sinon.spy() }
|
res = { send: sinon.spy(), getHeaders: sinon.stub(), statusCode: 200, status: sinon.spy(), set: sinon.spy() }
|
||||||
next = sinon.spy()
|
next = sinon.spy()
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user