Skip to content

Commit

Permalink
feat: auto add movie files
Browse files Browse the repository at this point in the history
  • Loading branch information
gallayl committed Jun 2, 2024
1 parent dfde129 commit 7b2edec
Show file tree
Hide file tree
Showing 36 changed files with 414 additions and 173 deletions.
1 change: 1 addition & 0 deletions common/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './models/index.js'
export * from './apis/index.js'
export * from './utils/index.js'
export * from './websocket/index.js'
2 changes: 1 addition & 1 deletion common/src/models/media/movie-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { FFProbeResult } from 'ffprobe'

export class MovieFile {
id!: string
imdbId!: string
imdbId?: string
driveLetter!: string
path!: string
fileName!: string
Expand Down
2 changes: 1 addition & 1 deletion common/src/models/media/movie-watch-history.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export class MovieWatchHistoryEntry {
id!: string
userName!: string
imdbId!: string
movieFileId!: string
driveLetter!: string
path!: string
fileName!: string
Expand Down
11 changes: 11 additions & 0 deletions common/src/websocket/add-movie-message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { MovieFile } from '../models/index.js'
import type { Movie } from '../models/media/movie.js'

export interface AddMovieMessage {
type: 'add-movie'
driveLetter: string
path: string
fileName: string
movie: Movie
movieFile: MovieFile
}
6 changes: 6 additions & 0 deletions common/src/websocket/device-connected-message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { Device } from '../models/iot/device.js'

export interface DeviceConnectedMessage {
type: 'device-connected'
device: Device
}
6 changes: 6 additions & 0 deletions common/src/websocket/device-disconnected-message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { Device } from '../models/iot/device.js'

export interface DeviceDisconnectedMessage {
type: 'device-disconnected'
device: Device
}
6 changes: 6 additions & 0 deletions common/src/websocket/file-change-message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type FileChangeMessage = {
type: 'file-change'
event: 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir'
path: string
drive: string
}
1 change: 1 addition & 0 deletions common/src/websocket/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './websocket-message.js'
6 changes: 6 additions & 0 deletions common/src/websocket/websocket-message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { AddMovieMessage } from './add-movie-message.js'
import type { DeviceConnectedMessage } from './device-connected-message.js'
import type { DeviceDisconnectedMessage } from './device-disconnected-message.js'
import type { FileChangeMessage } from './file-change-message.js'

export type WebsocketMessage = AddMovieMessage | DeviceConnectedMessage | DeviceDisconnectedMessage | FileChangeMessage
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,26 @@ export const continueWatchingCommandProvider: CommandProvider = async ({ term, i
order: { updatedAt: 'DESC' },
})
if (lastEntries.length) {
const movieFilesService = injector.getInstance(MovieFilesService)
const moviesService = injector.getInstance(MoviesService)
const movies = await moviesService.findMovie({

const movieFiles = await movieFilesService.findMovieFile({
filter: {
imdbId: {
$in: lastEntries.map((e) => e.imdbId),
id: {
$in: lastEntries.map((e) => e.movieFileId),
},
},
})
const movieFilesService = injector.getInstance(MovieFilesService)

const movies = movieFiles.entries.some((e) => e.imdbId)
? await moviesService.findMovie({
filter: {
imdbId: {
$in: movieFiles.entries.map((e) => e.imdbId as string),
},
},
})
: { entries: [] }
await movieFilesService.prefetchMovieFilesForMovies(movies.entries)

return [
Expand All @@ -32,7 +43,12 @@ export const continueWatchingCommandProvider: CommandProvider = async ({ term, i
<WidgetGroup
title="Continue watching"
type="group"
widgets={lastEntries.map((entry, index) => ({ type: 'movie', imdbId: entry.imdbId, index, size: 128 }))}
widgets={movieFiles.entries.map((entry, index) => ({
type: 'movie',
imdbId: entry.imdbId as string,
index,
size: 128,
}))}
/>
),
onSelected: () => {
Expand Down
20 changes: 17 additions & 3 deletions frontend/src/components/dashboard/continue-watching.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,27 @@ export const ContinueWatchingWidgetGroup = Shade<ContinueWatchingWidgetGroupProp
},
})

const movieFiles = await movieFileService.findMovieFile({
filter: {
imdbId: {
$in: Array.from(new Set(watchEntries.entries.map((entry) => entry.movieFileId))),
},
},
})

const { entries: movies } = await movieService.findMovie({
filter: {
imdbId: {
$in: Array.from(new Set(watchEntries.entries.map((entry) => entry.imdbId))),
$in: Array.from(
new Set(movieFiles.entries.filter((e) => e.imdbId).map((entry) => entry.imdbId as string)),
),
},
},
})

await Promise.all([
movieFileService.prefetchMovieFilesForMovies(movies),
watchProgressService.prefetchWatchProgressForMovies(movies),
watchProgressService.prefetchWatchProgressForMovieFiles(movieFiles.entries),
])

return (
Expand All @@ -52,7 +62,11 @@ export const ContinueWatchingWidgetGroup = Shade<ContinueWatchingWidgetGroupProp
<div style={{ display: 'flex', overflow: 'auto', scrollSnapType: 'x mandatory' }}>
{watchEntries.entries.map((entry, index) => (
<div style={{ scrollSnapAlign: 'start' }}>
<MovieWidget imdbId={entry.imdbId} index={index} size={size || 256} />
<MovieWidget
imdbId={movieFiles.entries.find((mf) => mf.id === entry.movieFileId)!.imdbId as string}
index={index}
size={size || 256}
/>
</div>
))}
</div>
Expand Down
12 changes: 9 additions & 3 deletions frontend/src/components/dashboard/movie-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,17 @@ export const MovieWidget = Shade<{
<LazyLoad
loader={<div />}
component={async () => {
const { entries: watchProgresses } = await watchProgressService.findWatchProgressesForMovie(
movie.value,
if (!isLoadedCacheResult(movieFile)) {
return <></>
}

const { entries: watchProgresses } = await watchProgressService.findWatchProgressesForMovieFile(
movieFile.value.entries[0],
)

const lastRecentWatchProgress = watchProgresses.find((w) => w.imdbId === imdbId)
const lastRecentWatchProgress = watchProgresses.find((w) =>
movieFile.value.entries.some((file) => file.id === w.movieFileId),
)

const percent =
lastRecentWatchProgress &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export const RelatedMoviesModalContent = Shade<{
<>
{`Found ${linkedFiles.value.count} related movies`}
{linkedFiles.value.entries.map((linkedFile) => (
<MovieWidget imdbId={linkedFile.imdbId} />
<MovieWidget imdbId={linkedFile.imdbId as string} />
))}
</>
)
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/files/video-player.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export const VideoPlayer = Shade<{ letter: string; path: string }>({
driveLetter: letter,
path: parentPath,
fileName: path.split('/').pop() as string,
imdbId: file.imdbId,
movieFileId: file.id,
watchedSeconds: progress,
})
},
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/pages/movies/movie-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ export const MovieList = Shade({
<PiRatLazyLoad
component={async () => {
const movies = await movieService.findMovie({})
const movieFiles = await movieFilesService.findMovieFile({})
await Promise.all([
movieFilesService.prefetchMovieFilesForMovies(movies.entries),
watchProgressService.prefetchWatchProgressForMovies(movies.entries),
watchProgressService.prefetchWatchProgressForMovieFiles(movieFiles.entries),
])

return (
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/pages/movies/movie-overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ export const PlayButtons = Shade<{ imdbId: string }>({
)
const [watchProgressResult] = useObservable(
'watchProgress',
watchProgressService.findWatchProgressAsObservable({ filter: { imdbId: { $eq: props.imdbId } } }),
watchProgressService.findWatchProgressAsObservable({
filter: { movieFileId: { $in: movieFilesResult?.value?.entries?.map((v) => v.id) || [] } },
}),
)

if (isLoadedCacheResult(movieFilesResult) && isLoadedCacheResult(watchProgressResult)) {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/movies/movie-player.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const MoviePlayer = Shade<MoviePlayerProps>({
driveLetter: movieFile.driveLetter,
path,
fileName,
imdbId: movieFile.imdbId,
movieFileId: movieFile.id,
watchedSeconds: progress,
})
},
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/pages/movies/series-overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,15 @@ export const SeriesOverview = Shade<SeriesListProps>({
return (
<PiRatLazyLoad
component={async () => {
const [series, relatedMovies] = await Promise.all([
const [series, relatedMovies, relatedMovieFiles] = await Promise.all([
seriesService.getSeries(props.imdbId),
moviesService.findMovie({ filter: { seriesId: { $eq: props.imdbId } } }),
movieFileService.findMovieFile({ filter: { imdbId: { $in: [props.imdbId] } } }),
])

await Promise.all([
movieFileService.prefetchMovieFilesForMovies(relatedMovies.entries),
watchProgresses.prefetchWatchProgressForMovies(relatedMovies.entries),
watchProgresses.prefetchWatchProgressForMovieFiles(relatedMovieFiles.entries),
])

const seasons = Array.from(
Expand Down
18 changes: 9 additions & 9 deletions frontend/src/services/watch-progress-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Injectable, Injected } from '@furystack/inject'
import { MediaApiClient } from './api-clients/media-api-client.js'
import { Cache } from '@furystack/cache'
import type { FindOptions } from '@furystack/core'
import type { Movie, MovieWatchHistoryEntry } from 'common'
import type { MovieFile, MovieWatchHistoryEntry } from 'common'

@Injectable({ lifetime: 'singleton' })
export class WatchProgressService {
Expand Down Expand Up @@ -94,10 +94,10 @@ export class WatchProgressService {
return result
}

public async findWatchProgressesForMovie(movie: Movie) {
public async findWatchProgressesForMovieFile(movieFile: MovieFile) {
return await this.findWatchProgress({
filter: {
imdbId: { $eq: movie.imdbId },
movieFileId: { $eq: movieFile.id },
},
})
}
Expand Down Expand Up @@ -129,22 +129,22 @@ export class WatchProgressService {
return result
}

public async prefetchWatchProgressForMovies(movies: Movie[]) {
const imdbIds = Array.from(new Set(movies.map((movie) => movie.imdbId)))
public async prefetchWatchProgressForMovieFiles(movieFiles: MovieFile[]) {
const movieFileIds = Array.from(new Set(movieFiles.map((movie) => movie.id)))

const result = await this.findWatchProgress({
filter: {
imdbId: { $in: imdbIds },
movieFileId: { $in: movieFileIds },
},
})

imdbIds.forEach((imdbId) => {
const relatedWatchProgresses = result.entries.filter((entry) => entry.imdbId === imdbId)
movieFileIds.forEach((movieFileId) => {
const relatedWatchProgresses = result.entries.filter((entry) => entry.movieFileId === movieFileId)
this.watchProgressQueryCache.setExplicitValue({
loadArgs: [
{
filter: {
imdbId: { $eq: imdbId },
movieFileId: { $eq: movieFileId },
},
},
],
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/services/websocket-events.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { EventHub } from '@furystack/utils'
import { environmentOptions } from '../environment-options.js'
import { Injectable } from '@furystack/inject'
import type { WebsocketMessage } from 'common'

@Injectable({ lifetime: 'singleton' })
export class WebsocketNotificationsService extends EventHub<{ onMessage: unknown }> {
export class WebsocketNotificationsService extends EventHub<{ onMessage: WebsocketMessage }> {
private readonly wsUrl = new URL(`${environmentOptions.serviceUrl}/ws`, window.location.href)

public socket = new WebSocket(this.wsUrl.toString().replace('http', 'ws'))
Expand Down
2 changes: 1 addition & 1 deletion service/src/config/setup-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const setupConfig = async (injector: Injector) => {
},
{ sequelize },
)
await sequelize.sync()
await ConfigModel.sync()
},
})

Expand Down
2 changes: 1 addition & 1 deletion service/src/dashboards/setup-dashboards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const setupDashboards = async (injector: Injector) => {
sequelize,
},
)
await sequelize.sync()
await DashboardModel.sync()
},
})
await logger.verbose({ message: 'Setting up repository...' })
Expand Down
Loading

0 comments on commit 7b2edec

Please sign in to comment.