From ac9e3861e497afd6586b987cb86b66b16dcb39e1 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Wed, 30 Apr 2025 03:50:08 +0300 Subject: [PATCH 1/7] Update dependencies --- package-lock.json | 8 ++++---- package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index ebd0afcd5..6578f823a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "@freearhey/core": "^0.8.2", "@freearhey/search-js": "^0.1.2", "@sveltejs/adapter-static": "^3.0.8", - "@sveltejs/kit": "^2.20.4", + "@sveltejs/kit": "^2.20.7", "@tailwindcss/line-clamp": "^0.4.4", "@tailwindcss/vite": "^4.1.3", "@types/qs": "^6.9.18", @@ -947,9 +947,9 @@ } }, "node_modules/@sveltejs/kit": { - "version": "2.20.4", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.20.4.tgz", - "integrity": "sha512-B3Y1mb1Qjt57zXLVch5tfqsK/ebHe6uYTcFSnGFNwRpId3+fplLgQK6Z2zhDVBezSsPuhDq6Pry+9PA88ocN6Q==", + "version": "2.20.7", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.20.7.tgz", + "integrity": "sha512-dVbLMubpJJSLI4OYB+yWYNHGAhgc2bVevWuBjDj8jFUXIJOAnLwYP3vsmtcgoxNGUXoq0rHS5f7MFCsryb6nzg==", "dev": true, "dependencies": { "@types/cookie": "^0.6.0", diff --git a/package.json b/package.json index 932fd2bfd..f1a7be0d7 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "@freearhey/core": "^0.8.2", "@freearhey/search-js": "^0.1.2", "@sveltejs/adapter-static": "^3.0.8", - "@sveltejs/kit": "^2.20.4", + "@sveltejs/kit": "^2.20.7", "@tailwindcss/line-clamp": "^0.4.4", "@tailwindcss/vite": "^4.1.3", "@types/qs": "^6.9.18", diff --git a/yarn.lock b/yarn.lock index 8c1dff5d2..aab5df121 100644 --- a/yarn.lock +++ b/yarn.lock @@ -157,10 +157,10 @@ resolved "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.8.tgz" integrity sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg== -"@sveltejs/kit@^2.0.0", "@sveltejs/kit@^2.20.4": - version "2.20.4" - resolved "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.20.4.tgz" - integrity sha512-B3Y1mb1Qjt57zXLVch5tfqsK/ebHe6uYTcFSnGFNwRpId3+fplLgQK6Z2zhDVBezSsPuhDq6Pry+9PA88ocN6Q== +"@sveltejs/kit@^2.0.0", "@sveltejs/kit@^2.20.7": + version "2.20.7" + resolved "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.20.7.tgz" + integrity sha512-dVbLMubpJJSLI4OYB+yWYNHGAhgc2bVevWuBjDj8jFUXIJOAnLwYP3vsmtcgoxNGUXoq0rHS5f7MFCsryb6nzg== dependencies: "@types/cookie" "^0.6.0" cookie "^0.6.0" From 24e13c482ce6ec126bdecbc6dc3bbbf327f6a4ea Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Wed, 30 Apr 2025 03:50:28 +0300 Subject: [PATCH 2/7] Update tests/__data__ --- tests/__data__/input/feeds.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/__data__/input/feeds.json b/tests/__data__/input/feeds.json index f1f4049eb..ee63822ce 100644 --- a/tests/__data__/input/feeds.json +++ b/tests/__data__/input/feeds.json @@ -49,8 +49,8 @@ }, { "channel": "13MaxTelevision.ar", - "id": "SD", - "name": "SD", + "id": "Panregional", + "name": "Panregional", "is_main": true, "broadcast_area": [ "s/AR-W" From e411cec5450420f10473f4fc4d3f8d6da1598fa4 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Wed, 30 Apr 2025 03:50:36 +0300 Subject: [PATCH 3/7] Update tests --- tests/core/searchEngine.test.js | 340 ++++++++++++++++++++++++++++++ tests/store.test.js | 361 -------------------------------- 2 files changed, 340 insertions(+), 361 deletions(-) create mode 100644 tests/core/searchEngine.test.js delete mode 100644 tests/store.test.js diff --git a/tests/core/searchEngine.test.js b/tests/core/searchEngine.test.js new file mode 100644 index 000000000..17ce43612 --- /dev/null +++ b/tests/core/searchEngine.test.js @@ -0,0 +1,340 @@ +import { ApiClient, DataProcessor, DataLoader, SearchEngine } from '../../src/core' +import { expect, it, describe, beforeEach } from 'vitest' +import AxiosMockAdapter from 'axios-mock-adapter' +import axios from 'axios' +import path from 'path' +import fs from 'fs' + +const searchEngine = new SearchEngine() + +beforeEach(async () => { + const client = new ApiClient() + const processor = new DataProcessor() + const dataLoader = new DataLoader({ client, processor }) + + client.instance = axios.create({ + baseURL: 'https://iptv-org.github.io/api' + }) + + const mockAxios = new AxiosMockAdapter(client.instance) + + mockAxios.onGet(`categories.json`).reply(200, loadJson('categories.json')) + mockAxios.onGet(`countries.json`).reply(200, loadJson('countries.json')) + mockAxios.onGet(`languages.json`).reply(200, loadJson('languages.json')) + mockAxios.onGet(`blocklist.json`).reply(200, loadJson('blocklist.json')) + mockAxios.onGet(`timezones.json`).reply(200, loadJson('timezones.json')) + mockAxios.onGet(`channels.json`).reply(200, loadJson('channels.json')) + mockAxios.onGet(`regions.json`).reply(200, loadJson('regions.json')) + mockAxios.onGet(`streams.json`).reply(200, loadJson('streams.json')) + mockAxios.onGet(`guides.json`).reply(200, loadJson('guides.json')) + mockAxios.onGet(`feeds.json`).reply(200, loadJson('feeds.json')) + mockAxios.onGet(`subdivisions.json`).reply(200, loadJson('subdivisions.json')) + + const data = await dataLoader.load() + const searchableData = data.channels.map(channel => channel.getSearchable()) + + searchEngine.createIndex(searchableData) +}) + +describe('search', () => { + it('returns empty list if there is no such channel', () => { + let results = searchEngine.search('lorem') + + expect(results.count()).toBe(0) + }) + + it('can find channel by name', () => { + let results = searchEngine.search('name:002') + + expect(results.count()).toBe(1) + expect(results.first()).toMatchObject({ + id: '002RadioTV.do' + }) + }) + + it('can find channels by multiple words', () => { + let results = searchEngine.search('Xtrema Cartoons') + + expect(results.count()).toBe(2) + expect(results.first()).toMatchObject({ + id: 'XtremaCartoons.ar' + }) + expect(results.all()[1]).toMatchObject({ + id: 'XtremaRetroCartoons.ar' + }) + }) + + it('can search for one of two words', () => { + let results = searchEngine.search('Johannesburg,002') + + expect(results.count()).toBe(2) + expect(results.first()).toMatchObject({ + id: '002RadioTV.do' + }) + expect(results.all()[1]).toMatchObject({ + id: 'FashionTVJohannesburg.fr' + }) + }) + + it('can search for exact word matches', () => { + let results = searchEngine.search('"Xtrema Cartoons"') + + expect(results.count()).toBe(1) + expect(results.first()).toMatchObject({ + id: 'XtremaCartoons.ar' + }) + }) + + it('can find channels by id', () => { + let results = searchEngine.search('id:002RadioTV.do') + + expect(results.count()).toBe(1) + expect(results.first()).toMatchObject({ + id: '002RadioTV.do' + }) + }) + + it('can find channels by feed name', () => { + let results = searchEngine.search('Panregional') + + expect(results.count()).toBe(1) + expect(results.first()).toMatchObject({ + id: '13MaxTelevision.ar' + }) + }) + + it('can find channels by alternative names', () => { + let results = searchEngine.search('alt_names:التلفزيون') + + expect(results.count()).toBe(1) + expect(results.first()).toMatchObject({ + id: 'TV1.dz' + }) + }) + + it('can find channels by network', () => { + let results = searchEngine.search('network:Hope') + + expect(results.count()).toBe(1) + expect(results.first()).toMatchObject({ + id: 'K11UUD1.as' + }) + }) + + it('can find channels without the owner', () => { + let results = searchEngine.search('owners:^$') + + expect(results.count()).toBe(7) + expect(results.first()).toMatchObject({ + id: '002RadioTV.do' + }) + }) + + it('can find channels by country code', () => { + let results = searchEngine.search('country:DO') + + expect(results.count()).toBe(1) + expect(results.first()).toMatchObject({ + id: '002RadioTV.do' + }) + }) + + it('can find channels that are broadcast from the same region', () => { + let results = searchEngine.search('subdivision:AR-W') + + expect(results.count()).toBe(1) + expect(results.first()).toMatchObject({ + id: '13MaxTelevision.ar' + }) + }) + + it('can find channels that are broadcast from the same city', () => { + let results = searchEngine.search('city:Corrientes') + + expect(results.count()).toBe(1) + expect(results.first()).toMatchObject({ + id: '13MaxTelevision.ar' + }) + }) + + it('can find channels that have the same category', () => { + let results = searchEngine.search('categories:lifestyle') + + expect(results.count()).toBe(1) + expect(results.first()).toMatchObject({ + id: 'FashionTVJohannesburg.fr' + }) + }) + + it('can find channels with website', () => { + let results = searchEngine.search('website:.') + + expect(results.count()).toBe(14) + expect(results.first()).toMatchObject({ + id: '002RadioTV.do' + }) + }) + + it('can find channels marked as NSFW', () => { + let results = searchEngine.search('is_nsfw:true') + + expect(results.count()).toBe(1) + expect(results.first()).toMatchObject({ + id: 'Bizarre.al' + }) + }) + + it('can find closed channels', () => { + let results = searchEngine.search('is_closed:true') + + expect(results.count()).toBe(1) + expect(results.first()).toMatchObject({ + id: 'AynaTV.af' + }) + }) + + it('can find blocked channels', () => { + let results = searchEngine.search('is_blocked:true') + + expect(results.count()).toBe(1) + expect(results.first()).toMatchObject({ + id: 'Bizarre.al' + }) + }) + + it('can find channels by query in lower case', () => { + let results = searchEngine.search('tv2') + + expect(results.count()).toBe(2) + expect(results.first()).toMatchObject({ + id: 'SEN502.us' + }) + expect(results.all()[1]).toMatchObject({ + id: 'CFCNTV2.ca' + }) + }) + + it('can find channel by alternative name after another query', () => { + searchEngine.search('tv2') + let results = searchEngine.search('alt_names:tv2') + + expect(results.count()).toBe(1) + expect(results.first()).toMatchObject({ + id: 'SEN502.us' + }) + }) + + it('can find channels that have streams', () => { + let results = searchEngine.search('streams:>0') + + expect(results.count()).toBe(1) + expect(results.first()).toMatchObject({ + id: 'XtremaCartoons.ar' + }) + }) + + it('can find channels that have guides', () => { + let results = searchEngine.search('guides:>0') + + expect(results.count()).toBe(1) + expect(results.first()).toMatchObject({ + id: 'LaLiganaZap.ao' + }) + }) + + it('can find channel by country name', () => { + let results = searchEngine.search('"dominican republic"') + + expect(results.count()).toBe(3) + expect(results.first()).toMatchObject({ + id: '002RadioTV.do' + }) + }) + + it('can find channel by display name from the guides', () => { + let results = searchEngine.search('La Liga HD') + + expect(results.count()).toBe(1) + expect(results.first()).toMatchObject({ + id: 'LaLiganaZap.ao' + }) + }) + + it('can find channel by stream url', () => { + let results = searchEngine.search( + 'https://stmv6.voxtvhd.com.br/xtremacartoons/xtremacartoons/playlist.m3u8' + ) + + expect(results.count()).toBe(1) + expect(results.first()).toMatchObject({ + id: 'XtremaCartoons.ar' + }) + }) + + it('can find channels by broadcast area code', () => { + let results = searchEngine.search('broadcast_area:s/AR-W') + + expect(results.count()).toBe(1) + expect(results.first()).toMatchObject({ + id: '13MaxTelevision.ar' + }) + }) + + it('can find channel by broadcast location code', () => { + let results = searchEngine.search('eur') + + expect(results.count()).toBe(2) + expect(results.first()).toMatchObject({ + id: 'Bizarre.al' + }) + }) + + it('can find channel by broadcast location name', () => { + let results = searchEngine.search('europe') + + expect(results.count()).toBe(2) + expect(results.first()).toMatchObject({ + id: 'Bizarre.al' + }) + }) + + it('can find channels by exact language code', () => { + let results = searchEngine.search('language:fra') + + expect(results.count()).toBe(1) + expect(results.first()).toMatchObject({ + id: 'SEN502.us' + }) + }) + + it('can find channels by language name', () => { + let results = searchEngine.search('french') + + expect(results.count()).toBe(1) + expect(results.first()).toMatchObject({ + id: 'SEN502.us' + }) + }) + + it('can find channels by video format', () => { + let results = searchEngine.search('video_format:576i') + + expect(results.count()).toBe(1) + expect(results.first()).toMatchObject({ + id: 'Bizarre.al' + }) + }) + + it('can find channels by timezone id', () => { + let results = searchEngine.search('timezone:Europe/London') + + expect(results.count()).toBe(1) + expect(results.first()).toMatchObject({ + id: 'Bizarre.al' + }) + }) +}) + +function loadJson(filepath) { + return JSON.parse(fs.readFileSync(path.resolve('tests/__data__/input/', filepath), 'utf8')) +} diff --git a/tests/store.test.js b/tests/store.test.js deleted file mode 100644 index 7f83ff4aa..000000000 --- a/tests/store.test.js +++ /dev/null @@ -1,361 +0,0 @@ -import { loadData, search, searchResults } from '../src/store' -import { expect, it, describe, beforeEach, afterEach, vi } from 'vitest' -import { get } from 'svelte/store' -import path from 'path' -import fs from 'fs' -import AxiosMockAdapter from 'axios-mock-adapter' -import axios from 'axios' -import { ApiClient, DataProcessor } from '../src/core' - -beforeEach(async () => { - const client = new ApiClient() - const processor = new DataProcessor() - - client.instance = axios.create({ - baseURL: 'https://iptv-org.github.io/api' - }) - - const mockAxios = new AxiosMockAdapter(client.instance) - - mockAxios.onGet(`categories.json`).reply(200, loadJson('categories.json')) - mockAxios.onGet(`countries.json`).reply(200, loadJson('countries.json')) - mockAxios.onGet(`languages.json`).reply(200, loadJson('languages.json')) - mockAxios.onGet(`blocklist.json`).reply(200, loadJson('blocklist.json')) - mockAxios.onGet(`timezones.json`).reply(200, loadJson('timezones.json')) - mockAxios.onGet(`channels.json`).reply(200, loadJson('channels.json')) - mockAxios.onGet(`regions.json`).reply(200, loadJson('regions.json')) - mockAxios.onGet(`streams.json`).reply(200, loadJson('streams.json')) - mockAxios.onGet(`guides.json`).reply(200, loadJson('guides.json')) - mockAxios.onGet(`feeds.json`).reply(200, loadJson('feeds.json')) - mockAxios.onGet(`subdivisions.json`).reply(200, loadJson('subdivisions.json')) - - await loadData({ client, processor }) -}) - -describe('search', () => { - it('return all channels by default', () => { - const results = get(searchResults).all() - expect(results.length).toBe(15) - }) - - it('returns empty list if there is no such channel', () => { - search('lorem') - - const results = get(searchResults).all() - expect(results.length).toBe(0) - }) - - it('can find channel by name', () => { - search('name:002') - - const results = get(searchResults).all() - expect(results.length).toBe(1) - expect(results[0]).toMatchObject({ - id: '002RadioTV.do' - }) - }) - - it('can find channels by multiple words', () => { - search('Xtrema Cartoons') - - const results = get(searchResults).all() - expect(results.length).toBe(2) - expect(results[0]).toMatchObject({ - id: 'XtremaCartoons.ar' - }) - expect(results[1]).toMatchObject({ - id: 'XtremaRetroCartoons.ar' - }) - }) - - it('can search for one of two words', () => { - search('Johannesburg,002') - - const results = get(searchResults).all() - expect(results.length).toBe(2) - expect(results[0]).toMatchObject({ - id: '002RadioTV.do' - }) - expect(results[1]).toMatchObject({ - id: 'FashionTVJohannesburg.fr' - }) - }) - - it('can search for exact word matches', () => { - search('"Xtrema Cartoons"') - - const results = get(searchResults).all() - expect(results.length).toBe(1) - expect(results[0]).toMatchObject({ - id: 'XtremaCartoons.ar' - }) - }) - - it('can find channels by id', () => { - search('id:002RadioTV.do') - - const results = get(searchResults).all() - expect(results.length).toBe(1) - expect(results[0]).toMatchObject({ - id: '002RadioTV.do' - }) - }) - - it('can find channels by alternative names', () => { - search('alt_names:التلفزيون') - - const results = get(searchResults).all() - expect(results.length).toBe(1) - expect(results[0]).toMatchObject({ - id: 'TV1.dz' - }) - }) - - it('can find channels by network', () => { - search('network:Hope') - - const results = get(searchResults).all() - expect(results.length).toBe(1) - expect(results[0]).toMatchObject({ - id: 'K11UUD1.as' - }) - }) - - it('can find channels without the owner', () => { - search('owners:^$') - - const results = get(searchResults).all() - expect(results.length).toBe(7) - expect(results[0]).toMatchObject({ - id: '002RadioTV.do' - }) - }) - - it('can find channels by country code', () => { - search('country:DO') - - const results = get(searchResults).all() - expect(results.length).toBe(1) - expect(results[0]).toMatchObject({ - id: '002RadioTV.do' - }) - }) - - it('can find channels that are broadcast from the same region', () => { - search('subdivision:AR-W') - - const results = get(searchResults).all() - expect(results.length).toBe(1) - expect(results[0]).toMatchObject({ - id: '13MaxTelevision.ar' - }) - }) - - it('can find channels that are broadcast from the same city', () => { - search('city:Corrientes') - - const results = get(searchResults).all() - expect(results.length).toBe(1) - expect(results[0]).toMatchObject({ - id: '13MaxTelevision.ar' - }) - }) - - it('can find channels that have the same category', () => { - search('categories:lifestyle') - - const results = get(searchResults).all() - expect(results.length).toBe(1) - expect(results[0]).toMatchObject({ - id: 'FashionTVJohannesburg.fr' - }) - }) - - it('can find channels with website', () => { - search('website:.') - - const results = get(searchResults).all() - expect(results.length).toBe(14) - expect(results[0]).toMatchObject({ - id: '002RadioTV.do' - }) - }) - - it('can find channels marked as NSFW', () => { - search('is_nsfw:true') - - const results = get(searchResults).all() - expect(results.length).toBe(1) - expect(results[0]).toMatchObject({ - id: 'Bizarre.al' - }) - }) - - it('can find closed channels', () => { - search('is_closed:true') - - const results = get(searchResults).all() - expect(results.length).toBe(1) - expect(results[0]).toMatchObject({ - id: 'AynaTV.af' - }) - }) - - it('can find blocked channels', () => { - search('is_blocked:true') - - const results = get(searchResults).all() - expect(results.length).toBe(1) - expect(results[0]).toMatchObject({ - id: 'Bizarre.al' - }) - }) - - it('can find channels by query in lower case', () => { - search('tv2') - - const results = get(searchResults).all() - expect(results.length).toBe(2) - expect(results[0]).toMatchObject({ - id: 'SEN502.us' - }) - expect(results[1]).toMatchObject({ - id: 'CFCNTV2.ca' - }) - }) - - it('can find channel by alternative name after another query', () => { - search('tv2') - search('alt_names:tv2') - - const results = get(searchResults).all() - expect(results.length).toBe(1) - expect(results[0]).toMatchObject({ - id: 'SEN502.us' - }) - }) - - it('can find channels that have streams', () => { - search('streams:>0') - - const results = get(searchResults).all() - expect(results.length).toBe(1) - expect(results[0]).toMatchObject({ - id: 'XtremaCartoons.ar' - }) - }) - - it('can find channels that have guides', () => { - search('guides:>0') - - const results = get(searchResults).all() - expect(results.length).toBe(1) - expect(results[0]).toMatchObject({ - id: 'LaLiganaZap.ao' - }) - }) - - it('can find channel by country name', () => { - search('"dominican republic"') - - const results = get(searchResults).all() - expect(results.length).toBe(3) - expect(results[0]).toMatchObject({ - id: '002RadioTV.do' - }) - }) - - it('can find channel by display name from the guides', () => { - search('La Liga HD') - - const results = get(searchResults).all() - expect(results.length).toBe(1) - expect(results[0]).toMatchObject({ - id: 'LaLiganaZap.ao' - }) - }) - - it('can find channel by stream url', () => { - search('https://stmv6.voxtvhd.com.br/xtremacartoons/xtremacartoons/playlist.m3u8') - - const results = get(searchResults).all() - expect(results.length).toBe(1) - expect(results[0]).toMatchObject({ - id: 'XtremaCartoons.ar' - }) - }) - - it('can find channels by broadcast area code', () => { - search('broadcast_area:s/AR-W') - - const results = get(searchResults).all() - expect(results.length).toBe(1) - expect(results[0]).toMatchObject({ - id: '13MaxTelevision.ar' - }) - }) - - it('can find channel by broadcast location code', () => { - search('eur') - - const results = get(searchResults).all() - expect(results.length).toBe(2) - expect(results[0]).toMatchObject({ - id: 'Bizarre.al' - }) - }) - - it('can find channel by broadcast location name', () => { - search('europe') - - const results = get(searchResults).all() - expect(results.length).toBe(2) - expect(results[0]).toMatchObject({ - id: 'Bizarre.al' - }) - }) - - it('can find channels by exact language code', () => { - search('language:fra') - - const results = get(searchResults).all() - expect(results.length).toBe(1) - expect(results[0]).toMatchObject({ - id: 'SEN502.us' - }) - }) - - it('can find channels by language name', () => { - search('french') - - const results = get(searchResults).all() - expect(results.length).toBe(1) - expect(results[0]).toMatchObject({ - id: 'SEN502.us' - }) - }) - - it('can find channels by video format', () => { - search('video_format:576i') - - const results = get(searchResults).all() - expect(results.length).toBe(1) - expect(results[0]).toMatchObject({ - id: 'Bizarre.al' - }) - }) - - it('can find channels by timezone id', () => { - search('timezone:Europe/London') - - const results = get(searchResults).all() - expect(results.length).toBe(1) - expect(results[0]).toMatchObject({ - id: 'Bizarre.al' - }) - }) -}) - -function loadJson(filepath) { - return JSON.parse(fs.readFileSync(path.resolve('tests/__data__/input/', filepath), 'utf8')) -} From 2a893a827bd7963acb97a727ca7dbdafdad740c8 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Wed, 30 Apr 2025 04:06:54 +0300 Subject: [PATCH 4/7] Update src/ --- src/components/BottomBar.svelte | 8 +- src/components/Button.svelte | 4 +- src/components/ChannelMenu.svelte | 23 ++++ src/components/ChannelPopup.svelte | 24 +--- src/components/DownloadButton.svelte | 4 +- src/components/FeedAddButton.svelte | 6 +- src/components/FeedAddIconButton.svelte | 28 ++++ src/components/FeedItem.svelte | 30 +--- src/components/FeedMenu.svelte | 30 ++++ src/components/FeedPopup.svelte | 11 +- src/components/GuidesPopup.svelte | 6 +- src/components/HTMLPreview.svelte | 128 +++++++++--------- src/components/IconButton.svelte | 8 +- src/components/Menu.svelte | 4 +- src/components/ResetButton.svelte | 4 +- src/components/SelectAllButton.svelte | 6 +- src/components/StreamAddButton.svelte | 29 ++++ src/components/StreamAddIconButton.svelte | 28 ++++ src/components/StreamEditButton.svelte | 29 ++++ src/components/StreamItem.svelte | 16 +-- src/components/StreamMenu.svelte | 23 ++++ src/components/StreamReportButton.svelte | 29 ++++ src/components/StreamsPopup.svelte | 11 +- src/components/index.ts | 8 ++ src/icons/AddCircle.svelte | 19 +++ src/icons/Alert.svelte | 16 +++ src/icons/index.ts | 2 + src/models/channel.ts | 7 +- src/models/feed.ts | 4 + src/models/stream.ts | 48 +++---- .../channels/[country]/[name]/+page.svelte | 6 +- src/types/channel.d.ts | 2 + src/types/htmlPreviewField.ts | 1 + 33 files changed, 416 insertions(+), 186 deletions(-) create mode 100644 src/components/ChannelMenu.svelte create mode 100644 src/components/FeedAddIconButton.svelte create mode 100644 src/components/FeedMenu.svelte create mode 100644 src/components/StreamAddButton.svelte create mode 100644 src/components/StreamAddIconButton.svelte create mode 100644 src/components/StreamEditButton.svelte create mode 100644 src/components/StreamMenu.svelte create mode 100644 src/components/StreamReportButton.svelte create mode 100644 src/icons/AddCircle.svelte create mode 100644 src/icons/Alert.svelte diff --git a/src/components/BottomBar.svelte b/src/components/BottomBar.svelte index 5d951b926..52c38d8c4 100644 --- a/src/components/BottomBar.svelte +++ b/src/components/BottomBar.svelte @@ -13,14 +13,14 @@ {$selected.count()} selected
- - - + + + { downloadMode.set(false) }} - variant="light" + variant="dark" />
diff --git a/src/components/Button.svelte b/src/components/Button.svelte index e892e7e9e..c75adceb9 100644 --- a/src/components/Button.svelte +++ b/src/components/Button.svelte @@ -9,11 +9,11 @@ class="w-full text-left rounded-md text-sm h-10 flex items-center text-gray-500 dark:text-gray-400 font-normal hover:bg-gray-100 dark:hover:bg-primary-750 space-x-3 px-2 border border-transparent cursor-pointer" {...$$restProps} > -
+
{label}
-
+
diff --git a/src/components/ChannelMenu.svelte b/src/components/ChannelMenu.svelte new file mode 100644 index 000000000..a2b0b6a77 --- /dev/null +++ b/src/components/ChannelMenu.svelte @@ -0,0 +1,23 @@ + + + + + + + diff --git a/src/components/ChannelPopup.svelte b/src/components/ChannelPopup.svelte index 329c59fad..adc934a33 100644 --- a/src/components/ChannelPopup.svelte +++ b/src/components/ChannelPopup.svelte @@ -1,20 +1,16 @@ @@ -58,11 +44,7 @@ {#if isTouchDevice} {/if} - - - - - +
diff --git a/src/components/DownloadButton.svelte b/src/components/DownloadButton.svelte index a142bc9d4..adde39fc2 100644 --- a/src/components/DownloadButton.svelte +++ b/src/components/DownloadButton.svelte @@ -6,6 +6,8 @@ import { selected } from '~/store' import * as Icon from '~/icons' + export let variant = 'default' + const playlistCreator = new PlaylistCreator() function onClick() { @@ -52,7 +54,7 @@ disabled={!$selected.count()} aria-label="Download Playlist" title="Download Playlist" - variant="light" + {variant} > diff --git a/src/components/FeedAddButton.svelte b/src/components/FeedAddButton.svelte index 933e59abb..4926e340c 100644 --- a/src/components/FeedAddButton.svelte +++ b/src/components/FeedAddButton.svelte @@ -11,7 +11,7 @@ const params = qs.stringify({ labels: 'feeds:add', template: '4_feeds_add.yml', - title: 'Add: ', + title: `Add: ${channel.name} Feed`, channel_id: channel.id }) @@ -23,7 +23,7 @@ } - diff --git a/src/components/FeedAddIconButton.svelte b/src/components/FeedAddIconButton.svelte new file mode 100644 index 000000000..e202a3d6d --- /dev/null +++ b/src/components/FeedAddIconButton.svelte @@ -0,0 +1,28 @@ + + + + + diff --git a/src/components/FeedItem.svelte b/src/components/FeedItem.svelte index 711234085..ba2370182 100644 --- a/src/components/FeedItem.svelte +++ b/src/components/FeedItem.svelte @@ -1,20 +1,16 @@
@@ -86,23 +72,19 @@ {/if}
- - - - - +
{#if isExpanded} -
+
{/if} diff --git a/src/components/FeedMenu.svelte b/src/components/FeedMenu.svelte new file mode 100644 index 000000000..346ff9136 --- /dev/null +++ b/src/components/FeedMenu.svelte @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/src/components/FeedPopup.svelte b/src/components/FeedPopup.svelte index 50ff01654..0e7072d84 100644 --- a/src/components/FeedPopup.svelte +++ b/src/components/FeedPopup.svelte @@ -1,5 +1,5 @@ @@ -37,9 +32,7 @@ {channel.getDisplayName()}
- - - +
diff --git a/src/components/GuidesPopup.svelte b/src/components/GuidesPopup.svelte index 14a756452..2ef2ab956 100644 --- a/src/components/GuidesPopup.svelte +++ b/src/components/GuidesPopup.svelte @@ -1,12 +1,12 @@ @@ -29,7 +29,7 @@
- {#each guides.all() as guide} + {#each feed.getGuides().all() as guide} {/each}
diff --git a/src/components/HTMLPreview.svelte b/src/components/HTMLPreview.svelte index 1e717cf53..d1fb2a256 100644 --- a/src/components/HTMLPreview.svelte +++ b/src/components/HTMLPreview.svelte @@ -8,74 +8,76 @@ {#each fieldset as field} - - - + + - + + {:else if field.type === 'link[]'} +
+ {#each field.value as value, i} + {#if i > 0} + {/if} + + {value.label} + + {/each} +
+ {:else if field.type === 'external_link'} +
+ {field.value.label} +
+ {:else if field.name === 'id'} + {field.value} + {:else if field.type === 'string[]'} +
+ {#each field.value as value, i} + {#if i > 0} + {/if} + {value} + {/each} +
+ {:else if field.type === 'string'} + {field.value} + {/if} + + + + {/if} {/each}
-
- {field.name} -
-
-
- {#if field.type === 'image'} - {field.value.alt} - {:else if field.type === 'link'} - - {:else if field.type === 'link[]'} -
- {#each field.value as value, i} - {#if i > 0} - {/if} + {#if field} +
+
+ {field.name} +
+
+
+ {#if field.type === 'image'} + {field.value.alt} + {:else if field.type === 'link'} + - {:else if field.type === 'external_link'} - - {:else if field.name === 'id'} - {field.value} - {:else if field.type === 'string[]'} -
- {#each field.value as value, i} - {#if i > 0} - {/if} - {value} - {/each} -
- {:else if field.type === 'string'} - {field.value} - {/if} -
-
diff --git a/src/components/IconButton.svelte b/src/components/IconButton.svelte index 721a40c14..26dc8e6b4 100644 --- a/src/components/IconButton.svelte +++ b/src/components/IconButton.svelte @@ -3,9 +3,11 @@ export let variant = 'default' export let size = 40 - let className = 'rounded-lg text-sm flex items-center justify-center cursor-pointer shrink-0' - if (variant === 'light') className += ' hover:bg-primary-810 text-gray-300' - else className += ' hover:bg-gray-100 dark:hover:bg-primary-750 text-gray-400' + let className = + 'rounded-lg text-sm flex items-center justify-center cursor-pointer shrink-0 text-gray-400' + if (variant === 'dark') className += ' hover:bg-primary-750' + else if (variant === 'light') className += ' hover:bg-gray-100' + else className += ' hover:bg-gray-100 dark:hover:bg-primary-750' diff --git a/src/components/StreamAddIconButton.svelte b/src/components/StreamAddIconButton.svelte new file mode 100644 index 000000000..ee67a123d --- /dev/null +++ b/src/components/StreamAddIconButton.svelte @@ -0,0 +1,28 @@ + + + + + diff --git a/src/components/StreamEditButton.svelte b/src/components/StreamEditButton.svelte new file mode 100644 index 000000000..92101678e --- /dev/null +++ b/src/components/StreamEditButton.svelte @@ -0,0 +1,29 @@ + + + diff --git a/src/components/StreamItem.svelte b/src/components/StreamItem.svelte index 0e95b83b8..fe27ba82b 100644 --- a/src/components/StreamItem.svelte +++ b/src/components/StreamItem.svelte @@ -1,5 +1,5 @@ -
+
@@ -33,14 +31,14 @@
-
- +
+
{#if isExpanded} -
- +
+
{/if}
diff --git a/src/components/StreamMenu.svelte b/src/components/StreamMenu.svelte new file mode 100644 index 000000000..56b636bb3 --- /dev/null +++ b/src/components/StreamMenu.svelte @@ -0,0 +1,23 @@ + + + + + + + diff --git a/src/components/StreamReportButton.svelte b/src/components/StreamReportButton.svelte new file mode 100644 index 000000000..d4f0375da --- /dev/null +++ b/src/components/StreamReportButton.svelte @@ -0,0 +1,29 @@ + + + diff --git a/src/components/StreamsPopup.svelte b/src/components/StreamsPopup.svelte index 429ba75c5..56c4dc718 100644 --- a/src/components/StreamsPopup.svelte +++ b/src/components/StreamsPopup.svelte @@ -1,11 +1,11 @@