Skip to content
This repository was archived by the owner on Feb 15, 2026. It is now read-only.

Commit 6d82eee

Browse files
authored
feat(plex): refresh token schedule (#3875)
* feat: refresh token schedule fix #3861 * fix(i18n): add i18n message * refactor(plextv): use randomUUID crypto instead custom function
1 parent 676a8c9 commit 6d82eee

6 files changed

Lines changed: 77 additions & 0 deletions

File tree

server/api/plextv.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { PlexDevice } from '@server/interfaces/api/plexInterfaces';
22
import cacheManager from '@server/lib/cache';
33
import { getSettings } from '@server/lib/settings';
44
import logger from '@server/logger';
5+
import { randomUUID } from 'node:crypto';
56
import xml2js from 'xml2js';
67
import ExternalAPI from './externalapi';
78

@@ -363,6 +364,24 @@ class PlexTvAPI extends ExternalAPI {
363364
};
364365
}
365366
}
367+
368+
public async pingToken() {
369+
try {
370+
const response = await this.axios.get('/api/v2/ping', {
371+
headers: {
372+
'X-Plex-Client-Identifier': randomUUID(),
373+
},
374+
});
375+
if (!response?.data?.pong) {
376+
throw new Error('No pong response');
377+
}
378+
} catch (e) {
379+
logger.error('Failed to ping token', {
380+
label: 'Plex Refresh Token',
381+
errorMessage: e.message,
382+
});
383+
}
384+
}
366385
}
367386

368387
export default PlexTvAPI;

server/job/schedule.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import availabilitySync from '@server/lib/availabilitySync';
22
import downloadTracker from '@server/lib/downloadtracker';
33
import ImageProxy from '@server/lib/imageproxy';
4+
import refreshToken from '@server/lib/refreshToken';
45
import { plexFullScanner, plexRecentScanner } from '@server/lib/scanners/plex';
56
import { radarrScanner } from '@server/lib/scanners/radarr';
67
import { sonarrScanner } from '@server/lib/scanners/sonarr';
@@ -168,5 +169,19 @@ export const startJobs = (): void => {
168169
}),
169170
});
170171

172+
scheduledJobs.push({
173+
id: 'plex-refresh-token',
174+
name: 'Plex Refresh Token',
175+
type: 'process',
176+
interval: 'fixed',
177+
cronSchedule: jobs['plex-refresh-token'].schedule,
178+
job: schedule.scheduleJob(jobs['plex-refresh-token'].schedule, () => {
179+
logger.info('Starting scheduled job: Plex Refresh Token', {
180+
label: 'Jobs',
181+
});
182+
refreshToken.run();
183+
}),
184+
});
185+
171186
logger.info('Scheduled jobs loaded', { label: 'Jobs' });
172187
};

server/lib/refreshToken.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import PlexTvAPI from '@server/api/plextv';
2+
import { getRepository } from '@server/datasource';
3+
import { User } from '@server/entity/User';
4+
import logger from '@server/logger';
5+
6+
class RefreshToken {
7+
public async run() {
8+
const userRepository = getRepository(User);
9+
10+
const users = await userRepository
11+
.createQueryBuilder('user')
12+
.addSelect('user.plexToken')
13+
.where("user.plexToken != ''")
14+
.getMany();
15+
16+
for (const user of users) {
17+
await this.refreshUserToken(user);
18+
}
19+
}
20+
21+
private async refreshUserToken(user: User) {
22+
if (!user.plexToken) {
23+
logger.warn('Skipping user refresh token for user without plex token', {
24+
label: 'Plex Refresh Token',
25+
user: user.displayName,
26+
});
27+
return;
28+
}
29+
30+
const plexTvApi = new PlexTvAPI(user.plexToken);
31+
plexTvApi.pingToken();
32+
}
33+
}
34+
35+
const refreshToken = new RefreshToken();
36+
37+
export default refreshToken;

server/lib/settings.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ export type JobId =
248248
| 'plex-recently-added-scan'
249249
| 'plex-full-scan'
250250
| 'plex-watchlist-sync'
251+
| 'plex-refresh-token'
251252
| 'radarr-scan'
252253
| 'sonarr-scan'
253254
| 'download-sync'
@@ -409,6 +410,9 @@ class Settings {
409410
'plex-watchlist-sync': {
410411
schedule: '0 */3 * * * *',
411412
},
413+
'plex-refresh-token': {
414+
schedule: '0 0 5 * * *',
415+
},
412416
'radarr-scan': {
413417
schedule: '0 0 4 * * *',
414418
},

src/components/Settings/SettingsJobsCache/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ const messages: { [messageName: string]: MessageDescriptor } = defineMessages({
5353
'plex-recently-added-scan': 'Plex Recently Added Scan',
5454
'plex-full-scan': 'Plex Full Library Scan',
5555
'plex-watchlist-sync': 'Plex Watchlist Sync',
56+
'plex-refresh-token': 'Plex Refresh Token',
5657
'availability-sync': 'Media Availability Sync',
5758
'radarr-scan': 'Radarr Scan',
5859
'sonarr-scan': 'Sonarr Scan',

src/i18n/locale/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,7 @@
768768
"components.Settings.SettingsJobsCache.nextexecution": "Next Execution",
769769
"components.Settings.SettingsJobsCache.plex-full-scan": "Plex Full Library Scan",
770770
"components.Settings.SettingsJobsCache.plex-recently-added-scan": "Plex Recently Added Scan",
771+
"components.Settings.SettingsJobsCache.plex-refresh-token": "Plex Refresh Token",
771772
"components.Settings.SettingsJobsCache.plex-watchlist-sync": "Plex Watchlist Sync",
772773
"components.Settings.SettingsJobsCache.process": "Process",
773774
"components.Settings.SettingsJobsCache.radarr-scan": "Radarr Scan",

0 commit comments

Comments
 (0)