mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-03-14 15:06:06 +00:00
update version
This commit is contained in:
parent
b8c519d4a7
commit
837decd9bd
6 changed files with 83 additions and 22 deletions
|
|
@ -95,8 +95,8 @@ android {
|
|||
applicationId 'com.nuvio.app'
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 37
|
||||
versionName "1.4.1"
|
||||
versionCode 38
|
||||
versionName "1.4.2"
|
||||
|
||||
buildConfigField "String", "REACT_NATIVE_RELEASE_LEVEL", "\"${findProperty('reactNativeReleaseLevel') ?: 'stable'}\""
|
||||
}
|
||||
|
|
@ -118,7 +118,7 @@ android {
|
|||
def abiVersionCodes = ['armeabi-v7a': 1, 'arm64-v8a': 2, 'x86': 3, 'x86_64': 4]
|
||||
applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
def baseVersionCode = 37 // Current versionCode 37 from defaultConfig
|
||||
def baseVersionCode = 38 // Current versionCode 38 from defaultConfig
|
||||
def abiName = output.getFilter(com.android.build.OutputFile.ABI)
|
||||
|
||||
def versionCode = baseVersionCode * 100 // Base multiplier
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@
|
|||
<string name="expo_splash_screen_resize_mode" translatable="false">contain</string>
|
||||
<string name="expo_splash_screen_status_bar_translucent" translatable="false">false</string>
|
||||
<string name="expo_system_ui_user_interface_style" translatable="false">dark</string>
|
||||
<string name="expo_runtime_version">1.4.1</string>
|
||||
<string name="expo_runtime_version">1.4.2</string>
|
||||
</resources>
|
||||
8
app.json
8
app.json
|
|
@ -2,7 +2,7 @@
|
|||
"expo": {
|
||||
"name": "Nuvio",
|
||||
"slug": "nuvio",
|
||||
"version": "1.4.1",
|
||||
"version": "1.4.2",
|
||||
"orientation": "default",
|
||||
"backgroundColor": "#020404",
|
||||
"icon": "./assets/ios/AppIcon.appiconset/Icon-App-60x60@3x.png",
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
"ios": {
|
||||
"supportsTablet": true,
|
||||
"icon": "./assets/ios/AppIcon.appiconset/Icon-App-60x60@3x.png",
|
||||
"buildNumber": "37",
|
||||
"buildNumber": "38",
|
||||
"infoPlist": {
|
||||
"NSAppTransportSecurity": {
|
||||
"NSAllowsArbitraryLoads": true
|
||||
|
|
@ -60,7 +60,7 @@
|
|||
"android.permission.WRITE_SETTINGS"
|
||||
],
|
||||
"package": "com.nuvio.app",
|
||||
"versionCode": 37,
|
||||
"versionCode": 38,
|
||||
"architectures": [
|
||||
"arm64-v8a",
|
||||
"armeabi-v7a",
|
||||
|
|
@ -113,6 +113,6 @@
|
|||
"fallbackToCacheTimeout": 30000,
|
||||
"url": "https://ota.nuvioapp.space/api/manifest"
|
||||
},
|
||||
"runtimeVersion": "1.4.1"
|
||||
"runtimeVersion": "1.4.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.4.1</string>
|
||||
<string>1.4.2</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>37</string>
|
||||
<string>38</string>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>vlc</string>
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ export interface DownloadItem {
|
|||
sourceUrl: string; // stream url
|
||||
headers?: Record<string, string>;
|
||||
fileUri?: string; // local file uri once downloading/finished
|
||||
relativeFilePath?: string; // stable path under the app documents dir (survives sandbox path changes)
|
||||
createdAt: number;
|
||||
updatedAt: number;
|
||||
// Additional metadata for progress tracking
|
||||
|
|
@ -231,6 +232,55 @@ function toFileUri(pathOrUri: string): string {
|
|||
return pathOrUri;
|
||||
}
|
||||
|
||||
function normalizeRelativePath(path: string): string {
|
||||
return path.replace(/\\/g, '/').replace(/^\/+/, '');
|
||||
}
|
||||
|
||||
function getDocumentsDirPath(): string {
|
||||
return stripFileScheme(String((directories as any).documents || (FileSystem as any).documentDirectory || ''));
|
||||
}
|
||||
|
||||
function getRelativeDownloadPath(pathOrUri?: string | null): string | undefined {
|
||||
if (!pathOrUri) return undefined;
|
||||
|
||||
const withoutScheme = stripFileScheme(String(pathOrUri)).replace(/\\/g, '/').trim();
|
||||
if (!withoutScheme) return undefined;
|
||||
|
||||
const relativeCandidate = normalizeRelativePath(withoutScheme);
|
||||
if (!withoutScheme.startsWith('/') && relativeCandidate.startsWith('downloads/')) {
|
||||
return relativeCandidate;
|
||||
}
|
||||
|
||||
const downloadsMatch = withoutScheme.match(/(?:^|\/)(downloads\/.+)$/);
|
||||
if (downloadsMatch?.[1]) {
|
||||
return normalizeRelativePath(downloadsMatch[1]);
|
||||
}
|
||||
|
||||
const documentsDir = getDocumentsDirPath().replace(/\\/g, '/').replace(/\/+$/, '');
|
||||
if (documentsDir && withoutScheme.startsWith(`${documentsDir}/`)) {
|
||||
return normalizeRelativePath(withoutScheme.slice(documentsDir.length + 1));
|
||||
}
|
||||
|
||||
if (!withoutScheme.startsWith('/') && !withoutScheme.includes('://')) {
|
||||
return relativeCandidate;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function resolveDownloadFileUri(relativeFilePath?: string | null, fileUri?: string | null): string | undefined {
|
||||
const relativePath = getRelativeDownloadPath(relativeFilePath) || getRelativeDownloadPath(fileUri);
|
||||
if (relativePath) {
|
||||
const documentsDir = getDocumentsDirPath();
|
||||
if (documentsDir) {
|
||||
return toFileUri(`${documentsDir}/${relativePath}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (fileUri) return toFileUri(String(fileUri));
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export const DownloadsProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const [downloads, setDownloads] = useState<DownloadItem[]>([]);
|
||||
const downloadsRef = useRef(downloads);
|
||||
|
|
@ -272,7 +322,8 @@ export const DownloadsProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
|||
posterUrl: (d.posterUrl as any) ?? null,
|
||||
sourceUrl: String(d.sourceUrl ?? ''),
|
||||
headers: (d.headers as any) ?? undefined,
|
||||
fileUri: d.fileUri ? String(d.fileUri) : undefined,
|
||||
fileUri: resolveDownloadFileUri((d as any).relativeFilePath, d.fileUri),
|
||||
relativeFilePath: getRelativeDownloadPath((d as any).relativeFilePath) || getRelativeDownloadPath(d.fileUri),
|
||||
createdAt: typeof d.createdAt === 'number' ? d.createdAt : Date.now(),
|
||||
updatedAt: typeof d.updatedAt === 'number' ? d.updatedAt : Date.now(),
|
||||
// Restore metadata for progress tracking
|
||||
|
|
@ -463,6 +514,7 @@ export const DownloadsProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
|||
.done(({ location, bytesDownloaded, bytesTotal }: any) => {
|
||||
const finalPath = location ? String(location) : '';
|
||||
const finalUri = finalPath ? toFileUri(finalPath) : undefined;
|
||||
const relativeFilePath = getRelativeDownloadPath(finalPath || finalUri);
|
||||
|
||||
updateDownload(taskId, (d) => ({
|
||||
...d,
|
||||
|
|
@ -472,6 +524,7 @@ export const DownloadsProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
|||
progress: 100,
|
||||
updatedAt: Date.now(),
|
||||
fileUri: finalUri || d.fileUri,
|
||||
relativeFilePath: relativeFilePath || d.relativeFilePath,
|
||||
resumeData: undefined,
|
||||
}));
|
||||
|
||||
|
|
@ -539,7 +592,8 @@ export const DownloadsProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
|||
posterUrl: meta.posterUrl ?? null,
|
||||
sourceUrl: String(meta.sourceUrl ?? ''),
|
||||
headers: meta.headers,
|
||||
fileUri: meta.fileUri,
|
||||
fileUri: resolveDownloadFileUri(meta.relativeFilePath, meta.fileUri),
|
||||
relativeFilePath: getRelativeDownloadPath(meta.relativeFilePath) || getRelativeDownloadPath(meta.fileUri),
|
||||
createdAt,
|
||||
updatedAt: createdAt,
|
||||
imdbId: meta.imdbId,
|
||||
|
|
@ -564,11 +618,12 @@ export const DownloadsProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
|||
const list = downloadsRef.current;
|
||||
await Promise.all(
|
||||
list.map(async (d) => {
|
||||
if (!d.fileUri) return;
|
||||
const resolvedFileUri = resolveDownloadFileUri(d.relativeFilePath, d.fileUri);
|
||||
if (!resolvedFileUri) return;
|
||||
if (d.status === 'completed' || d.status === 'queued') return;
|
||||
|
||||
try {
|
||||
const info = await FileSystem.getInfoAsync(d.fileUri);
|
||||
const info = await FileSystem.getInfoAsync(resolvedFileUri);
|
||||
if (!info.exists || typeof info.size !== 'number') return;
|
||||
|
||||
let totalBytes = d.totalBytes;
|
||||
|
|
@ -588,6 +643,8 @@ export const DownloadsProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
|||
totalBytes: totalBytes || prev.totalBytes,
|
||||
progress: looksComplete ? 100 : Math.min(99, Math.max(prev.progress, progress)),
|
||||
status: looksComplete ? 'completed' : prev.status,
|
||||
fileUri: resolvedFileUri,
|
||||
relativeFilePath: prev.relativeFilePath || getRelativeDownloadPath(resolvedFileUri),
|
||||
resumeData: looksComplete ? undefined : prev.resumeData,
|
||||
updatedAt: Date.now(),
|
||||
}));
|
||||
|
|
@ -595,7 +652,7 @@ export const DownloadsProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
|||
if (looksComplete) {
|
||||
const done = downloadsRef.current.find(x => x.id === d.id);
|
||||
if (done) {
|
||||
notifyCompleted({ ...done, status: 'completed', progress: 100, fileUri: d.fileUri } as DownloadItem);
|
||||
notifyCompleted({ ...done, status: 'completed', progress: 100, fileUri: resolvedFileUri } as DownloadItem);
|
||||
stopLiveActivityForDownload(d.id, { title: done.title, subtitle: 'Completed', progressPercent: 100 });
|
||||
} else {
|
||||
stopLiveActivityForDownload(d.id, { subtitle: 'Completed', progressPercent: 100 });
|
||||
|
|
@ -693,7 +750,7 @@ export const DownloadsProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
|||
}
|
||||
}
|
||||
|
||||
const documentsDir = stripFileScheme(String((directories as any).documents || ''));
|
||||
const documentsDir = getDocumentsDirPath();
|
||||
if (!documentsDir) throw new Error('Downloads directory is not available');
|
||||
|
||||
const uniqueId = `${Date.now()}_${Math.random().toString(36).substring(7)}`;
|
||||
|
|
@ -713,6 +770,7 @@ export const DownloadsProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
|||
} catch { }
|
||||
|
||||
const fileUri = toFileUri(destinationPath);
|
||||
const relativeFilePath = getRelativeDownloadPath(destinationPath);
|
||||
|
||||
const createdAt = Date.now();
|
||||
const newItem: DownloadItem = {
|
||||
|
|
@ -737,6 +795,7 @@ export const DownloadsProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
|||
sourceUrl: input.url,
|
||||
headers: input.headers,
|
||||
fileUri,
|
||||
relativeFilePath,
|
||||
createdAt,
|
||||
updatedAt: createdAt,
|
||||
// Store metadata for progress tracking
|
||||
|
|
@ -770,6 +829,7 @@ export const DownloadsProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
|||
sourceUrl: input.url,
|
||||
headers: input.headers,
|
||||
fileUri,
|
||||
relativeFilePath,
|
||||
imdbId: input.imdbId,
|
||||
tmdbId: input.tmdbId,
|
||||
},
|
||||
|
|
@ -823,8 +883,9 @@ export const DownloadsProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
|||
}
|
||||
|
||||
const item = downloadsRef.current.find(d => d.id === id);
|
||||
if (item?.fileUri) {
|
||||
await FileSystem.deleteAsync(item.fileUri, { idempotent: true }).catch(() => { });
|
||||
const resolvedFileUri = resolveDownloadFileUri(item?.relativeFilePath, item?.fileUri);
|
||||
if (resolvedFileUri) {
|
||||
await FileSystem.deleteAsync(resolvedFileUri, { idempotent: true }).catch(() => { });
|
||||
}
|
||||
setDownloads(prev => prev.filter(d => d.id !== id));
|
||||
}, [stopLiveActivityForDownload]);
|
||||
|
|
@ -832,8 +893,9 @@ export const DownloadsProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
|||
const removeDownload = useCallback(async (id: string) => {
|
||||
const item = downloadsRef.current.find(d => d.id === id);
|
||||
await stopLiveActivityForDownload(id, { title: item?.title, subtitle: 'Removed', progressPercent: item?.progress });
|
||||
if (item?.fileUri && item.status === 'completed') {
|
||||
await FileSystem.deleteAsync(item.fileUri, { idempotent: true }).catch(() => { });
|
||||
const resolvedFileUri = resolveDownloadFileUri(item?.relativeFilePath, item?.fileUri);
|
||||
if (resolvedFileUri && item?.status === 'completed') {
|
||||
await FileSystem.deleteAsync(resolvedFileUri, { idempotent: true }).catch(() => { });
|
||||
}
|
||||
setDownloads(prev => prev.filter(d => d.id !== id));
|
||||
}, [stopLiveActivityForDownload]);
|
||||
|
|
@ -863,4 +925,3 @@ export function useDownloads(): DownloadsContextValue {
|
|||
return ctx;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Single source of truth for the app version displayed in Settings
|
||||
// Update this when bumping app version
|
||||
|
||||
export const APP_VERSION = '1.4.1';
|
||||
export const APP_VERSION = '1.4.2';
|
||||
|
||||
export function getDisplayedAppVersion(): string {
|
||||
return APP_VERSION;
|
||||
|
|
|
|||
Loading…
Reference in a new issue