@tricoteuses/senat 2.21.2 → 2.21.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -97,7 +97,7 @@ async function writeMatchArtifacts(args) {
|
|
|
97
97
|
if (finalTxt)
|
|
98
98
|
await fsp.writeFile(path.join(ctx.baseDir, "finalplayer.nvs"), finalTxt, "utf-8");
|
|
99
99
|
}
|
|
100
|
-
async function processGroupedReunion(agenda, session, dataDir) {
|
|
100
|
+
async function processGroupedReunion(agenda, session, dataDir, lastByVideo) {
|
|
101
101
|
// 1) GuardRails
|
|
102
102
|
if (shouldSkipAgenda(agenda))
|
|
103
103
|
return;
|
|
@@ -137,18 +137,30 @@ async function processGroupedReunion(agenda, session, dataDir) {
|
|
|
137
137
|
session: ctx.session,
|
|
138
138
|
options,
|
|
139
139
|
writeIfChanged,
|
|
140
|
+
lastByVideo, // NEW
|
|
141
|
+
getAgendaSegmentTimecodes,
|
|
142
|
+
buildSenatVodMasterM3u8FromNvs,
|
|
143
|
+
});
|
|
144
|
+
await processBisIfNeeded({
|
|
145
|
+
agenda,
|
|
146
|
+
secondBest,
|
|
147
|
+
ctx,
|
|
148
|
+
skipDownload,
|
|
149
|
+
options,
|
|
150
|
+
lastByVideo,
|
|
151
|
+
writeIfChanged,
|
|
152
|
+
processOneReunionMatch,
|
|
140
153
|
getAgendaSegmentTimecodes,
|
|
141
154
|
buildSenatVodMasterM3u8FromNvs,
|
|
142
155
|
});
|
|
143
|
-
// 4) Optional BIS
|
|
144
|
-
await processBisIfNeeded({ agenda, secondBest, ctx, skipDownload, options });
|
|
145
156
|
}
|
|
146
157
|
async function processAll(dataDir, sessions) {
|
|
147
158
|
console.log("Process all Agendas and fetch video's url");
|
|
148
159
|
for (const session of sessions) {
|
|
160
|
+
const lastByVideo = new Map();
|
|
149
161
|
for (const { item: agenda } of iterLoadSenatAgendas(dataDir, session)) {
|
|
150
162
|
try {
|
|
151
|
-
await processGroupedReunion(agenda, session, dataDir);
|
|
163
|
+
await processGroupedReunion(agenda, session, dataDir, lastByVideo);
|
|
152
164
|
}
|
|
153
165
|
catch (e) {
|
|
154
166
|
console.error(`[error] ${agenda?.uid ?? "unknown-uid"}:`, e?.message || e);
|
|
@@ -103,7 +103,7 @@ export function parseDataNvs(nvs) {
|
|
|
103
103
|
}
|
|
104
104
|
export function buildSenatVodMasterM3u8FromNvs(nvsText) {
|
|
105
105
|
// serverfiles://senat/2025/10/encoder10_20251022084451_2.mp4
|
|
106
|
-
const m = nvsText.match(/serverfiles:\/\/senat\/(\d{4})\/(\d{2})\/(encoder\d+)_([0-9]{14})/i);
|
|
106
|
+
const m = nvsText.match(/serverfiles:\/\/senat\/(\d{4})\/(\d{2})\/(encoder\d+)_([0-9]{13,14})(?:_[0-9]+)?\.mp4/i);
|
|
107
107
|
if (!m)
|
|
108
108
|
return null;
|
|
109
109
|
const [, yyyy, mm, encoder, stamp] = m;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Reunion } from "../types/agenda";
|
|
2
|
-
import { BestMatch, MatchContext } from "./types";
|
|
2
|
+
import { BestMatch, LastForVideo, MatchContext } from "./types";
|
|
3
3
|
import { CommandLineOptions } from "command-line-args";
|
|
4
4
|
export declare function processOneReunionMatch(args: {
|
|
5
5
|
agenda: Reunion;
|
|
@@ -8,6 +8,11 @@ export declare function processOneReunionMatch(args: {
|
|
|
8
8
|
session: number;
|
|
9
9
|
options: Record<string, any>;
|
|
10
10
|
writeIfChanged: (p: string, content: string) => Promise<void>;
|
|
11
|
+
lastByVideo: Map<string, {
|
|
12
|
+
agendaUid: string;
|
|
13
|
+
agendaJsonPath: string;
|
|
14
|
+
start: number;
|
|
15
|
+
}>;
|
|
11
16
|
getAgendaSegmentTimecodes: (dataNvs: string, finalNvs: string, agendaKey: string) => {
|
|
12
17
|
start: number;
|
|
13
18
|
end: number | null;
|
|
@@ -20,5 +25,26 @@ export declare function processBisIfNeeded(args: {
|
|
|
20
25
|
ctx: MatchContext;
|
|
21
26
|
skipDownload: boolean;
|
|
22
27
|
options: CommandLineOptions;
|
|
28
|
+
lastByVideo: Map<string, LastForVideo>;
|
|
29
|
+
writeIfChanged: (p: string, content: string) => Promise<void>;
|
|
30
|
+
processOneReunionMatch: (args: {
|
|
31
|
+
agenda: Reunion;
|
|
32
|
+
baseDir: string;
|
|
33
|
+
dataDir: string;
|
|
34
|
+
session: number;
|
|
35
|
+
options: Record<string, any>;
|
|
36
|
+
writeIfChanged: (p: string, content: string) => Promise<void>;
|
|
37
|
+
lastByVideo: Map<string, LastForVideo>;
|
|
38
|
+
getAgendaSegmentTimecodes: (dataNvs: string, finalNvs: string, agendaKey: string) => {
|
|
39
|
+
start: number;
|
|
40
|
+
end: number | null;
|
|
41
|
+
} | null;
|
|
42
|
+
buildSenatVodMasterM3u8FromNvs: (dataNvs: string) => string | null;
|
|
43
|
+
}) => Promise<void>;
|
|
44
|
+
getAgendaSegmentTimecodes: (dataNvs: string, finalNvs: string, agendaKey: string) => {
|
|
45
|
+
start: number;
|
|
46
|
+
end: number | null;
|
|
47
|
+
} | null;
|
|
48
|
+
buildSenatVodMasterM3u8FromNvs: (dataNvs: string) => string | null;
|
|
23
49
|
}): Promise<void>;
|
|
24
50
|
export declare function writeIfChanged(p: string, content: string): Promise<void>;
|
|
@@ -5,9 +5,8 @@ import { fetchText } from "./search";
|
|
|
5
5
|
import fs from "fs-extra";
|
|
6
6
|
import fsp from "fs/promises";
|
|
7
7
|
import path from "path";
|
|
8
|
-
import { getAgendaSegmentTimecodes, buildSenatVodMasterM3u8FromNvs } from "../utils/nvs-parsing";
|
|
9
8
|
export async function processOneReunionMatch(args) {
|
|
10
|
-
const { agenda, baseDir, dataDir, session, options, writeIfChanged, getAgendaSegmentTimecodes, buildSenatVodMasterM3u8FromNvs, } = args;
|
|
9
|
+
const { agenda, baseDir, dataDir, session, options, writeIfChanged, lastByVideo, getAgendaSegmentTimecodes, buildSenatVodMasterM3u8FromNvs, } = args;
|
|
11
10
|
const reunionUid = agenda.uid;
|
|
12
11
|
let dataTxt;
|
|
13
12
|
let finalTxt;
|
|
@@ -25,18 +24,40 @@ export async function processOneReunionMatch(args) {
|
|
|
25
24
|
return;
|
|
26
25
|
}
|
|
27
26
|
const agendaJsonPath = path.join(dataDir, AGENDA_FOLDER, DATA_TRANSFORMED_FOLDER, String(session), `${agenda.uid}.json`);
|
|
27
|
+
// Ensure it exists first.
|
|
28
|
+
if (!(await fs.pathExists(agendaJsonPath))) {
|
|
29
|
+
console.warn(`[warn] agenda file not found: ${agendaJsonPath}`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
28
32
|
let timecodeDebutVideo = null;
|
|
29
33
|
let timecodeFinVideo = null;
|
|
30
34
|
const agendaKey = agenda.titre || agenda.objet || "";
|
|
31
35
|
const seg = getAgendaSegmentTimecodes(dataTxt, finalTxt, agendaKey);
|
|
32
36
|
if (seg) {
|
|
33
37
|
timecodeDebutVideo = seg.start;
|
|
34
|
-
timecodeFinVideo =
|
|
38
|
+
timecodeFinVideo = null; // keep open by default
|
|
35
39
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
// 1) If we have a start timecode, close the previous agenda for this SAME master
|
|
41
|
+
if (timecodeDebutVideo != null) {
|
|
42
|
+
const prev = lastByVideo.get(master);
|
|
43
|
+
if (prev && prev.agendaJsonPath !== agendaJsonPath) {
|
|
44
|
+
// micro-safety: do not close with an earlier timecode
|
|
45
|
+
if (timecodeDebutVideo <= prev.start) {
|
|
46
|
+
console.warn(`[warn] timecode order inversion on same video: ` +
|
|
47
|
+
`prev=${prev.agendaUid}(${prev.start}s) -> cur=${agenda.uid}(${timecodeDebutVideo}s). ` +
|
|
48
|
+
`Skip closing prev to avoid negative segment.`);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
await patchAgendaTimecodeFin({
|
|
52
|
+
agendaJsonPath: prev.agendaJsonPath,
|
|
53
|
+
timecodeFinVideo: timecodeDebutVideo,
|
|
54
|
+
writeIfChanged,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
lastByVideo.set(master, { agendaUid: agenda.uid, agendaJsonPath, start: timecodeDebutVideo });
|
|
39
59
|
}
|
|
60
|
+
// 2) Update current agenda JSON with urlVideo (+ start/end if any)
|
|
40
61
|
const raw = await fsp.readFile(agendaJsonPath, "utf-8");
|
|
41
62
|
let obj;
|
|
42
63
|
try {
|
|
@@ -49,7 +70,10 @@ export async function processOneReunionMatch(args) {
|
|
|
49
70
|
const next = { ...obj, urlVideo: master, startTime: agenda.startTime };
|
|
50
71
|
if (timecodeDebutVideo != null) {
|
|
51
72
|
next.timecodeDebutVideo = timecodeDebutVideo;
|
|
52
|
-
|
|
73
|
+
if (timecodeFinVideo != null)
|
|
74
|
+
next.timecodeFinVideo = timecodeFinVideo;
|
|
75
|
+
else
|
|
76
|
+
delete next.timecodeFinVideo;
|
|
53
77
|
}
|
|
54
78
|
await writeIfChanged(agendaJsonPath, JSON.stringify(next, null, 2));
|
|
55
79
|
if (!options["silent"]) {
|
|
@@ -58,7 +82,7 @@ export async function processOneReunionMatch(args) {
|
|
|
58
82
|
}
|
|
59
83
|
}
|
|
60
84
|
export async function processBisIfNeeded(args) {
|
|
61
|
-
const { agenda, secondBest, ctx, skipDownload, options } = args;
|
|
85
|
+
const { agenda, secondBest, ctx, skipDownload, options, lastByVideo, writeIfChanged, processOneReunionMatch, getAgendaSegmentTimecodes, buildSenatVodMasterM3u8FromNvs, } = args;
|
|
62
86
|
if (skipDownload)
|
|
63
87
|
return;
|
|
64
88
|
if (!secondBest)
|
|
@@ -83,6 +107,7 @@ export async function processBisIfNeeded(args) {
|
|
|
83
107
|
session: ctx.session,
|
|
84
108
|
options,
|
|
85
109
|
writeIfChanged,
|
|
110
|
+
lastByVideo,
|
|
86
111
|
getAgendaSegmentTimecodes,
|
|
87
112
|
buildSenatVodMasterM3u8FromNvs,
|
|
88
113
|
});
|
|
@@ -128,3 +153,19 @@ export async function writeIfChanged(p, content) {
|
|
|
128
153
|
}
|
|
129
154
|
await fsp.writeFile(p, content, "utf-8");
|
|
130
155
|
}
|
|
156
|
+
async function patchAgendaTimecodeFin(args) {
|
|
157
|
+
const { agendaJsonPath, timecodeFinVideo, writeIfChanged } = args;
|
|
158
|
+
if (!(await fs.pathExists(agendaJsonPath)))
|
|
159
|
+
return;
|
|
160
|
+
const raw = await fsp.readFile(agendaJsonPath, "utf-8");
|
|
161
|
+
let obj;
|
|
162
|
+
try {
|
|
163
|
+
obj = JSON.parse(raw);
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
console.warn(`[warn] invalid JSON in ${agendaJsonPath}`);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const next = { ...obj, timecodeFinVideo };
|
|
170
|
+
await writeIfChanged(agendaJsonPath, JSON.stringify(next, null, 2));
|
|
171
|
+
}
|