├── docker
├── env.js.template
├── docker-entrypoint.sh
├── Dockerfile
└── nginx.conf
├── public
├── _redirects
├── manifest.webmanifest
└── icon.svg
├── screenshots
├── album.png
├── artist.png
├── album-list.png
└── artist-list.png
├── src
├── app
│ ├── layout
│ │ ├── Fullscreen.vue
│ │ └── Default.vue
│ ├── App.vue
│ ├── ErrorToast.vue
│ ├── Logo.vue
│ ├── About.vue
│ ├── SidebarNav.vue
│ ├── Sidebar.vue
│ └── TopNav.vue
├── global.d.ts
├── library
│ ├── track
│ │ ├── BaseTable.vue
│ │ ├── CellTrackNumber.vue
│ │ ├── BaseTableHead.vue
│ │ ├── CellAlbum.vue
│ │ ├── CellArtist.vue
│ │ ├── CellDuration.vue
│ │ ├── CellTitle.vue
│ │ ├── CellActions.vue
│ │ └── TrackList.vue
│ ├── search
│ │ ├── SearchForm.vue
│ │ └── SearchResult.vue
│ ├── artist
│ │ ├── ArtistTracks.vue
│ │ ├── ArtistList.vue
│ │ ├── ArtistLibrary.vue
│ │ └── ArtistDetails.vue
│ ├── favourite
│ │ ├── store.ts
│ │ └── Favourites.vue
│ ├── playlist
│ │ ├── CreatePlaylistModal.vue
│ │ ├── store.ts
│ │ ├── PlaylistNav.vue
│ │ ├── PlaylistLibrary.vue
│ │ └── Playlist.vue
│ ├── podcast
│ │ ├── AddPodcastModal.vue
│ │ ├── PodcastLibrary.vue
│ │ └── PodcastDetails.vue
│ ├── genre
│ │ ├── GenreDetails.vue
│ │ └── GenreLibrary.vue
│ ├── album
│ │ ├── AlbumLibrary.vue
│ │ ├── AlbumList.vue
│ │ └── AlbumDetails.vue
│ ├── radio
│ │ └── RadioStations.vue
│ └── file
│ │ └── Files.vue
├── shared
│ ├── config.ts
│ ├── components
│ │ ├── ContentLoader.vue
│ │ ├── ExternalLink.vue
│ │ ├── EmptyIndicator.vue
│ │ ├── IconReplayGain.vue
│ │ ├── IconReplayGainAlbum.vue
│ │ ├── IconReplayGainTrack.vue
│ │ ├── Avatar.vue
│ │ ├── OverflowMenu.vue
│ │ ├── SwitchInput.vue
│ │ ├── InfiniteList.vue
│ │ ├── DropdownItem.vue
│ │ ├── index.ts
│ │ ├── IconLastFm.vue
│ │ ├── Tile.vue
│ │ ├── EditModal.vue
│ │ ├── Tiles.vue
│ │ ├── Hero.vue
│ │ ├── InfiniteLoader.vue
│ │ ├── ContextMenu.vue
│ │ ├── OverflowFade.vue
│ │ ├── Slider.vue
│ │ ├── Dropdown.vue
│ │ ├── IconMusicBrainz.vue
│ │ └── Icon.vue
│ ├── index.ts
│ ├── store.ts
│ ├── assets
│ │ └── fallback.svg
│ ├── compat.ts
│ ├── utils.ts
│ └── router.ts
├── style
│ ├── nav-underlined.scss
│ ├── table.scss
│ └── main.scss
├── main.ts
├── discover
│ └── Discover.vue
├── player
│ ├── ProgressBar.vue
│ ├── Queue.vue
│ ├── audio.ts
│ ├── Player.vue
│ └── store.ts
└── auth
│ ├── Login.vue
│ └── service.ts
├── .editorconfig
├── .gitignore
├── .github
└── workflows
│ ├── pr.yml
│ └── ci.yml
├── index.html
├── tsconfig.json
├── vite.config.mjs
├── .eslintrc.js
├── package.json
└── README.md
/docker/env.js.template:
--------------------------------------------------------------------------------
1 | window.env = {
2 | SERVER_URL: "$SERVER_URL",
3 | }
4 |
--------------------------------------------------------------------------------
/public/_redirects:
--------------------------------------------------------------------------------
1 | /api/* http://demo.subsonic.org/:splat 200
2 | /* /index.html 200
3 |
--------------------------------------------------------------------------------
/screenshots/album.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tamland/airsonic-refix/HEAD/screenshots/album.png
--------------------------------------------------------------------------------
/screenshots/artist.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tamland/airsonic-refix/HEAD/screenshots/artist.png
--------------------------------------------------------------------------------
/screenshots/album-list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tamland/airsonic-refix/HEAD/screenshots/album-list.png
--------------------------------------------------------------------------------
/screenshots/artist-list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tamland/airsonic-refix/HEAD/screenshots/artist-list.png
--------------------------------------------------------------------------------
/docker/docker-entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 | envsubst < env.js.template > /var/www/html/env.js
4 | exec "$@"
5 |
--------------------------------------------------------------------------------
/src/app/layout/Fullscreen.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
--------------------------------------------------------------------------------
/src/shared/config.ts:
--------------------------------------------------------------------------------
1 | export interface Config {
2 | serverUrl: string
3 | }
4 |
5 | const env = (window as any).env
6 |
7 | export const config: Config = {
8 | serverUrl: env?.SERVER_URL || '',
9 | }
10 |
--------------------------------------------------------------------------------
/src/shared/components/ContentLoader.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/shared/components/ExternalLink.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
4 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # local env files
6 | .env.local
7 | .env.*.local
8 |
9 | # Log files
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 |
14 | # Editor directories and files
15 | .idea
16 | .vscode
17 | *.suo
18 | *.ntvs*
19 | *.njsproj
20 | *.sln
21 | *.sw?
22 |
--------------------------------------------------------------------------------
/public/manifest.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Airsonic (refix)",
3 | "short_name": "Airsonic (refix)",
4 | "start_url": "/",
5 | "display": "standalone",
6 | "theme_color": "#000",
7 | "background_color": "#000",
8 | "icons": [
9 | {
10 | "src": "./icon.svg",
11 | "type": "image/svg+xml",
12 | "sizes": "any",
13 | "purpose": "any"
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM nginx:alpine
2 |
3 | EXPOSE 80
4 |
5 | RUN apk add gettext
6 | COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
7 | COPY docker/env.js.template /env.js.template
8 | COPY docker/docker-entrypoint.sh /docker-entrypoint.sh
9 | RUN chmod +x /docker-entrypoint.sh
10 | COPY dist/ /var/www/html/
11 |
12 | ENTRYPOINT ["/docker-entrypoint.sh"]
13 | CMD ["nginx", "-g", "daemon off;"]
14 |
--------------------------------------------------------------------------------
/src/library/track/CellAlbum.vue:
--------------------------------------------------------------------------------
1 |
2 | #
5 |
6 | Title
7 |
8 |
10 | Actions
11 |
12 |
3 |
4 |
12 |
13 |
--------------------------------------------------------------------------------
/src/shared/components/EmptyIndicator.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ,
5 |
13 |
14 |
--------------------------------------------------------------------------------
/src/library/track/CellDuration.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ duration }}
4 |
5 |
6 |
21 |
--------------------------------------------------------------------------------
/src/shared/components/IconReplayGainAlbum.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
--------------------------------------------------------------------------------
/src/shared/components/IconReplayGainTrack.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
--------------------------------------------------------------------------------
/src/library/search/SearchForm.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
25 |
--------------------------------------------------------------------------------
/src/shared/index.ts:
--------------------------------------------------------------------------------
1 | import { API } from '@/shared/api'
2 | import { inject } from 'vue'
3 | import { AuthService } from '@/auth/service'
4 | import { App, Plugin } from '@/shared/compat'
5 |
6 | const apiSymbol = Symbol('')
7 |
8 | export function useApi(): API {
9 | return inject(apiSymbol) as API
10 | }
11 |
12 | export function createApi(auth: AuthService): API & Plugin {
13 | const instance = new API(auth)
14 | return Object.assign(instance, {
15 | install: (app: App) => {
16 | app.config.globalProperties.$api = instance
17 | app.provide(apiSymbol, instance)
18 | }
19 | })
20 | }
21 |
--------------------------------------------------------------------------------
/src/style/nav-underlined.scss:
--------------------------------------------------------------------------------
1 | ul.nav-underlined {
2 | white-space: nowrap;
3 | overflow-x: auto;
4 | scrollbar-width: none;
5 | padding-left: 0;
6 | margin-bottom: 0;
7 |
8 | li {
9 | display: inline-block;
10 | float: none;
11 | a {
12 | display: block;
13 | padding: 0.5rem 1rem;
14 | }
15 | a:hover, a:focus {
16 | text-decoration: none;
17 | border-bottom: 2px solid rgba(#fff, .25);
18 | }
19 | a.active {
20 | border-bottom: 2px solid;
21 | }
22 | a.active:hover {
23 | color: var(--bs-primary);
24 | }
25 | }
26 | }
27 |
28 | ul.nav-underlined::-webkit-scrollbar {
29 | display: none;
30 | }
31 |
--------------------------------------------------------------------------------
/src/shared/components/Avatar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
3 |
11 |
12 |
25 |
--------------------------------------------------------------------------------
/src/shared/components/OverflowMenu.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 | Licensed under the AGPLv3 license. 12 |
13 |