Compare commits

...

2 Commits

Author SHA1 Message Date
sucupira 6262a02fec docs: README à jour (stack, fonctionnalités mobile/PWA, exemple RSS en +page.server.ts) 2026-07-21 19:35:15 -04:00
sucupira 220d6d8f2d chore: retire les fichiers et l'outillage hérités de la librairie
- Configs cassées sous Svelte 5 : eslint (eslint-plugin-svelte3), prettier
  (plugin v2), leurs scripts lint/format et dépendances associées
- Fichiers sans usage : .npmrc, .vscode/, .github/ (vide), CHANGELOG.md
  de la librairie, .eslintcache
- Player.svelte (composant headless) conservé : c'est le moteur de
  radio-player.svelte — check 0/0, build vert
2026-07-21 19:33:45 -04:00
11 changed files with 70 additions and 1054 deletions
-20
View File
@@ -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
-43
View File
@@ -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
View File
@@ -1 +0,0 @@
engine-strict=true
-14
View File
@@ -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
View File
@@ -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" }
}
]
}
-10
View File
@@ -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"
]
}
-45
View File
@@ -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
View File
@@ -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
+65 -46
View File
@@ -4,42 +4,31 @@ 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).
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).
## Utilitaires RSS (SSR)
## Stack
La librairie inclut des utilitaires pour consommer des flux RSS de podcasts,
compatibles SSR (pas de `DOMParser`, parsing via `fast-xml-parser`) :
```ts
// src/routes/podcast/+page.ts
import { fetch_podcast_feed } from '$lib/rss';
import type { PageLoad } from './$types';
export const load: PageLoad = async ({ fetch }) => ({
// passer le `fetch` de SvelteKit : SSR, cache et hydration
feed: await fetch_podcast_feed('https://feed.syntax.fm/rss', fetch),
});
```
- `fetch_podcast_feed(url, fetcher?)` — télécharge et parse un flux (RSS 2.0 + tags iTunes)
- `parse_podcast_feed(xml)` — parse une chaîne XML déjà récupérée
- Types exportés : `PodcastFeed`, `PodcastEpisode` (titre, guid, date ISO, durée en
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 épisodes sans enclosure audio sont ignorés. Voir la route `/podcast` pour un
exemple complet (flux chargé côté serveur, épisodes joués dans le lecteur).
- **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 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
- 📻 **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
@@ -47,27 +36,57 @@ exemple complet (flux chargé côté serveur, épisodes joués dans le lecteur).
yarn install
yarn dev # serveur de dev
yarn build # build statique (adapter-static) dans build/
yarn run check # svelte-check
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)
```ts
// src/routes/podcast/+page.server.ts
import { fetch_podcast_feed } from '$lib/rss';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ fetch }) => ({
// 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),
});
```
- `fetch_podcast_feed(url, fetcher?)` — télécharge et parse un flux (RSS 2.0 + tags iTunes)
- `parse_podcast_feed(xml)` — parse une chaîne XML déjà récupérée
- Types exportés : `PodcastFeed`, `PodcastEpisode` (titre, guid, date ISO, durée en
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 épisodes sans enclosure audio sont ignorés.
## Comment ça marche
L'API publique de Funkwhale est utilisée côté client, sans authentification :
- `GET /api/v1/radios/radios/7/tracks/?page=N&page_size=50` — liste des titres de la radio
- `GET /api/v1/listen/<uuid>/` — flux audio du titre (CORS ouvert, supporte `Range`)
- `GET /api/v1/radios/radios/7/tracks/?page=N&page_size=50` — liste des titres de la radio
- `GET /api/v1/listen/<uuid>/` — flux audio du titre (CORS ouvert, supporte `Range`)
Structure du code :
| Fichier | Rôle |
| ------------------------------- | ----------------------------------------------------------------- |
| `src/lib/` | Librairie svelte-podcast (store audio, composants headless) |
| `src/lib/rss.ts` | Utilitaires SSR pour flux RSS de podcasts (parse + fetch) |
| `src/radio/funkwhale.ts` | Client de l'API Funkwhale |
| `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/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/track-list.svelte` | Liste des titres avec recherche |
| `src/routes/+page.svelte` | Page radio Radyobòkaz |
| `src/routes/podcast/` | Démo des utilitaires RSS (flux chargé en SSR, épisodes jouables) |
| Fichier | Rôle |
| --- | --- |
| `src/lib/` | Librairie svelte-podcast (store audio, composants headless) |
| `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/playable.ts` | Type `PlayableTrack` (titre jouable, quelle que soit la source) |
| `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/radio-player.svelte` | Barre de lecture (pochette, contrôles, progression) |
| `src/radio/track-list.svelte` | Liste des titres avec recherche |
| `src/routes/+page.svelte` | Page radio Radyobòkaz |
| `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 |
-13
View File
@@ -11,10 +11,6 @@
"sync": "svelte-kit sync",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"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"
},
"dependencies": {
@@ -31,18 +27,9 @@
"@sveltejs/vite-plugin-svelte": "^5",
"@tailwindcss/forms": "^0.5.3",
"@tailwindcss/typography": "^0.5.9",
"@typescript-eslint/eslint-plugin": "^5.61.0",
"@typescript-eslint/parser": "^5.61.0",
"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-load-config": "^4.0.1",
"prettier": "^2.8.8",
"prettier-plugin-svelte": "^2.10.1",
"prettier-plugin-tailwindcss": "^0.3.0",
"svelte": "^5",
"svelte-check": "^4",
"tailwindcss": "^3.3.2",
+5 -735
View File
File diff suppressed because it is too large Load Diff