smithery/chiraitori

Download System

Background chapter downloads and offline reading

Installation

$ npx skills add smithery/chiraitori --skill download-system

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/chiraitori.

npx skills add smithery/chiraitori

Browse all from smithery/chiraitori

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 4,420 B
  • docs SUMMARY.md 71 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

Download System

Download Service

import { downloadService } from '../services/downloadService';

// Download a chapter
await downloadService.downloadChapter(manga, chapter, sourceId);

// Download multiple chapters
await downloadService.downloadChapters(manga, chapters, sourceId);

// Cancel download
await downloadService.cancelDownload(chapterId);

// Get download status
const status = await downloadService.getDownloadStatus(chapterId);

// Get all downloads
const downloads = await downloadService.getAllDownloads();

// Delete downloaded chapter
await downloadService.deleteDownload(chapterId);

Download Status

type DownloadStatus = 
  | 'pending'
  | 'downloading'
  | 'completed'
  | 'failed'
  | 'cancelled';

interface Download {
  mangaId: string;
  chapterId: string;
  status: DownloadStatus;
  progress: number;      // 0-100
  totalPages: number;
  downloadedPages: number;
  error?: string;
}

Background Downloads (Android)

Uses react-native-background-actions:

import BackgroundService from 'react-native-background-actions';

const options = {
  taskName: 'Download',
  taskTitle: 'Downloading chapters',
  taskDesc: 'Chapter 1 of 10',
  taskIcon: { name: 'ic_launcher', type: 'mipmap' },
  progressBar: { max: 100, value: 0 },
};

await BackgroundService.start(downloadTask, options);
await BackgroundService.updateNotification({ taskDesc: 'Chapter 2 of 10' });
await BackgroundService.stop();

Foreground Service (Android 14+)

Required config in plugins/withBackgroundActionsServiceType.js:

// Adds foregroundServiceType="dataSync" to AndroidManifest.xml
module.exports = function withBackgroundActionsServiceType(config) {
  return withAndroidManifest(config, (config) => {
    // Modify service declaration
    return config;
  });
};

Headless Extension Runtime

When the app goes to background, the ExtensionRunner WebView may become unavailable. The headlessExtensionRuntime.ts provides a fallback that runs extensions directly in the React Native JS engine:

import { headlessRuntime } from '../services/headlessExtensionRuntime';

// Check if headless runtime is available
if (headlessRuntime.isAvailable()) {
  // Run extension method without WebView
  const result = await headlessRuntime.runExtensionMethod(
    extensionId,
    'getChapterDetails',
    [mangaId, chapterId]
  );
}

The headless runtime:

  • Executes extension JavaScript using eval() in a sandboxed environment
  • Provides mock App object with createRequestManager, createSourceStateManager, etc.
  • Uses native fetch for HTTP requests (no CORS in React Native)
  • Falls back automatically when WebView bridge is unavailable

Storage Structure

documentDirectory/
└── downloads/
    └── {mangaId}/
        └── {chapterId}/
            ├── page_001.jpg
            ├── page_002.jpg
            └── ...

File Operations

import * as FileSystem from 'expo-file-system';

const downloadDir = `${FileSystem.documentDirectory}downloads/`;

// Create directory
await FileSystem.makeDirectoryAsync(
  `${downloadDir}${mangaId}/${chapterId}`,
  { intermediates: true }
);

// Download image
await FileSystem.downloadAsync(
  imageUrl,
  `${downloadDir}${mangaId}/${chapterId}/page_${index}.jpg`
);

// Check if exists
const info = await FileSystem.getInfoAsync(path);
if (info.exists) { /* ... */ }

// Delete
await FileSystem.deleteAsync(path, { idempotent: true });

Download Queue

// Queue management
const queue: DownloadTask[] = [];
let isProcessing = false;

async function processQueue() {
  if (isProcessing || queue.length === 0) return;
  isProcessing = true;
  
  const task = queue.shift();
  await downloadChapter(task);
  
  isProcessing = false;
  processQueue(); // Process next
}

function addToQueue(task: DownloadTask) {
  queue.push(task);
  processQueue();
}

Notifications

import * as Notifications from 'expo-notifications';

// Show progress notification
await Notifications.scheduleNotificationAsync({
  content: {
    title: 'Downloading',
    body: `${manga.title} - Chapter ${chapter.chapNum}`,
    data: { mangaId, chapterId },
  },
  trigger: null, // Immediate
});