mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Fix scan for audiobook directories in root dir
This commit is contained in:
		
							parent
							
								
									f4cb5d101e
								
							
						
					
					
						commit
						73a786879e
					
				| @ -23,6 +23,8 @@ | |||||||
|             </ui-btn> |             </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 :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' : ''"> |             <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="leading-6">Your Progress: {{ Math.round(progressPercent * 100) }}%</p> | ||||||
|               <p class="text-gray-400 text-xs">{{ $elapsedPretty(userTimeRemaining) }} remaining</p> |               <p class="text-gray-400 text-xs">{{ $elapsedPretty(userTimeRemaining) }} remaining</p> | ||||||
| @ -82,6 +84,9 @@ export default { | |||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   computed: { |   computed: { | ||||||
|  |     isDeveloperMode() { | ||||||
|  |       return this.$store.state.developerMode | ||||||
|  |     }, | ||||||
|     missingPartChunks() { |     missingPartChunks() { | ||||||
|       if (this.missingParts === 1) return this.missingParts[0] |       if (this.missingParts === 1) return this.missingParts[0] | ||||||
|       var chunks = [] |       var chunks = [] | ||||||
| @ -180,6 +185,18 @@ export default { | |||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   methods: { |   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() { |     startStream() { | ||||||
|       this.$store.commit('setStreamAudiobook', this.audiobook) |       this.$store.commit('setStreamAudiobook', this.audiobook) | ||||||
|       this.$root.socket.emit('open_stream', this.audiobook.id) |       this.$root.socket.emit('open_stream', this.audiobook.id) | ||||||
|  | |||||||
| @ -53,6 +53,7 @@ | |||||||
|         </a> |         </a> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|  |     <div class="fixed bottom-0 left-0 w-10 h-10" @dblclick="setDeveloperMode"></div> | ||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| @ -70,6 +71,11 @@ export default { | |||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|  |     setDeveloperMode() { | ||||||
|  |       var value = !this.$store.state.developerMode | ||||||
|  |       this.$store.commit('setDeveloperMode', value) | ||||||
|  |       this.$toast.info(`Developer Mode ${value ? 'Enabled' : 'Disabled'}`) | ||||||
|  |     }, | ||||||
|     scan() { |     scan() { | ||||||
|       this.$root.socket.emit('scan') |       this.$root.socket.emit('scan') | ||||||
|     }, |     }, | ||||||
|  | |||||||
| @ -6,7 +6,8 @@ export const state = () => ({ | |||||||
|   selectedAudiobook: null, |   selectedAudiobook: null, | ||||||
|   playOnLoad: false, |   playOnLoad: false, | ||||||
|   isScanning: false, |   isScanning: false, | ||||||
|   scanProgress: null |   scanProgress: null, | ||||||
|  |   developerMode: false | ||||||
| }) | }) | ||||||
| 
 | 
 | ||||||
| export const getters = { | export const getters = { | ||||||
| @ -59,5 +60,8 @@ export const mutations = { | |||||||
|   setScanProgress(state, progress) { |   setScanProgress(state, progress) { | ||||||
|     if (progress > 0) state.isScanning = true |     if (progress > 0) state.isScanning = true | ||||||
|     state.scanProgress = progress |     state.scanProgress = progress | ||||||
|  |   }, | ||||||
|  |   setDeveloperMode(state, val) { | ||||||
|  |     state.developerMode = val | ||||||
|   } |   } | ||||||
| } | } | ||||||
							
								
								
									
										44
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										44
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "audiobookshelf", |   "name": "audiobookshelf", | ||||||
|   "version": "0.9.61-beta.0", |   "version": "0.9.64-beta", | ||||||
|   "lockfileVersion": 1, |   "lockfileVersion": 1, | ||||||
|   "requires": true, |   "requires": true, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
| @ -561,6 +561,11 @@ | |||||||
|       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", |       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", | ||||||
|       "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" |       "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": { |     "ipaddr.js": { | ||||||
|       "version": "1.9.1", |       "version": "1.9.1", | ||||||
|       "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", |       "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", |       "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", | ||||||
|       "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==" |       "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": { |     "proper-lockfile": { | ||||||
|       "version": "4.1.2", |       "version": "4.1.2", | ||||||
|       "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", |       "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", |       "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", | ||||||
|       "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" |       "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": { |     "safe-buffer": { | ||||||
|       "version": "5.1.2", |       "version": "5.1.2", | ||||||
|       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", |       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", | ||||||
| @ -1101,6 +1138,11 @@ | |||||||
|       "version": "7.4.6", |       "version": "7.4.6", | ||||||
|       "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", |       "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", | ||||||
|       "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" |       "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=" | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -17,11 +17,13 @@ | |||||||
|     "express": "^4.17.1", |     "express": "^4.17.1", | ||||||
|     "fluent-ffmpeg": "^2.1.2", |     "fluent-ffmpeg": "^2.1.2", | ||||||
|     "fs-extra": "^10.0.0", |     "fs-extra": "^10.0.0", | ||||||
|  |     "ip": "^1.1.5", | ||||||
|     "jsonwebtoken": "^8.5.1", |     "jsonwebtoken": "^8.5.1", | ||||||
|     "libgen": "^2.1.0", |     "libgen": "^2.1.0", | ||||||
|     "njodb": "^0.4.20", |     "njodb": "^0.4.20", | ||||||
|     "node-dir": "^0.1.17", |     "node-dir": "^0.1.17", | ||||||
|  |     "podcast": "^1.3.0", | ||||||
|     "socket.io": "^4.1.3" |     "socket.io": "^4.1.3" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": {} |   "devDependencies": {} | ||||||
| } | } | ||||||
|  | |||||||
| @ -2,11 +2,12 @@ const express = require('express') | |||||||
| const Logger = require('./Logger') | const Logger = require('./Logger') | ||||||
| 
 | 
 | ||||||
| class ApiController { | class ApiController { | ||||||
|   constructor(db, scanner, auth, streamManager, emitter) { |   constructor(db, scanner, auth, streamManager, rssFeeds, emitter) { | ||||||
|     this.db = db |     this.db = db | ||||||
|     this.scanner = scanner |     this.scanner = scanner | ||||||
|     this.auth = auth |     this.auth = auth | ||||||
|     this.streamManager = streamManager |     this.streamManager = streamManager | ||||||
|  |     this.rssFeeds = rssFeeds | ||||||
|     this.emitter = emitter |     this.emitter = emitter | ||||||
| 
 | 
 | ||||||
|     this.router = express() |     this.router = express() | ||||||
| @ -35,6 +36,8 @@ class ApiController { | |||||||
|     this.router.post('/authorize', this.authorize.bind(this)) |     this.router.post('/authorize', this.authorize.bind(this)) | ||||||
| 
 | 
 | ||||||
|     this.router.get('/genres', this.getGenres.bind(this)) |     this.router.get('/genres', this.getGenres.bind(this)) | ||||||
|  | 
 | ||||||
|  |     this.router.post('/feed', this.openRssFeed.bind(this)) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   find(req, res) { |   find(req, res) { | ||||||
| @ -42,7 +45,6 @@ class ApiController { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   findCovers(req, res) { |   findCovers(req, res) { | ||||||
|     console.log('Find covers', req.query) |  | ||||||
|     this.scanner.findCovers(req, res) |     this.scanner.findCovers(req, res) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -174,6 +176,15 @@ class ApiController { | |||||||
|     this.auth.userChangePassword(req, res) |     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) { |   getGenres(req, res) { | ||||||
|     res.json({ |     res.json({ | ||||||
|       genres: this.db.getGenres() |       genres: this.db.getGenres() | ||||||
|  | |||||||
| @ -75,6 +75,10 @@ class Auth { | |||||||
|   verifyToken(token) { |   verifyToken(token) { | ||||||
|     return new Promise((resolve) => { |     return new Promise((resolve) => { | ||||||
|       jwt.verify(token, process.env.TOKEN_SECRET, (err, payload) => { |       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) |         var user = this.users.find(u => u.id === payload.userId) | ||||||
|         resolve(user || null) |         resolve(user || null) | ||||||
|       }) |       }) | ||||||
|  | |||||||
							
								
								
									
										53
									
								
								server/RssFeeds.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								server/RssFeeds.js
									
									
									
									
									
										Normal 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 | ||||||
| @ -11,6 +11,7 @@ const Db = require('./Db') | |||||||
| const ApiController = require('./ApiController') | const ApiController = require('./ApiController') | ||||||
| const HlsController = require('./HlsController') | const HlsController = require('./HlsController') | ||||||
| const StreamManager = require('./StreamManager') | const StreamManager = require('./StreamManager') | ||||||
|  | const RssFeeds = require('./RssFeeds') | ||||||
| const Logger = require('./Logger') | const Logger = require('./Logger') | ||||||
| 
 | 
 | ||||||
| class Server { | class Server { | ||||||
| @ -30,9 +31,11 @@ class Server { | |||||||
|     this.watcher = new Watcher(this.AudiobookPath) |     this.watcher = new Watcher(this.AudiobookPath) | ||||||
|     this.scanner = new Scanner(this.AudiobookPath, this.MetadataPath, this.db, this.emitter.bind(this)) |     this.scanner = new Scanner(this.AudiobookPath, this.MetadataPath, this.db, this.emitter.bind(this)) | ||||||
|     this.streamManager = new StreamManager(this.db, this.MetadataPath) |     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.hlsController = new HlsController(this.db, this.scanner, this.auth, this.streamManager, this.emitter.bind(this), this.MetadataPath) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|     this.server = null |     this.server = null | ||||||
|     this.io = null |     this.io = null | ||||||
| 
 | 
 | ||||||
| @ -112,11 +115,13 @@ class Server { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     app.use(express.static(this.MetadataPath)) |     app.use(express.static(this.MetadataPath)) | ||||||
|  |     app.use(express.static(Path.join(global.appRoot, 'static'))) | ||||||
|     app.use(express.urlencoded({ extended: true })); |     app.use(express.urlencoded({ extended: true })); | ||||||
|     app.use(express.json()) |     app.use(express.json()) | ||||||
| 
 | 
 | ||||||
|     app.use('/api', this.authMiddleware.bind(this), this.apiController.router) |     app.use('/api', this.authMiddleware.bind(this), this.apiController.router) | ||||||
|     app.use('/hls', this.authMiddleware.bind(this), this.hlsController.router) |     app.use('/hls', this.authMiddleware.bind(this), this.hlsController.router) | ||||||
|  |     app.use('/feeds', this.rssFeeds.router) | ||||||
| 
 | 
 | ||||||
|     app.get('/', (req, res) => { |     app.get('/', (req, res) => { | ||||||
|       res.sendFile('/index.html') |       res.sendFile('/index.html') | ||||||
|  | |||||||
| @ -39,13 +39,15 @@ async function getAllAudiobookFiles(abRootPath) { | |||||||
|     var pathformat = Path.parse(relpath) |     var pathformat = Path.parse(relpath) | ||||||
|     var path = pathformat.dir |     var path = pathformat.dir | ||||||
| 
 | 
 | ||||||
|     // If relative file directory has 3 folders, then the middle folder will be series
 |     if (!path) { | ||||||
|     var splitDir = pathformat.dir.split(Path.sep) |       Logger.error('Ignoring file in root dir', filepath) | ||||||
|     if (splitDir.length === 1) { |  | ||||||
|       Logger.error('Invalid file in root dir', filepath) |  | ||||||
|       return |       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 |     var series = null | ||||||
|     if (splitDir.length > 1) series = splitDir.shift() |     if (splitDir.length > 1) series = splitDir.shift() | ||||||
|     var title = splitDir.shift() |     var title = splitDir.shift() | ||||||
|  | |||||||
							
								
								
									
										
											BIN
										
									
								
								static/Logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/Logo.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 72 KiB | 
		Loading…
	
		Reference in New Issue
	
	Block a user