diff --git a/go.mod b/go.mod
index 4a1758e..2429348 100644
--- a/go.mod
+++ b/go.mod
@@ -1,5 +1,9 @@
module github.com/Eyevinn/VMAP
-go 1.22
+go 1.23.0
+
+toolchain go1.24.2
require github.com/matryer/is v1.4.1
+
+require github.com/CarlLindqvist/xmltokenizer v0.0.10
diff --git a/go.sum b/go.sum
index f95502a..f99e3f4 100644
--- a/go.sum
+++ b/go.sum
@@ -1,2 +1,6 @@
+github.com/CarlLindqvist/xmltokenizer v0.0.10 h1:pdp+yJZTOijVnGR6oeuqecXY9zIt7O28A5JXbL6Yp00=
+github.com/CarlLindqvist/xmltokenizer v0.0.10/go.mod h1:OlBoGMMzCOY2cnz7NLSuBQjlVRYYbarlqbFelQf14XM=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
diff --git a/vmap/decoder.go b/vmap/decoder.go
new file mode 100644
index 0000000..157a488
--- /dev/null
+++ b/vmap/decoder.go
@@ -0,0 +1,551 @@
+package vmap
+
+import (
+ "bytes"
+ "errors"
+ "io"
+ "strconv"
+
+ "github.com/CarlLindqvist/xmltokenizer"
+)
+
+func DecodeVast(input []byte) (VAST, error) {
+ var vast VAST
+ found := false
+ f := bytes.NewReader([]byte(input))
+
+ tok := xmltokenizer.New(f, xmltokenizer.WithAttrBufferSize(5))
+
+ for {
+ token, err := tok.Token() // Token is only valid until next tok.Token() invocation (short-lived object).
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ panic(err)
+ }
+ switch string(token.Name.Local) {
+ case "VAST":
+ found = true
+ // Reuse Token object in the sync.Pool since we only use it temporarily.
+ se := xmltokenizer.GetToken().Copy(token)
+ err = vast.UnmarshalToken(tok, se)
+ xmltokenizer.PutToken(se) // Put back to sync.Pool.
+ if err != nil {
+ return vast, err
+ }
+ }
+ }
+
+ if !found {
+ return vast, errors.New("no VAST token found in document")
+ }
+ return vast, nil
+}
+
+func DecodeVmap(input []byte) (VMAP, error) {
+ var vmap VMAP
+ found := false
+
+ f := bytes.NewReader([]byte(input))
+
+ tok := xmltokenizer.New(f, xmltokenizer.WithAttrBufferSize(5))
+
+ for {
+ token, err := tok.Token() // Token is only valid until next tok.Token() invocation (short-lived object).
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ panic(err)
+ }
+ switch string(token.Name.Local) {
+ case "VMAP":
+ found = true
+ for i := range token.Attrs {
+ attr := &token.Attrs[i]
+ switch string(attr.Name.Local) {
+ case "version":
+ vmap.Version = string(attr.Value)
+ case "vmap":
+ vmap.Vmap = string(attr.Value)
+ vmap.XMLName.Space = string(attr.Value)
+ }
+ vmap.XMLName.Local = "VMAP"
+ }
+
+ case "AdBreak":
+ var adBreak AdBreak
+ // Reuse Token object in the sync.Pool since we only use it temporarily.
+ se := xmltokenizer.GetToken().Copy(token)
+ err = adBreak.UnmarshalToken(tok, se)
+ xmltokenizer.PutToken(se) // Put back to sync.Pool.
+ if err != nil {
+ return vmap, err
+ }
+ vmap.AdBreaks = append(vmap.AdBreaks, adBreak)
+ }
+ }
+
+ if !found {
+ return vmap, errors.New("no VMAP token found in document")
+ }
+ return vmap, nil
+}
+
+func (adBreak *AdBreak) UnmarshalToken(tok *xmltokenizer.Tokenizer, se *xmltokenizer.Token) error {
+ adBreak.AdSource = &AdSource{
+ VASTData: &VASTData{},
+ }
+ var err error
+ for i := range se.Attrs {
+ attr := &se.Attrs[i]
+ switch string(attr.Name.Local) {
+ case "breakId":
+ adBreak.Id = string(attr.Value)
+ case "breakType":
+ adBreak.BreakType = string(attr.Value)
+ case "timeOffset":
+ err = adBreak.TimeOffset.UnmarshalText(attr.Value)
+ if err != nil {
+ return err
+ }
+ }
+ }
+
+ for {
+ token, err := tok.Token()
+ if err != nil {
+ return err
+ }
+ if token.IsEndElementOf(se) { // Reach desired EndElement
+ return nil
+ }
+ if token.IsEndElement { // Ignore child's EndElements
+ continue
+ }
+ switch string(token.Name.Local) {
+ case "VAST":
+ var vast VAST
+ // Reuse Token object in the sync.Pool since we only use it temporarily.
+ se := xmltokenizer.GetToken().Copy(token)
+ err = vast.UnmarshalToken(tok, se)
+ xmltokenizer.PutToken(se) // Put back to sync.Pool.
+ if err != nil {
+ return err
+ }
+ adBreak.AdSource.VASTData.VAST = &vast
+ case "Tracking":
+ if adBreak.TrackingEvents == nil {
+ adBreak.TrackingEvents = []TrackingEvent{}
+ }
+ var t TrackingEvent
+ for i := range token.Attrs {
+ attr := &token.Attrs[i]
+ switch string(attr.Name.Local) {
+ case "event":
+ t.Event = string(attr.Value)
+ }
+ }
+ if token.WasCDATA {
+ t.Text = string(token.Data)
+ } else {
+ t.Text = string(xmlStringToString(token.Data))
+ }
+ adBreak.TrackingEvents = append(adBreak.TrackingEvents, t)
+ }
+ }
+}
+
+func (vast *VAST) UnmarshalToken(tok *xmltokenizer.Tokenizer, se *xmltokenizer.Token) error {
+ for i := range se.Attrs {
+ attr := &se.Attrs[i]
+ switch string(attr.Name.Local) {
+ case "version":
+ vast.Version = string(attr.Value)
+ }
+ }
+
+ for {
+ token, err := tok.Token()
+ if err != nil {
+ return err
+ }
+ if token.IsEndElementOf(se) { // Reach desired EndElement
+ return nil
+ }
+ if token.IsEndElement { // Ignore child's EndElements
+ continue
+ }
+ switch string(token.Name.Local) {
+ case "Ad":
+ var ad Ad
+ // Reuse Token object in the sync.Pool since we only use it temporarily.
+ se := xmltokenizer.GetToken().Copy(token)
+ err = ad.UnmarshalToken(tok, se)
+ xmltokenizer.PutToken(se) // Put back to sync.Pool.
+ if err != nil {
+ return err
+ }
+ vast.Ad = append(vast.Ad, ad)
+ }
+ }
+}
+
+func (ad *Ad) UnmarshalToken(tok *xmltokenizer.Tokenizer, se *xmltokenizer.Token) error {
+ for i := range se.Attrs {
+ attr := &se.Attrs[i]
+ switch string(attr.Name.Local) {
+ case "sequence":
+ seq, err := strconv.Atoi(string(attr.Value))
+ if err != nil {
+ return err
+ }
+ ad.Sequence = seq
+ case "id":
+ ad.Id = string(attr.Value)
+ }
+ }
+ for {
+ token, err := tok.Token()
+ if err != nil {
+ return err
+ }
+ if token.IsEndElementOf(se) { // Reach desired EndElement
+ return nil
+ }
+ if token.IsEndElement { // Ignore child's EndElements
+ continue
+ }
+ switch string(token.Name.Local) {
+ case "InLine":
+ var inline InLine
+ // Reuse Token object in the sync.Pool since we only use it temporarily.
+ se := xmltokenizer.GetToken().Copy(token)
+ err = inline.UnmarshalToken(tok, se)
+ xmltokenizer.PutToken(se) // Put back to sync.Pool.
+ if err != nil {
+ return err
+ }
+ ad.InLine = &inline
+ }
+ }
+}
+
+func (inline *InLine) UnmarshalToken(tok *xmltokenizer.Tokenizer, se *xmltokenizer.Token) error {
+ for {
+ token, err := tok.Token()
+ if err != nil {
+ return err
+ }
+ if token.IsEndElementOf(se) { // Reach desired EndElement
+ return nil
+ }
+ if token.IsEndElement { // Ignore child's EndElements
+ continue
+ }
+ switch string(token.Name.Local) {
+ case "Creative":
+ var c Creative
+ se := xmltokenizer.GetToken().Copy(token)
+ err = c.UnmarshalToken(tok, se)
+ xmltokenizer.PutToken(se) // Put back to sync.Pool.
+ if err != nil {
+ return err
+ }
+ inline.Creatives = append(inline.Creatives, c)
+ case "Impression":
+ var imp Impression
+ for i := range token.Attrs {
+ attr := &token.Attrs[i]
+ switch string(attr.Name.Local) {
+ case "id":
+ imp.Id = string(attr.Value)
+ }
+ }
+ if token.WasCDATA {
+ imp.Text = string(token.Data)
+ } else {
+ imp.Text = string(xmlStringToString(token.Data))
+ }
+ inline.Impression = append(inline.Impression, imp)
+ case "AdSystem":
+ if token.WasCDATA {
+ inline.AdSystem = string(token.Data)
+ } else {
+ inline.AdSystem = string(xmlStringToString(token.Data))
+ }
+ case "AdTitle":
+ if token.WasCDATA {
+ inline.AdTitle = string(token.Data)
+ } else {
+ inline.AdTitle = string(xmlStringToString(token.Data))
+ }
+ case "Extension":
+ var e Extension
+ // Reuse Token object in the sync.Pool since we only use it temporarily.
+ se := xmltokenizer.GetToken().Copy(token)
+ err = e.UnmarshalToken(tok, se)
+ xmltokenizer.PutToken(se) // Put back to sync.Pool.
+ if err != nil {
+ return err
+ }
+ inline.Extensions = append(inline.Extensions, e)
+ case "Error":
+ var er Error
+ er.Value = string(token.Data)
+ if token.WasCDATA {
+ er.Value = string(token.Data)
+ } else {
+ er.Value = string(xmlStringToString(token.Data))
+ }
+ inline.Error = &er
+ }
+ }
+}
+
+func (c *Creative) UnmarshalToken(tok *xmltokenizer.Tokenizer, se *xmltokenizer.Token) error {
+ for i := range se.Attrs {
+ attr := &se.Attrs[i]
+ switch string(attr.Name.Local) {
+ case "id":
+ c.Id = string(attr.Value)
+ case "adId":
+ c.AdId = string(attr.Value)
+ case "sequence":
+ //TODO
+ }
+ }
+
+ for {
+ token, err := tok.Token()
+ if err != nil {
+ return err
+ }
+ if token.IsEndElementOf(se) { // Reach desired EndElement
+ return nil
+ }
+ if token.IsEndElement { // Ignore child's EndElements
+ continue
+ }
+
+ switch string(token.Name.Local) {
+ case "UniversalAdId":
+ var uaid UniversalAdId
+ for i := range token.Attrs {
+ attr := &token.Attrs[i]
+ switch string(attr.Name.Local) {
+ case "idRegistry":
+ uaid.IdRegistry = string(attr.Value)
+ }
+ }
+ if token.WasCDATA {
+ uaid.Id = string(token.Data)
+ } else {
+ uaid.Id = string(xmlStringToString(token.Data))
+ }
+ c.UniversalAdId = &uaid
+ case "Tracking":
+ if c.Linear == nil {
+ c.Linear = &Linear{}
+ }
+ var t TrackingEvent
+ for i := range token.Attrs {
+ attr := &token.Attrs[i]
+ switch string(attr.Name.Local) {
+ case "event":
+ t.Event = string(attr.Value)
+ }
+ }
+ if token.WasCDATA {
+ t.Text = string(token.Data)
+ } else {
+ t.Text = string(xmlStringToString(token.Data))
+ }
+ c.Linear.TrackingEvents = append(c.Linear.TrackingEvents, t)
+ case "ClickThrough":
+ c.Linear.ClickThrough = &ClickThrough{}
+ for i := range token.Attrs {
+ attr := &token.Attrs[i]
+ switch string(attr.Name.Local) {
+ case "id":
+ c.Linear.ClickThrough.Id = string(attr.Value)
+ }
+ }
+ if token.WasCDATA {
+ c.Linear.ClickThrough.Text = string(token.Data)
+ } else {
+ c.Linear.ClickThrough.Text = string(xmlStringToString(token.Data))
+ }
+ case "ClickTracking":
+ if c.Linear == nil {
+ c.Linear = &Linear{}
+ }
+ var ct ClickTracking
+ for i := range token.Attrs {
+ attr := &token.Attrs[i]
+ switch string(attr.Name.Local) {
+ case "id":
+ ct.Id = string(attr.Value)
+ }
+ }
+ if token.WasCDATA {
+ ct.Text = string(token.Data)
+ } else {
+ ct.Text = string(xmlStringToString(token.Data))
+ }
+ c.Linear.ClickTracking = append(c.Linear.ClickTracking, ct)
+ case "Duration":
+ if c.Linear == nil {
+ c.Linear = &Linear{}
+ }
+ if token.WasCDATA {
+ err = c.Linear.Duration.UnmarshalText(token.Data)
+ } else {
+ err = c.Linear.Duration.UnmarshalText(xmlStringToString(token.Data))
+ }
+
+ if err != nil {
+ return err
+ }
+ case "MediaFile":
+ if c.Linear == nil {
+ c.Linear = &Linear{}
+ }
+ var m MediaFile
+ for i := range token.Attrs {
+ attr := &token.Attrs[i]
+ switch string(attr.Name.Local) {
+ case "bitrate":
+ m.Bitrate, err = strconv.Atoi(string(attr.Value))
+ if err != nil {
+ return err
+ }
+ case "height":
+ m.Height, err = strconv.Atoi(string(attr.Value))
+ if err != nil {
+ return err
+ }
+ case "width":
+ m.Width, err = strconv.Atoi(string(attr.Value))
+ if err != nil {
+ return err
+ }
+ case "delivery":
+ m.Delivery = string(attr.Value)
+ case "type":
+ m.MediaType = string(attr.Value)
+ case "codec":
+ m.Codec = string(attr.Value)
+ }
+ }
+ if token.WasCDATA {
+ m.Text = string(token.Data)
+ } else {
+ m.Text = string(xmlStringToString(token.Data))
+ }
+ c.Linear.MediaFiles = append(c.Linear.MediaFiles, m)
+ }
+ }
+}
+
+func (ext *Extension) UnmarshalToken(tok *xmltokenizer.Tokenizer, se *xmltokenizer.Token) error {
+ for i := range se.Attrs {
+ attr := &se.Attrs[i]
+ switch string(attr.Name.Local) {
+ case "type":
+ ext.ExtensionType = string(attr.Value)
+ }
+ }
+ for {
+ token, err := tok.Token()
+ if err != nil {
+ return err
+ }
+ if token.IsEndElementOf(se) { // Reach desired EndElement
+ return nil
+ }
+ if token.IsEndElement { // Ignore child's EndElements
+ continue
+ }
+
+ switch string(token.Name.Local) {
+ case "CreativeParameter":
+ var par CreativeParameter
+ for i := range token.Attrs {
+ attr := &token.Attrs[i]
+ switch string(attr.Name.Local) {
+ case "creativeId":
+ par.CreativeId = string(attr.Value)
+ case "name":
+ par.Name = string(attr.Value)
+ case "type":
+ par.CreativeParameterType = string(attr.Value)
+ }
+ }
+ if token.WasCDATA {
+ par.Value = string(token.Data)
+ } else {
+ par.Value = string(xmlStringToString(token.Data))
+ }
+ ext.CreativeParameters = append(ext.CreativeParameters, par)
+ }
+ }
+}
+
+func xmlStringToString(input []byte) []byte {
+ o := 0
+ for i := 0; i < len(input); i++ {
+ b := input[i]
+
+ switch b {
+ //If we see a '&' we have a special character that needs decoding
+ case '&':
+ cb := make([]byte, 0, 4)
+ specialCharLoop:
+ for {
+ i++
+ if i >= len(input) {
+ break
+ }
+
+ c := input[i]
+ switch c {
+ case '#', 'x':
+ case ';':
+ break specialCharLoop
+ default:
+ cb = append(cb, c)
+ }
+ }
+ ch := decodeSpecialCharacterFromHexCode(cb)
+ for _, l := range []byte(string(ch)) {
+ input[o] = l
+ o++
+ }
+ //This is just a normal byte, just output it
+ default:
+ input[o] = b
+ o++
+ }
+ }
+ return input[0:o]
+}
+
+func decodeSpecialCharacterFromHexCode(input []byte) rune {
+ // Handle & < > ' "
+ switch string(input) {
+ case "amp":
+ return '&'
+ case "lt":
+ return '<'
+ case "gt":
+ return '>'
+ case "apos":
+ return '\''
+ case "quot":
+ return '"'
+ }
+ codePoint, _ := strconv.ParseInt(string(input), 16, 32)
+ return rune(codePoint)
+}
diff --git a/vmap/sample-vmap/testVast2.xml b/vmap/sample-vmap/testVast2.xml
new file mode 100644
index 0000000..7a250ba
--- /dev/null
+++ b/vmap/sample-vmap/testVast2.xml
@@ -0,0 +1,377 @@
+
+
+
+
+
+ FreeWheel
+ Blommande körsbärsträd
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=&r=524918&adid=84694610&reid=589423750&arid=0&async=0&iw=&uxnw=&uxss=&uxct=&et=e&cn=[ERRORCODE]
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=&r=524918&adid=84694610&reid=589423750&arid=0&auid=&cn=defaultImpression&et=i&_cc=84694610,589423750,,,1746536264,1&tpos=919&async=0&iw=&uxnw=&uxss=&uxct=&metr=7&init=1&vcid2=794e2760-28a4-4b07-bf80-96e963a6020e&pingids=5991
+ https://730721846599843.tv4mms.a2d.tv/tracker.png
+
+
+ 145507734
+
+ 00:00:03
+
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=&r=524918&adid=84694610&reid=589423750&arid=0&auid=&cn=complete&et=i&_cc=&tpos=919&async=0&init=1&iw=&uxnw=&uxss=&uxct=&metr=7
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=&r=524918&adid=84694610&reid=589423750&arid=0&auid=&cn=firstQuartile&et=i&_cc=&tpos=919&async=0&init=1&iw=&uxnw=&uxss=&uxct=&metr=7
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=&r=524918&adid=84694610&reid=589423750&arid=0&auid=&cn=midPoint&et=i&_cc=&tpos=919&async=0&init=1&iw=&uxnw=&uxss=&uxct=&metr=7
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=&r=524918&adid=84694610&reid=589423750&arid=0&auid=&cn=thirdQuartile&et=i&_cc=&tpos=919&async=0&init=1&iw=&uxnw=&uxss=&uxct=&metr=7
+
+
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=&r=524918&adid=84694610&reid=589423750&arid=0&auid=&cn=defaultClick&et=c&_cc=&tpos=919&async=0
+
+
+ https://prism-ads-cdn.a2d.tv/abr/start_4_RV3_K_RSB_RSBLOMMOR_2023_P8_mp4_1712561245_11776886_981/f65c0f0e-9da5-49da-9ba7-3eced8070fe8/index.m3u8
+
+
+
+
+
+
+ 145507734
+
+ bumper
+
+
+
+
+
+
+
+ FreeWheel
+ Alla 20-49
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=201326592&r=524918&adid=84892990&reid=890417550&arid=0&async=0&iw=&uxnw=&uxss=&uxct=&et=e&cn=[ERRORCODE]
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=201326592&r=524918&adid=84892990&reid=890417550&arid=0&auid=&cn=defaultImpression&et=i&_cc=84892990,890417550,,,1746536264,1&tpos=919&async=0&iw=&uxnw=&uxss=&uxct=&metr=7&init=1&vcid2=794e2760-28a4-4b07-bf80-96e963a6020e&asid=434600981&ssid=-1&pingids=5991
+ https://visitanalytics.userreport.com/hit?t=FFTdaf53103&event=impression&gdpr=1&gdpr_consent=&camp_id=84302701&camp_name=%5BWOO%5D%20Tre%20Lansering%20v.%2017-21%20%2B%2029-31%20%C3%85lder%3AK%C3%B6n&io_id=84892987&pl_id=84892989&pl_name=Alla%2020-49&cr_id=235953821&cr_name=E1H32W3000&deal_id=&d=dpid&ip=193.45.52.147&rnd=1477005635
+ https://730721846599843.tv4mms.a2d.tv/tracker.png
+
+
+ E1H32W3000
+
+ 00:00:30
+
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=201326592&r=524918&adid=84892990&reid=890417550&arid=0&auid=&cn=complete&et=i&_cc=&tpos=919&async=0&init=1&iw=&uxnw=&uxss=&uxct=&metr=7
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=201326592&r=524918&adid=84892990&reid=890417550&arid=0&auid=&cn=firstQuartile&et=i&_cc=&tpos=919&async=0&init=1&iw=&uxnw=&uxss=&uxct=&metr=7
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=201326592&r=524918&adid=84892990&reid=890417550&arid=0&auid=&cn=midPoint&et=i&_cc=&tpos=919&async=0&init=1&iw=&uxnw=&uxss=&uxct=&metr=7
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=201326592&r=524918&adid=84892990&reid=890417550&arid=0&auid=&cn=thirdQuartile&et=i&_cc=&tpos=919&async=0&init=1&iw=&uxnw=&uxss=&uxct=&metr=7
+
+
+ https://www.tre.se/handla/mobilabonnemang?utm_medium=paid_video&utm_source=tv4play&utm_content=p3_25_mammis_30s&utm_campaign=3b2c_2025_p3_konceptlansering_seefeel
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=201326592&r=524918&adid=84892990&reid=890417550&arid=0&auid=&cn=defaultClick&et=c&_cc=&tpos=919&async=0
+
+
+ https://prism-ads-cdn.a2d.tv/abr/5ZcbCx_16848953_981/c1315035-4031-4456-b9a6-d4bdef88f99b/index.m3u8
+
+
+
+
+
+
+ E1H32W3000
+
+
+
+
+
+
+ FreeWheel
+ ProgMod_Placeholder_AD
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=201326592&r=524918%3B518308&adid=53236803&reid=831875215&arid=1441792&async=0&iw=&uxnw=&uxss=&uxct=&et=e&cn=[ERRORCODE]
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=201326592&r=524918%3B518308&adid=53236803&reid=831875215&arid=1441792&auid=&cn=defaultImpression&et=i&_cc=53236803,831875215,m264360;0;0.,,1746536264,1&tpos=919&async=0&iw=&uxnw=&uxss=&uxct=&metr=7&init=1&vcid2=794e2760-28a4-4b07-bf80-96e963a6020e&asid=434600981&ssid=-1&dealid=256113&pingids=5991
+ https://googleads4.g.doubleclick.net/pcs/view?xai=AKAOjstp9uelNrSZC68oNLIgV2he0ylYJDYaKlopaoVYuYFESNM7cf_VT0kq1NnQoyjXTZVxZ7_yM2P-4VAu82UAL2GfJbCowpm8BenEimOm0YT-Rve67rP7ImM2khFbPwWeYkA-DpLHTdrrLhRONHCsBN2E&sai=AMfl-YRDzoKACRaiphAvDnpKd3K8kMVaN4x09rlMMfMkPbK995ZvlOpfy4dhHW_u1ba0ZKCiTFyPyH7UHyR2EtBtTHP-4YQ&sig=Cg0ArKJSzNzYXzpKt0RNEAE&uach_m=%5BUACH%5D&cry=1&fbs_aeid=%5Bgw_fbsaeid%5D&urlfix=1&adurl=
+ https://aax-eu.amazon-adsystem.com/e/is/6c383505a97d267185466970b0d4b7ad/imp?b=JKKypjcLNiORtYxJV9S6rBwAAAGWpaxx-AMAAAe_BAAzcHhfdHhuX2JpZDEgICAzcHhfdHhuX2ltcDEgICBlzhah&w=AAAAAAAAAAAAAAAAAAAAALb8ggZpo6MjtfHDBg&bi=KCP-6wKQZWQbzM6Bjw-vPnNr4NOkuY2wHZ80D8M2hOKQIZDKYysbM2oqHXDgtvb8NdzIMUkEh1vq6bARbUkOYeY53P0nF.7qEu888Buw9MqkoVWmGSo6v-fTnwWqqTESIVDlCX4tRgc9c4-IeDAFyqePG5n1LPFROJzbTnev5AlpRwXeECX3uyQcmYlMQyVff9LmIOeUj8ZVqmRSHiAAVGMsUTej2XAWnskwGgdul7gJHtqJgV90BiG2Nhvln--RcV0ch5PxQeEkTxThtfmQwJ6s1UMHMBVqIB1wZGmiuY8LMbje-bH63M5E4G0gBBsfZTD7y6Sxb.hAyTk2vscuhItU7Wd4ZsLJEcBnGiPi7Z5Lb9bcPbREIzsxBb6HkbYiav2KOr3mNZSo4y2kgz2Jp3KYigHuChO1UDe9jY1fyb0oX5pnUrRzRL1d7WcGUU-e4PD-4bbPEDcw9TYjVDbziLW964bgS7at0gOUxgLIwvblziFbibCiDPolC.1j01z9tDxmhQ2rYrDuLi5hAQhXVdxcUkzTQUASCdqOcvkKe4pPls9kIacOYTGpWKMdXocJpF3y3a0ScsLjbcO.oVJBGC7w125yA62UZzrNbi-jrbYeniYvZ-MPqDKL4.DhaXhmjgbFcKcVcnYQ4Z5ZG4xYwihw2-VgoTMJb2yeM8lf-5hDfrQFhI-j5Zi6cIO2WQTy
+ https://aax-eu.amazon-adsystem.com/s/iui3?ex-fch=416719&d=forester-did&cb=4956751576937641&gdpr_pd=1&gdpr_consent=CQNEqlgQNEqlgAcABBSVBdFsAP_gAAAAAChQK1wNAAEQAKAAsACAAFQALgAZAA8ACAAGQANAAiQBNAE4ALYAXwAxABuADmAICAQQBBgCFAEYANEAfoBCACIgEWAI6ATgArIBcwDFAG2AO2AmQBSYCwwF5gMZAZYA4QBy4E9IKRgpXBS0FMgKaQU2BT-CoIKiQVGBVCCqgKsQVaBV-CsIKxwVlBWsAAABISAYAAgABYAFQAPAAggBkAGgARAB-gFzAMUAvMBy44AKAAgAC4BCACIgKTHQDwAFgAVABBADIANAAiABiAGiAP0AiwBcwDFAJkAXmAywBy5AAEAAgBSZCAIAAsAMQBcwDFEoA4ACAAFgBEADEAYoBeYDLCQAIAC4CkykAwABYAFQAQQAyADQAIgAYgBogD9AIsAXMAxQC8ygAIAC4B2w.f_wAAAAAAAAA&gdpr=1&v-args=%3Ft%3D3%26d%3D15%26ct%3D%255B1014%252C1020%255D%26ca%3D%255B7%255D%26s%3D1983&ex-fargs=%3Fi%3DorKmNws2I5G1jElX1LqsHA%26e%3DvideoImpression%26a%3D577981708224706615%26c%3D577469246600535182%26s%3Dpda%26u%3DorKmNws2I5G1jElX1LqsHA&vdb=%5B1014%2C1020%5D%3A%5B7%5D%3A3%3Anull%3A15%3A1983
+ https://ade.googlesyndication.com/ddm/activity/dc_oe=ChMI2fX3zPKOjQMVKZlQBh1R9xWSEAEYACD82pJvKgTBLTSTSABQOljPdWD-4_sPaM2izMcB;dc_eps=AHas8cDsBHGGsBOQIDkqH05lGBgdSs08YEQXY4sssz3gVFnR_feS7jWkm_9g9JMKSztLy0FwR_5sE8puQ1-PnTXg7ZCjCTog2wT0;met=1;ecn1=1;etm1=0;eid1=11;
+ https://aax-eu.amazon-adsystem.com/s/iui3?ex-fch=416719&d=forester-did&cb=8329697195822472&gdpr_pd=1&gdpr_consent=CQNEqlgQNEqlgAcABBSVBdFsAP_gAAAAAChQK1wNAAEQAKAAsACAAFQALgAZAA8ACAAGQANAAiQBNAE4ALYAXwAxABuADmAICAQQBBgCFAEYANEAfoBCACIgEWAI6ATgArIBcwDFAG2AO2AmQBSYCwwF5gMZAZYA4QBy4E9IKRgpXBS0FMgKaQU2BT-CoIKiQVGBVCCqgKsQVaBV-CsIKxwVlBWsAAABISAYAAgABYAFQAPAAggBkAGgARAB-gFzAMUAvMBy44AKAAgAC4BCACIgKTHQDwAFgAVABBADIANAAiABiAGiAP0AiwBcwDFAJkAXmAywBy5AAEAAgBSZCAIAAsAMQBcwDFEoA4ACAAFgBEADEAYoBeYDLCQAIAC4CkykAwABYAFQAQQAyADQAIgAYgBogD9AIsAXMAxQC8ygAIAC4B2w.f_wAAAAAAAAA&gdpr=1&v-args=%3Ft%3D3%26d%3D15%26ct%3D%255B1014%252C1020%255D%26ca%3D%255B7%255D%26s%3D1983&ex-fargs=%3Fi%3DorKmNws2I5G1jElX1LqsHA%26e%3DvideoStart%26a%3D577981708224706615%26c%3D577469246600535182%26s%3Dpda%26u%3DorKmNws2I5G1jElX1LqsHA&vdb=%5B1014%2C1020%5D%3A%5B7%5D%3A3%3Anull%3A15%3A1983
+ https://aax-eu.amazon-adsystem.com/x/px/JKKypjcLNiORtYxJV9S6rBwAAAGWpaxx-AMAAAe_BAAzcHhfdHhuX2JpZDEgICAzcHhfdHhuX2ltcDEgICBlzhah/%7B%22c%22%3A%22video%22%2C%22src%22%3A1983%2C%22start%22%3A1%7D
+ https://730721846599843.tv4mms.a2d.tv/tracker.png
+
+
+ 233090428-1
+
+ 00:00:15
+
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=201326592&r=524918%3B518308&adid=53236803&reid=831875215&arid=1441792&auid=&cn=firstQuartile&et=i&_cc=&tpos=919&async=0&iw=&uxnw=&uxss=&uxct=&metr=7&init=1
+ https://ade.googlesyndication.com/ddm/activity/dc_oe=ChMI2fX3zPKOjQMVKZlQBh1R9xWSEAEYACD82pJvKgTBLTSTSABQOljPdWD-4_sPaM2izMcB;dc_eps=AHas8cDsBHGGsBOQIDkqH05lGBgdSs08YEQXY4sssz3gVFnR_feS7jWkm_9g9JMKSztLy0FwR_5sE8puQ1-PnTXg7ZCjCTog2wT0;met=1;ecn1=1;etm1=0;eid1=960584;
+ https://aax-eu.amazon-adsystem.com/s/iui3?ex-fch=416719&d=forester-did&cb=346710249895589&gdpr_pd=1&gdpr_consent=CQNEqlgQNEqlgAcABBSVBdFsAP_gAAAAAChQK1wNAAEQAKAAsACAAFQALgAZAA8ACAAGQANAAiQBNAE4ALYAXwAxABuADmAICAQQBBgCFAEYANEAfoBCACIgEWAI6ATgArIBcwDFAG2AO2AmQBSYCwwF5gMZAZYA4QBy4E9IKRgpXBS0FMgKaQU2BT-CoIKiQVGBVCCqgKsQVaBV-CsIKxwVlBWsAAABISAYAAgABYAFQAPAAggBkAGgARAB-gFzAMUAvMBy44AKAAgAC4BCACIgKTHQDwAFgAVABBADIANAAiABiAGiAP0AiwBcwDFAJkAXmAywBy5AAEAAgBSZCAIAAsAMQBcwDFEoA4ACAAFgBEADEAYoBeYDLCQAIAC4CkykAwABYAFQAQQAyADQAIgAYgBogD9AIsAXMAxQC8ygAIAC4B2w.f_wAAAAAAAAA&gdpr=1&v-args=%3Ft%3D3%26d%3D15%26ct%3D%255B1014%252C1020%255D%26ca%3D%255B7%255D%26s%3D1983&ex-fargs=%3Fi%3DorKmNws2I5G1jElX1LqsHA%26e%3DvideoFirstQuartile%26a%3D577981708224706615%26c%3D577469246600535182%26s%3Dpda%26u%3DorKmNws2I5G1jElX1LqsHA&vdb=%5B1014%2C1020%5D%3A%5B7%5D%3A3%3Anull%3A15%3A1983
+ https://aax-eu.amazon-adsystem.com/x/px/JKKypjcLNiORtYxJV9S6rBwAAAGWpaxx-AMAAAe_BAAzcHhfdHhuX2JpZDEgICAzcHhfdHhuX2ltcDEgICBlzhah/%7B%221q%22%3A1%2C%22c%22%3A%22video%22%2C%22src%22%3A1983%7D
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=201326592&r=524918%3B518308&adid=53236803&reid=831875215&arid=1441792&auid=&cn=midPoint&et=i&_cc=&tpos=919&async=0&iw=&uxnw=&uxss=&uxct=&metr=7&init=1
+ https://ade.googlesyndication.com/ddm/activity/dc_oe=ChMI2fX3zPKOjQMVKZlQBh1R9xWSEAEYACD82pJvKgTBLTSTSABQOljPdWD-4_sPaM2izMcB;dc_eps=AHas8cDsBHGGsBOQIDkqH05lGBgdSs08YEQXY4sssz3gVFnR_feS7jWkm_9g9JMKSztLy0FwR_5sE8puQ1-PnTXg7ZCjCTog2wT0;met=1;ecn1=1;etm1=0;eid1=18;
+ https://aax-eu.amazon-adsystem.com/s/iui3?ex-fch=416719&d=forester-did&cb=1019708848455052&gdpr_pd=1&gdpr_consent=CQNEqlgQNEqlgAcABBSVBdFsAP_gAAAAAChQK1wNAAEQAKAAsACAAFQALgAZAA8ACAAGQANAAiQBNAE4ALYAXwAxABuADmAICAQQBBgCFAEYANEAfoBCACIgEWAI6ATgArIBcwDFAG2AO2AmQBSYCwwF5gMZAZYA4QBy4E9IKRgpXBS0FMgKaQU2BT-CoIKiQVGBVCCqgKsQVaBV-CsIKxwVlBWsAAABISAYAAgABYAFQAPAAggBkAGgARAB-gFzAMUAvMBy44AKAAgAC4BCACIgKTHQDwAFgAVABBADIANAAiABiAGiAP0AiwBcwDFAJkAXmAywBy5AAEAAgBSZCAIAAsAMQBcwDFEoA4ACAAFgBEADEAYoBeYDLCQAIAC4CkykAwABYAFQAQQAyADQAIgAYgBogD9AIsAXMAxQC8ygAIAC4B2w.f_wAAAAAAAAA&gdpr=1&v-args=%3Ft%3D3%26d%3D15%26ct%3D%255B1014%252C1020%255D%26ca%3D%255B7%255D%26s%3D1983&ex-fargs=%3Fi%3DorKmNws2I5G1jElX1LqsHA%26e%3DvideoMidpoint%26a%3D577981708224706615%26c%3D577469246600535182%26s%3Dpda%26u%3DorKmNws2I5G1jElX1LqsHA&vdb=%5B1014%2C1020%5D%3A%5B7%5D%3A3%3Anull%3A15%3A1983
+ https://aax-eu.amazon-adsystem.com/x/px/JKKypjcLNiORtYxJV9S6rBwAAAGWpaxx-AMAAAe_BAAzcHhfdHhuX2JpZDEgICAzcHhfdHhuX2ltcDEgICBlzhah/%7B%22c%22%3A%22video%22%2C%22src%22%3A1983%2C%222q%22%3A1%7D
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=201326592&r=524918%3B518308&adid=53236803&reid=831875215&arid=1441792&auid=&cn=thirdQuartile&et=i&_cc=&tpos=919&async=0&iw=&uxnw=&uxss=&uxct=&metr=7&init=1
+ https://ade.googlesyndication.com/ddm/activity/dc_oe=ChMI2fX3zPKOjQMVKZlQBh1R9xWSEAEYACD82pJvKgTBLTSTSABQOljPdWD-4_sPaM2izMcB;dc_eps=AHas8cDsBHGGsBOQIDkqH05lGBgdSs08YEQXY4sssz3gVFnR_feS7jWkm_9g9JMKSztLy0FwR_5sE8puQ1-PnTXg7ZCjCTog2wT0;met=1;ecn1=1;etm1=0;eid1=960585;
+ https://aax-eu.amazon-adsystem.com/s/iui3?ex-fch=416719&d=forester-did&cb=4738944078196523&gdpr_pd=1&gdpr_consent=CQNEqlgQNEqlgAcABBSVBdFsAP_gAAAAAChQK1wNAAEQAKAAsACAAFQALgAZAA8ACAAGQANAAiQBNAE4ALYAXwAxABuADmAICAQQBBgCFAEYANEAfoBCACIgEWAI6ATgArIBcwDFAG2AO2AmQBSYCwwF5gMZAZYA4QBy4E9IKRgpXBS0FMgKaQU2BT-CoIKiQVGBVCCqgKsQVaBV-CsIKxwVlBWsAAABISAYAAgABYAFQAPAAggBkAGgARAB-gFzAMUAvMBy44AKAAgAC4BCACIgKTHQDwAFgAVABBADIANAAiABiAGiAP0AiwBcwDFAJkAXmAywBy5AAEAAgBSZCAIAAsAMQBcwDFEoA4ACAAFgBEADEAYoBeYDLCQAIAC4CkykAwABYAFQAQQAyADQAIgAYgBogD9AIsAXMAxQC8ygAIAC4B2w.f_wAAAAAAAAA&gdpr=1&v-args=%3Ft%3D3%26d%3D15%26ct%3D%255B1014%252C1020%255D%26ca%3D%255B7%255D%26s%3D1983&ex-fargs=%3Fi%3DorKmNws2I5G1jElX1LqsHA%26e%3DvideoThirdQuartile%26a%3D577981708224706615%26c%3D577469246600535182%26s%3Dpda%26u%3DorKmNws2I5G1jElX1LqsHA&vdb=%5B1014%2C1020%5D%3A%5B7%5D%3A3%3Anull%3A15%3A1983
+ https://aax-eu.amazon-adsystem.com/x/px/JKKypjcLNiORtYxJV9S6rBwAAAGWpaxx-AMAAAe_BAAzcHhfdHhuX2JpZDEgICAzcHhfdHhuX2ltcDEgICBlzhah/%7B%22c%22%3A%22video%22%2C%22src%22%3A1983%2C%223q%22%3A1%7D
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=201326592&r=524918%3B518308&adid=53236803&reid=831875215&arid=1441792&auid=&cn=complete&et=i&_cc=&tpos=919&async=0&iw=&uxnw=&uxss=&uxct=&metr=7&init=1
+ https://ade.googlesyndication.com/ddm/activity/dc_oe=ChMI2fX3zPKOjQMVKZlQBh1R9xWSEAEYACD82pJvKgTBLTSTSABQOljPdWD-4_sPaM2izMcB;dc_eps=AHas8cDsBHGGsBOQIDkqH05lGBgdSs08YEQXY4sssz3gVFnR_feS7jWkm_9g9JMKSztLy0FwR_5sE8puQ1-PnTXg7ZCjCTog2wT0;met=1;ecn1=1;etm1=0;eid1=13;
+ https://aax-eu.amazon-adsystem.com/s/iui3?ex-fch=416719&d=forester-did&cb=9041028638469008&gdpr_pd=1&gdpr_consent=CQNEqlgQNEqlgAcABBSVBdFsAP_gAAAAAChQK1wNAAEQAKAAsACAAFQALgAZAA8ACAAGQANAAiQBNAE4ALYAXwAxABuADmAICAQQBBgCFAEYANEAfoBCACIgEWAI6ATgArIBcwDFAG2AO2AmQBSYCwwF5gMZAZYA4QBy4E9IKRgpXBS0FMgKaQU2BT-CoIKiQVGBVCCqgKsQVaBV-CsIKxwVlBWsAAABISAYAAgABYAFQAPAAggBkAGgARAB-gFzAMUAvMBy44AKAAgAC4BCACIgKTHQDwAFgAVABBADIANAAiABiAGiAP0AiwBcwDFAJkAXmAywBy5AAEAAgBSZCAIAAsAMQBcwDFEoA4ACAAFgBEADEAYoBeYDLCQAIAC4CkykAwABYAFQAQQAyADQAIgAYgBogD9AIsAXMAxQC8ygAIAC4B2w.f_wAAAAAAAAA&gdpr=1&v-args=%3Ft%3D3%26d%3D15%26ct%3D%255B1014%252C1020%255D%26ca%3D%255B7%255D%26s%3D1983&ex-fargs=%3Fi%3DorKmNws2I5G1jElX1LqsHA%26e%3DvideoComplete%26a%3D577981708224706615%26c%3D577469246600535182%26s%3Dpda%26u%3DorKmNws2I5G1jElX1LqsHA&vdb=%5B1014%2C1020%5D%3A%5B7%5D%3A3%3Anull%3A15%3A1983
+ https://aax-eu.amazon-adsystem.com/x/px/JKKypjcLNiORtYxJV9S6rBwAAAGWpaxx-AMAAAe_BAAzcHhfdHhuX2JpZDEgICAzcHhfdHhuX2ltcDEgICBlzhah/%7B%22c%22%3A%22video%22%2C%22src%22%3A1983%2C%22cpl%22%3A1%7D
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=201326592&r=524918%3B518308&adid=53236803&reid=831875215&arid=1441792&auid=&cn=_mute&et=s&_cc=&tpos=919&async=0
+ https://ade.googlesyndication.com/ddm/activity/dc_oe=ChMI2fX3zPKOjQMVKZlQBh1R9xWSEAEYACD82pJvKgTBLTSTSABQOljPdWD-4_sPaM2izMcB;dc_eps=AHas8cDsBHGGsBOQIDkqH05lGBgdSs08YEQXY4sssz3gVFnR_feS7jWkm_9g9JMKSztLy0FwR_5sE8puQ1-PnTXg7ZCjCTog2wT0;met=1;ecn1=1;etm1=0;eid1=16;
+ https://aax-eu.amazon-adsystem.com/s/iui3?ex-fch=416719&d=forester-did&cb=8089363953119340&gdpr_pd=1&gdpr_consent=CQNEqlgQNEqlgAcABBSVBdFsAP_gAAAAAChQK1wNAAEQAKAAsACAAFQALgAZAA8ACAAGQANAAiQBNAE4ALYAXwAxABuADmAICAQQBBgCFAEYANEAfoBCACIgEWAI6ATgArIBcwDFAG2AO2AmQBSYCwwF5gMZAZYA4QBy4E9IKRgpXBS0FMgKaQU2BT-CoIKiQVGBVCCqgKsQVaBV-CsIKxwVlBWsAAABISAYAAgABYAFQAPAAggBkAGgARAB-gFzAMUAvMBy44AKAAgAC4BCACIgKTHQDwAFgAVABBADIANAAiABiAGiAP0AiwBcwDFAJkAXmAywBy5AAEAAgBSZCAIAAsAMQBcwDFEoA4ACAAFgBEADEAYoBeYDLCQAIAC4CkykAwABYAFQAQQAyADQAIgAYgBogD9AIsAXMAxQC8ygAIAC4B2w.f_wAAAAAAAAA&gdpr=1&v-args=%3Ft%3D3%26d%3D15%26ct%3D%255B1014%252C1020%255D%26ca%3D%255B7%255D%26s%3D1983&ex-fargs=%3Fi%3DorKmNws2I5G1jElX1LqsHA%26e%3DvideoMute%26a%3D577981708224706615%26c%3D577469246600535182%26s%3Dpda%26u%3DorKmNws2I5G1jElX1LqsHA&vdb=%5B1014%2C1020%5D%3A%5B7%5D%3A3%3Anull%3A15%3A1983
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=201326592&r=524918%3B518308&adid=53236803&reid=831875215&arid=1441792&auid=&cn=_un-mute&et=s&_cc=&tpos=919&async=0
+ https://ade.googlesyndication.com/ddm/activity/dc_oe=ChMI2fX3zPKOjQMVKZlQBh1R9xWSEAEYACD82pJvKgTBLTSTSABQOljPdWD-4_sPaM2izMcB;dc_eps=AHas8cDsBHGGsBOQIDkqH05lGBgdSs08YEQXY4sssz3gVFnR_feS7jWkm_9g9JMKSztLy0FwR_5sE8puQ1-PnTXg7ZCjCTog2wT0;met=1;ecn1=1;etm1=0;eid1=149645;
+ https://aax-eu.amazon-adsystem.com/s/iui3?ex-fch=416719&d=forester-did&cb=66912368996410&gdpr_pd=1&gdpr_consent=CQNEqlgQNEqlgAcABBSVBdFsAP_gAAAAAChQK1wNAAEQAKAAsACAAFQALgAZAA8ACAAGQANAAiQBNAE4ALYAXwAxABuADmAICAQQBBgCFAEYANEAfoBCACIgEWAI6ATgArIBcwDFAG2AO2AmQBSYCwwF5gMZAZYA4QBy4E9IKRgpXBS0FMgKaQU2BT-CoIKiQVGBVCCqgKsQVaBV-CsIKxwVlBWsAAABISAYAAgABYAFQAPAAggBkAGgARAB-gFzAMUAvMBy44AKAAgAC4BCACIgKTHQDwAFgAVABBADIANAAiABiAGiAP0AiwBcwDFAJkAXmAywBy5AAEAAgBSZCAIAAsAMQBcwDFEoA4ACAAFgBEADEAYoBeYDLCQAIAC4CkykAwABYAFQAQQAyADQAIgAYgBogD9AIsAXMAxQC8ygAIAC4B2w.f_wAAAAAAAAA&gdpr=1&v-args=%3Ft%3D3%26d%3D15%26ct%3D%255B1014%252C1020%255D%26ca%3D%255B7%255D%26s%3D1983&ex-fargs=%3Fi%3DorKmNws2I5G1jElX1LqsHA%26e%3DvideoUnmute%26a%3D577981708224706615%26c%3D577469246600535182%26s%3Dpda%26u%3DorKmNws2I5G1jElX1LqsHA&vdb=%5B1014%2C1020%5D%3A%5B7%5D%3A3%3Anull%3A15%3A1983
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=201326592&r=524918%3B518308&adid=53236803&reid=831875215&arid=1441792&auid=&cn=_pause&et=s&_cc=&tpos=919&async=0
+ https://ade.googlesyndication.com/ddm/activity/dc_oe=ChMI2fX3zPKOjQMVKZlQBh1R9xWSEAEYACD82pJvKgTBLTSTSABQOljPdWD-4_sPaM2izMcB;dc_eps=AHas8cDsBHGGsBOQIDkqH05lGBgdSs08YEQXY4sssz3gVFnR_feS7jWkm_9g9JMKSztLy0FwR_5sE8puQ1-PnTXg7ZCjCTog2wT0;met=1;ecn1=1;etm1=0;eid1=15;
+ https://aax-eu.amazon-adsystem.com/s/iui3?ex-fch=416719&d=forester-did&cb=6910527340519603&gdpr_pd=1&gdpr_consent=CQNEqlgQNEqlgAcABBSVBdFsAP_gAAAAAChQK1wNAAEQAKAAsACAAFQALgAZAA8ACAAGQANAAiQBNAE4ALYAXwAxABuADmAICAQQBBgCFAEYANEAfoBCACIgEWAI6ATgArIBcwDFAG2AO2AmQBSYCwwF5gMZAZYA4QBy4E9IKRgpXBS0FMgKaQU2BT-CoIKiQVGBVCCqgKsQVaBV-CsIKxwVlBWsAAABISAYAAgABYAFQAPAAggBkAGgARAB-gFzAMUAvMBy44AKAAgAC4BCACIgKTHQDwAFgAVABBADIANAAiABiAGiAP0AiwBcwDFAJkAXmAywBy5AAEAAgBSZCAIAAsAMQBcwDFEoA4ACAAFgBEADEAYoBeYDLCQAIAC4CkykAwABYAFQAQQAyADQAIgAYgBogD9AIsAXMAxQC8ygAIAC4B2w.f_wAAAAAAAAA&gdpr=1&v-args=%3Ft%3D3%26d%3D15%26ct%3D%255B1014%252C1020%255D%26ca%3D%255B7%255D%26s%3D1983&ex-fargs=%3Fi%3DorKmNws2I5G1jElX1LqsHA%26e%3DvideoPause%26a%3D577981708224706615%26c%3D577469246600535182%26s%3Dpda%26u%3DorKmNws2I5G1jElX1LqsHA&vdb=%5B1014%2C1020%5D%3A%5B7%5D%3A3%3Anull%3A15%3A1983
+ https://aax-eu.amazon-adsystem.com/x/px/JKKypjcLNiORtYxJV9S6rBwAAAGWpaxx-AMAAAe_BAAzcHhfdHhuX2JpZDEgICAzcHhfdHhuX2ltcDEgICBlzhah/%7B%22p%22%3A1%2C%22c%22%3A%22video%22%2C%22src%22%3A1983%7D
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=201326592&r=524918%3B518308&adid=53236803&reid=831875215&arid=1441792&auid=&cn=_expand&et=s&_cc=&tpos=919&async=0
+ https://ade.googlesyndication.com/ddm/activity/dc_oe=ChMI2fX3zPKOjQMVKZlQBh1R9xWSEAEYACD82pJvKgTBLTSTSABQOljPdWD-4_sPaM2izMcB;dc_eps=AHas8cDsBHGGsBOQIDkqH05lGBgdSs08YEQXY4sssz3gVFnR_feS7jWkm_9g9JMKSztLy0FwR_5sE8puQ1-PnTXg7ZCjCTog2wT0;met=1;ecn1=1;etm1=0;eid1=19;
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=201326592&r=524918%3B518308&adid=53236803&reid=831875215&arid=1441792&auid=&cn=_resume&et=s&_cc=&tpos=919&async=0
+ https://aax-eu.amazon-adsystem.com/s/iui3?ex-fch=416719&d=forester-did&cb=2320475029873650&gdpr_pd=1&gdpr_consent=CQNEqlgQNEqlgAcABBSVBdFsAP_gAAAAAChQK1wNAAEQAKAAsACAAFQALgAZAA8ACAAGQANAAiQBNAE4ALYAXwAxABuADmAICAQQBBgCFAEYANEAfoBCACIgEWAI6ATgArIBcwDFAG2AO2AmQBSYCwwF5gMZAZYA4QBy4E9IKRgpXBS0FMgKaQU2BT-CoIKiQVGBVCCqgKsQVaBV-CsIKxwVlBWsAAABISAYAAgABYAFQAPAAggBkAGgARAB-gFzAMUAvMBy44AKAAgAC4BCACIgKTHQDwAFgAVABBADIANAAiABiAGiAP0AiwBcwDFAJkAXmAywBy5AAEAAgBSZCAIAAsAMQBcwDFEoA4ACAAFgBEADEAYoBeYDLCQAIAC4CkykAwABYAFQAQQAyADQAIgAYgBogD9AIsAXMAxQC8ygAIAC4B2w.f_wAAAAAAAAA&gdpr=1&v-args=%3Ft%3D3%26d%3D15%26ct%3D%255B1014%252C1020%255D%26ca%3D%255B7%255D%26s%3D1983&ex-fargs=%3Fi%3DorKmNws2I5G1jElX1LqsHA%26e%3DvideoResume%26a%3D577981708224706615%26c%3D577469246600535182%26s%3Dpda%26u%3DorKmNws2I5G1jElX1LqsHA&vdb=%5B1014%2C1020%5D%3A%5B7%5D%3A3%3Anull%3A15%3A1983
+ https://aax-eu.amazon-adsystem.com/x/px/JKKypjcLNiORtYxJV9S6rBwAAAGWpaxx-AMAAAe_BAAzcHhfdHhuX2JpZDEgICAzcHhfdHhuX2ltcDEgICBlzhah/%7B%22r%22%3A1%2C%22c%22%3A%22video%22%2C%22src%22%3A1983%7D
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=201326592&r=524918%3B518308&adid=53236803&reid=831875215&arid=1441792&auid=&cn=_rewind&et=s&_cc=&tpos=919&async=0
+ https://aax-eu.amazon-adsystem.com/s/iui3?ex-fch=416719&d=forester-did&cb=8817632692896136&gdpr_pd=1&gdpr_consent=CQNEqlgQNEqlgAcABBSVBdFsAP_gAAAAAChQK1wNAAEQAKAAsACAAFQALgAZAA8ACAAGQANAAiQBNAE4ALYAXwAxABuADmAICAQQBBgCFAEYANEAfoBCACIgEWAI6ATgArIBcwDFAG2AO2AmQBSYCwwF5gMZAZYA4QBy4E9IKRgpXBS0FMgKaQU2BT-CoIKiQVGBVCCqgKsQVaBV-CsIKxwVlBWsAAABISAYAAgABYAFQAPAAggBkAGgARAB-gFzAMUAvMBy44AKAAgAC4BCACIgKTHQDwAFgAVABBADIANAAiABiAGiAP0AiwBcwDFAJkAXmAywBy5AAEAAgBSZCAIAAsAMQBcwDFEoA4ACAAFgBEADEAYoBeYDLCQAIAC4CkykAwABYAFQAQQAyADQAIgAYgBogD9AIsAXMAxQC8ygAIAC4B2w.f_wAAAAAAAAA&gdpr=1&v-args=%3Ft%3D3%26d%3D15%26ct%3D%255B1014%252C1020%255D%26ca%3D%255B7%255D%26s%3D1983&ex-fargs=%3Fi%3DorKmNws2I5G1jElX1LqsHA%26e%3DvideoSkipBackward%26a%3D577981708224706615%26c%3D577469246600535182%26s%3Dpda%26u%3DorKmNws2I5G1jElX1LqsHA&vdb=%5B1014%2C1020%5D%3A%5B7%5D%3A3%3Anull%3A15%3A1983
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=201326592&r=524918%3B518308&adid=53236803&reid=831875215&arid=1441792&auid=&cn=_accept-invitation&et=s&_cc=&tpos=919&async=0
+ https://aax-eu.amazon-adsystem.com/s/iui3?ex-fch=416719&d=forester-did&cb=4633332100950709&gdpr_pd=1&gdpr_consent=CQNEqlgQNEqlgAcABBSVBdFsAP_gAAAAAChQK1wNAAEQAKAAsACAAFQALgAZAA8ACAAGQANAAiQBNAE4ALYAXwAxABuADmAICAQQBBgCFAEYANEAfoBCACIgEWAI6ATgArIBcwDFAG2AO2AmQBSYCwwF5gMZAZYA4QBy4E9IKRgpXBS0FMgKaQU2BT-CoIKiQVGBVCCqgKsQVaBV-CsIKxwVlBWsAAABISAYAAgABYAFQAPAAggBkAGgARAB-gFzAMUAvMBy44AKAAgAC4BCACIgKTHQDwAFgAVABBADIANAAiABiAGiAP0AiwBcwDFAJkAXmAywBy5AAEAAgBSZCAIAAsAMQBcwDFEoA4ACAAFgBEADEAYoBeYDLCQAIAC4CkykAwABYAFQAQQAyADQAIgAYgBogD9AIsAXMAxQC8ygAIAC4B2w.f_wAAAAAAAAA&gdpr=1&v-args=%3Ft%3D3%26d%3D15%26ct%3D%255B1014%252C1020%255D%26ca%3D%255B7%255D%26s%3D1983&ex-fargs=%3Fi%3DorKmNws2I5G1jElX1LqsHA%26e%3DvideoCreativeView%26a%3D577981708224706615%26c%3D577469246600535182%26s%3Dpda%26u%3DorKmNws2I5G1jElX1LqsHA&vdb=%5B1014%2C1020%5D%3A%5B7%5D%3A3%3Anull%3A15%3A1983
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=201326592&r=524918%3B518308&adid=53236803&reid=831875215&arid=1441792&auid=&cn=_close&et=s&_cc=&tpos=919&async=0
+ https://aax-eu.amazon-adsystem.com/s/iui3?ex-fch=416719&d=forester-did&cb=3154322839807089&gdpr_pd=1&gdpr_consent=CQNEqlgQNEqlgAcABBSVBdFsAP_gAAAAAChQK1wNAAEQAKAAsACAAFQALgAZAA8ACAAGQANAAiQBNAE4ALYAXwAxABuADmAICAQQBBgCFAEYANEAfoBCACIgEWAI6ATgArIBcwDFAG2AO2AmQBSYCwwF5gMZAZYA4QBy4E9IKRgpXBS0FMgKaQU2BT-CoIKiQVGBVCCqgKsQVaBV-CsIKxwVlBWsAAABISAYAAgABYAFQAPAAggBkAGgARAB-gFzAMUAvMBy44AKAAgAC4BCACIgKTHQDwAFgAVABBADIANAAiABiAGiAP0AiwBcwDFAJkAXmAywBy5AAEAAgBSZCAIAAsAMQBcwDFEoA4ACAAFgBEADEAYoBeYDLCQAIAC4CkykAwABYAFQAQQAyADQAIgAYgBogD9AIsAXMAxQC8ygAIAC4B2w.f_wAAAAAAAAA&gdpr=1&v-args=%3Ft%3D3%26d%3D15%26ct%3D%255B1014%252C1020%255D%26ca%3D%255B7%255D%26s%3D1983&ex-fargs=%3Fi%3DorKmNws2I5G1jElX1LqsHA%26e%3DvideoClose%26a%3D577981708224706615%26c%3D577469246600535182%26s%3Dpda%26u%3DorKmNws2I5G1jElX1LqsHA&vdb=%5B1014%2C1020%5D%3A%5B7%5D%3A3%3Anull%3A15%3A1983
+
+
+ https://adclick.g.doubleclick.net/pcs/click?xai=AKAOjsum1W1Iy3YtJhj6KxB4cQE8j0x3vR-aT9pbzCJFJwlT_aSQlO72J1OkcMP-pmUl9jbhyhyJ_VVLDg-DBX_AN1dsS25rtBDWHLhhebk6bOS7RBc1YPwHwy7UkE-jEuW_gdG6lP52wdBgMuhvzizAEyNRar7_RcDqHfih1uckQw&sai=AMfl-YT2IVdJu9wtGQmBhak4mnTnBS1yvC_FqT07BD2y9M0PT-z7r7ChnMHiR7iW_xm6cMrFtclLJ8ZTWVql&sig=Cg0ArKJSzETz0QfuvdjFEAE&cry=1&fbs_aeid=%5Bgw_fbsaeid%5D&urlfix=1&adurl=https://havrefras.se%3Futm_source%3Dtv4play%26utm_medium%3Dpaid_video%26utm_campaign%3D2024_of_havrefras_v.16-19%26dclid%3D%25edclid!%26gad_source%3D7
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=201326592&r=524918%3B518308&adid=53236803&reid=831875215&arid=1441792&auid=&cn=defaultClick&et=c&_cc=&tpos=919&async=0
+ https://aax-eu.amazon-adsystem.com/s/iui3?aref=%5B%5BCS_MADS_TOKEN%5D%5D&ex-fch=416719&d=forester-did&cb=9599340948680342&gdpr_pd=1&gdpr_consent=CQNEqlgQNEqlgAcABBSVBdFsAP_gAAAAAChQK1wNAAEQAKAAsACAAFQALgAZAA8ACAAGQANAAiQBNAE4ALYAXwAxABuADmAICAQQBBgCFAEYANEAfoBCACIgEWAI6ATgArIBcwDFAG2AO2AmQBSYCwwF5gMZAZYA4QBy4E9IKRgpXBS0FMgKaQU2BT-CoIKiQVGBVCCqgKsQVaBV-CsIKxwVlBWsAAABISAYAAgABYAFQAPAAggBkAGgARAB-gFzAMUAvMBy44AKAAgAC4BCACIgKTHQDwAFgAVABBADIANAAiABiAGiAP0AiwBcwDFAJkAXmAywBy5AAEAAgBSZCAIAAsAMQBcwDFEoA4ACAAFgBEADEAYoBeYDLCQAIAC4CkykAwABYAFQAQQAyADQAIgAYgBogD9AIsAXMAxQC8ygAIAC4B2w.f_wAAAAAAAAA&gdpr=1&v-args=%3Ft%3D3%26d%3D15%26ct%3D%255B1014%252C1020%255D%26ca%3D%255B7%255D%26s%3D1983&ex-fargs=%3Fi%3DorKmNws2I5G1jElX1LqsHA%26e%3DvideoClick%26a%3D577981708224706615%26c%3D577469246600535182%26s%3Dpda%26u%3DorKmNws2I5G1jElX1LqsHA&vdb=%5B1014%2C1020%5D%3A%5B7%5D%3A3%3Anull%3A15%3A1983
+ https://aax-eu.amazon-adsystem.com/x/c/JKKypjcLNiORtYxJV9S6rBwAAAGWpaxx-AMAAAe_BAAzcHhfdHhuX2JpZDEgICAzcHhfdHhuX2ltcDEgICBlzhah/clv1_CEuOPUxokZB9hHrVXf0OxgTPWypaD5VstroDxWWu7vhV4X8Vny6df_w18HHE8xvbmq442pNCnbWmP0nDHwJkJE1FKDrLN5fcSkMgYnEob0K6PK8EYFEE89IliATjLfm-EsjLNhP9s9RSA0MhlFD1a1sJOldNDd93Q1piUbI6XVeDyfnNkkszRVC7PITyOqgoCHBLaoni0btJbVVKcNtLSeMVuNWYuPIKWEnyFLRwyQktuYpotp68QXYWqm2D_gB6OEbQaNz-5Cdf6WE0GPkvayyIflibqg3lG824rp_VY68nBn358p-A_TCvIzb9JaONztpB2s9TF_7mrt7QUMiXFJtPjsB1eDwP94g1_49OPLtAz1BBHHuPthaW0bYbWX6dEztAj1qksWQIlgrWCZaPfz99drPqmfxJP707El00ogpoBehRnSxrQDww0rRSZBEWDJO3SP2xo9maXyNNYSyilL0ScWuSxFjtOjFMe41-mtP9ATU_KUI2G9A556MhdlUrXTDscLCse8OVQ7orPeVFLF2X07byr52koNLnmlHwRPT1bWb-Y2olmD9HVxOtLjmvRuV7r9JhbHLAy7xSrdsaGwSGMRd_mjd42vts-I6hjVoHppCt3e9Jfke55QqHa_bgTHgnTt57LAcL7OVeKNUS8Ucx15XPMQ9hWWBGnsJC6d47GwkYUAjkrXUerOmwLS81r3Rc_1dL1u0q1_MKCVu5AEJEo53SYK0ws9SKS0EQGg2J-UmrHY0tnmAB0uIOdvW5vo5Anjt3X5v221ooGiuuHcrqfQ/
+ https://aax-eu.amazon-adsystem.com/x/px/JKKypjcLNiORtYxJV9S6rBwAAAGWpaxx-AMAAAe_BAAzcHhfdHhuX2JpZDEgICAzcHhfdHhuX2ltcDEgICBlzhah/%7B%22c%22%3A%22video%22%2C%22clk%22%3A1%2C%22src%22%3A1983%7D
+
+
+ https://prism-ads-cdn.a2d.tv/abr/file_16659921_981/3ebcf19f-016d-4be2-abd7-1f746d106c37/index.m3u8
+
+
+
+
+
+
+ 233090428-1
+
+
+
+
+
+
+ FreeWheel
+ Blommande körsbärsträd
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=&r=524918&adid=84694611&reid=589423891&arid=0&async=0&iw=&uxnw=&uxss=&uxct=&et=e&cn=[ERRORCODE]
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=&r=524918&adid=84694611&reid=589423891&arid=0&auid=&cn=defaultImpression&et=i&_cc=84694611,589423891,,,1746536264,1&tpos=919&async=0&iw=&uxnw=&uxss=&uxct=&metr=7&init=1&vcid2=794e2760-28a4-4b07-bf80-96e963a6020e&pingids=5991
+ https://730721846599843.tv4mms.a2d.tv/tracker.png
+
+
+ 145507756
+
+ 00:00:03
+
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=&r=524918&adid=84694611&reid=589423891&arid=0&auid=&cn=complete&et=i&_cc=&tpos=919&async=0&init=1&iw=&uxnw=&uxss=&uxct=&metr=7
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=&r=524918&adid=84694611&reid=589423891&arid=0&auid=&cn=firstQuartile&et=i&_cc=&tpos=919&async=0&init=1&iw=&uxnw=&uxss=&uxct=&metr=7
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=&r=524918&adid=84694611&reid=589423891&arid=0&auid=&cn=midPoint&et=i&_cc=&tpos=919&async=0&init=1&iw=&uxnw=&uxss=&uxct=&metr=7
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=&r=524918&adid=84694611&reid=589423891&arid=0&auid=&cn=thirdQuartile&et=i&_cc=&tpos=919&async=0&init=1&iw=&uxnw=&uxss=&uxct=&metr=7
+
+
+ https://80276.v.fwmrm.net/ad/l/1?s=v2c22&n=524918%3B524918%3B512166%3B512167%3B512188%3B516869%3B517250%3B517327%3B517424%3B518308%3B529333&t=1746536263310556650&f=&r=524918&adid=84694611&reid=589423891&arid=0&auid=&cn=defaultClick&et=c&_cc=&tpos=919&async=0
+
+
+ https://prism-ads-cdn.a2d.tv/abr/end_4_HV3_K_RSB_RSBLOMMOR_2023_P8_mp4_1712561286_11776887_981/41c8dc1f-da69-48f2-9883-814a65404fe2/index.m3u8
+
+
+
+
+
+
+ 145507756
+
+ bumper
+
+
+
+
+
+
diff --git a/vmap/sample-vmap/testVastSpecialChars.xml b/vmap/sample-vmap/testVastSpecialChars.xml
new file mode 100644
index 0000000..4430067
--- /dev/null
+++ b/vmap/sample-vmap/testVastSpecialChars.xml
@@ -0,0 +1,8 @@
+
+
+
+
+ Hej&ö
<>"
+
+
+
diff --git a/vmap/structure_test.go b/vmap/structure_test.go
index ddde73f..c656142 100644
--- a/vmap/structure_test.go
+++ b/vmap/structure_test.go
@@ -3,8 +3,11 @@ package vmap
import (
"encoding/json"
"encoding/xml"
+ "fmt"
"io"
"os"
+ "strings"
+ "sync"
"testing"
"time"
@@ -20,6 +23,7 @@ func TestUnmarshalVMAP(t *testing.T) {
var vmap VMAP
xmlBytes, err := io.ReadAll(f)
is.NoErr(err)
+
err = xml.Unmarshal(xmlBytes, &vmap)
is.NoErr(err)
@@ -47,6 +51,43 @@ func TestUnmarshalVMAP(t *testing.T) {
is.Equal(len(thirdBreak.TrackingEvents), 1)
}
+func TestDecodeVmap(t *testing.T) {
+ is := is.New(t)
+ f, err := os.Open("sample-vmap/testVmap.xml")
+ is.NoErr(err)
+ defer f.Close()
+
+ var vmap VMAP
+ xmlBytes, err := io.ReadAll(f)
+ is.NoErr(err)
+
+ vmap, err = DecodeVmap(xmlBytes)
+ is.NoErr(err)
+
+ is.Equal(len(vmap.AdBreaks), 3)
+ firstBreak := vmap.AdBreaks[0]
+ is.Equal(firstBreak.Id, "midroll.ad-1")
+ is.Equal(firstBreak.BreakType, "linear")
+ is.True(firstBreak.TimeOffset.Duration == nil)
+ is.Equal(firstBreak.TimeOffset.Position, OffsetStart)
+ is.True(firstBreak.AdSource.VASTData.VAST != nil)
+ is.Equal(len(firstBreak.TrackingEvents), 1)
+
+ secondBreak := vmap.AdBreaks[1]
+ is.Equal(secondBreak.Id, "midroll.ad-2")
+ is.Equal(secondBreak.BreakType, "linear")
+ is.Equal(*secondBreak.TimeOffset.Duration, Duration{5 * time.Minute})
+ is.True(firstBreak.AdSource.VASTData.VAST != nil)
+ is.Equal(len(secondBreak.TrackingEvents), 1)
+
+ thirdBreak := vmap.AdBreaks[2]
+ is.Equal(thirdBreak.Id, "midroll.ad-3")
+ is.Equal(thirdBreak.BreakType, "linear")
+ is.Equal(*thirdBreak.TimeOffset.Duration, Duration{7 * time.Minute})
+ is.True(thirdBreak.AdSource.VASTData.VAST != nil)
+ is.Equal(len(thirdBreak.TrackingEvents), 1)
+}
+
func TestUnmarshalVast(t *testing.T) {
is := is.New(t)
f, err := os.Open("sample-vmap/testVast.xml")
@@ -105,6 +146,64 @@ func TestUnmarshalVast(t *testing.T) {
is.Equal(mediaFile.Codec, "H.264")
}
+func TestDecodeVast(t *testing.T) {
+ is := is.New(t)
+ f, err := os.Open("sample-vmap/testVast.xml")
+ is.NoErr(err)
+ defer f.Close()
+
+ var vast VAST
+ xmlBytes, err := io.ReadAll(f)
+ is.NoErr(err)
+ vast, err = DecodeVast(xmlBytes)
+ is.NoErr(err)
+
+ is.Equal(len(vast.Ad), 2)
+ firstAd := vast.Ad[0]
+ is.Equal(firstAd.Id, "POD_AD-ID_001")
+ firstAdInLine := firstAd.InLine
+ is.Equal(firstAdInLine.AdSystem, "Test Adserver")
+ is.Equal(firstAdInLine.AdTitle, "Ad That Test-Adserver Wants Player To See #1")
+
+ // Error validation
+ firstAdError := firstAdInLine.Error
+ is.True(firstAdError != nil)
+ is.Equal(firstAdError.Value, "https://error-url/code")
+ // Extension validation
+ firstAdExtensions := firstAdInLine.Extensions
+ is.Equal(len(firstAdExtensions), 1)
+ firstAdExtension := firstAdExtensions[0]
+ is.Equal(firstAdExtension.ExtensionType, "FreeWheel")
+ firstAdExtensionCParams := firstAdExtension.CreativeParameters[0]
+ is.Equal(firstAdExtensionCParams.CreativeId, "132285420")
+ is.Equal(firstAdExtensionCParams.Name, "AdType")
+ is.Equal(firstAdExtensionCParams.Value, "bumper")
+ is.Equal(firstAdExtensionCParams.CreativeParameterType, "Linear")
+ // Impression validation
+ firstAdImpression := firstAdInLine.Impression
+ is.Equal(len(firstAdImpression), 1)
+ // Creatives validation
+ firstAdCreatives := firstAdInLine.Creatives
+ is.Equal(len(firstAdCreatives), 1)
+ firstCreative := firstAdCreatives[0]
+ is.Equal(firstCreative.Id, "CRETIVE-ID_001")
+ is.Equal(firstCreative.AdId, "alvedon-10s")
+ is.Equal(len(firstCreative.Linear.TrackingEvents), 5)
+ is.Equal(firstCreative.Linear.Duration, Duration{10 * time.Second})
+ is.Equal(len(firstCreative.Linear.MediaFiles), 1)
+ is.True(firstCreative.Linear.ClickThrough != nil)
+ is.Equal(len(firstCreative.Linear.ClickTracking), 0)
+ is.Equal(len(firstCreative.Linear.CustomClick), 0)
+ // MediaFile validation
+ mediaFile := firstCreative.Linear.MediaFiles[0]
+ is.Equal(mediaFile.Width, 718)
+ is.Equal(mediaFile.Height, 404)
+ is.Equal(mediaFile.MediaType, "video/mp4")
+ is.Equal(mediaFile.Delivery, "progressive")
+ is.Equal(mediaFile.Bitrate, 1300)
+ is.Equal(mediaFile.Codec, "H.264")
+}
+
func TestUnmarshalDuration(t *testing.T) {
is := is.New(t)
d := Duration{}
@@ -145,3 +244,135 @@ func TestMarshalJson(t *testing.T) {
is.NoErr(err)
is.Equal(vmap, vmap2)
}
+
+func BenchmarkUnmarshal(b *testing.B) {
+ doc, err := os.ReadFile("sample-vmap/testVmap.xml")
+ if err != nil {
+ panic(err)
+ }
+
+ var vmap VMAP
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _ = xml.Unmarshal(doc, &vmap)
+ }
+}
+
+func BenchmarkFasterDecode(b *testing.B) {
+ doc, err := os.ReadFile("sample-vmap/testVmap.xml")
+ if err != nil {
+ panic(err)
+ }
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _ = DecodeVmap(doc)
+ }
+}
+
+func TestSpecialCharacters(t *testing.T) {
+ is := is.New(t)
+ doc, err := os.ReadFile("sample-vmap/testVastSpecialChars.xml")
+ if err != nil {
+ panic(err)
+ }
+ var vastUnmarshal VAST
+ _ = xml.Unmarshal(doc, &vastUnmarshal)
+ vastDecoded, _ := DecodeVast(doc)
+
+ is.Equal(vastUnmarshal.Ad[0].InLine.AdTitle, vastDecoded.Ad[0].InLine.AdTitle)
+ is.Equal(vastDecoded.Ad[0].InLine.AdTitle, "Hej&รถ\n<>\"")
+}
+
+func TestDecodeCompliance(t *testing.T) {
+ wg := sync.WaitGroup{}
+ //Check for race conditions
+ for range 1000 {
+ wg.Add(1)
+ go func(wg *sync.WaitGroup, t *testing.T) {
+ defer wg.Done()
+ is := is.New(t)
+ doc, err := os.ReadFile("sample-vmap/testVmap.xml")
+ is.NoErr(err)
+
+ var vmap1 VMAP
+ err = xml.Unmarshal(doc, &vmap1)
+ is.NoErr(err)
+
+ vmap2, err := DecodeVmap(doc)
+ is.NoErr(err)
+
+ is.Equal(vmap1.Version, vmap2.Version)
+ is.Equal(vmap1.Vmap, vmap2.Vmap)
+ is.Equal(vmap1.XMLName.Local, vmap2.XMLName.Local)
+ is.Equal(vmap1.XMLName.Space, vmap2.XMLName.Space)
+
+ is.Equal(len(vmap1.AdBreaks), len(vmap2.AdBreaks))
+ for i := range vmap1.AdBreaks {
+ adb1 := vmap1.AdBreaks[i]
+ adb2 := vmap2.AdBreaks[i]
+ is.Equal(adb1.BreakType, adb2.BreakType)
+ is.Equal(adb1.Id, adb2.Id)
+ is.Equal(adb1.TimeOffset, adb2.TimeOffset)
+ is.Equal(adb1.TimeOffset.Duration, adb2.TimeOffset.Duration)
+ is.Equal(adb1.TimeOffset.Position, adb2.TimeOffset.Position)
+
+ if adb1.TrackingEvents != nil {
+ te1 := adb1.TrackingEvents
+ te2 := adb2.TrackingEvents
+
+ for j := range te1 {
+ abt1 := te1[j]
+ abt2 := te2[j]
+ is.Equal(abt1.Event, abt2.Event)
+ //Decode trims spaces, so not checking whitespace
+ is.Equal(strings.TrimSpace(abt1.Text), strings.TrimSpace(abt2.Text))
+ }
+ }
+
+ is.True(adb1.AdSource.VASTData.VAST != nil)
+ is.True(adb2.AdSource.VASTData.VAST != nil)
+ v1 := *adb1.AdSource.VASTData.VAST
+ v2 := *adb2.AdSource.VASTData.VAST
+ is.Equal(v1.Version, v2.Version)
+ is.Equal(v1.NoNamespaceSchemaLocation, v2.NoNamespaceSchemaLocation)
+ is.Equal(v1.Xsi, v2.Xsi)
+
+ for j := range v1.Ad {
+ ad1 := v1.Ad[j]
+ ad2 := v2.Ad[j]
+ is.Equal(ad1.Id, ad2.Id)
+ is.Equal(ad1.Sequence, ad2.Sequence)
+ if ad1.InLine != nil {
+ is.Equal(strings.TrimSpace(ad1.InLine.AdSystem), strings.TrimSpace(ad2.InLine.AdSystem))
+ is.Equal(strings.TrimSpace(ad1.InLine.AdTitle), strings.TrimSpace(ad2.InLine.AdTitle))
+ is.Equal(ad1.InLine.Error, ad2.InLine.Error)
+ if ad1.InLine.Error != nil {
+ is.Equal(ad1.InLine.Error.Value, ad2.InLine.Error.Value)
+ }
+ if ad1.InLine.Creatives != nil {
+ for i := range ad1.InLine.Creatives {
+ for j := range ad1.InLine.Creatives[i].Linear.TrackingEvents {
+ is.Equal(
+ strings.TrimSpace(ad1.InLine.Creatives[i].Linear.TrackingEvents[j].Text),
+ strings.TrimSpace(ad2.InLine.Creatives[i].Linear.TrackingEvents[j].Text),
+ )
+ }
+ for j := range ad1.InLine.Creatives[i].Linear.ClickTracking {
+ fmt.Println(strings.TrimSpace(ad1.InLine.Creatives[i].Linear.ClickTracking[j].Text))
+ is.Equal(
+ strings.TrimSpace(ad1.InLine.Creatives[i].Linear.ClickTracking[j].Text),
+ strings.TrimSpace(ad2.InLine.Creatives[i].Linear.ClickTracking[j].Text),
+ )
+ }
+ }
+
+ }
+ }
+ }
+ }
+ }(&wg, t)
+ }
+ wg.Wait()
+}