update version

This commit is contained in:
tapframe 2026-03-13 09:08:02 +05:30
parent b8c519d4a7
commit 837decd9bd
6 changed files with 83 additions and 22 deletions

View file

@ -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

View file

@ -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>

View file

@ -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"
}
}

View file

@ -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>

View file

@ -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;
}

View file

@ -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;