Compare commits
2 Commits
67c701f85a
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 6262a02fec | |||
| 220d6d8f2d |
@@ -1,20 +0,0 @@
|
|||||||
.DS_Store
|
|
||||||
node_modules
|
|
||||||
/build
|
|
||||||
/.svelte-kit
|
|
||||||
/package
|
|
||||||
/dist
|
|
||||||
.env
|
|
||||||
.env.*
|
|
||||||
!.env.example
|
|
||||||
|
|
||||||
# Ignore files for PNPM, NPM and YARN
|
|
||||||
pnpm-lock.yaml
|
|
||||||
package-lock.json
|
|
||||||
yarn.lock
|
|
||||||
|
|
||||||
**/components/_*
|
|
||||||
|
|
||||||
*.config.ts
|
|
||||||
*.config.js
|
|
||||||
*.config.cjs
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
extends: [
|
|
||||||
'eslint:recommended',
|
|
||||||
'plugin:@typescript-eslint/recommended',
|
|
||||||
// 'plugin:@typescript-eslint/recommended-requiring-type-checking',
|
|
||||||
'prettier',
|
|
||||||
],
|
|
||||||
plugins: ['svelte3', '@typescript-eslint', 'unused-imports'],
|
|
||||||
ignorePatterns: ['*.cjs'],
|
|
||||||
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
|
|
||||||
settings: {
|
|
||||||
'svelte3/typescript': () => require('typescript'),
|
|
||||||
},
|
|
||||||
parserOptions: {
|
|
||||||
sourceType: 'module',
|
|
||||||
ecmaVersion: 2020,
|
|
||||||
project: ['./tsconfig.json'],
|
|
||||||
},
|
|
||||||
env: {
|
|
||||||
browser: true,
|
|
||||||
es2017: true,
|
|
||||||
node: true,
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
// '@typescript-eslint/no-inferrable-types': 'off',
|
|
||||||
// '@typescript-eslint/no-unsafe-argument': 'off',
|
|
||||||
// '@typescript-eslint/no-unsafe-assignment': 'off',
|
|
||||||
'logical-assignment-operators': ['error', 'always'],
|
|
||||||
// "no-unused-vars": "off",
|
|
||||||
'unused-imports/no-unused-imports': 'error',
|
|
||||||
'unused-imports/no-unused-vars': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
vars: 'all',
|
|
||||||
varsIgnorePattern: '^_',
|
|
||||||
args: 'after-used',
|
|
||||||
argsIgnorePattern: '^_',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
.DS_Store
|
|
||||||
node_modules
|
|
||||||
/build
|
|
||||||
/.svelte-kit
|
|
||||||
/package
|
|
||||||
/dist
|
|
||||||
.env
|
|
||||||
.env.*
|
|
||||||
!.env.example
|
|
||||||
|
|
||||||
# Ignore files for PNPM, NPM and YARN
|
|
||||||
pnpm-lock.yaml
|
|
||||||
package-lock.json
|
|
||||||
yarn.lock
|
|
||||||
-17
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
|
|
||||||
"arrowParens": "always",
|
|
||||||
"bracketSpacing": true,
|
|
||||||
"bracketSameLine": false,
|
|
||||||
"semi": true,
|
|
||||||
"singleQuote": true,
|
|
||||||
"useTabs": true,
|
|
||||||
"tabWidth": 3,
|
|
||||||
"trailingComma": "all",
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": "*.svelte",
|
|
||||||
"options": { "parser": "svelte" }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
Vendored
-10
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
|
||||||
// for the documentation about the extensions.json format
|
|
||||||
"recommendations": [
|
|
||||||
"dbaeumer.vscode-eslint",
|
|
||||||
"aaron-bond.better-comments",
|
|
||||||
"esbenp.prettier-vscode",
|
|
||||||
"gruntfuggly.todo-tree"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
Vendored
-45
@@ -1,45 +0,0 @@
|
|||||||
{
|
|
||||||
"[svelte]": {
|
|
||||||
"editor.defaultFormatter": "svelte.svelte-vscode",
|
|
||||||
"editor.codeActionsOnSave": {
|
|
||||||
"source.organizeImports": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"[typescript]": {
|
|
||||||
"editor.codeActionsOnSave": {
|
|
||||||
"source.organizeImports": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"[javascript]": {
|
|
||||||
"editor.codeActionsOnSave": {
|
|
||||||
"source.organizeImports": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"eslint.rules.customizations": [{ "rule": "*", "severity": "warn" }],
|
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
||||||
"editor.formatOnSave": true,
|
|
||||||
"editor.formatOnType": false,
|
|
||||||
"files.exclude": {
|
|
||||||
"**/.cache": true,
|
|
||||||
"**/.DS_Store": true,
|
|
||||||
"**/.gitattributes": true,
|
|
||||||
"**/.svelte-kit": true,
|
|
||||||
"**/*-lock.*": true,
|
|
||||||
"**/*.Identifier": true,
|
|
||||||
"**/*.map": true,
|
|
||||||
"**/.turbo": true,
|
|
||||||
"**/.vercel": true,
|
|
||||||
"**/node_modules": true,
|
|
||||||
"**/tsconfig.tsbuildinfo": true,
|
|
||||||
"**/.npmrc": true
|
|
||||||
},
|
|
||||||
"files.watcherExclude": {
|
|
||||||
"**/node_modules": true,
|
|
||||||
"**/.svelte-kit": true
|
|
||||||
},
|
|
||||||
"problems.sortOrder": "severity",
|
|
||||||
"javascript.preferences.importModuleSpecifier": "relative",
|
|
||||||
"typescript.preferences.importModuleSpecifier": "relative",
|
|
||||||
"todo-tree.tree.scanMode": "workspace only"
|
|
||||||
}
|
|
||||||
-110
@@ -1,110 +0,0 @@
|
|||||||
# svelte-podcast
|
|
||||||
|
|
||||||
## 0.9.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- 528457f: Restructures package exports
|
|
||||||
- 528457f: - improved export structure
|
|
||||||
- simplify audio methods
|
|
||||||
- improve typescript types
|
|
||||||
- bba2421: Improve example components
|
|
||||||
- bba2421: improve progress component style overrides
|
|
||||||
- f42a00f: removes autoplay as it doesn't behave as users would expect
|
|
||||||
|
|
||||||
## 0.8.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- d18b0c7: refactor audio element logic
|
|
||||||
- 29ef657: switch to jsdoc from ts
|
|
||||||
- d18b0c7: rewrite docs with code samples
|
|
||||||
|
|
||||||
## 0.7.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- 16366ec: improvements to headless components
|
|
||||||
- 415d083: switch to jsdoc from ts
|
|
||||||
|
|
||||||
## 0.6.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- 9232f4d: Improves theming with css variables
|
|
||||||
Improves developer logs
|
|
||||||
Fixes various package export issues
|
|
||||||
Adds player components:
|
|
||||||
|
|
||||||
- Player: `<PlayerStack />`
|
|
||||||
- Player: `<PlayerWidget />`
|
|
||||||
- Utility: `<HeadlessTimeline />`
|
|
||||||
|
|
||||||
## 0.5.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- feba0d4: Improvements to audio stores:
|
|
||||||
|
|
||||||
- Adds missing `is_paused` value.
|
|
||||||
- Fixes to `start_at` value
|
|
||||||
- Refactors localStorage stores & user preferences
|
|
||||||
|
|
||||||
## 0.4.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- ff6a6d9: refactored audio element bindings for better control over element
|
|
||||||
|
|
||||||
## 0.3.4
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- ff5caf2: fix to allow override of internal types
|
|
||||||
|
|
||||||
## 0.3.3
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 802c704: fixes dist exports
|
|
||||||
|
|
||||||
## 0.3.2
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 6c849b7: Fixes derived store causing exessive updates
|
|
||||||
|
|
||||||
## 0.3.1
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 02598d4: fixes exported typings
|
|
||||||
|
|
||||||
## 0.3.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- 0b12aab: Adds mechanic for managing user preferences
|
|
||||||
|
|
||||||
## 0.2.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- 6f89448: Adds the ability to bind episode metadata to the audio store
|
|
||||||
- a70d68d:
|
|
||||||
- Restructures lib to make dist cleaner and dev easier to grock
|
|
||||||
- Refactors <audio /> bindings to make it easier to change values when loading a different source
|
|
||||||
- Moves more values to micro-stores
|
|
||||||
- 1841199: Adds save/load mechanic for episode progress
|
|
||||||
|
|
||||||
## 0.1.1
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 549d858: Fixes release build
|
|
||||||
|
|
||||||
## 0.1.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- 2e6f082: Adds initial audio element abstraction with store
|
|
||||||
@@ -4,20 +4,55 @@ Lecteur audio web pour la radio **Radyobòkaz**, hébergée sur l'instance Funkw
|
|||||||
[mizik.o-k-i.net](https://mizik.o-k-i.net/library/radios/7) (~2000 titres).
|
[mizik.o-k-i.net](https://mizik.o-k-i.net/library/radios/7) (~2000 titres).
|
||||||
|
|
||||||
Construit sur une version modifiée de [svelte-podcast](https://github.com/OllieJT/svelte-podcast)
|
Construit sur une version modifiée de [svelte-podcast](https://github.com/OllieJT/svelte-podcast)
|
||||||
(librairie headless de lecteur audio, voir `src/lib`).
|
(librairie headless de lecteur audio, voir `src/lib`). Dépôt de travail :
|
||||||
|
[labola.o-k-i.net/cyber-mawonaj/oki-podcast-reader](https://labola.o-k-i.net/cyber-mawonaj/oki-podcast-reader).
|
||||||
|
|
||||||
|
## Stack
|
||||||
|
|
||||||
|
- **SvelteKit 2 + Svelte 5** (mode compatibilité : syntaxe `on:click`/`$:`/`export let` conservée) + TypeScript
|
||||||
|
- **Tailwind CSS 3** — aucune autre dépendance UI
|
||||||
|
- **adapter-static** — pré-rendu statique dans `build/`
|
||||||
|
- Audio : `<audio>` natif unique (aucune lib tierce), stores Svelte legacy
|
||||||
|
- Flux RSS : parsing maison via `fast-xml-parser` (compatible SSR, pas de `DOMParser`)
|
||||||
|
|
||||||
|
## Fonctionnalités
|
||||||
|
|
||||||
|
- 📻 **Mode radio aléatoire** : file de lecture infinie de titres piochés au hasard
|
||||||
|
- 🎵 **Écoute à la carte** : liste complète des titres avec recherche (titre, artiste, album)
|
||||||
|
- 🎙️ **Flux RSS de podcasts** : parsing côté serveur + lecture des épisodes (démo Syntax.fm sur `/podcast`)
|
||||||
|
- ⏭️ Passage automatique au titre suivant en fin de piste
|
||||||
|
- 🎛️ Contrôles : lecture/pause, précédent/suivant, volume persisté (masqué sur iOS, où `volume` est en lecture seule), mute persistant, seek
|
||||||
|
- 📱 **Mobile** : cibles tactiles ≥ 48 px, barre de progression avec zone tactile de 44 px (tap + scrub au doigt), listes paginées 50×50
|
||||||
|
- 🔒 **Media Session API** : titre/artiste/pochette + contrôles depuis l'écran de verrouillage
|
||||||
|
- 📲 **PWA installable** : manifest, service worker (shell + pochettes en cache, streams audio exclus)
|
||||||
|
- 🖼️ Pochette, titre, artiste et album du morceau en cours
|
||||||
|
- 🔃 L'audio continue de jouer lors de la navigation entre pages
|
||||||
|
- ⚠️ Message d'erreur + bouton Réessayer si l'API est indisponible
|
||||||
|
- 💾 Préférences (volume, vitesse, mute) et progression persistées en localStorage (bornées à 200 entrées)
|
||||||
|
|
||||||
|
## Développement
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn install
|
||||||
|
yarn dev # serveur de dev
|
||||||
|
yarn build # build statique (adapter-static) dans build/
|
||||||
|
yarn preview # prévisualiser le build
|
||||||
|
yarn run check # types + accessibilité (svelte-check)
|
||||||
|
```
|
||||||
|
|
||||||
|
Note : la page `/podcast/` fetch son flux au build (pré-rendu côté serveur) —
|
||||||
|
un build hors-ligne la fait échouer ; c'est attendu.
|
||||||
|
|
||||||
## Utilitaires RSS (SSR)
|
## Utilitaires RSS (SSR)
|
||||||
|
|
||||||
La librairie inclut des utilitaires pour consommer des flux RSS de podcasts,
|
|
||||||
compatibles SSR (pas de `DOMParser`, parsing via `fast-xml-parser`) :
|
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// src/routes/podcast/+page.ts
|
// src/routes/podcast/+page.server.ts
|
||||||
import { fetch_podcast_feed } from '$lib/rss';
|
import { fetch_podcast_feed } from '$lib/rss';
|
||||||
import type { PageLoad } from './$types';
|
import type { PageServerLoad } from './$types';
|
||||||
|
|
||||||
export const load: PageLoad = async ({ fetch }) => ({
|
export const load: PageServerLoad = async ({ fetch }) => ({
|
||||||
// passer le `fetch` de SvelteKit : SSR, cache et hydration
|
// le fetch reste côté serveur : le XML n'est jamais inliné dans le HTML
|
||||||
|
// et la navigation client n'a pas de problème CORS
|
||||||
feed: await fetch_podcast_feed('https://feed.syntax.fm/rss', fetch),
|
feed: await fetch_podcast_feed('https://feed.syntax.fm/rss', fetch),
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
@@ -28,27 +63,7 @@ export const load: PageLoad = async ({ fetch }) => ({
|
|||||||
secondes, enclosure, pochette, numéros d'épisode/saison, explicit…)
|
secondes, enclosure, pochette, numéros d'épisode/saison, explicit…)
|
||||||
|
|
||||||
Les durées `itunes:duration` sont acceptées aux formats `3723`, `62:03` et `1:02:03`.
|
Les durées `itunes:duration` sont acceptées aux formats `3723`, `62:03` et `1:02:03`.
|
||||||
Les épisodes sans enclosure audio sont ignorés. Voir la route `/podcast` pour un
|
Les épisodes sans enclosure audio sont ignorés.
|
||||||
exemple complet (flux chargé côté serveur, épisodes joués dans le lecteur).
|
|
||||||
|
|
||||||
## Fonctionnalités
|
|
||||||
|
|
||||||
- 📻 **Mode radio aléatoire** : file de lecture infinie de titres piochés au hasard
|
|
||||||
- 🎵 **Écoute à la carte** : liste complète des titres avec recherche (titre, artiste, album)
|
|
||||||
- 🎙️ **Flux RSS de podcasts** : parsing SSR + lecture des épisodes (démo sur `/podcast`)
|
|
||||||
- ⏭️ Passage automatique au titre suivant en fin de piste
|
|
||||||
- 🎛️ Contrôles : lecture/pause, précédent/suivant, volume, mute, seek
|
|
||||||
- 🖼️ Pochette, titre, artiste et album du morceau en cours
|
|
||||||
- 🔃 L'audio continue de jouer lors de la navigation entre pages
|
|
||||||
|
|
||||||
## Développement
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yarn install
|
|
||||||
yarn dev # serveur de dev
|
|
||||||
yarn build # build statique (adapter-static) dans build/
|
|
||||||
yarn run check # svelte-check
|
|
||||||
```
|
|
||||||
|
|
||||||
## Comment ça marche
|
## Comment ça marche
|
||||||
|
|
||||||
@@ -60,14 +75,18 @@ L'API publique de Funkwhale est utilisée côté client, sans authentification :
|
|||||||
Structure du code :
|
Structure du code :
|
||||||
|
|
||||||
| Fichier | Rôle |
|
| Fichier | Rôle |
|
||||||
| ------------------------------- | ----------------------------------------------------------------- |
|
| --- | --- |
|
||||||
| `src/lib/` | Librairie svelte-podcast (store audio, composants headless) |
|
| `src/lib/` | Librairie svelte-podcast (store audio, composants headless) |
|
||||||
| `src/lib/rss.ts` | Utilitaires SSR pour flux RSS de podcasts (parse + fetch) |
|
| `src/lib/rss.ts` | Utilitaires SSR pour flux RSS de podcasts (parse + fetch) |
|
||||||
|
| `src/lib/volume-support.ts` | `can_set_volume()` — détection iOS par capacité |
|
||||||
|
| `src/lib/Seo.svelte`, `src/lib/site.js` | Meta SEO (canonical, OG, Twitter) + URL du site |
|
||||||
| `src/radio/funkwhale.ts` | Client de l'API Funkwhale |
|
| `src/radio/funkwhale.ts` | Client de l'API Funkwhale |
|
||||||
| `src/radio/playable.ts` | Type `PlayableTrack` (titre jouable, quelle que soit la source) |
|
| `src/radio/playable.ts` | Type `PlayableTrack` (titre jouable, quelle que soit la source) |
|
||||||
| `src/radio/radio-store.ts` | File de lecture : mode radio aléatoire / liste, suivant/précédent |
|
| `src/radio/radio-store.ts` | File de lecture : radio aléatoire / liste, erreurs, Media Session |
|
||||||
| `src/radio/library-store.ts` | Chargement progressif de la liste complète des titres |
|
| `src/radio/library-store.ts` | Chargement progressif de la liste complète des titres |
|
||||||
| `src/radio/radio-player.svelte` | Barre de lecture (pochette, contrôles, progression) |
|
| `src/radio/radio-player.svelte` | Barre de lecture (pochette, contrôles, progression) |
|
||||||
| `src/radio/track-list.svelte` | Liste des titres avec recherche |
|
| `src/radio/track-list.svelte` | Liste des titres avec recherche |
|
||||||
| `src/routes/+page.svelte` | Page radio Radyobòkaz |
|
| `src/routes/+page.svelte` | Page radio Radyobòkaz |
|
||||||
| `src/routes/podcast/` | Démo des utilitaires RSS (flux chargé en SSR, épisodes jouables) |
|
| `src/routes/podcast/` | Démo des utilitaires RSS (flux chargé en SSR, épisodes jouables) |
|
||||||
|
| `src/service-worker.js` | PWA : shell + pochettes en cache, streams audio exclus |
|
||||||
|
| `static/` | Icônes PWA, manifest.webmanifest, robots.txt |
|
||||||
|
|||||||
@@ -11,10 +11,6 @@
|
|||||||
"sync": "svelte-kit sync",
|
"sync": "svelte-kit sync",
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||||
"lint:prettier": "prettier --check --plugin-search-dir=. .",
|
|
||||||
"lint:eslint": "TIMING=1 eslint . --ext .ts,.tsx,.svelte --cache",
|
|
||||||
"lint": "yarn lint:prettier && yarn lint:eslint",
|
|
||||||
"format": "prettier --write --plugin-search-dir=. .",
|
|
||||||
"ts": "tsc --pretty --noImplicitAny --noEmit"
|
"ts": "tsc --pretty --noImplicitAny --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -31,18 +27,9 @@
|
|||||||
"@sveltejs/vite-plugin-svelte": "^5",
|
"@sveltejs/vite-plugin-svelte": "^5",
|
||||||
"@tailwindcss/forms": "^0.5.3",
|
"@tailwindcss/forms": "^0.5.3",
|
||||||
"@tailwindcss/typography": "^0.5.9",
|
"@tailwindcss/typography": "^0.5.9",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.61.0",
|
|
||||||
"@typescript-eslint/parser": "^5.61.0",
|
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
"eslint": "^8.44.0",
|
|
||||||
"eslint-config-prettier": "^8.8.0",
|
|
||||||
"eslint-plugin-svelte3": "^4.0.0",
|
|
||||||
"eslint-plugin-unused-imports": "^2.0.0",
|
|
||||||
"postcss": "^8.4.25",
|
"postcss": "^8.4.25",
|
||||||
"postcss-load-config": "^4.0.1",
|
"postcss-load-config": "^4.0.1",
|
||||||
"prettier": "^2.8.8",
|
|
||||||
"prettier-plugin-svelte": "^2.10.1",
|
|
||||||
"prettier-plugin-tailwindcss": "^0.3.0",
|
|
||||||
"svelte": "^5",
|
"svelte": "^5",
|
||||||
"svelte-check": "^4",
|
"svelte-check": "^4",
|
||||||
"tailwindcss": "^3.3.2",
|
"tailwindcss": "^3.3.2",
|
||||||
|
|||||||
Reference in New Issue
Block a user