Report playback stopped when download finishes

This commit is contained in:
Bill Thornton 2022-12-01 10:53:06 -05:00
parent c2408ec37c
commit edb7fc6087
5 changed files with 90 additions and 4 deletions

23
App.js
View File

@ -8,6 +8,7 @@
import 'react-native-url-polyfill/auto';
import { Ionicons } from '@expo/vector-icons';
import { getPlaystateApi } from '@jellyfin/sdk/lib/utils/api/playstate-api';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { NavigationContainer } from '@react-navigation/native';
import { Asset } from 'expo-asset';
@ -20,7 +21,7 @@ import { observer } from 'mobx-react-lite';
import { AsyncTrunk } from 'mobx-sync-lite';
import PropTypes from 'prop-types';
import React, { useContext, useEffect, useState } from 'react';
import { useColorScheme } from 'react-native';
import { Alert, useColorScheme } from 'react-native';
import { ThemeContext, ThemeProvider } from 'react-native-elements';
import { SafeAreaProvider } from 'react-native-safe-area-context';
@ -140,7 +141,27 @@ const App = observer(({ skipLoadingScreen }) => {
download.isDownloading = false;
} catch (e) {
console.error('[App] Download failed', e);
Alert.alert('Download Failed', `"${download.title}" failed to download.`);
// TODO: If a download fails, we should probably remove it from the queue
download.isDownloading = false;
} finally {
const serverUrl = download.serverUrl.endsWith('/') ? download.serverUrl.slice(0, -1) : download.serverUrl;
const api = rootStore.sdk.createApi(serverUrl);
console.log('[App] Reporting download stopped', download.sessionId);
getPlaystateApi(api)
.reportPlaybackStopped({
playbackStopInfo: {
PlaySessionId: download.sessionId
}
}, {
query: {
api_key: download.apiKey
}
})
.catch(err => {
console.error('[App] Failed reporting download stopped', err.response || err.request || err.message);
});
}
};

View File

@ -7,6 +7,7 @@
import * as FileSystem from 'expo-file-system';
import { computed, decorate, observable } from 'mobx';
import { ignore } from 'mobx-sync-lite';
import { v4 as uuidv4 } from 'uuid';
export default class DownloadModel {
isComplete = false
@ -15,6 +16,8 @@ export default class DownloadModel {
apiKey: string
itemId: string
/** The "play" session ID for reporting a download has stopped. */
sessionId = uuidv4()
serverId: string
serverUrl: string
@ -61,6 +64,7 @@ export default class DownloadModel {
const streamParams = new URLSearchParams({
deviceId,
api_key: this.apiKey,
playSessionId: this.sessionId,
// TODO: add mediaSourceId to support alternate media versions
videoCodec: 'hevc,h264',
audioCodec: 'aac,mp3,ac3,eac3,flac,alac',
@ -79,6 +83,7 @@ decorate(DownloadModel, {
isNew: observable,
apiKey: observable,
itemId: observable,
sessionId: observable,
serverId: observable,
serverUrl: observable,
title: observable,

44
package-lock.json generated
View File

@ -2126,6 +2126,41 @@
"integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
"dev": true
},
"@jellyfin/sdk": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/@jellyfin/sdk/-/sdk-0.7.0.tgz",
"integrity": "sha512-GNoGv+2qY+xK7WpO7sUUNpZvzgN7RwXMyOhIy9mE/LdDSr6bqZHwrzT1Pv0+vUW7Epw67bwIMWuYivyBYejEHw==",
"requires": {
"axios": "0.27.2",
"compare-versions": "5.0.1"
},
"dependencies": {
"axios": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
"requires": {
"follow-redirects": "^1.14.9",
"form-data": "^4.0.0"
}
},
"compare-versions": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-5.0.1.tgz",
"integrity": "sha512-v8Au3l0b+Nwkp4G142JcgJFh1/TUhdxut7wzD1Nq1dyp5oa3tXaqb03EXOAB6jS4gMlalkjAUPZBMiAfKUixHQ=="
},
"form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
}
}
},
"@jest/console": {
"version": "26.6.2",
"resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz",
@ -3694,6 +3729,12 @@
}
}
},
"@types/uuid": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
"integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==",
"dev": true
},
"@types/webpack": {
"version": "4.41.33",
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.33.tgz",
@ -9320,8 +9361,7 @@
"follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"dev": true
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
},
"fontfaceobserver": {
"version": "2.3.0",

View File

@ -34,6 +34,7 @@
"Android >= 5"
],
"dependencies": {
"@jellyfin/sdk": "^0.7.0",
"@react-native-async-storage/async-storage": "~1.17.3",
"@react-native-community/masked-view": "0.1.10",
"@react-navigation/bottom-tabs": "^6.0.7",
@ -82,6 +83,7 @@
"@testing-library/react-native": "^7.2.0",
"@types/jest": "^27.0.2",
"@types/react": "~17.0.21",
"@types/uuid": "^8.3.4",
"@typescript-eslint/eslint-plugin": "^5.16.0",
"@typescript-eslint/parser": "^4.33.0",
"babel-preset-expo": "~9.1.0",

View File

@ -7,10 +7,14 @@
// polyfill crypto.getRandomValues
import 'react-native-get-random-values';
import { action, decorate, observable } from 'mobx';
import { Jellyfin } from '@jellyfin/sdk';
import Constants from 'expo-constants';
import { action, computed, decorate, observable } from 'mobx';
import { ignore } from 'mobx-sync-lite';
import { v4 as uuidv4 } from 'uuid';
import { getAppName, getSafeDeviceName } from '../utils/Device';
import DownloadStore from './DownloadStore';
import MediaStore from './MediaStore';
import ServerStore from './ServerStore';
@ -47,6 +51,19 @@ export default class RootStore {
serverStore = new ServerStore()
settingStore = new SettingStore()
get sdk() {
return (new Jellyfin({
clientInfo: {
name: getAppName(),
version: Constants.nativeAppVersion
},
deviceInfo: {
name: getSafeDeviceName(),
id: this.deviceId
}
}));
}
reset() {
this.deviceId = uuidv4();
@ -69,5 +86,6 @@ decorate(RootStore, {
isFullscreen: [ ignore, observable ],
isReloadRequired: [ ignore, observable ],
didPlayerCloseManually: [ ignore, observable ],
sdk: computed,
reset: action
});