Ariver
2026-06-24 a5adc6d1d88cadeead81b79fd270501c243524c7
privatevoice.src/frontend/src/components/settings/LanguagePage.svelte
@@ -41,8 +41,30 @@
  Events.On('model:download-progress', (ev: any) => {
    const data = ev?.data
    if (data?.modelID && data.modelID === modelBusyID && typeof data.percent === 'number') {
    if (data?.modelID && typeof data.percent === 'number') {
      modelBusyID = data.modelID
      modelBusyKind = 'download'
      if (modelCancellingID !== data.modelID) {
        modelCancellingID = ''
      }
      modelProgress = data.percent
    }
  })
  Events.On('model:download-cancelled', (ev: any) => {
    const data = ev?.data
    if (data?.modelID) {
      modelBusyID = data.modelID
      modelBusyKind = 'download'
      modelCancellingID = data.modelID
    }
  })
  Events.On('model:download-finished', (ev: any) => {
    const data = ev?.data
    if (!data?.modelID || data.modelID === modelBusyID) {
      clearModelBusy()
      loadModelOptions()
    }
  })
@@ -52,6 +74,7 @@
      applyLanguageSettings(lang)
    } catch {}
    await loadModelOptions()
    await syncModelDownloadStatus()
    settingsLoaded = true
  }
  loadSettings()
@@ -92,6 +115,37 @@
    } catch {
      modelOptions = []
    }
  }
  async function syncModelDownloadStatus(): Promise<boolean> {
    try {
      const status: any = await Call.ByName('voicesnap/services.EngineService.GetModelDownloadStatus')
      return applyModelDownloadStatus(status)
    } catch {
      return false
    }
  }
  function applyModelDownloadStatus(status: any): boolean {
    if (status?.active && status?.modelID) {
      modelBusyID = status.modelID
      modelBusyKind = 'download'
      modelCancellingID = status.cancelling ? status.modelID : ''
      modelProgress = typeof status.percent === 'number' ? status.percent : 0
      modelError = ''
      return true
    }
    if (modelBusyKind === 'download') {
      clearModelBusy()
    }
    return false
  }
  function clearModelBusy() {
    modelBusyID = ''
    modelBusyKind = ''
    modelCancellingID = ''
    modelProgress = 0
  }
  function modelDescription(option: ModelOption): string {
@@ -144,8 +198,23 @@
  }
  function isCancelError(err: any): boolean {
    const text = String(err?.message || err || '').toLowerCase()
    const text = errorMessage(err).toLowerCase()
    return text.includes('context canceled') || text.includes('cancelled') || text.includes('canceled')
  }
  function isAlreadyDownloadingError(err: any): boolean {
    return errorMessage(err).toLowerCase().includes('already downloading')
  }
  function errorMessage(err: any): string {
    const raw = String(err?.message || err || '')
    if (!raw.startsWith('{')) return raw
    try {
      const parsed = JSON.parse(raw)
      return String(parsed?.message || raw)
    } catch {
      return raw
    }
  }
  function canCancelModel(option: ModelOption): boolean {
@@ -167,19 +236,15 @@
    try {
      const cancelled: any = await Call.ByName('voicesnap/services.EngineService.CancelModelDownload', option.modelID)
      if (!cancelled) {
        modelBusyID = ''
        modelBusyKind = ''
        modelCancellingID = ''
        modelProgress = 0
        clearModelBusy()
        await loadModelOptions()
        await syncModelDownloadStatus()
      }
    } catch (err: any) {
      modelError = err?.message || String(err || t('settings.modelActionFailed'))
      modelBusyID = ''
      modelBusyKind = ''
      modelCancellingID = ''
      modelProgress = 0
      modelError = errorMessage(err) || t('settings.modelActionFailed')
      clearModelBusy()
      await loadModelOptions()
      await syncModelDownloadStatus()
    }
  }
@@ -199,16 +264,21 @@
      }
      await loadModelOptions()
    } catch (err: any) {
      if (!isCancelError(err)) {
        modelError = err?.message || String(err || t('settings.modelActionFailed'))
      if (isAlreadyDownloadingError(err)) {
        const active = await syncModelDownloadStatus()
        if (!active) {
          modelError = errorMessage(err)
        }
      } else if (!isCancelError(err)) {
        modelError = errorMessage(err) || t('settings.modelActionFailed')
      }
      await loadModelOptions()
    } finally {
      if (modelBusyID === option.modelID) {
        modelBusyID = ''
        modelBusyKind = ''
        modelCancellingID = ''
        modelProgress = 0
        const active = await syncModelDownloadStatus()
        if (!active || modelBusyID !== option.modelID) {
          clearModelBusy()
        }
      }
    }
  }
@@ -232,6 +302,7 @@
      applyLanguageSettings(settings)
      await syncEngineForCurrentModel()
      await loadModelOptions()
      await syncModelDownloadStatus()
    } catch {
      languageModeVal = prevMode
      languageIDVal = prevID