ASS naar JSON
Of plak ondertiteltekst
Wat deze conversie verandert
1 ding vervalt of vraagt om een beslissing
- Formaatspecifieke inhoud
inhoud die het bronformaat buiten zijn blokken bewaart (blokken die geen ondertitel zijn, koptekst, instellingen per blok, uitgecommentarieerde gebeurtenissen) — het doelformaat heeft er geen plek voor
Deze lijst komt uit de capaciteitenmatrix voor deze twee formaten, niet uit het bestand dat je hebt geladen. Een specifiek bestand kan meer verliezen wanneer het kenmerken gebruikt die het formaat toestaat maar de matrix niet modelleert.
Deze pagina maakt van een ASS-script gestructureerde data: een JSON-document met een versienummer, een optionele titel, de volledige stijltabel en een array cues. Elke cue houdt zijn begin en eind als hele milliseconden, zijn regels en de stijl waarnaar hij verwijst; een positie wordt een object met x en y, en karaokelettergrepen worden tags met hun waarde. Dat is precies wat het cue-model van deze site kan dragen.
Bijna alles komt dus mee, en dat is de reden dat de lijst met verliezen hier zo kort is. Wat JSON niet kan bevatten, is de rest van het Script Info-blok — de resolutie, de wrap-instelling en dergelijke — evenals event-regels die geen Dialogue zijn en alles wat in de secties Fonts en Graphics is ingebed. De stijltabel zelf gaat volledig over, met alle kolommen, ook de kolommen die deze site niet modelleert.
Het resultaat is een bestand dat je in een script kunt laden, kunt diffen of in versiebeheer kunt zetten, en terug omzetten geeft je de ondertitels weer. Het werk gebeurt in je browser.
Voorbeelden
Een typisch ASS-bestand, omgezet naar JSON
Invoer
[Script Info]
Title: Example
ScriptType: v4.00+
WrapStyle: 0
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Arial,20,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2,2,2,10,10,10,1
Style: Title,Arial,36,&H0000FFFF,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,2,2,2,10,10,10,1
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:01.00,0:00:04.00,Default,,0,0,0,,Hello there.
Dialogue: 0,0:00:05.00,0:00:08.50,Default,,0,0,0,,{\i1}Italic{\i0} and {\b1}bold{\b0} text.
Dialogue: 0,0:00:09.00,0:00:12.00,Title,,0,0,0,,{\pos(320,240)}Positioned
Dialogue: 0,0:00:13.00,0:00:16.00,Default,,0,0,0,,{\k50}ka{\k30}ra{\k20}o{\k10}ke
Dialogue: 0,0:00:17.00,0:00:20.00,Default,,0,0,0,,Line one\NLine two
Dialogue: 1,0:00:21.00,0:00:24.00,Default,,0,0,0,,{\an7}Top left
Uitvoer
{
"version": 1,
"title": "Example",
"styles": {
"Default": {
"name": "Default",
"Name": "Default",
"Fontname": "Arial",
"Fontsize": "20",
"PrimaryColour": "&H00FFFFFF",
"SecondaryColour": "&H000000FF",
"OutlineColour": "&H00000000",
"BackColour": "&H00000000",
"Bold": "0",
"Italic": "0",
"Underline": "0",
"StrikeOut": "0",
"ScaleX": "100",
"ScaleY": "100",
"Spacing": "0",
"Angle": "0",
"BorderStyle": "1",
"Outline": "2",
"Shadow": "2",
"Alignment": "2",
"MarginL": "10",
"MarginR": "10",
"MarginV": "10",
"Encoding": "1",
"fontFamily": "Arial",
"fontSize": 20,
"primaryColor": "#ffffff",
"outlineColor": "#000000",
"bold": false,
"italic": false
},
"Title": {
"name": "Title",
"Name": "Title",
"Fontname": "Arial",
"Fontsize": "36",
"PrimaryColour": "&H0000FFFF",
"SecondaryColour": "&H000000FF",
"OutlineColour": "&H00000000",
"BackColour": "&H00000000",
"Bold": "-1",
"Italic": "0",
"Underline": "0",
"StrikeOut": "0",
"ScaleX": "100",
"ScaleY": "100",
"Spacing": "0",
"Angle": "0",
"BorderStyle": "1",
"Outline": "2",
"Shadow": "2",
"Alignment": "2",
"MarginL": "10",
"MarginR": "10",
"MarginV": "10",
"Encoding": "1",
"fontFamily": "Arial",
"fontSize": 36,
"primaryColor": "#ffff00",
"outlineColor": "#000000",
"bold": true,
"italic": false
}
},
"cues": [
{
"start": 1000,
"end": 4000,
"lines": [
"Hello there."
],
"style": "Default"
},
{
"start": 5000,
"end": 8500,
"lines": [
{
"text": "Italic and bold text.",
"tags": [
{
"kind": "italic",
"start": 0,
"end": 6
},
{
"kind": "bold",
"start": 11,
"end": 15
}
]
}
],
"style": "Default"
},
{
"start": 9000,
"end": 12000,
"lines": [
"Positioned"
],
"style": "Title",
"position": {
"x": 320,
"y": 240
}
},
{
"start": 13000,
"end": 16000,
"lines": [
{
"text": "karaoke",
"tags": [
{
"kind": "karaoke",
"start": 0,
"end": 2,
"value": "50"
},
{
"kind": "karaoke",
"start": 2,
"end": 4,
"value": "30"
},
{
"kind": "karaoke",
"start": 4,
"end": 5,
"value": "20"
},
{
"kind": "karaoke",
"start": 5,
"end": 7,
"value": "10"
}
]
}
],
"style": "Default"
},
{
"start": 17000,
"end": 20000,
"lines": [
"Line one",
"Line two"
],
"style": "Default"
},
{
"start": 21000,
"end": 24000,
"lines": [
"Top left"
],
"style": "Default"
}
]
}
In deze uitvoer verloren: formaateigen inhoud. Al het andere komt regel voor regel overeen.
Dezelfde omzetting op een rommelige invoer
Invoer
[Script Info]
; 一份真实的脏文件:BOM + CRLF + 认不出的覆写码 + Comment + 坏时间码
Title: Dirty sample
ScriptType: v4.00+
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Arial,20,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,1,0,0,0,100,100,0,0,1,2,2,2,10,10,10,1
[Fonts]
fontname: Arial
!!!!!!!!!!!!!!!!!!!!!!!!!!!!
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Comment: 0,0:00:00.00,0:00:01.00,Default,,0,0,0,,这是一条注释,不该变成字幕
Dialogue: 0,0:00:01.00,0:00:04.00,Default,,0,0,0,,Hello, world
Dialogue: 0,0:00:05.00,0:00:08.00,Default,,0,0,0,,{\fad(300,300)}Unmodelled override
Dialogue: 0,0:00:09.00,0:00:12.00,Default,,0,0,0,,{\an7\pos(320,240)}Both, wrong order
Dialogue: 0,0:00:13.00,0:00:16.5,Default,,0,0,0,,One decimal centisecond
Dialogue: 0,0:00:17.00,0:00:20.00,Default,,0,0,0,,Soft\nbreak
Dialogue: 0,0:00:xx.00,0:00:24.00,Default,,0,0,0,,Broken timecodeUitvoer
{
"version": 1,
"title": "Dirty sample",
"styles": {
"Default": {
"name": "Default",
"Name": "Default",
"Fontname": "Arial",
"Fontsize": "20",
"PrimaryColour": "&H00FFFFFF",
"SecondaryColour": "&H000000FF",
"OutlineColour": "&H00000000",
"BackColour": "&H00000000",
"Bold": "1",
"Italic": "0",
"Underline": "0",
"StrikeOut": "0",
"ScaleX": "100",
"ScaleY": "100",
"Spacing": "0",
"Angle": "0",
"BorderStyle": "1",
"Outline": "2",
"Shadow": "2",
"Alignment": "2",
"MarginL": "10",
"MarginR": "10",
"MarginV": "10",
"Encoding": "1",
"fontFamily": "Arial",
"fontSize": 20,
"primaryColor": "#ffffff",
"outlineColor": "#000000",
"bold": true,
"italic": false
}
},
"cues": [
{
"start": 1000,
"end": 4000,
"lines": [
"Hello, world"
],
"style": "Default"
},
{
"start": 5000,
"end": 8000,
"lines": [
"{\\fad(300,300)}Unmodelled override"
],
"style": "Default"
},
{
"start": 9000,
"end": 12000,
"lines": [
"Both, wrong order"
],
"style": "Default",
"position": {
"x": 320,
"y": 240
}
},
{
"start": 13000,
"end": 16500,
"lines": [
"One decimal centisecond"
],
"style": "Default"
},
{
"start": 17000,
"end": 20000,
"lines": [
"Soft",
"break"
],
"style": "Default"
}
]
}
Zo zien echte bestanden eruit — gemengde regeleinden, losse spaties, lege velden. Vergelijk met het nette voorbeeld hierboven om te zien wat de parser vergeeft.
Veelgestelde vragen
Wat gebeurt er met Script Info, Comment-regels en ingebedde lettertypen in JSON?
Meer blijft bewaard dan je misschien verwacht: de titel komt over, en de hele ASS-stijltabel ook — elke kolom, inclusief de kolommen die deze site niet modelleert, plus de gelezen lettertype-, grootte-, kleur- en vet/cursiefwaarden. Wat JSON geen plek kan geven, is de rest van Script Info (de resolutie, de wrap-instelling en dergelijke), event-regels die geen Dialogue zijn (Comment, Picture, Sound) en alles wat in Fonts of Graphics is ingebed. Die vallen weg.
Hoe ziet de JSON-uitvoer eruit als ASS wordt omgezet?
Als JSON: een array cue-objecten, elk met start en eind. Een cue krijgt alleen een id als de invoer er een had, en in dit voorbeeld heeft geen enkele ASS-regel er een, dus komt er geen id in de uitvoer. De stijltabel staat apart onder styles, zodat een cue alleen naar de naam van zijn stijl hoeft te verwijzen.
Zijn de ASS-tijden exact in het JSON-bestand?
Ja, tot op de milliseconde — fijner gaat geen van beide formaten. ASS schrijft een moment als 0:00:04.13 en JSON schrijft hetzelfde moment als 4130; niets wordt afgerond of verschoven, en een cue die 2,9 seconden in beeld was, blijft dat.
Hoe komt een ASS-blok van twee regels in JSON terecht?
Als twee entries in de lines-array van de cue. Het paar blijft bij elkaar: de twee ASS-regels horen in de JSON-uitvoer nog steeds bij één ondertitel, en een speler toont ze samen.
Blijft de volgorde van ASS-blokken bewaard in JSON?
Ja, en ze worden onderweg op begintijd gesorteerd — vroegste eerst, zoals elke afnemer van dit schema ze verwacht. Stond je ASS-bestand niet in tijdsorde, dan zet de JSON-uitvoer de cues terug in de juiste; dat is een structurele wijziging, geen inhoudelijke.
Kan ik de JSON daarna weer naar ASS omzetten?
Ja, en die richting heeft zijn eigen lijst: JSON naar ASS verliest precisie, omdat ASS in honderdsten van een seconde denkt. Die lijst verschilt van de lijst hierboven omdat de twee formaten verschillende dingen bewaren.
Behoudt de JSON-uitvoer de tekencodering van het ASS-bestand?
Nee — de uitvoer is UTF-8, met Unix-regeleinden en zonder byte-order mark, wat het ASS-bestand ook gebruikte. Een bestand in UTF-16 of een Windows-eenbytecodering wordt bij het inlezen gedecodeerd; wat eruit komt is altijd dezelfde UTF-8-JSON.