diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index ddd7c5f30..16ae302ca 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/.devcontainer/dev.js b/.devcontainer/dev.js index 28c0074b4..054690d4f 100644 --- a/.devcontainer/dev.js +++ b/.devcontainer/dev.js @@ -6,5 +6,5 @@ module.exports.config = { MetadataPath: Path.resolve('metadata'), FFmpegPath: '/usr/bin/ffmpeg', FFProbePath: '/usr/bin/ffprobe', - SkipBinariesCheck: true -} \ No newline at end of file + SkipBinariesCheck: false +} diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 2c6cc1912..d04ad818f 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -3,6 +3,3 @@ contact_links: - name: Discord url: https://discord.gg/HQgCbd6E75 about: Ask questions, get help troubleshooting, and join the Abs community here. - - name: Matrix - url: https://matrix.to/#/#audiobookshelf:matrix.org - about: Ask questions, get help troubleshooting, and join the Abs community here. diff --git a/.github/workflows/apply_comments.yaml b/.github/workflows/apply_comments.yaml new file mode 100644 index 000000000..69a7ce280 --- /dev/null +++ b/.github/workflows/apply_comments.yaml @@ -0,0 +1,55 @@ +name: Add issue comments by label +on: + issues: + types: + - labeled +jobs: + help-wanted: + if: github.event.label.name == 'help wanted' + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - name: Help wanted comment + run: gh issue comment "$NUMBER" --body "$BODY" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + NUMBER: ${{ github.event.issue.number }} + BODY: > + This issue is not able to be completed due to limited bandwidth or access to the required test hardware. + + This issue is available for anyone to work on. + + + config-issue: + if: github.event.label.name == 'config-issue' + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - name: Config issue comment + run: gh issue close "$NUMBER" --reason "not planned" --comment "$BODY" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + NUMBER: ${{ github.event.issue.number }} + BODY: > + After reviewing this issue, this appears to be a problem with your setup and not Audiobookshelf. This issue is being closed to keep the issue tracker focused on Audiobookshelf itself. Please reach out on the Audiobookshelf Discord for community support. + + Some common search terms to help you find the solution to your problem: + - Reverse proxy + - Enabling websockets + - SSL (https vs http) + - Configuring a static IP + - `localhost` versus IP address + - hairpin NAT + - VPN + - firewall ports + - public versus private network + - bridge versus host mode + - Docker networking + - DNS (such as EAI_AGAIN errors) + + After you have followed these steps, please post the solution or steps you followed to fix the problem to help others in the future, or show that it is a problem with Audiobookshelf so we can reopen the issue. + diff --git a/.github/workflows/close-issues-on-release.yml b/.github/workflows/close-issues-on-release.yml new file mode 100644 index 000000000..9c5907589 --- /dev/null +++ b/.github/workflows/close-issues-on-release.yml @@ -0,0 +1,20 @@ +name: Close fixed issues on release. +on: + release: + types: [published] + +permissions: + contents: read + issues: write + +jobs: + comment: + runs-on: ubuntu-latest + steps: + - name: Close issues marked as fixed upon a release. + uses: gcampbell-msft/fixed-pending-release@7fa1b75a0c04bcd4b375110522878e5f6100cff5 + with: + label: 'awaiting release' + removeLabel: true + applyToAll: true + message: Fixed in [${releaseTag}](${releaseUrl}). diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index e783fce63..846a55639 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -70,6 +70,7 @@ jobs: uses: docker/build-push-action@v3 with: tags: ${{ github.event.inputs.tags || steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} context: . platforms: linux/amd64,linux/arm64 push: true diff --git a/.github/workflows/i18n-integration.yml b/.github/workflows/i18n-integration.yml index 12d82c3e6..fc8441546 100644 --- a/.github/workflows/i18n-integration.yml +++ b/.github/workflows/i18n-integration.yml @@ -20,11 +20,11 @@ jobs: - name: Set up node uses: actions/setup-node@v4 with: - node-version: "20" + node-version: '20' # The only argument is the `directory`, which is where the i18n files are # stored. - name: Run Update JSON Files action - uses: audiobookshelf/audiobookshelf-i18n-updater@v1.2.0 + uses: audiobookshelf/audiobookshelf-i18n-updater@v1.3.0 with: - directory: "client/strings/" # Adjust the directory path as needed + directory: 'client/strings/' # Adjust the directory path as needed diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index df639ef79..3e499468e 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/.github/workflows/lint-openapi.yml b/.github/workflows/lint-openapi.yml new file mode 100644 index 000000000..817e94b97 --- /dev/null +++ b/.github/workflows/lint-openapi.yml @@ -0,0 +1,32 @@ +name: API linting + +# Run on pull requests or pushes when there is a change to any OpenAPI files in docs/ +on: + pull_request: + push: + paths: + - 'docs/**' + +# This action only needs read permissions +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + steps: + # Check out the repository + - name: Checkout + uses: actions/checkout@v4 + # Set up node to run the javascript + - name: Set up node + uses: actions/setup-node@v4 + # Install Redocly CLI + - name: Install Redocly CLI + run: npm install -g @redocly/cli@latest + # Perform linting for exploded spec + - name: Run linting for exploded spec + run: redocly lint docs/root.yaml --format=github-actions + # Perform linting for bundled spec + - name: Run linting for bundled spec + run: redocly lint docs/openapi.json --format=github-actions diff --git a/.gitignore b/.gitignore index 9ddb1a707..1eda8e1fa 100644 --- a/.gitignore +++ b/.gitignore @@ -15,8 +15,10 @@ /.nyc_output/ /ffmpeg* /ffprobe* +/unicode* +/libnusqlite3* sw.* .DS_STORE .idea/* -tailwind.compiled.css \ No newline at end of file +tailwind.compiled.css diff --git a/Dockerfile b/Dockerfile index 97bb4732f..0d4629f84 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,26 +6,40 @@ 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 RUN apk update && \ - apk add --no-cache --update \ - curl \ - tzdata \ - ffmpeg \ - make \ - python3 \ - g++ \ - tini + apk add --no-cache --update \ + curl \ + tzdata \ + ffmpeg \ + make \ + python3 \ + g++ \ + tini \ + unzip -COPY --from=tone /usr/local/bin/tone /usr/local/bin/ COPY --from=build /client/dist /client/dist COPY index.js package* / COPY server server +ARG TARGETPLATFORM + +ENV NUSQLITE3_DIR="/usr/local/lib/nusqlite3" +ENV NUSQLITE3_PATH="${NUSQLITE3_DIR}/libnusqlite3.so" + +RUN case "$TARGETPLATFORM" in \ + "linux/amd64") \ + curl -L -o /tmp/library.zip "https://github.com/mikiher/nunicode-sqlite/releases/download/v1.2/libnusqlite3-linux-musl-x64.zip" ;; \ + "linux/arm64") \ + curl -L -o /tmp/library.zip "https://github.com/mikiher/nunicode-sqlite/releases/download/v1.2/libnusqlite3-linux-musl-arm64.zip" ;; \ + *) echo "Unsupported platform: $TARGETPLATFORM" && exit 1 ;; \ + esac && \ + unzip /tmp/library.zip -d $NUSQLITE3_DIR && \ + rm /tmp/library.zip + RUN npm ci --only=production RUN apk del make python3 g++ diff --git a/build/debian/DEBIAN/preinst b/build/debian/DEBIAN/preinst index f43f2683e..e30bc490c 100644 --- a/build/debian/DEBIAN/preinst +++ b/build/debian/DEBIAN/preinst @@ -2,7 +2,6 @@ set -e set -o pipefail -FFMPEG_INSTALL_DIR="/usr/lib/audiobookshelf-ffmpeg" DEFAULT_DATA_DIR="/usr/share/audiobookshelf" CONFIG_PATH="/etc/default/audiobookshelf" DEFAULT_PORT=13378 @@ -46,43 +45,11 @@ add_group() { fi } -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" - mkdir "$FFMPEG_INSTALL_DIR" - chown -R 'audiobookshelf:audiobookshelf' "$FFMPEG_INSTALL_DIR" - cd "$FFMPEG_INSTALL_DIR" - fi - - $WGET - 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" -} - setup_config() { if [ -f "$CONFIG_PATH" ]; then 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 @@ -96,9 +63,6 @@ setup_config() { config_text="METADATA_PATH=$DEFAULT_DATA_DIR/metadata 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" @@ -115,5 +79,3 @@ add_group 'audiobookshelf' '' add_user 'audiobookshelf' '' 'audiobookshelf' 'audiobookshelf user-daemon' '/bin/false' setup_config - -install_ffmpeg diff --git a/build/linuxpackager b/build/linuxpackager index 5f03a2e8e..52a9beba9 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/assets/fonts.css b/client/assets/fonts.css index 9504d4c3d..c568ffa6e 100644 --- a/client/assets/fonts.css +++ b/client/assets/fonts.css @@ -1,19 +1,12 @@ @font-face { - font-family: 'Material Icons'; + font-family: 'Material Symbols Rounded'; font-style: normal; font-weight: 400; - src: url(~static/fonts/MaterialIcons.woff2) format('woff2'); + src: url(~static/fonts/MaterialSymbolsRounded.woff2) format('woff2'); } -@font-face { - font-family: 'Material Icons Outlined'; - font-style: normal; - font-weight: 400; - src: url(~static/fonts/MaterialIconsOutlined.woff2) format('woff2'); -} - -.material-icons { - font-family: 'Material Icons'; +.material-symbols { + font-family: 'Material Symbols Rounded'; font-weight: normal; font-style: normal; line-height: 1; @@ -27,26 +20,9 @@ vertical-align: top; } -.material-icons:not([class*="text-"]) { - font-size: 1.5rem; -} - -.material-icons-outlined { - font-family: 'Material Icons Outlined'; - font-weight: normal; - font-style: normal; - line-height: 1; - letter-spacing: normal; - text-transform: none; - display: inline-block; - white-space: nowrap; - word-wrap: normal; - direction: ltr; - -webkit-font-smoothing: antialiased; -} - -.material-icons-outlined:not([class*="text-"]) { - font-size: 1.5rem; +.material-symbols.fill { + font-variation-settings: + 'FILL' 1 } /* cyrillic-ext */ @@ -317,4 +293,4 @@ font-display: swap; src: url(~static/fonts/Ubuntu_Mono/UbuntuMono-Regular.ttf) format('truetype'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} \ No newline at end of file +} diff --git a/client/components/app/Appbar.vue b/client/components/app/Appbar.vue index c281f8214..19b8fe3c6 100644 --- a/client/components/app/Appbar.vue +++ b/client/components/app/Appbar.vue @@ -16,7 +16,7 @@
- cast + cast
@@ -26,19 +26,19 @@ - upload + - settings + @@ -47,7 +47,7 @@ {{ username }} - person +
@@ -55,7 +55,7 @@

{{ $getString('MessageItemsSelected', [numMediaItemsSelected]) }}

- play_arrow + play_arrow {{ $strings.ButtonPlay }} @@ -76,7 +76,7 @@ - close + close
@@ -170,13 +170,13 @@ export default { if (!this.isPodcastLibrary && this.selectedMediaItemsArePlayable) { options.push({ - text: 'Quick Embed Metadata', + text: this.$strings.ButtonQuickEmbedMetadata, action: 'quick-embed' }) } options.push({ - text: 'Re-Scan', + text: this.$strings.ButtonReScan, action: 'rescan' }) @@ -264,7 +264,6 @@ export default { libraryItems.forEach((item) => { let subtitle = '' if (item.mediaType === 'book') subtitle = item.media.metadata.authors.map((au) => au.name).join(', ') - else if (item.mediaType === 'music') subtitle = item.media.metadata.artists.join(', ') queueItems.push({ libraryItemId: item.id, libraryId: item.libraryId, @@ -332,13 +331,13 @@ export default { libraryItemIds: this.selectedMediaItems.map((i) => i.id) }) .then(() => { - this.$toast.success('Batch delete success') + this.$toast.success(this.$strings.ToastBatchDeleteSuccess) this.$store.commit('globals/resetSelectedMediaItems', []) this.$eventBus.$emit('bookshelf_clear_selection') }) .catch((error) => { console.error('Batch delete failed', error) - this.$toast.error('Batch delete failed') + this.$toast.error(this.$strings.ToastBatchDeleteFailed) }) .finally(() => { this.$store.commit('setProcessingBatch', false) diff --git a/client/components/app/BookShelfCategorized.vue b/client/components/app/BookShelfCategorized.vue index 0c4562a4d..a977dd213 100644 --- a/client/components/app/BookShelfCategorized.vue +++ b/client/components/app/BookShelfCategorized.vue @@ -1,5 +1,5 @@ diff --git a/client/components/modals/collections/EditModal.vue b/client/components/modals/collections/EditModal.vue index 0ad9fd4cf..8c8d53922 100644 --- a/client/components/modals/collections/EditModal.vue +++ b/client/components/modals/collections/EditModal.vue @@ -28,7 +28,7 @@ \ No newline at end of file + diff --git a/client/components/ui/MultiSelectQueryInput.vue b/client/components/ui/MultiSelectQueryInput.vue index 6e9c0f10e..099ee7099 100644 --- a/client/components/ui/MultiSelectQueryInput.vue +++ b/client/components/ui/MultiSelectQueryInput.vue @@ -6,13 +6,13 @@
- edit - close + edit + close
{{ item[textKey] }}
- add + add
@@ -20,12 +20,12 @@