mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 02:48:38 -04:00
A new database is refreshed on startup (#827)
Download and Upload are disabled until a full refresh completes, and a fresh install never had one until the user found the refresh link. Startup now queues it, and the dashboard says why Download and Upload are disabled meanwhile. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
295b3ccfc8
commit
1554dfb268
6 changed files with 66 additions and 5 deletions
|
|
@ -94,8 +94,8 @@ These steps are the same for every install.
|
|||
3. **Trust the certificate.** Your browser will warn about the HTTPS certificate because it was made by your WROLPi,
|
||||
not by a public authority. Download https://wrolpi.local/ca.crt and trust it on each device you use; see
|
||||
*Help > Certificates* on your WROLPi for per-device instructions.
|
||||
4. **Refresh your files.** If you copied files onto the drive, the dashboard will ask you to refresh so WROLPi can find
|
||||
them. Your library looks empty until the refresh completes.
|
||||
4. **Wait for the first refresh.** WROLPi refreshes all your files when it first starts so it can find anything already
|
||||
on the drive. Your library looks empty, and Download and Upload are disabled, until the refresh completes.
|
||||
5. Start adding content: download a video channel, archive a web page, or add a Zim (Wikipedia) file. The *Help* page
|
||||
on your WROLPi explains each module.
|
||||
|
||||
|
|
|
|||
|
|
@ -252,6 +252,8 @@ export function Getters() {
|
|||
</div>
|
||||
<span className='wrolpi-or-label'>Or</span>
|
||||
</div>
|
||||
{status?.flags && gettersDisabled &&
|
||||
<p>Download and Upload are available after your files are refreshed.</p>}
|
||||
</Panel>;
|
||||
|
||||
let getterModal;
|
||||
|
|
|
|||
|
|
@ -21,11 +21,11 @@ jest.mock('./hooks/customHooks', () => ({
|
|||
useWROLMode: () => false,
|
||||
}));
|
||||
|
||||
function renderAtUrl(url) {
|
||||
function renderAtUrl(url, flags = {refresh_complete: true}) {
|
||||
// test-utils' render() wraps content in a BrowserRouter (not MemoryRouter),
|
||||
// so point the jsdom location at the desired URL before rendering.
|
||||
window.history.pushState({}, '', url);
|
||||
const status = {flags: {refresh_complete: true}};
|
||||
const status = {flags};
|
||||
return render(
|
||||
<StatusContext.Provider value={{status}}>
|
||||
<Getters/>
|
||||
|
|
@ -67,6 +67,25 @@ describe('DashboardPage Getters deep-link', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('DashboardPage Getters before the first refresh', () => {
|
||||
const note = /available after your files are refreshed/i;
|
||||
|
||||
it('explains why Download and Upload are disabled', () => {
|
||||
renderAtUrl('/', {refresh_complete: false});
|
||||
expect(screen.getByText(note)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('says nothing once the refresh has completed', () => {
|
||||
renderAtUrl('/');
|
||||
expect(screen.queryByText(note)).toBeNull();
|
||||
});
|
||||
|
||||
it('says nothing before the status has loaded', () => {
|
||||
renderAtUrl('/', null);
|
||||
expect(screen.queryByText(note)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
// The DownloadMenu's seed-object construction (joining initialUrls with
|
||||
// newlines into the `download` prop's `urls` field) is exercised in the
|
||||
// browser via manual verification, not unit-tested here — reaching into the
|
||||
|
|
|
|||
2
main.py
2
main.py
|
|
@ -310,6 +310,8 @@ async def start_single_tasks(app: Sanic):
|
|||
if flags.refresh_complete.is_set():
|
||||
# Set all downloads to new.
|
||||
download_manager.retry_downloads_on_startup()
|
||||
else:
|
||||
file_worker.queue_first_refresh()
|
||||
|
||||
# Hotspot/throttle on startup are now handled by the Controller service.
|
||||
|
||||
|
|
|
|||
|
|
@ -1949,3 +1949,29 @@ async def test_directory_refresh_deletes_stale_ignored_file_groups(
|
|||
test_session.expire_all()
|
||||
primary_paths = {str(i.primary_path) for i in test_session.query(FileGroup)}
|
||||
assert primary_paths == {str(video)}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_queue_first_refresh(async_client, test_session, test_directory, make_files_structure,
|
||||
await_background_tasks, flags_lock):
|
||||
"""A new database is refreshed without the user asking, so a fresh WROLPi can download right away."""
|
||||
make_files_structure(['docs/file1.txt'])
|
||||
flags.refresh_complete.clear()
|
||||
|
||||
assert file_worker.queue_first_refresh() is not None
|
||||
await await_background_tasks()
|
||||
|
||||
assert flags.refresh_complete.is_set() is True
|
||||
assert test_session.query(FileGroup).count() == 1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_queue_first_refresh_skipped(async_client, test_session, test_directory, wrol_mode_fixture,
|
||||
flags_lock):
|
||||
"""Only the first refresh is automatic, and WROL Mode never refreshes."""
|
||||
flags.refresh_complete.set()
|
||||
assert file_worker.queue_first_refresh() is None
|
||||
|
||||
flags.refresh_complete.clear()
|
||||
await wrol_mode_fixture(True)
|
||||
assert file_worker.queue_first_refresh() is None
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ from sqlalchemy import text, or_
|
|||
|
||||
from wrolpi import flags
|
||||
from wrolpi.common import apply_modelers, apply_refresh_cleanup
|
||||
from wrolpi.common import get_media_directory, logger
|
||||
from wrolpi.common import get_media_directory, logger, wrol_mode_enabled
|
||||
from wrolpi.db import get_db_session, get_db_curs
|
||||
from wrolpi.events import Events
|
||||
from wrolpi.vars import PYTEST
|
||||
|
|
@ -254,6 +254,18 @@ class FileWorker(FileMoveMixin, FileReorganizeMixin):
|
|||
self.public_queue.put_nowait(task)
|
||||
return job_id
|
||||
|
||||
def queue_first_refresh(self) -> str | None:
|
||||
"""Queue a refresh of the entire media directory if one has never completed for this database (a fresh
|
||||
install, or a rebuilt database). Downloads and uploads are disabled until it completes.
|
||||
|
||||
Returns:
|
||||
The job_id, or None if no refresh was queued.
|
||||
"""
|
||||
if flags.refresh_complete.is_set() or wrol_mode_enabled():
|
||||
return None
|
||||
logger.info('Refreshing all files because no refresh has completed')
|
||||
return self.queue_refresh([get_media_directory()])
|
||||
|
||||
def queue_move(self, destination: pathlib.Path, sources: list[pathlib.Path]) -> str:
|
||||
"""Queue a move task for background processing.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue