Fix scan for audiobook directories in root dir

This commit is contained in:
advplyr 2021-08-23 14:08:54 -05:00
parent 5ecfaa88c2
commit a93f409dcd
11 changed files with 157 additions and 11 deletions

View File

@ -23,6 +23,8 @@
</ui-btn>
<ui-btn :padding-x="4" class="flex items-center ml-4" @click="editClick"><span class="material-icons text-white pr-2" style="font-size: 18px">edit</span>Edit</ui-btn>
<ui-btn v-if="isDeveloperMode" class="mx-2" @click="openRssFeed">Open RSS Feed</ui-btn>
<div v-if="progressPercent > 0" class="px-4 py-2 bg-primary text-sm font-semibold rounded-md text-gray-200 ml-4 relative" :class="resettingProgress ? 'opacity-25' : ''">
<p class="leading-6">Your Progress: {{ Math.round(progressPercent * 100) }}%</p>
<p class="text-gray-400 text-xs">{{ $elapsedPretty(userTimeRemaining) }} remaining</p>
@ -82,6 +84,9 @@ export default {
}
},
computed: {
isDeveloperMode() {
return this.$store.state.developerMode
},
missingPartChunks() {
if (this.missingParts === 1) return this.missingParts[0]
var chunks = []
@ -180,6 +185,18 @@ export default {
}
},
methods: {
openRssFeed() {
this.$axios
.$post('/api/feed', { audiobookId: this.audiobook.id })
.then((res) => {
console.log('Feed open', res)
this.$toast.success('RSS Feed Open')
})
.catch((error) => {
console.error('Failed', error)
this.$toast.error('Failed to open feed')
})
},
startStream() {
this.$store.commit('setStreamAudiobook', this.audiobook)
this.$root.socket.emit('open_stream', this.audiobook.id)

View File

@ -53,6 +53,7 @@
</a>
</div>
</div>
<div class="fixed bottom-0 left-0 w-10 h-10" @dblclick="setDeveloperMode"></div>
</div>
</template>
@ -70,6 +71,11 @@ export default {
}
},
methods: {
setDeveloperMode() {
var value = !this.$store.state.developerMode
this.$store.commit('setDeveloperMode', value)
this.$toast.info(`Developer Mode ${value ? 'Enabled' : 'Disabled'}`)
},
scan() {
this.$root.socket.emit('scan')
},

View File

@ -6,7 +6,8 @@ export const state = () => ({
selectedAudiobook: null,
playOnLoad: false,
isScanning: false,
scanProgress: null
scanProgress: null,
developerMode: false
})
export const getters = {
@ -59,5 +60,8 @@ export const mutations = {
setScanProgress(state, progress) {
if (progress > 0) state.isScanning = true
state.scanProgress = progress
},
setDeveloperMode(state, val) {
state.developerMode = val
}
}

44
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "audiobookshelf",
"version": "0.9.61-beta.0",
"version": "0.9.64-beta",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -561,6 +561,11 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ip": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
},
"ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
@ -833,6 +838,14 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
"integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw=="
},
"podcast": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/podcast/-/podcast-1.3.0.tgz",
"integrity": "sha512-L0UNP8SMdoihxgpdXCaXZEKZBBCGzld5PSy8QbQYsk83bdzq14cdW8flJduZjQNbB2If5frwVIC5VpMq9CHchA==",
"requires": {
"rss": "^1.2.2"
}
},
"proper-lockfile": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz",
@ -913,6 +926,30 @@
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
"integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs="
},
"rss": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/rss/-/rss-1.2.2.tgz",
"integrity": "sha1-UKFpiHYTgTOnT5oF0r3I240nqSE=",
"requires": {
"mime-types": "2.1.13",
"xml": "1.0.1"
},
"dependencies": {
"mime-db": {
"version": "1.25.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.25.0.tgz",
"integrity": "sha1-wY29fHOl2/b0SgJNwNFloeexw5I="
},
"mime-types": {
"version": "2.1.13",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.13.tgz",
"integrity": "sha1-4HqqnGxrmnyjASxpADrSWjnpKog=",
"requires": {
"mime-db": "~1.25.0"
}
}
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@ -1101,6 +1138,11 @@
"version": "7.4.6",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A=="
},
"xml": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz",
"integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU="
}
}
}

View File

@ -17,10 +17,12 @@
"express": "^4.17.1",
"fluent-ffmpeg": "^2.1.2",
"fs-extra": "^10.0.0",
"ip": "^1.1.5",
"jsonwebtoken": "^8.5.1",
"libgen": "^2.1.0",
"njodb": "^0.4.20",
"node-dir": "^0.1.17",
"podcast": "^1.3.0",
"socket.io": "^4.1.3"
},
"devDependencies": {}

View File

@ -2,11 +2,12 @@ const express = require('express')
const Logger = require('./Logger')
class ApiController {
constructor(db, scanner, auth, streamManager, emitter) {
constructor(db, scanner, auth, streamManager, rssFeeds, emitter) {
this.db = db
this.scanner = scanner
this.auth = auth
this.streamManager = streamManager
this.rssFeeds = rssFeeds
this.emitter = emitter
this.router = express()
@ -35,6 +36,8 @@ class ApiController {
this.router.post('/authorize', this.authorize.bind(this))
this.router.get('/genres', this.getGenres.bind(this))
this.router.post('/feed', this.openRssFeed.bind(this))
}
find(req, res) {
@ -42,7 +45,6 @@ class ApiController {
}
findCovers(req, res) {
console.log('Find covers', req.query)
this.scanner.findCovers(req, res)
}
@ -174,6 +176,15 @@ class ApiController {
this.auth.userChangePassword(req, res)
}
async openRssFeed(req, res) {
var audiobookId = req.body.audiobookId
var audiobook = this.db.audiobooks.find(ab => ab.id === audiobookId)
if (!audiobook) return res.sendStatus(404)
var feed = await this.rssFeeds.openFeed(audiobook)
console.log('Feed open', feed)
res.json(feed)
}
getGenres(req, res) {
res.json({
genres: this.db.getGenres()

View File

@ -75,6 +75,10 @@ class Auth {
verifyToken(token) {
return new Promise((resolve) => {
jwt.verify(token, process.env.TOKEN_SECRET, (err, payload) => {
if (!payload || err) {
Logger.error('JWT Verify Token Failed', err)
return resolve(null)
}
var user = this.users.find(u => u.id === payload.userId)
resolve(user || null)
})

53
server/RssFeeds.js Normal file
View File

@ -0,0 +1,53 @@
const Podcast = require('podcast')
const express = require('express')
const ip = require('ip')
const Logger = require('./Logger')
class RssFeeds {
constructor(Port, db) {
this.Port = Port
this.db = db
this.feeds = {}
this.router = express()
this.init()
}
init() {
this.router.get('/:id', this.getFeed.bind(this))
}
getFeed(req, res) {
var feed = this.feeds[req.params.id]
if (!feed) return null
var xml = feed.buildXml()
res.set('Content-Type', 'text/xml')
res.send(xml)
}
openFeed(audiobook) {
var serverAddress = 'http://' + ip.address('public', 'ipv4') + ':' + this.Port
Logger.info('Open RSS Feed', 'Server address', serverAddress)
var feedId = (Date.now() + Math.floor(Math.random() * 1000)).toString(36)
const feed = new Podcast({
title: audiobook.title,
description: 'AudioBookshelf RSS Feed',
feedUrl: `${serverAddress}/feeds/${feedId}`,
imageUrl: `${serverAddress}/Logo.png`,
author: 'advplyr',
language: 'en'
})
audiobook.tracks.forEach((track) => {
feed.addItem({
title: `Track ${track.index}`,
description: `AudioBookshelf Audiobook Track #${track.index}`,
url: `${serverAddress}/feeds/${feedId}?track=${track.index}`,
author: 'advplyr'
})
})
this.feeds[feedId] = feed
return feed
}
}
module.exports = RssFeeds

View File

@ -11,6 +11,7 @@ const Db = require('./Db')
const ApiController = require('./ApiController')
const HlsController = require('./HlsController')
const StreamManager = require('./StreamManager')
const RssFeeds = require('./RssFeeds')
const Logger = require('./Logger')
class Server {
@ -30,9 +31,11 @@ class Server {
this.watcher = new Watcher(this.AudiobookPath)
this.scanner = new Scanner(this.AudiobookPath, this.MetadataPath, this.db, this.emitter.bind(this))
this.streamManager = new StreamManager(this.db, this.MetadataPath)
this.apiController = new ApiController(this.db, this.scanner, this.auth, this.streamManager, this.emitter.bind(this))
this.rssFeeds = new RssFeeds(this.Port, this.db)
this.apiController = new ApiController(this.db, this.scanner, this.auth, this.streamManager, this.rssFeeds, this.emitter.bind(this))
this.hlsController = new HlsController(this.db, this.scanner, this.auth, this.streamManager, this.emitter.bind(this), this.MetadataPath)
this.server = null
this.io = null
@ -112,11 +115,13 @@ class Server {
}
app.use(express.static(this.MetadataPath))
app.use(express.static(Path.join(global.appRoot, 'static')))
app.use(express.urlencoded({ extended: true }));
app.use(express.json())
app.use('/api', this.authMiddleware.bind(this), this.apiController.router)
app.use('/hls', this.authMiddleware.bind(this), this.hlsController.router)
app.use('/feeds', this.rssFeeds.router)
app.get('/', (req, res) => {
res.sendFile('/index.html')

View File

@ -39,13 +39,15 @@ async function getAllAudiobookFiles(abRootPath) {
var pathformat = Path.parse(relpath)
var path = pathformat.dir
// If relative file directory has 3 folders, then the middle folder will be series
var splitDir = pathformat.dir.split(Path.sep)
if (splitDir.length === 1) {
Logger.error('Invalid file in root dir', filepath)
if (!path) {
Logger.error('Ignoring file in root dir', filepath)
return
}
var author = splitDir.shift()
// If relative file directory has 3 folders, then the middle folder will be series
var splitDir = pathformat.dir.split(Path.sep)
var author = null
if (splitDir.length > 1) author = splitDir.shift()
var series = null
if (splitDir.length > 1) series = splitDir.shift()
var title = splitDir.shift()

BIN
static/Logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB