diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index ddd7c5f3..16ae302c 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -10,6 +10,3 @@ RUN apt-get update && \ DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends \ curl tzdata ffmpeg && \ rm -rf /var/lib/apt/lists/* - -# Move tone executable to appropriate directory -COPY --from=sandreas/tone:v0.1.5 /usr/local/bin/tone /usr/local/bin/ diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index df639ef7..3e499468 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -4,7 +4,7 @@ on: pull_request: push: branches-ignore: - - 'dependabot/**' # Don't run dependabot branches, as they are already covered by pull requests + - 'dependabot/**' # Don't run dependabot branches, as they are already covered by pull requests jobs: build: @@ -18,8 +18,8 @@ jobs: with: node-version: 20 - - name: install pkg - run: npm install -g pkg + - name: install pkg (using yao-pkg fork for targetting node20) + run: npm install -g @yao-pkg/pkg - name: get client dependencies working-directory: client @@ -33,7 +33,7 @@ jobs: run: npm ci --only=production - name: build binary - run: pkg -t node18-linux-x64 -o audiobookshelf . + run: pkg -t node20-linux-x64 -o audiobookshelf . - name: run audiobookshelf run: | diff --git a/Dockerfile b/Dockerfile index 97bb4732..fe68b304 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,6 @@ RUN npm ci && npm cache clean --force RUN npm run generate ### STAGE 1: Build server ### -FROM sandreas/tone:v0.1.5 AS tone FROM node:20-alpine ENV NODE_ENV=production @@ -21,7 +20,6 @@ RUN apk update && \ g++ \ tini -COPY --from=tone /usr/local/bin/tone /usr/local/bin/ COPY --from=build /client/dist /client/dist COPY index.js package* / COPY server server diff --git a/build/debian/DEBIAN/preinst b/build/debian/DEBIAN/preinst index f43f2683..c4692ed3 100644 --- a/build/debian/DEBIAN/preinst +++ b/build/debian/DEBIAN/preinst @@ -50,7 +50,6 @@ install_ffmpeg() { echo "Starting FFMPEG Install" WGET="wget https://johnvansickle.com/ffmpeg/builds/ffmpeg-git-amd64-static.tar.xz --output-document=ffmpeg-git-amd64-static.tar.xz" - WGET_TONE="wget https://github.com/sandreas/tone/releases/download/v0.1.5/tone-0.1.5-linux-x64.tar.gz --output-document=tone-0.1.5-linux-x64.tar.gz" if ! cd "$FFMPEG_INSTALL_DIR"; then echo "Creating ffmpeg install dir at $FFMPEG_INSTALL_DIR" @@ -63,13 +62,7 @@ install_ffmpeg() { tar xvf ffmpeg-git-amd64-static.tar.xz --strip-components=1 --no-same-owner rm ffmpeg-git-amd64-static.tar.xz - # Temp downloading tone library to the ffmpeg dir - echo "Getting tone.." - $WGET_TONE - tar xvf tone-0.1.5-linux-x64.tar.gz --strip-components=1 --no-same-owner - rm tone-0.1.5-linux-x64.tar.gz - - echo "Good to go on Ffmpeg (& tone)... hopefully" + echo "Good to go on Ffmpeg... hopefully" } setup_config() { @@ -77,12 +70,6 @@ setup_config() { echo "Existing config found." cat $CONFIG_PATH - # TONE_PATH variable added in 2.1.6, if it doesnt exist then add it - if ! grep -q "TONE_PATH" "$CONFIG_PATH"; then - echo "Adding TONE_PATH to existing config" - echo "TONE_PATH=$FFMPEG_INSTALL_DIR/tone" >> "$CONFIG_PATH" - fi - else if [ ! -d "$DEFAULT_DATA_DIR" ]; then @@ -98,7 +85,6 @@ setup_config() { CONFIG_PATH=$DEFAULT_DATA_DIR/config FFMPEG_PATH=$FFMPEG_INSTALL_DIR/ffmpeg FFPROBE_PATH=$FFMPEG_INSTALL_DIR/ffprobe -TONE_PATH=$FFMPEG_INSTALL_DIR/tone PORT=$DEFAULT_PORT HOST=$DEFAULT_HOST" diff --git a/build/linuxpackager b/build/linuxpackager index 5f03a2e8..52a9beba 100755 --- a/build/linuxpackager +++ b/build/linuxpackager @@ -48,7 +48,7 @@ Description: $DESCRIPTION" echo "$controlfile" > dist/debian/DEBIAN/control; # Package debian -pkg -t node18-linux-x64 -o dist/debian/usr/share/audiobookshelf/audiobookshelf . +pkg -t node20-linux-x64 -o dist/debian/usr/share/audiobookshelf/audiobookshelf . fakeroot dpkg-deb -Zxz --build dist/debian diff --git a/client/components/app/BookShelfCategorized.vue b/client/components/app/BookShelfCategorized.vue index ff0c5ff7..c8a70a2a 100644 --- a/client/components/app/BookShelfCategorized.vue +++ b/client/components/app/BookShelfCategorized.vue @@ -408,6 +408,36 @@ export default { } }) }, + shareOpen(mediaItemShare) { + this.shelves.forEach((shelf) => { + if (shelf.type == 'book') { + shelf.entities = shelf.entities.map((ent) => { + if (ent.media.id === mediaItemShare.mediaItemId) { + return { + ...ent, + mediaItemShare + } + } + return ent + }) + } + }) + }, + shareClosed(mediaItemShare) { + this.shelves.forEach((shelf) => { + if (shelf.type == 'book') { + shelf.entities = shelf.entities.map((ent) => { + if (ent.media.id === mediaItemShare.mediaItemId) { + return { + ...ent, + mediaItemShare: null + } + } + return ent + }) + } + }) + }, initListeners() { if (this.$root.socket) { this.$root.socket.on('user_updated', this.userUpdated) @@ -419,6 +449,8 @@ export default { this.$root.socket.on('items_updated', this.libraryItemsUpdated) this.$root.socket.on('items_added', this.libraryItemsAdded) this.$root.socket.on('episode_added', this.episodeAdded) + this.$root.socket.on('share_open', this.shareOpen) + this.$root.socket.on('share_closed', this.shareClosed) } else { console.error('Error socket not initialized') } @@ -434,6 +466,8 @@ export default { this.$root.socket.off('items_updated', this.libraryItemsUpdated) this.$root.socket.off('items_added', this.libraryItemsAdded) this.$root.socket.off('episode_added', this.episodeAdded) + this.$root.socket.off('share_open', this.shareOpen) + this.$root.socket.off('share_closed', this.shareClosed) } else { console.error('Error socket not initialized') } diff --git a/client/components/app/LazyBookshelf.vue b/client/components/app/LazyBookshelf.vue index fa47ce99..6137cdca 100644 --- a/client/components/app/LazyBookshelf.vue +++ b/client/components/app/LazyBookshelf.vue @@ -601,6 +601,30 @@ export default { this.executeRebuild() } }, + shareOpen(mediaItemShare) { + if (this.entityName === 'items' || this.entityName === 'series-books') { + var indexOf = this.entities.findIndex((ent) => ent?.media?.id === mediaItemShare.mediaItemId) + if (indexOf >= 0) { + if (this.entityComponentRefs[indexOf]) { + const libraryItem = { ...this.entityComponentRefs[indexOf].libraryItem } + libraryItem.mediaItemShare = mediaItemShare + this.entityComponentRefs[indexOf].setEntity?.(libraryItem) + } + } + } + }, + shareClosed(mediaItemShare) { + if (this.entityName === 'items' || this.entityName === 'series-books') { + var indexOf = this.entities.findIndex((ent) => ent?.media?.id === mediaItemShare.mediaItemId) + if (indexOf >= 0) { + if (this.entityComponentRefs[indexOf]) { + const libraryItem = { ...this.entityComponentRefs[indexOf].libraryItem } + libraryItem.mediaItemShare = null + this.entityComponentRefs[indexOf].setEntity?.(libraryItem) + } + } + } + }, updatePagesLoaded() { let numPages = Math.ceil(this.totalEntities / this.booksPerFetch) for (let page = 0; page < numPages; page++) { @@ -703,6 +727,8 @@ export default { this.$root.socket.on('playlist_added', this.playlistAdded) this.$root.socket.on('playlist_updated', this.playlistUpdated) this.$root.socket.on('playlist_removed', this.playlistRemoved) + this.$root.socket.on('share_open', this.shareOpen) + this.$root.socket.on('share_closed', this.shareClosed) } else { console.error('Bookshelf - Socket not initialized') } @@ -730,6 +756,8 @@ export default { this.$root.socket.off('playlist_added', this.playlistAdded) this.$root.socket.off('playlist_updated', this.playlistUpdated) this.$root.socket.off('playlist_removed', this.playlistRemoved) + this.$root.socket.off('share_open', this.shareOpen) + this.$root.socket.off('share_closed', this.shareClosed) } else { console.error('Bookshelf - Socket not initialized') } diff --git a/client/components/app/SideRail.vue b/client/components/app/SideRail.vue index 56207526..9720bc6b 100644 --- a/client/components/app/SideRail.vue +++ b/client/components/app/SideRail.vue @@ -121,7 +121,7 @@

{{ Source }}

- + @@ -219,9 +219,6 @@ export default { githubTagUrl() { return this.versionData.githubTagUrl }, - currentVersionChangelog() { - return this.versionData.currentVersionChangelog || 'No Changelog Available' - }, streamLibraryItem() { return this.$store.state.streamLibraryItem }, @@ -245,4 +242,4 @@ export default { #siderail-buttons-container.player-open { max-height: calc(100vh - 64px - 48px - 160px); } - \ No newline at end of file + diff --git a/client/components/cards/LazyBookCard.vue b/client/components/cards/LazyBookCard.vue index 3cd36bcc..77a1ebb2 100644 --- a/client/components/cards/LazyBookCard.vue +++ b/client/components/cards/LazyBookCard.vue @@ -539,6 +539,12 @@ export default { func: 'openPlaylists', text: this.$strings.LabelAddToPlaylist }) + if (this.userIsAdminOrUp) { + items.push({ + func: 'openShare', + text: this.$strings.LabelShare + }) + } } if (this.ebookFormat && this.store.state.libraries.ereaderDevices?.length) { items.push({ @@ -897,6 +903,10 @@ export default { this.store.commit('globals/setSelectedPlaylistItems', [{ libraryItem: this.libraryItem, episode: this.recentEpisode }]) this.store.commit('globals/setShowPlaylistsModal', true) }, + openShare() { + this.store.commit('setSelectedLibraryItem', this.libraryItem) + this.store.commit('globals/setShareModal', this.mediaItemShare) + }, deleteLibraryItem() { const payload = { message: this.$strings.MessageConfirmDeleteLibraryItem, diff --git a/client/components/controls/LibraryFilterSelect.vue b/client/components/controls/LibraryFilterSelect.vue index e3cb0d16..d8951321 100644 --- a/client/components/controls/LibraryFilterSelect.vue +++ b/client/components/controls/LibraryFilterSelect.vue @@ -89,6 +89,9 @@ export default { this.$emit('input', val) } }, + userIsAdminOrUp() { + return this.$store.getters['user/getIsAdminOrUp'] + }, libraryMediaType() { return this.$store.getters['libraries/getCurrentLibraryMediaType'] }, @@ -148,7 +151,7 @@ export default { ] }, bookItems() { - return [ + const items = [ { text: this.$strings.LabelAll, value: 'all' @@ -229,13 +232,16 @@ export default { text: this.$strings.LabelRSSFeedOpen, value: 'feed-open', sublist: false - }, - { + } + ] + if (this.userIsAdminOrUp) { + items.push({ text: this.$strings.LabelShareOpen, value: 'share-open', sublist: false - } - ] + }) + } + return items }, podcastItems() { return [ diff --git a/client/components/modals/ShareModal.vue b/client/components/modals/ShareModal.vue index dc781a74..f79b3796 100644 --- a/client/components/modals/ShareModal.vue +++ b/client/components/modals/ShareModal.vue @@ -6,6 +6,13 @@