mirror of
https://github.com/leinelissen/jellyfin-audio-player.git
synced 2026-09-03 05:10:01 +03:00
chore: fix indent
This commit is contained in:
+1
-15
@@ -1,8 +1,3 @@
|
||||
# EditorConfig is awesome: https://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
end_of_line = lf
|
||||
@@ -13,13 +8,4 @@ trim_trailing_whitespace = true
|
||||
# TypeScript, JavaScript, JSON, and JSX files
|
||||
[*.{ts,tsx,js,jsx,json}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# YAML files
|
||||
[*.{yml,yaml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# Markdown files
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
indent_size = 4
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"tabWidth": 4,
|
||||
"useTabs": false,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 100,
|
||||
"arrowParens": "always"
|
||||
}
|
||||
@@ -26,7 +26,7 @@ const ReText = (props: TextProps) => {
|
||||
return {
|
||||
text: text.value,
|
||||
// Here we use any because the text prop is not available in the type
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
} as any;
|
||||
});
|
||||
|
||||
|
||||
@@ -180,38 +180,38 @@ function NowPlaying({ offset = 0, inset }: { offset?: number, inset?: boolean })
|
||||
borderRadius: 100,
|
||||
}}
|
||||
>
|
||||
<InnerContainer onPress={openNowPlayingModal} activeOpacity={0.5} testID="open-player-modal">
|
||||
<ShadowWrapper size="small">
|
||||
<Cover source={{ uri: (track.artwork || '') as string }} style={defaultStyles.imageBackground} />
|
||||
</ShadowWrapper>
|
||||
<TrackNameContainer>
|
||||
<Text numberOfLines={1}>{track.title}</Text>
|
||||
{(track.artist || track.album) && (
|
||||
<Text style={{ opacity: 0.5 }} numberOfLines={1}>
|
||||
{track.artist}{track.album ? ` — ${track.album}` : ''}
|
||||
</Text>
|
||||
)}
|
||||
</TrackNameContainer>
|
||||
<ActionButton>
|
||||
<SelectActionButton />
|
||||
</ActionButton>
|
||||
<ProgressTrack
|
||||
style={[
|
||||
{ transform: [{ translateX: bufferAnimation.current }]},
|
||||
defaultStyles.themeBackground,
|
||||
]}
|
||||
opacity={0.15}
|
||||
stroke={4}
|
||||
/>
|
||||
<ProgressTrack
|
||||
style={[
|
||||
{ transform: [{ translateX: progressAnimation.current }]},
|
||||
defaultStyles.themeBackground,
|
||||
]}
|
||||
stroke={4}
|
||||
/>
|
||||
</InnerContainer>
|
||||
</LiquidGlassView>
|
||||
<InnerContainer onPress={openNowPlayingModal} activeOpacity={0.5} testID="open-player-modal">
|
||||
<ShadowWrapper size="small">
|
||||
<Cover source={{ uri: (track.artwork || '') as string }} style={defaultStyles.imageBackground} />
|
||||
</ShadowWrapper>
|
||||
<TrackNameContainer>
|
||||
<Text numberOfLines={1}>{track.title}</Text>
|
||||
{(track.artist || track.album) && (
|
||||
<Text style={{ opacity: 0.5 }} numberOfLines={1}>
|
||||
{track.artist}{track.album ? ` — ${track.album}` : ''}
|
||||
</Text>
|
||||
)}
|
||||
</TrackNameContainer>
|
||||
<ActionButton>
|
||||
<SelectActionButton />
|
||||
</ActionButton>
|
||||
<ProgressTrack
|
||||
style={[
|
||||
{ transform: [{ translateX: bufferAnimation.current }]},
|
||||
defaultStyles.themeBackground,
|
||||
]}
|
||||
opacity={0.15}
|
||||
stroke={4}
|
||||
/>
|
||||
<ProgressTrack
|
||||
style={[
|
||||
{ transform: [{ translateX: progressAnimation.current }]},
|
||||
defaultStyles.themeBackground,
|
||||
]}
|
||||
stroke={4}
|
||||
/>
|
||||
</InnerContainer>
|
||||
</LiquidGlassView>
|
||||
</Animated.View>
|
||||
</Container>
|
||||
);
|
||||
|
||||
@@ -30,21 +30,21 @@ export function createRecentAlbumsTemplate(): ListTemplate {
|
||||
|
||||
console.log('[AlbumsList] Recent albums count:', albums.length);
|
||||
|
||||
const items = albums.map(album => ({
|
||||
type: 'default' as const,
|
||||
title: { text: album.Name },
|
||||
detailedText: { text: album.AlbumArtist || t('unknown-artist') },
|
||||
onPress: async () => {
|
||||
console.log('[AlbumsList] Album selected:', album.Name);
|
||||
try {
|
||||
const detailTemplate = await createAlbumDetailTemplate(album);
|
||||
await detailTemplate.push();
|
||||
console.log('[AlbumsList] Album detail pushed');
|
||||
} catch (error) {
|
||||
console.error('[AlbumsList] Error pushing album detail:', error);
|
||||
}
|
||||
},
|
||||
}));
|
||||
const items = albums.map(album => ({
|
||||
type: 'default' as const,
|
||||
title: { text: album.Name },
|
||||
detailedText: { text: album.AlbumArtist || t('unknown-artist') },
|
||||
onPress: async () => {
|
||||
console.log('[AlbumsList] Album selected:', album.Name);
|
||||
try {
|
||||
const detailTemplate = await createAlbumDetailTemplate(album);
|
||||
await detailTemplate.push();
|
||||
console.log('[AlbumsList] Album detail pushed');
|
||||
} catch (error) {
|
||||
console.error('[AlbumsList] Error pushing album detail:', error);
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
return new ListTemplate({
|
||||
title: { text: t('recent-albums') },
|
||||
|
||||
+25
-25
@@ -21,26 +21,26 @@ import { syncCursors } from './schema/sync-cursors';
|
||||
|
||||
// Combined schema for drizzle
|
||||
const schema = {
|
||||
sources,
|
||||
appSettings,
|
||||
sleepTimer,
|
||||
artists,
|
||||
albums,
|
||||
tracks,
|
||||
playlists,
|
||||
downloads,
|
||||
searchQueries,
|
||||
albumArtists,
|
||||
trackArtists,
|
||||
playlistTracks,
|
||||
albumSimilar,
|
||||
syncCursors,
|
||||
sources,
|
||||
appSettings,
|
||||
sleepTimer,
|
||||
artists,
|
||||
albums,
|
||||
tracks,
|
||||
playlists,
|
||||
downloads,
|
||||
searchQueries,
|
||||
albumArtists,
|
||||
trackArtists,
|
||||
playlistTracks,
|
||||
albumSimilar,
|
||||
syncCursors,
|
||||
};
|
||||
|
||||
// Open the SQLite database
|
||||
const sqliteDb = open({
|
||||
name: 'fintunes.db',
|
||||
location: '../databases',
|
||||
name: 'fintunes.db',
|
||||
location: '../databases',
|
||||
});
|
||||
|
||||
// Create drizzle instance with schema - exported as singleton
|
||||
@@ -51,19 +51,19 @@ export const db = drizzle(sqliteDb, { schema });
|
||||
* Migrations should be generated using drizzle-kit
|
||||
*/
|
||||
export async function runMigrations() {
|
||||
try {
|
||||
await migrate(db, migrations);
|
||||
console.log('Database migrations completed');
|
||||
} catch (error) {
|
||||
console.error('Migration error:', error);
|
||||
throw error;
|
||||
}
|
||||
try {
|
||||
await migrate(db, migrations);
|
||||
console.log('Database migrations completed');
|
||||
} catch (error) {
|
||||
console.error('Migration error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the database
|
||||
*/
|
||||
export async function initializeDatabase() {
|
||||
await runMigrations();
|
||||
return db;
|
||||
await runMigrations();
|
||||
return db;
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
import journal from './meta/_journal.json';
|
||||
import m0000 from './0000_cuddly_captain_cross.sql';
|
||||
|
||||
export default {
|
||||
export default {
|
||||
journal,
|
||||
migrations: {
|
||||
m0000
|
||||
m0000
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@ import { sources } from './sources';
|
||||
* Album-Artists relation table (many-to-many)
|
||||
*/
|
||||
export const albumArtists = sqliteTable('album_artists', {
|
||||
sourceId: text('source_id').notNull().references(() => sources.id, { onDelete: 'cascade' }),
|
||||
albumId: text('album_id').notNull(),
|
||||
artistId: text('artist_id').notNull(),
|
||||
orderIndex: integer('order_index'),
|
||||
sourceId: text('source_id').notNull().references(() => sources.id, { onDelete: 'cascade' }),
|
||||
albumId: text('album_id').notNull(),
|
||||
artistId: text('artist_id').notNull(),
|
||||
orderIndex: integer('order_index'),
|
||||
}, (table) => ({
|
||||
pk: primaryKey({ columns: [table.sourceId, table.albumId, table.artistId] }),
|
||||
sourceArtistIdx: index('album_artists_source_artist_idx').on(table.sourceId, table.artistId),
|
||||
pk: primaryKey({ columns: [table.sourceId, table.albumId, table.artistId] }),
|
||||
sourceArtistIdx: index('album_artists_source_artist_idx').on(table.sourceId, table.artistId),
|
||||
}));
|
||||
|
||||
@@ -5,9 +5,9 @@ import { sources } from './sources';
|
||||
* Album-Similar relation table (for similar albums)
|
||||
*/
|
||||
export const albumSimilar = sqliteTable('album_similar', {
|
||||
sourceId: text('source_id').notNull().references(() => sources.id, { onDelete: 'cascade' }),
|
||||
albumId: text('album_id').notNull(),
|
||||
similarAlbumId: text('similar_album_id').notNull(),
|
||||
sourceId: text('source_id').notNull().references(() => sources.id, { onDelete: 'cascade' }),
|
||||
albumId: text('album_id').notNull(),
|
||||
similarAlbumId: text('similar_album_id').notNull(),
|
||||
}, (table) => ({
|
||||
pk: primaryKey({ columns: [table.sourceId, table.albumId, table.similarAlbumId] }),
|
||||
pk: primaryKey({ columns: [table.sourceId, table.albumId, table.similarAlbumId] }),
|
||||
}));
|
||||
|
||||
@@ -5,18 +5,18 @@ import { sources } from './sources';
|
||||
* Albums table
|
||||
*/
|
||||
export const albums = sqliteTable('albums', {
|
||||
sourceId: text('source_id').notNull().references(() => sources.id, { onDelete: 'cascade' }),
|
||||
id: text('id').primaryKey(),
|
||||
name: text('name').notNull(),
|
||||
productionYear: integer('production_year'),
|
||||
isFolder: integer('is_folder', { mode: 'boolean' }).notNull(),
|
||||
albumArtist: text('album_artist'),
|
||||
dateCreated: integer('date_created'),
|
||||
lastRefreshed: integer('last_refreshed'),
|
||||
metadataJson: text('metadata_json'), // JSON-encoded additional fields
|
||||
createdAt: integer('created_at').notNull(),
|
||||
updatedAt: integer('updated_at').notNull(),
|
||||
sourceId: text('source_id').notNull().references(() => sources.id, { onDelete: 'cascade' }),
|
||||
id: text('id').primaryKey(),
|
||||
name: text('name').notNull(),
|
||||
productionYear: integer('production_year'),
|
||||
isFolder: integer('is_folder', { mode: 'boolean' }).notNull(),
|
||||
albumArtist: text('album_artist'),
|
||||
dateCreated: integer('date_created'),
|
||||
lastRefreshed: integer('last_refreshed'),
|
||||
metadataJson: text('metadata_json'), // JSON-encoded additional fields
|
||||
createdAt: integer('created_at').notNull(),
|
||||
updatedAt: integer('updated_at').notNull(),
|
||||
}, (table) => ({
|
||||
sourceNameIdx: index('albums_source_name_idx').on(table.sourceId, table.name),
|
||||
sourceYearIdx: index('albums_source_year_idx').on(table.sourceId, table.productionYear),
|
||||
sourceNameIdx: index('albums_source_name_idx').on(table.sourceId, table.name),
|
||||
sourceYearIdx: index('albums_source_year_idx').on(table.sourceId, table.productionYear),
|
||||
}));
|
||||
|
||||
@@ -4,12 +4,12 @@ import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
|
||||
* App settings - global application settings (single row, id=1)
|
||||
*/
|
||||
export const appSettings = sqliteTable('app_settings', {
|
||||
id: integer('id').primaryKey().$default(() => 1),
|
||||
bitrate: integer('bitrate').notNull(),
|
||||
isOnboardingComplete: integer('is_onboarding_complete', { mode: 'boolean' }).notNull(),
|
||||
hasReceivedErrorReportingAlert: integer('has_received_error_reporting_alert', { mode: 'boolean' }).notNull(),
|
||||
enablePlaybackReporting: integer('enable_playback_reporting', { mode: 'boolean' }).notNull(),
|
||||
colorScheme: text('color_scheme').notNull(),
|
||||
createdAt: integer('created_at').notNull(),
|
||||
updatedAt: integer('updated_at').notNull(),
|
||||
id: integer('id').primaryKey().$default(() => 1),
|
||||
bitrate: integer('bitrate').notNull(),
|
||||
isOnboardingComplete: integer('is_onboarding_complete', { mode: 'boolean' }).notNull(),
|
||||
hasReceivedErrorReportingAlert: integer('has_received_error_reporting_alert', { mode: 'boolean' }).notNull(),
|
||||
enablePlaybackReporting: integer('enable_playback_reporting', { mode: 'boolean' }).notNull(),
|
||||
colorScheme: text('color_scheme').notNull(),
|
||||
createdAt: integer('created_at').notNull(),
|
||||
updatedAt: integer('updated_at').notNull(),
|
||||
});
|
||||
|
||||
@@ -5,13 +5,13 @@ import { sources } from './sources';
|
||||
* Artists table
|
||||
*/
|
||||
export const artists = sqliteTable('artists', {
|
||||
sourceId: text('source_id').notNull().references(() => sources.id, { onDelete: 'cascade' }),
|
||||
id: text('id').primaryKey(),
|
||||
name: text('name').notNull(),
|
||||
isFolder: integer('is_folder', { mode: 'boolean' }).notNull(),
|
||||
metadataJson: text('metadata_json'), // JSON-encoded additional fields
|
||||
createdAt: integer('created_at').notNull(),
|
||||
updatedAt: integer('updated_at').notNull(),
|
||||
sourceId: text('source_id').notNull().references(() => sources.id, { onDelete: 'cascade' }),
|
||||
id: text('id').primaryKey(),
|
||||
name: text('name').notNull(),
|
||||
isFolder: integer('is_folder', { mode: 'boolean' }).notNull(),
|
||||
metadataJson: text('metadata_json'), // JSON-encoded additional fields
|
||||
createdAt: integer('created_at').notNull(),
|
||||
updatedAt: integer('updated_at').notNull(),
|
||||
}, (table) => ({
|
||||
sourceNameIdx: index('artists_source_name_idx').on(table.sourceId, table.name),
|
||||
sourceNameIdx: index('artists_source_name_idx').on(table.sourceId, table.name),
|
||||
}));
|
||||
|
||||
@@ -5,15 +5,15 @@ import { sources } from './sources';
|
||||
* Downloads table
|
||||
*/
|
||||
export const downloads = sqliteTable('downloads', {
|
||||
sourceId: text('source_id').notNull().references(() => sources.id, { onDelete: 'cascade' }),
|
||||
id: text('id').primaryKey(),
|
||||
hash: text('hash'),
|
||||
filename: text('filename'),
|
||||
mimetype: text('mimetype'),
|
||||
progress: integer('progress'),
|
||||
isFailed: integer('is_failed', { mode: 'boolean' }).notNull(),
|
||||
isComplete: integer('is_complete', { mode: 'boolean' }).notNull(),
|
||||
metadataJson: text('metadata_json'), // JSON-encoded additional fields
|
||||
createdAt: integer('created_at').notNull(),
|
||||
updatedAt: integer('updated_at').notNull(),
|
||||
sourceId: text('source_id').notNull().references(() => sources.id, { onDelete: 'cascade' }),
|
||||
id: text('id').primaryKey(),
|
||||
hash: text('hash'),
|
||||
filename: text('filename'),
|
||||
mimetype: text('mimetype'),
|
||||
progress: integer('progress'),
|
||||
isFailed: integer('is_failed', { mode: 'boolean' }).notNull(),
|
||||
isComplete: integer('is_complete', { mode: 'boolean' }).notNull(),
|
||||
metadataJson: text('metadata_json'), // JSON-encoded additional fields
|
||||
createdAt: integer('created_at').notNull(),
|
||||
updatedAt: integer('updated_at').notNull(),
|
||||
});
|
||||
|
||||
@@ -5,11 +5,11 @@ import { sources } from './sources';
|
||||
* Playlist-Tracks relation table (many-to-many with position)
|
||||
*/
|
||||
export const playlistTracks = sqliteTable('playlist_tracks', {
|
||||
sourceId: text('source_id').notNull().references(() => sources.id, { onDelete: 'cascade' }),
|
||||
playlistId: text('playlist_id').notNull(),
|
||||
trackId: text('track_id').notNull(),
|
||||
position: integer('position'),
|
||||
sourceId: text('source_id').notNull().references(() => sources.id, { onDelete: 'cascade' }),
|
||||
playlistId: text('playlist_id').notNull(),
|
||||
trackId: text('track_id').notNull(),
|
||||
position: integer('position'),
|
||||
}, (table) => ({
|
||||
pk: primaryKey({ columns: [table.sourceId, table.playlistId, table.trackId] }),
|
||||
sourcePlaylistPositionIdx: index('playlist_tracks_source_playlist_position_idx').on(table.sourceId, table.playlistId, table.position),
|
||||
pk: primaryKey({ columns: [table.sourceId, table.playlistId, table.trackId] }),
|
||||
sourcePlaylistPositionIdx: index('playlist_tracks_source_playlist_position_idx').on(table.sourceId, table.playlistId, table.position),
|
||||
}));
|
||||
|
||||
@@ -5,15 +5,15 @@ import { sources } from './sources';
|
||||
* Playlists table
|
||||
*/
|
||||
export const playlists = sqliteTable('playlists', {
|
||||
sourceId: text('source_id').notNull().references(() => sources.id, { onDelete: 'cascade' }),
|
||||
id: text('id').primaryKey(),
|
||||
name: text('name').notNull(),
|
||||
canDelete: integer('can_delete', { mode: 'boolean' }).notNull(),
|
||||
childCount: integer('child_count'),
|
||||
lastRefreshed: integer('last_refreshed'),
|
||||
metadataJson: text('metadata_json'), // JSON-encoded additional fields
|
||||
createdAt: integer('created_at').notNull(),
|
||||
updatedAt: integer('updated_at').notNull(),
|
||||
sourceId: text('source_id').notNull().references(() => sources.id, { onDelete: 'cascade' }),
|
||||
id: text('id').primaryKey(),
|
||||
name: text('name').notNull(),
|
||||
canDelete: integer('can_delete', { mode: 'boolean' }).notNull(),
|
||||
childCount: integer('child_count'),
|
||||
lastRefreshed: integer('last_refreshed'),
|
||||
metadataJson: text('metadata_json'), // JSON-encoded additional fields
|
||||
createdAt: integer('created_at').notNull(),
|
||||
updatedAt: integer('updated_at').notNull(),
|
||||
}, (table) => ({
|
||||
sourceNameIdx: index('playlists_source_name_idx').on(table.sourceId, table.name),
|
||||
sourceNameIdx: index('playlists_source_name_idx').on(table.sourceId, table.name),
|
||||
}));
|
||||
|
||||
@@ -5,14 +5,14 @@ import { sources } from './sources';
|
||||
* Search queries table
|
||||
*/
|
||||
export const searchQueries = sqliteTable('search_queries', {
|
||||
sourceId: text('source_id').notNull().references(() => sources.id, { onDelete: 'cascade' }),
|
||||
id: text('id').primaryKey(),
|
||||
query: text('query').notNull(),
|
||||
timestamp: integer('timestamp').notNull(),
|
||||
localPlaybackOnly: integer('local_playback_only', { mode: 'boolean' }).notNull(),
|
||||
metadataJson: text('metadata_json'), // JSON-encoded additional fields
|
||||
createdAt: integer('created_at').notNull(),
|
||||
updatedAt: integer('updated_at').notNull(),
|
||||
sourceId: text('source_id').notNull().references(() => sources.id, { onDelete: 'cascade' }),
|
||||
id: text('id').primaryKey(),
|
||||
query: text('query').notNull(),
|
||||
timestamp: integer('timestamp').notNull(),
|
||||
localPlaybackOnly: integer('local_playback_only', { mode: 'boolean' }).notNull(),
|
||||
metadataJson: text('metadata_json'), // JSON-encoded additional fields
|
||||
createdAt: integer('created_at').notNull(),
|
||||
updatedAt: integer('updated_at').notNull(),
|
||||
}, (table) => ({
|
||||
sourceTimestampIdx: index('search_queries_source_timestamp_idx').on(table.sourceId, table.timestamp),
|
||||
sourceTimestampIdx: index('search_queries_source_timestamp_idx').on(table.sourceId, table.timestamp),
|
||||
}));
|
||||
|
||||
@@ -4,8 +4,8 @@ import { sqliteTable, integer } from 'drizzle-orm/sqlite-core';
|
||||
* Sleep timer - global sleep timer settings (single row, id=1)
|
||||
*/
|
||||
export const sleepTimer = sqliteTable('sleep_timer', {
|
||||
id: integer('id').primaryKey().$default(() => 1),
|
||||
date: integer('date'), // nullable - epoch ms
|
||||
createdAt: integer('created_at').notNull(),
|
||||
updatedAt: integer('updated_at').notNull(),
|
||||
id: integer('id').primaryKey().$default(() => 1),
|
||||
date: integer('date'), // nullable - epoch ms
|
||||
createdAt: integer('created_at').notNull(),
|
||||
updatedAt: integer('updated_at').notNull(),
|
||||
});
|
||||
|
||||
@@ -4,12 +4,12 @@ import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
|
||||
* Sources table - stores Jellyfin/Emby server connections
|
||||
*/
|
||||
export const sources = sqliteTable('sources', {
|
||||
id: text('id').primaryKey(),
|
||||
uri: text('uri').notNull(),
|
||||
userId: text('user_id'),
|
||||
accessToken: text('access_token'),
|
||||
deviceId: text('device_id'),
|
||||
type: text('type').notNull(), // 'jellyfin.v1' or 'emby.v1'
|
||||
createdAt: integer('created_at').notNull(),
|
||||
updatedAt: integer('updated_at').notNull(),
|
||||
id: text('id').primaryKey(),
|
||||
uri: text('uri').notNull(),
|
||||
userId: text('user_id'),
|
||||
accessToken: text('access_token'),
|
||||
deviceId: text('device_id'),
|
||||
type: text('type').notNull(), // 'jellyfin.v1' or 'emby.v1'
|
||||
createdAt: integer('created_at').notNull(),
|
||||
updatedAt: integer('updated_at').notNull(),
|
||||
});
|
||||
|
||||
@@ -5,12 +5,12 @@ import { sources } from './sources';
|
||||
* Sync cursors table - tracks prefill progress
|
||||
*/
|
||||
export const syncCursors = sqliteTable('sync_cursors', {
|
||||
sourceId: text('source_id').notNull().references(() => sources.id, { onDelete: 'cascade' }),
|
||||
entityType: text('entity_type').notNull(), // 'artists', 'albums', 'tracks', 'playlists', etc.
|
||||
startIndex: integer('start_index').notNull(),
|
||||
pageSize: integer('page_size').notNull(),
|
||||
completed: integer('completed', { mode: 'boolean' }).notNull(),
|
||||
updatedAt: integer('updated_at').notNull(),
|
||||
sourceId: text('source_id').notNull().references(() => sources.id, { onDelete: 'cascade' }),
|
||||
entityType: text('entity_type').notNull(), // 'artists', 'albums', 'tracks', 'playlists', etc.
|
||||
startIndex: integer('start_index').notNull(),
|
||||
pageSize: integer('page_size').notNull(),
|
||||
completed: integer('completed', { mode: 'boolean' }).notNull(),
|
||||
updatedAt: integer('updated_at').notNull(),
|
||||
}, (table) => ({
|
||||
pk: primaryKey({ columns: [table.sourceId, table.entityType] }),
|
||||
pk: primaryKey({ columns: [table.sourceId, table.entityType] }),
|
||||
}));
|
||||
|
||||
@@ -5,11 +5,11 @@ import { sources } from './sources';
|
||||
* Track-Artists relation table (many-to-many)
|
||||
*/
|
||||
export const trackArtists = sqliteTable('track_artists', {
|
||||
sourceId: text('source_id').notNull().references(() => sources.id, { onDelete: 'cascade' }),
|
||||
trackId: text('track_id').notNull(),
|
||||
artistId: text('artist_id').notNull(),
|
||||
orderIndex: integer('order_index'),
|
||||
sourceId: text('source_id').notNull().references(() => sources.id, { onDelete: 'cascade' }),
|
||||
trackId: text('track_id').notNull(),
|
||||
artistId: text('artist_id').notNull(),
|
||||
orderIndex: integer('order_index'),
|
||||
}, (table) => ({
|
||||
pk: primaryKey({ columns: [table.sourceId, table.trackId, table.artistId] }),
|
||||
sourceArtistIdx: index('track_artists_source_artist_idx').on(table.sourceId, table.artistId),
|
||||
pk: primaryKey({ columns: [table.sourceId, table.trackId, table.artistId] }),
|
||||
sourceArtistIdx: index('track_artists_source_artist_idx').on(table.sourceId, table.artistId),
|
||||
}));
|
||||
|
||||
@@ -5,22 +5,22 @@ import { sources } from './sources';
|
||||
* Tracks table
|
||||
*/
|
||||
export const tracks = sqliteTable('tracks', {
|
||||
sourceId: text('source_id').notNull().references(() => sources.id, { onDelete: 'cascade' }),
|
||||
id: text('id').primaryKey(),
|
||||
name: text('name').notNull(),
|
||||
albumId: text('album_id'),
|
||||
album: text('album'),
|
||||
albumArtist: text('album_artist'),
|
||||
productionYear: integer('production_year'),
|
||||
indexNumber: integer('index_number'),
|
||||
parentIndexNumber: integer('parent_index_number'),
|
||||
hasLyrics: integer('has_lyrics', { mode: 'boolean' }).notNull().default(false),
|
||||
runTimeTicks: integer('run_time_ticks'),
|
||||
lyrics: text('lyrics'),
|
||||
metadataJson: text('metadata_json'), // JSON-encoded additional fields
|
||||
createdAt: integer('created_at').notNull(),
|
||||
updatedAt: integer('updated_at').notNull(),
|
||||
sourceId: text('source_id').notNull().references(() => sources.id, { onDelete: 'cascade' }),
|
||||
id: text('id').primaryKey(),
|
||||
name: text('name').notNull(),
|
||||
albumId: text('album_id'),
|
||||
album: text('album'),
|
||||
albumArtist: text('album_artist'),
|
||||
productionYear: integer('production_year'),
|
||||
indexNumber: integer('index_number'),
|
||||
parentIndexNumber: integer('parent_index_number'),
|
||||
hasLyrics: integer('has_lyrics', { mode: 'boolean' }).notNull().default(false),
|
||||
runTimeTicks: integer('run_time_ticks'),
|
||||
lyrics: text('lyrics'),
|
||||
metadataJson: text('metadata_json'), // JSON-encoded additional fields
|
||||
createdAt: integer('created_at').notNull(),
|
||||
updatedAt: integer('updated_at').notNull(),
|
||||
}, (table) => ({
|
||||
sourceAlbumIdx: index('tracks_source_album_idx').on(table.sourceId, table.albumId),
|
||||
sourceNameIdx: index('tracks_source_name_idx').on(table.sourceId, table.name),
|
||||
sourceAlbumIdx: index('tracks_source_album_idx').on(table.sourceId, table.albumId),
|
||||
sourceNameIdx: index('tracks_source_name_idx').on(table.sourceId, table.name),
|
||||
}));
|
||||
|
||||
+410
-410
File diff suppressed because it is too large
Load Diff
@@ -6,27 +6,27 @@
|
||||
|
||||
// Re-export all common types
|
||||
export type {
|
||||
Source,
|
||||
SourceInfo,
|
||||
Credentials,
|
||||
ListParams,
|
||||
Artist,
|
||||
Album,
|
||||
Track,
|
||||
Playlist,
|
||||
SearchFilter,
|
||||
SearchResultItem,
|
||||
CodecMetadata,
|
||||
Lyrics,
|
||||
StreamOptions,
|
||||
DownloadOptions,
|
||||
DownloadInfo,
|
||||
Source,
|
||||
SourceInfo,
|
||||
Credentials,
|
||||
ListParams,
|
||||
Artist,
|
||||
Album,
|
||||
Track,
|
||||
Playlist,
|
||||
SearchFilter,
|
||||
SearchResultItem,
|
||||
CodecMetadata,
|
||||
Lyrics,
|
||||
StreamOptions,
|
||||
DownloadOptions,
|
||||
DownloadInfo,
|
||||
} from '../types';
|
||||
|
||||
export {
|
||||
SourceType,
|
||||
SearchFilterType,
|
||||
SourceDriver,
|
||||
SourceType,
|
||||
SearchFilterType,
|
||||
SourceDriver,
|
||||
} from '../types';
|
||||
|
||||
// Emby-specific types can be added here as needed
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,27 +6,27 @@
|
||||
|
||||
// Re-export all common types
|
||||
export type {
|
||||
Source,
|
||||
SourceInfo,
|
||||
Credentials,
|
||||
ListParams,
|
||||
Artist,
|
||||
Album,
|
||||
Track,
|
||||
Playlist,
|
||||
SearchFilter,
|
||||
SearchResultItem,
|
||||
CodecMetadata,
|
||||
Lyrics,
|
||||
StreamOptions,
|
||||
DownloadOptions,
|
||||
DownloadInfo,
|
||||
Source,
|
||||
SourceInfo,
|
||||
Credentials,
|
||||
ListParams,
|
||||
Artist,
|
||||
Album,
|
||||
Track,
|
||||
Playlist,
|
||||
SearchFilter,
|
||||
SearchResultItem,
|
||||
CodecMetadata,
|
||||
Lyrics,
|
||||
StreamOptions,
|
||||
DownloadOptions,
|
||||
DownloadInfo,
|
||||
} from '../types';
|
||||
|
||||
export {
|
||||
SourceType,
|
||||
SearchFilterType,
|
||||
SourceDriver,
|
||||
SourceType,
|
||||
SearchFilterType,
|
||||
SourceDriver,
|
||||
} from '../types';
|
||||
|
||||
// Jellyfin-specific types can be added here as needed
|
||||
|
||||
+12
-12
@@ -179,11 +179,11 @@ export interface DownloadInfo {
|
||||
* Optional methods have default implementations
|
||||
*/
|
||||
export abstract class SourceDriver {
|
||||
protected source: Source;
|
||||
protected source: Source;
|
||||
|
||||
constructor(source: Source) {
|
||||
this.source = source;
|
||||
}
|
||||
constructor(source: Source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the source and retrieve server info
|
||||
@@ -195,7 +195,7 @@ export abstract class SourceDriver {
|
||||
* Default implementation throws error
|
||||
*/
|
||||
refreshCredentials(): Promise<Credentials> {
|
||||
throw new Error('refreshCredentials not implemented');
|
||||
throw new Error('refreshCredentials not implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -203,12 +203,12 @@ export abstract class SourceDriver {
|
||||
* Default implementation tries to connect
|
||||
*/
|
||||
async validateCredentials(): Promise<boolean> {
|
||||
try {
|
||||
await this.connect();
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
await this.connect();
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -216,7 +216,7 @@ export abstract class SourceDriver {
|
||||
* Default implementation does nothing
|
||||
*/
|
||||
async signOut(): Promise<void> {
|
||||
// Default: no-op
|
||||
// Default: no-op
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user