feat(v5): Etappe W-6 — Setup, Selbst-Update, Erst-Einrichtung, Lizenzen
Ampel / ampel (push) Successful in 1m22s
Ampel / ampel (push) Successful in 1m22s
electron-builder → NSIS: EIN RippySetup-<version>.exe, pro Benutzer, ohne Adminrechte, eigenes Icon (PNG-basiertes ICO selbst gebaut). extraResources: die MITGELIEFERTEN Werkzeuge (HandBrakeCLI GPL-2, flac.exe MIT libFLAC.dll — die Ohne-DLL-Falle aus § 9) samt Lizenztexten und Quellverweisen (bau/lizenzen, Entscheid 9); vendor/ liegt nicht im Git (74 MB), Befuellung steht in BAUEN.md. Der Kern findet die mitgelieferten Werkzeuge ueber RIPPY_VENDOR (resourcesvendor) VOR allen anderen Suchwegen. haupt/update.ts (§ 6.8): electron-updater gegen das oeffentliche Gitea (festes Tag 'aktuell' als Rueckfallebene, Entscheid 6) — NUR https (http wird ABGELEHNT; Regel-Tests), NIE waehrend eines Rips (der Haupt kennt die aktiven Vorgaenge), Fehlschlaege still aber ablesbar (letzte erfolgreiche Pruefung im Status), installiert beim naechsten Start. haupt/autostart.ts: HKCU-Run ueber app.setLoginItemSettings, Toggle in den Einstellungen. Erst-Einrichtung (§ 6.9) im Programm: 5 Schritte, NUR ein Fehler blockiert (kein Laufwerk = Warnung, MakeMKV-Pflicht mit Klartext-Weg). GEBAUT UND GEMESSEN (30.08.2026): - dist-setup/RippySetup-5.0.0-w0.exe — 130,8 MB, SHA-256 07D39D6BAB74A47C21AA53D847FB5A5DB6990AE9FA1CBC7BE814B51050883115, plus .blockmap. - MESSUNG: Die Update-Auskunft heisst bei Vorab-Versionen nach dem KANAL (w0.yml) — erst ein Release ohne Suffix erzeugt latest.yml (steht jetzt in BAUEN.md). - Das GEPACKTE Programm (win-unpacked, asar + resources) besteht den Smoke: SMOKE OK, und der Screenshot zeigt die Erst-Einrichtung — das Bild eines frischen Rechners. Bewusst offen (SAVEPOINT): die automatische MakeMKV-Beschaffung (beschaffen.ts — Download+Installer-Start braucht einen Live-Test); das Setup NENNT den Weg klar. 185 Tests gruen. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
2d9cef22b3
commit
9fa3df500d
@@ -2,3 +2,9 @@
|
||||
node_modules/
|
||||
out/
|
||||
*.tsbuildinfo
|
||||
# Setup-Bau (electron-builder) und die MITGELIEFERTEN Binaerdateien —
|
||||
# vendor/ wird beim Paketieren aus LOCALAPPDATA\Rippy\tools befuellt
|
||||
# (BAUEN.md), 74 MB gehoeren nicht ins Git.
|
||||
dist-setup/
|
||||
vendor/
|
||||
.electron-cache/
|
||||
|
||||
@@ -48,6 +48,42 @@ vorhanden ist wie in Electrons Node (§ 3.4). Was zwingend Windows braucht
|
||||
(Smoke, später die Laufwerks-Messungen), wird lokal ausgeführt und im
|
||||
`SAVEPOINT.md` mit Ausgabe belegt.
|
||||
|
||||
## Das Setup bauen (W-6)
|
||||
|
||||
```
|
||||
npm run paket
|
||||
```
|
||||
|
||||
erzeugt `dist-setup/RippySetup-<version>.exe` (NSIS, pro Benutzer, ohne
|
||||
Adminrechte) plus `latest.yml` und `.blockmap` — **alle drei Dateien
|
||||
gehören zum Release** (§ 6.8: `latest.yml` ist die Update-Auskunft, die
|
||||
`.blockmap` macht aus dem Voll-Download einen Differenz-Download).
|
||||
|
||||
Vorher einmalig `vendor/` befüllen (die MITGELIEFERTEN Werkzeuge, § 9 —
|
||||
liegen nicht im Git, 74 MB):
|
||||
|
||||
```
|
||||
vendor/handbrake/HandBrakeCLI.exe (GPL-2 — Lizenztext liegt bei)
|
||||
vendor/flac/flac.exe + libFLAC.dll (Xiph — OHNE die DLL endet jeder
|
||||
Aufruf ohne eine Zeile Ausgabe)
|
||||
```
|
||||
|
||||
Quelle auf dieser Maschine: `%LOCALAPPDATA%\Rippy\tools\` (die rc11-
|
||||
Beschaffung hat sie dort abgelegt); Original-Quellen und Lizenzen stehen
|
||||
in `bau/lizenzen/QUELLEN.md`.
|
||||
|
||||
**Release veröffentlichen (Entscheid 6):** Die drei Dateien in ein
|
||||
Gitea-Release mit dem festen Tag `aktuell` hochladen (Dateien dort
|
||||
ERSETZEN) — `electron-updater` prüft
|
||||
`…/releases/download/aktuell/latest.yml`. Ob Gitea 1.27 zusätzlich das
|
||||
GitHub-Muster `releases/latest/download/…` bedient, ist am ersten echten
|
||||
Release zu messen (§ 3.6 ⚠); das feste Tag ist die Rückfallebene.
|
||||
|
||||
**SmartScreen (Entscheid 5):** Die EXE ist unsigniert. Beim ersten Start
|
||||
eines Downloads: „Weitere Informationen → Trotzdem ausführen". Beim
|
||||
Release Größe und SHA-256 mit angeben, damit Bekannte die echte Datei
|
||||
erkennen.
|
||||
|
||||
## Festgelegte Versionen und warum
|
||||
|
||||
- **Electron 44.0.0** — exakt die am 30.08.2026 gemessene Version
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 7.2 KiB |
@@ -0,0 +1,29 @@
|
||||
Copyright (C) 2000-2009 Josh Coalson
|
||||
Copyright (C) 2011-2025 Xiph.Org Foundation
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
- Neither the name of the Xiph.Org Foundation nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,338 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
<https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Moe Ghoul>, 1 April 1989
|
||||
Moe Ghoul, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
@@ -0,0 +1,25 @@
|
||||
# Mitgelieferte Werkzeuge — Lizenzen und Quellen
|
||||
|
||||
Rippy v5 liefert zwei Werkzeuge mit (KONZEPT-WINDOWS.md § 9). Weitergabe an
|
||||
Bekannte ist Weitergabe (Entscheid 9) — deshalb liegen die Lizenztexte und
|
||||
Quellverweise hier bei. MakeMKV wird NICHT mitgeliefert (proprietär); Rippy
|
||||
holt es beim Einrichten vom Hersteller.
|
||||
|
||||
## HandBrakeCLI
|
||||
|
||||
- Lizenz: GNU General Public License v2 — Volltext in
|
||||
`HANDBRAKE-GPL-2.0.txt` (bezogen von
|
||||
https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt am 30.08.2026).
|
||||
- Quelltext und Projekt: https://handbrake.fr bzw.
|
||||
https://github.com/HandBrake/HandBrake — der Quelltext der
|
||||
mitgelieferten Fassung ist dort erhältlich (GPL-2 § 3).
|
||||
|
||||
## FLAC (flac.exe + libFLAC.dll)
|
||||
|
||||
- Lizenz der Kommandozeilen-Werkzeuge/Bibliothek: BSD-artig — Volltext in
|
||||
`FLAC-COPYING.Xiph` (bezogen von
|
||||
https://raw.githubusercontent.com/xiph/flac/master/COPYING.Xiph am
|
||||
30.08.2026).
|
||||
- Projekt: https://xiph.org/flac/
|
||||
- ⚠ `flac.exe` braucht `libFLAC.dll` DANEBEN — mit nur der EXE endet
|
||||
jeder Aufruf ohne eine Zeile Ausgabe (§ 9, gemessen).
|
||||
@@ -0,0 +1,42 @@
|
||||
# Der Bau zum Setup (KONZEPT § 8): electron-builder → NSIS, EIN
|
||||
# RippySetup.exe. Pro Benutzer, ohne Administratorrechte, Ziel
|
||||
# %LOCALAPPDATA%\Programs\Rippy. Unsigniert (Entscheid 5) — SmartScreen-Weg
|
||||
# und Prüfsumme stehen in der Anleitung; Updates NUR über HTTPS (§ 6.8).
|
||||
appId: de.krbrz.rippy
|
||||
productName: Rippy
|
||||
directories:
|
||||
output: dist-setup
|
||||
buildResources: bau
|
||||
files:
|
||||
- out/**
|
||||
- package.json
|
||||
extraResources:
|
||||
# Mitgelieferte Werkzeuge (§ 9, Lizenzgründe in bau/lizenzen/QUELLEN.md)
|
||||
- from: vendor
|
||||
to: vendor
|
||||
- from: bau/lizenzen
|
||||
to: lizenzen
|
||||
- from: bau/tray.png
|
||||
to: bau/tray.png
|
||||
win:
|
||||
icon: bau/icon.ico
|
||||
target:
|
||||
- nsis
|
||||
nsis:
|
||||
oneClick: true
|
||||
perMachine: false
|
||||
# Drüber-Installieren funktioniert: Der Installer beendet die laufende
|
||||
# Fassung, bevor er kopiert (§ 8, der Rippy.exe.neu-Fund).
|
||||
allowToChangeInstallationDirectory: false
|
||||
deleteAppDataOnUninstall: false
|
||||
artifactName: RippySetup-${version}.${ext}
|
||||
publish:
|
||||
# Entscheid 6: das öffentliche Gitea, festes Update-Tag „aktuell" als
|
||||
# Rückfallebene (ob Gitea 1.27 releases/latest/download bedient, wird am
|
||||
# ersten echten Release gemessen — § 3.6 ⚠).
|
||||
provider: generic
|
||||
url: https://git.tobisniceshomelab.ddnsfree.com/Hitonabi/rippy/releases/download/aktuell
|
||||
electronDownload:
|
||||
# npm 11 blockt Electrons Install-Skript (§ 3.5) — electron-builder
|
||||
# lädt die Binärdateien selbst über diesen Cache-Weg.
|
||||
cache: .electron-cache
|
||||
Generated
+3440
-2
File diff suppressed because it is too large
Load Diff
@@ -13,9 +13,11 @@
|
||||
"build": "npm run typecheck && electron-vite build",
|
||||
"test": "vitest run",
|
||||
"smoke": "electron-vite build && electron . --smoke",
|
||||
"paket": "npm run build && electron-builder --win --config electron-builder.yml",
|
||||
"werkzeug:electron": "node node_modules/electron/install.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"electron-updater": "^6.8.9",
|
||||
"koffi": "^3.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -25,6 +27,7 @@
|
||||
"@types/react-dom": "^19.2.5",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
"electron": "44.0.0",
|
||||
"electron-builder": "^26.15.3",
|
||||
"electron-vite": "^5.0.0",
|
||||
"react": "^19.2.8",
|
||||
"react-dom": "^19.2.8",
|
||||
|
||||
@@ -52,4 +52,8 @@ contextBridge.exposeInMainWorld('rippy', {
|
||||
ordnerWaehlen(titel: string): Promise<string | null> {
|
||||
return ipcRenderer.invoke('ordner-waehlen', String(titel)) as Promise<string | null>
|
||||
},
|
||||
/** Autostart an/aus (§ 8) — liefert den tatsächlichen Zustand zurück. */
|
||||
autostartSetzen(an: boolean): Promise<boolean> {
|
||||
return ipcRenderer.invoke('autostart-setzen', an === true) as Promise<boolean>
|
||||
},
|
||||
})
|
||||
|
||||
@@ -248,7 +248,7 @@ function PresetWahl({
|
||||
)
|
||||
}
|
||||
|
||||
function Einstellungen({ verbindung }: { verbindung: Verbindung }) {
|
||||
function Einstellungen({ verbindung, haupt }: { verbindung: Verbindung; haupt: HauptStatus | null }) {
|
||||
const werte = verbindung.einstellungen
|
||||
const presets = verbindung.werkzeuge?.presets ?? []
|
||||
const setzen = einstellungSetzen
|
||||
@@ -353,6 +353,31 @@ function Einstellungen({ verbindung }: { verbindung: Verbindung }) {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="space-y-3">
|
||||
<h2 className="text-xs font-semibold uppercase tracking-widest text-slate-500">Programm</h2>
|
||||
<label className="flex items-center gap-2 text-xs text-slate-300">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={haupt?.autostart === true}
|
||||
onChange={(e) => void window.rippy.autostartSetzen(e.target.checked)}
|
||||
/>
|
||||
Rippy mit Windows starten (HKCU\…\Run — kein Dienst, keine Adminrechte)
|
||||
</label>
|
||||
<Feld
|
||||
name="Update-Adresse"
|
||||
hinweis="leer = Gitea des Commanders; NUR https:// wird angenommen (§ 6.8)"
|
||||
wert={werte['updateUrl'] ?? ''}
|
||||
aufAendern={(w) => setzen('updateUrl', w)}
|
||||
/>
|
||||
{haupt !== null && (
|
||||
<p className="font-mono text-xs text-slate-400">
|
||||
Selbst-Update: {haupt.update.text}
|
||||
{haupt.update.letzteErfolgreichePruefung.length > 0 &&
|
||||
` · letzte erfolgreiche Prüfung: ${haupt.update.letzteErfolgreichePruefung.slice(0, 16).replace('T', ' ')}`}
|
||||
</p>
|
||||
)}
|
||||
</section>
|
||||
|
||||
<section className="space-y-3">
|
||||
<h2 className="text-xs font-semibold uppercase tracking-widest text-slate-500">Werkzeuge</h2>
|
||||
<div className="space-y-1 rounded-xl border border-slate-700/60 bg-slate-800/40 p-4 font-mono text-xs">
|
||||
@@ -418,6 +443,169 @@ function Bibliothek({ verbindung }: { verbindung: Verbindung }) {
|
||||
)
|
||||
}
|
||||
|
||||
// ── Erst-Einrichtung (§ 6.9): nach der Installation, im Programm ────────
|
||||
// Zwei Regeln aus dem alten Setup: NUR ein Fehler blockiert (kein
|
||||
// Laufwerk ist eine WARNUNG — eine reine Komprimier-Maschine ist ein
|
||||
// vorgesehener Betriebsfall), und Erfolg wird am BESTAND gemessen.
|
||||
function ErstEinrichtung({ verbindung }: { verbindung: Verbindung }) {
|
||||
const [schritt, setSchritt] = useState(0)
|
||||
const werkzeuge = verbindung.werkzeuge
|
||||
const makemkvFehlt = werkzeuge !== null && werkzeuge.makemkvcon.length === 0
|
||||
|
||||
const pruefzeilen: Array<{ ok: boolean | null; pflicht: boolean; text: string }> = [
|
||||
{
|
||||
ok: werkzeuge === null ? null : werkzeuge.makemkvcon.length > 0,
|
||||
pflicht: true,
|
||||
text:
|
||||
werkzeuge === null
|
||||
? 'MakeMKV wird gesucht …'
|
||||
: werkzeuge.makemkvcon.length > 0
|
||||
? `MakeMKV gefunden: ${werkzeuge.makemkvcon}`
|
||||
: 'MakeMKV FEHLT — ohne makemkvcon kann Rippy keine Video-Disc lesen. Von makemkv.com/download installieren, dann findet Rippy es von selbst.',
|
||||
},
|
||||
{
|
||||
ok: werkzeuge === null ? null : werkzeuge.handbrake.length > 0,
|
||||
pflicht: false,
|
||||
text:
|
||||
werkzeuge === null
|
||||
? 'HandBrakeCLI wird gesucht …'
|
||||
: werkzeuge.handbrake.length > 0
|
||||
? `HandBrakeCLI dabei: ${werkzeuge.handbrake}`
|
||||
: 'HandBrakeCLI fehlt — Rippen geht, Komprimieren nicht.',
|
||||
},
|
||||
{
|
||||
ok: werkzeuge === null ? null : werkzeuge.flac.length > 0,
|
||||
pflicht: false,
|
||||
text:
|
||||
werkzeuge === null
|
||||
? 'FLAC wird gesucht …'
|
||||
: werkzeuge.flac.length > 0
|
||||
? `FLAC dabei: ${werkzeuge.flac}`
|
||||
: 'FLAC fehlt — nur für Audio-CDs nötig.',
|
||||
},
|
||||
{
|
||||
ok: verbindung.laufwerke.length > 0 ? true : null,
|
||||
pflicht: false,
|
||||
text:
|
||||
verbindung.laufwerke.length > 0
|
||||
? `${verbindung.laufwerke.length} optisches Laufwerk gefunden (${verbindung.laufwerke.map((l) => l.id + ':').join(', ')})`
|
||||
: 'Kein optisches Laufwerk — nur eine Warnung: Eine reine Komprimier-Maschine ist ein vorgesehener Betriebsfall.',
|
||||
},
|
||||
]
|
||||
|
||||
const schritte = ['Willkommen', 'Prüfung', 'Ordner', 'Automatik', 'Fertig']
|
||||
|
||||
return (
|
||||
<div className="mx-auto max-w-xl space-y-6">
|
||||
<div className="flex gap-2">
|
||||
{schritte.map((name, i) => (
|
||||
<span
|
||||
key={name}
|
||||
className={`rounded-full px-3 py-1 text-xs ${i === schritt ? 'bg-sky-500/20 text-sky-300' : i < schritt ? 'text-emerald-400' : 'text-slate-500'}`}
|
||||
>
|
||||
{i + 1} {name}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{schritt === 0 && (
|
||||
<div className="space-y-3 text-sm text-slate-300">
|
||||
<h2 className="text-xl font-bold text-slate-100">Willkommen bei Rippy</h2>
|
||||
<p>Rippy sichert deine DVDs, Blu-rays und Audio-CDs — erst verlustfrei, dann komprimiert.</p>
|
||||
<p>Disc rein genügt: Rippy erkennt, was drauf ist, und sagt Bescheid.</p>
|
||||
<p>Fertige Filme landen so abgelegt, dass Jellyfin, Emby, Kodi und Plex sie sofort erkennen.</p>
|
||||
<p>Dieses Setup stellt vier Dinge ein und dauert eine Minute.</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{schritt === 1 && (
|
||||
<div className="space-y-2">
|
||||
{pruefzeilen.map((zeile, i) => (
|
||||
<p
|
||||
key={i}
|
||||
className={`font-mono text-xs ${zeile.ok === true ? 'text-emerald-300' : zeile.ok === null ? 'text-slate-400' : zeile.pflicht ? 'text-red-300' : 'text-amber-300'}`}
|
||||
>
|
||||
{zeile.ok === true ? '✓' : zeile.ok === null ? '…' : zeile.pflicht ? '✗' : '⚠'} {zeile.text}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{schritt === 2 && (
|
||||
<div className="space-y-3">
|
||||
<p className="text-sm text-slate-300">Wohin sollen fertige Filme, Musik und Abbilder?</p>
|
||||
<p className="break-all font-mono text-xs text-slate-400">{verbindung.einstellungen['ablage'] ?? ''}</p>
|
||||
<button
|
||||
onClick={() =>
|
||||
void window.rippy.ordnerWaehlen('Ablage-Ordner wählen').then((ordner) => {
|
||||
if (ordner !== null) einstellungSetzen('ablage', ordner)
|
||||
})
|
||||
}
|
||||
className="rounded-lg border border-slate-600 px-3 py-1.5 text-xs text-slate-300 hover:bg-slate-700"
|
||||
>
|
||||
Ordner wählen … (auch Netzlaufwerke)
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{schritt === 3 && (
|
||||
<div className="space-y-3">
|
||||
<p className="text-sm text-slate-300">Wie viel soll Rippy allein entscheiden?</p>
|
||||
<label className="flex items-center gap-2 text-xs text-slate-300">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={verbindung.einstellungen['transcodeEnabled'] !== 'false'}
|
||||
onChange={(e) => einstellungSetzen('transcodeEnabled', e.target.checked ? 'true' : 'false')}
|
||||
/>
|
||||
Nach dem Rip automatisch komprimieren (je Disc-Typ später fein einstellbar)
|
||||
</label>
|
||||
<p className="text-xs text-slate-500">
|
||||
Alles Weitere — Presets je Disc-Typ, Sprachen, Medienserver, API-Schlüssel — steht unter
|
||||
„Einstellungen".
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{schritt === 4 && (
|
||||
<div className="space-y-3 text-sm text-slate-300">
|
||||
<p>Das war's. Disc einlegen — Rippy meldet sich.</p>
|
||||
{makemkvFehlt && (
|
||||
<p className="text-xs text-red-300">
|
||||
Denk an MakeMKV: Ohne makemkvcon bleibt der Video-Weg zu (Einstellungen → Werkzeuge zeigt
|
||||
den Stand, sobald es installiert ist).
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex justify-between">
|
||||
<button
|
||||
onClick={() => setSchritt((s) => Math.max(0, s - 1))}
|
||||
disabled={schritt === 0}
|
||||
className="rounded-lg border border-slate-600 px-4 py-1.5 text-xs text-slate-300 hover:bg-slate-700 disabled:opacity-40"
|
||||
>
|
||||
Zurück
|
||||
</button>
|
||||
{schritt < schritte.length - 1 ? (
|
||||
<button
|
||||
onClick={() => setSchritt((s) => s + 1)}
|
||||
className="rounded-lg border border-sky-500/60 px-4 py-1.5 text-xs text-sky-300 hover:bg-sky-500/10"
|
||||
>
|
||||
Weiter
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
onClick={() => einstellungSetzen('setup.done', 'true')}
|
||||
className="rounded-lg border border-emerald-500/60 px-4 py-1.5 text-xs text-emerald-300 hover:bg-emerald-500/10"
|
||||
>
|
||||
Los geht's
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type Bereich = 'uebersicht' | 'einstellungen' | 'bibliothek'
|
||||
|
||||
export default function App() {
|
||||
@@ -447,6 +635,11 @@ export default function App() {
|
||||
|
||||
const db = verbindung.kern?.datenbank ?? null
|
||||
|
||||
// § 6.9: Die Erst-Einrichtung läuft NACH der Installation, im Programm —
|
||||
// solange sie nicht abgeschlossen ist, ist sie die ganze Ansicht.
|
||||
const setupOffen =
|
||||
Object.keys(verbindung.einstellungen).length > 0 && verbindung.einstellungen['setup.done'] !== 'true'
|
||||
|
||||
const knopf = (ziel: Bereich, text: string): React.JSX.Element => (
|
||||
<button
|
||||
onClick={() => setBereich(ziel)}
|
||||
@@ -477,7 +670,8 @@ export default function App() {
|
||||
</header>
|
||||
|
||||
<main className="flex-1 space-y-6 p-6">
|
||||
{bereich === 'uebersicht' && (
|
||||
{setupOffen && <ErstEinrichtung verbindung={verbindung} />}
|
||||
{!setupOffen && bereich === 'uebersicht' && (
|
||||
<>
|
||||
<section>
|
||||
<h2 className="mb-3 text-xs font-semibold uppercase tracking-widest text-slate-500">
|
||||
@@ -581,8 +775,8 @@ export default function App() {
|
||||
</>
|
||||
)}
|
||||
|
||||
{bereich === 'einstellungen' && <Einstellungen verbindung={verbindung} />}
|
||||
{bereich === 'bibliothek' && <Bibliothek verbindung={verbindung} />}
|
||||
{!setupOffen && bereich === 'einstellungen' && <Einstellungen verbindung={verbindung} haupt={haupt} />}
|
||||
{!setupOffen && bereich === 'bibliothek' && <Bibliothek verbindung={verbindung} />}
|
||||
</main>
|
||||
|
||||
<footer className="border-t border-slate-700/60 px-6 py-3 text-center text-xs text-slate-500">
|
||||
|
||||
+1
@@ -8,6 +8,7 @@ declare global {
|
||||
aufStatus(horcher: (status: unknown) => void): void
|
||||
melden(art: string): void
|
||||
ordnerWaehlen(titel: string): Promise<string | null>
|
||||
autostartSetzen(an: boolean): Promise<boolean>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,6 +131,9 @@ export interface HauptStatus {
|
||||
/** Prozess-Leine (Job Object, § 3.3): gesetzt oder mit Fehlertext. */
|
||||
leine: { ok: boolean; fehler?: string }
|
||||
kern: { laeuft: boolean; pid?: number; neustarts: number }
|
||||
autostart: boolean
|
||||
/** Selbst-Update (§ 6.8): letzter Prüf-Erfolg + Klartext-Stand. */
|
||||
update: { letzteErfolgreichePruefung: string; text: string }
|
||||
}
|
||||
|
||||
function istObjekt(wert: unknown): wert is Record<string, unknown> {
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// Autostart über HKCU\...\Run (KONZEPT § 8) — via Electrons eigener
|
||||
// API (app.setLoginItemSettings nutzt unter Windows genau diesen
|
||||
// Registry-Zweig). Kein Dienst: Ein Dienst kostete bei jeder Installation
|
||||
// eine Administrator-Abfrage und könnte kein Tray zeigen.
|
||||
import { app } from 'electron'
|
||||
|
||||
export function autostartSetzen(an: boolean): void {
|
||||
app.setLoginItemSettings({ openAtLogin: an })
|
||||
}
|
||||
|
||||
export function autostartAktiv(): boolean {
|
||||
return app.getLoginItemSettings().openAtLogin
|
||||
}
|
||||
@@ -12,10 +12,12 @@ import { app, dialog, ipcMain, Notification, type BrowserWindow } from 'electron
|
||||
import { writeFile } from 'node:fs/promises'
|
||||
import { join } from 'node:path'
|
||||
import type { HauptStatus, KernNachricht } from '../gemeinsam/nachrichten'
|
||||
import { autostartAktiv, autostartSetzen } from './autostart'
|
||||
import { fensterErstellen } from './fenster'
|
||||
import { KernVerwaltung } from './kernstart'
|
||||
import { leineSetzen, type LeineStatus } from './leine'
|
||||
import { trayErstellen, trayHinweis } from './tray'
|
||||
import { updateEinrichten, updateStand } from './update'
|
||||
|
||||
const smokeModus = process.argv.includes('--smoke')
|
||||
|
||||
@@ -44,6 +46,10 @@ function start(): void {
|
||||
let fenster: BrowserWindow | null = null
|
||||
let kernBereit: Extract<KernNachricht, { art: 'kern-bereit' }> | null = null
|
||||
let beendenGewollt = smokeModus
|
||||
// § 6.8 Regel 2: Der Haupt weiß, ob irgendwo gerippt wird — Updates
|
||||
// warten dann.
|
||||
const aktiveVorgaenge = new Set<string>()
|
||||
let updateGestartet = false
|
||||
|
||||
const smoke = {
|
||||
fenster: false,
|
||||
@@ -67,6 +73,17 @@ function start(): void {
|
||||
smokePruefen()
|
||||
} else if (nachricht.art === 'rip-status') {
|
||||
ripStatusVerarbeiten(nachricht.status)
|
||||
} else if (nachricht.art === 'einstellungen') {
|
||||
// Einmalig das Selbst-Update einrichten — die Adresse kommt aus
|
||||
// der EINEN Einstellungs-Quelle im Kern (§ 6.7/§ 6.8).
|
||||
if (!updateGestartet && !smokeModus) {
|
||||
updateGestartet = true
|
||||
updateEinrichten(
|
||||
nachricht.werte['updateUrl'] ?? '',
|
||||
() => aktiveVorgaenge.size > 0,
|
||||
(text) => toast('Rippy-Update', text),
|
||||
)
|
||||
}
|
||||
} else if (nachricht.art === 'disc-info' && nachricht.info.titel.length > 0 && !smokeModus) {
|
||||
// Windows-Toast beim Erkennen (§ 7) — Klick holt das Fenster hoch.
|
||||
toast(
|
||||
@@ -85,6 +102,11 @@ function start(): void {
|
||||
// § 7: Toasts, Taskleisten-Fortschritt und Tray-Zustand aus dem
|
||||
// Rip-Status — die Information, die man will, ohne das Fenster zu öffnen.
|
||||
function ripStatusVerarbeiten(status: Extract<KernNachricht, { art: 'rip-status' }>['status']): void {
|
||||
if (status.phase === 'rippt' || status.phase === 'komprimiert' || status.phase === 'legt-ab' || status.phase === 'liest-info') {
|
||||
aktiveVorgaenge.add(status.id)
|
||||
} else {
|
||||
aktiveVorgaenge.delete(status.id)
|
||||
}
|
||||
if (fenster !== null && !fenster.isDestroyed()) {
|
||||
if ((status.phase === 'rippt' || status.phase === 'komprimiert') && status.prozent >= 0) {
|
||||
fenster.setProgressBar(status.prozent / 100)
|
||||
@@ -126,6 +148,8 @@ function start(): void {
|
||||
version: app.getVersion(),
|
||||
leine,
|
||||
kern: kernVerwaltung.zustand(),
|
||||
autostart: autostartAktiv(),
|
||||
update: updateStand(),
|
||||
}
|
||||
fenster.webContents.send('haupt-status', status)
|
||||
}
|
||||
@@ -173,6 +197,13 @@ function start(): void {
|
||||
}
|
||||
})
|
||||
|
||||
// Autostart (§ 8): HKCU\...\Run über Electrons eigene API.
|
||||
ipcMain.handle('autostart-setzen', (_ereignis, an: unknown) => {
|
||||
autostartSetzen(an === true)
|
||||
statusSenden()
|
||||
return autostartAktiv()
|
||||
})
|
||||
|
||||
// Echter Windows-Ordnerdialog (§ 7) — der Nutzer sieht seine
|
||||
// Netzlaufwerke und Schnellzugriffe statt eines nachgebauten Browsers.
|
||||
ipcMain.handle('ordner-waehlen', async (_ereignis, titel: unknown) => {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// utilityProcess statt child_process.fork ist Electrons eigene Empfehlung;
|
||||
// nur er kann einen MessagePort DIREKT zwischen Kern und Fenster aufspannen
|
||||
// (Electron-Doku »utilityProcess« / »MessagePorts in Electron«).
|
||||
import { MessageChannelMain, utilityProcess, type UtilityProcess, type WebContents } from 'electron'
|
||||
import { app, MessageChannelMain, utilityProcess, type UtilityProcess, type WebContents } from 'electron'
|
||||
import { join } from 'node:path'
|
||||
import { istKernNachricht, type HauptNachricht, type KernNachricht } from '../gemeinsam/nachrichten'
|
||||
|
||||
@@ -35,9 +35,16 @@ export class KernVerwaltung {
|
||||
// Der Smoke-Beweis läuft ohne Netz und ohne MakeMKV — der Kern lässt
|
||||
// die Schlüsselkette dann aus.
|
||||
if (this.smokeModus) args.push('--smoke-kern')
|
||||
// Im installierten Rippy liegen die mitgelieferten Werkzeuge unter
|
||||
// resources\vendor — der Kern findet sie über RIPPY_VENDOR (§ 9).
|
||||
const umgebung: NodeJS.ProcessEnv = { ...process.env }
|
||||
if (app.isPackaged) {
|
||||
umgebung['RIPPY_VENDOR'] = join(process.resourcesPath, 'vendor')
|
||||
}
|
||||
const kern = utilityProcess.fork(kernDatei, args, {
|
||||
serviceName: 'Rippy-Kern',
|
||||
stdio: 'inherit',
|
||||
env: umgebung,
|
||||
})
|
||||
this.kern = kern
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// Die reinen Update-Regeln (§ 6.8) — getrennt von update.ts, damit die
|
||||
// Tests sie ohne Electron laden können.
|
||||
|
||||
export const STANDARD_UPDATE_URL =
|
||||
'https://git.tobisniceshomelab.ddnsfree.com/Hitonabi/rippy/releases/download/aktuell'
|
||||
|
||||
/** Prüft die Update-Adresse: NUR https:// ist zulässig (§ 6.8 Regel 1) —
|
||||
* ohne Code-Signing (Entscheid 5) ist der Transportweg die einzige
|
||||
* Absicherung; eine http://-Adresse wird ABGELEHNT, nicht still
|
||||
* akzeptiert. */
|
||||
export function updateUrlPruefen(url: string): { ok: boolean; grund: string } {
|
||||
const sauber = url.trim()
|
||||
if (sauber.length === 0) return { ok: true, grund: '' } // Standard gilt
|
||||
if (sauber.startsWith('https://')) return { ok: true, grund: '' }
|
||||
return {
|
||||
ok: false,
|
||||
grund:
|
||||
'Die Update-Adresse muss mit https:// beginnen — ohne Signatur ist der sichere Transportweg die einzige Absicherung (KONZEPT § 6.8).',
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// Selbst-Update (KONZEPT § 6.8) — electron-updater gegen die Releases im
|
||||
// öffentlichen Gitea (Entscheid 6). Die vier Regeln:
|
||||
// 1. NUR HTTPS — ohne Code-Signing (Entscheid 5) ist der Transportweg
|
||||
// die einzige Absicherung; eine http://-Adresse wird ABGELEHNT.
|
||||
// 2. NIE während eines Rips — weder laden noch installieren.
|
||||
// 3. Ein Fehlschlag ist sichtbar, aber nicht laut: Der Zeitpunkt der
|
||||
// letzten erfolgreichen Prüfung steht im Status.
|
||||
// 4. Die laufende Version ist ablesbar (Fenster + Protokollkopf).
|
||||
import { app } from 'electron'
|
||||
import electronUpdater from 'electron-updater'
|
||||
import { updateUrlPruefen } from './update-regeln'
|
||||
|
||||
export { STANDARD_UPDATE_URL, updateUrlPruefen } from './update-regeln'
|
||||
|
||||
const { autoUpdater } = electronUpdater
|
||||
|
||||
export interface UpdateStand {
|
||||
/** Wann die letzte Prüfung GELANG — '' wenn noch nie. */
|
||||
letzteErfolgreichePruefung: string
|
||||
text: string
|
||||
}
|
||||
|
||||
const stand: UpdateStand = { letzteErfolgreichePruefung: '', text: 'noch nicht geprüft' }
|
||||
|
||||
export function updateStand(): UpdateStand {
|
||||
return { ...stand }
|
||||
}
|
||||
|
||||
export function updateEinrichten(
|
||||
eigeneUrl: string,
|
||||
ripLaeuft: () => boolean,
|
||||
melden: (text: string) => void,
|
||||
): void {
|
||||
if (!app.isPackaged) {
|
||||
stand.text = 'Entwicklungslauf — Selbst-Update ist nur im installierten Rippy aktiv.'
|
||||
return
|
||||
}
|
||||
const pruefung = updateUrlPruefen(eigeneUrl)
|
||||
if (!pruefung.ok) {
|
||||
stand.text = pruefung.grund
|
||||
melden(pruefung.grund)
|
||||
return
|
||||
}
|
||||
if (eigeneUrl.trim().length > 0) {
|
||||
autoUpdater.setFeedURL({ provider: 'generic', url: eigeneUrl.trim() })
|
||||
}
|
||||
autoUpdater.autoDownload = false
|
||||
// Installiert wird beim NÄCHSTEN Start — nie mitten in einem Rip.
|
||||
autoUpdater.autoInstallOnAppQuit = true
|
||||
|
||||
autoUpdater.on('update-available', () => {
|
||||
if (ripLaeuft()) {
|
||||
// Regel 2: nicht einmal herunterladen, solange gerippt wird — der
|
||||
// tägliche Takt versucht es wieder.
|
||||
stand.text = 'Update verfügbar — wird geladen, sobald kein Rip läuft.'
|
||||
return
|
||||
}
|
||||
void autoUpdater.downloadUpdate().catch((fehler: unknown) => {
|
||||
stand.text = `Update-Download fehlgeschlagen: ${String(fehler)}`
|
||||
})
|
||||
})
|
||||
autoUpdater.on('update-downloaded', (info) => {
|
||||
stand.text = `Version ${info.version} liegt bereit — sie wird beim nächsten Start installiert.`
|
||||
melden(stand.text)
|
||||
})
|
||||
autoUpdater.on('update-not-available', () => {
|
||||
stand.letzteErfolgreichePruefung = new Date().toISOString()
|
||||
stand.text = 'Rippy ist aktuell.'
|
||||
})
|
||||
autoUpdater.on('error', (fehler) => {
|
||||
// Regel 3: still scheitern (WLAN weg, Server aus) — aber ablesbar.
|
||||
stand.text = `Letzte Prüfung scheiterte: ${String(fehler).slice(0, 160)}`
|
||||
})
|
||||
|
||||
const pruefen = (): void => {
|
||||
if (ripLaeuft()) return
|
||||
void autoUpdater.checkForUpdates().catch(() => {
|
||||
// der error-Handler oben trägt den Grund ein
|
||||
})
|
||||
}
|
||||
pruefen()
|
||||
setInterval(pruefen, 24 * 60 * 60 * 1000).unref?.()
|
||||
}
|
||||
@@ -261,6 +261,8 @@ const ERLAUBTE_EINSTELLUNGEN = new Set([
|
||||
'tmdbApiKey',
|
||||
'omdbApiKey',
|
||||
'makemkvAppKey',
|
||||
'updateUrl',
|
||||
'setup.done',
|
||||
'autoAuswurf',
|
||||
'werkzeug.makemkv',
|
||||
'werkzeug.handbrake',
|
||||
@@ -273,7 +275,9 @@ function einstellungenSenden(): void {
|
||||
const wert = db?.einstellungLesen(schluessel)
|
||||
if (wert !== null && wert !== undefined) werte[schluessel] = wert
|
||||
}
|
||||
anFenster({ art: 'einstellungen', werte })
|
||||
// AUCH ans Haupt: Es braucht die Update-Adresse (§ 6.8) — die eine
|
||||
// Einstellungs-Quelle bleibt trotzdem der Kern.
|
||||
anHauptUndFenster({ art: 'einstellungen', werte })
|
||||
}
|
||||
|
||||
// ── MakeMKV-Schlüsselkette (§ 9.1): beim Start und täglich ──────────────
|
||||
@@ -400,6 +404,8 @@ function bereitNachricht(): KernNachricht {
|
||||
}
|
||||
|
||||
process.parentPort.postMessage(bereitNachricht())
|
||||
// Das Haupt braucht die Einstellungen (Update-Adresse) auch ohne Fenster.
|
||||
einstellungenSenden()
|
||||
// Nicht im Smoke: Der Schlüssel-Lauf spricht Forum + makemkvcon an —
|
||||
// der Gerüst-Beweis soll ohne Netz und ohne MakeMKV grün sein.
|
||||
if (!process.argv.includes('--smoke-kern') && process.env['RIPPY_OHNE_SCHLUESSEL'] !== '1') {
|
||||
|
||||
@@ -92,6 +92,14 @@ export function kandidaten(
|
||||
): string[] {
|
||||
const werkzeug = WERKZEUGE[name]
|
||||
const pfade: string[] = []
|
||||
// Das MITGELIEFERTE Werkzeug (§ 9) — im installierten Rippy zeigt
|
||||
// RIPPY_VENDOR auf resources\vendor (der Haupt setzt es beim Kern-Start).
|
||||
const vendor = umgebung['RIPPY_VENDOR'] ?? ''
|
||||
if (vendor.length > 0) {
|
||||
for (const programm of werkzeug.programme) {
|
||||
pfade.push(join(vendor, name, programm))
|
||||
}
|
||||
}
|
||||
for (const programm of werkzeug.programme) {
|
||||
pfade.push(join(eigenerOrdner, name, programm))
|
||||
pfade.push(join(eigenerOrdner, programm))
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// Tests der Update-Regeln (§ 6.8) — die HTTPS-Pflicht ist keine Kür:
|
||||
// Ohne Code-Signing (Entscheid 5) ist der Transportweg die EINZIGE
|
||||
// Absicherung des Selbst-Updates.
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { updateUrlPruefen, STANDARD_UPDATE_URL } from '../src/haupt/update-regeln'
|
||||
|
||||
describe('updateUrlPruefen', () => {
|
||||
it('leer heißt Standard (der ist https)', () => {
|
||||
expect(updateUrlPruefen('').ok).toBe(true)
|
||||
expect(STANDARD_UPDATE_URL.startsWith('https://')).toBe(true)
|
||||
})
|
||||
it('https wird angenommen', () => {
|
||||
expect(updateUrlPruefen('https://mein-server.example/rippy').ok).toBe(true)
|
||||
})
|
||||
it('http wird ABGELEHNT, nicht stillschweigend akzeptiert', () => {
|
||||
const urteil = updateUrlPruefen('http://mein-server.example/rippy')
|
||||
expect(urteil.ok).toBe(false)
|
||||
expect(urteil.grund).toContain('https://')
|
||||
})
|
||||
it('anderes Zeug wird abgelehnt', () => {
|
||||
expect(updateUrlPruefen('ftp://x').ok).toBe(false)
|
||||
expect(updateUrlPruefen('file://x').ok).toBe(false)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user