<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="/xsl/rss-style.xsl?v=1780727378" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">

    <title>Blog - tfe-ren webgunea</title>
    <link>https://tfe.eus/blog/</link>
    <author><name>tfe</name></author>
    

        <entry>
        <title>Guaua.eus-en podkastak deskargatzeko scripta</title>
        <content type="html"><![CDATA[
        <p>Guau.eus webgunea eta Guau android aplikazioa hain txarra diren ikusita, scripta bat sortu dut podkastak nire ordenagailuan deskargatzeko: <a href="../../download/Blog/guau/download_guau_podcast.py/">guau.py</a></p>
<p>&nbsp;</p>
<p>Oso erraza da scrtipa erabiltzea:</p>
<pre class="language-markup"><code>$ ./guau.py https://guau.eus/s/ainguratuak</code></pre>
<p><br><br></p>
<pre class="language-python"><code>#!/usr/bin/env python3
"""
Guau-ko podcasteko audio atalak deskargatzen ditu honetatik:
  - sailaren orria: https://guau.eus/s/&lt;slug&gt;
  - RSS jario zuzena: https://guau.eus/series/&lt;id&gt;/rss.xml

Guau-ren RSS jarioak normalean sail bereko denboraldi guztiak biltzen ditu
 (itunes:season etiketa anitz, jario berean).

Mendeko liburutegia: Python liburutegi estandarra soilik.
"""

from __future__ import annotations

import argparse
import re
import ssl
import sys
import urllib.error
import urllib.request
import xml.etree.ElementTree as ET
from pathlib import Path
from urllib.parse import urlparse, urlunparse

GUAU_HOSTS = ("guau.eus", "www.guau.eus")
ITUNES_NS = {"itunes": "http://www.itunes.com/dtds/podcast-1.0.dtd"}
DEFAULT_UA = (
    "Mozilla/5.0 (compatible; GuauPodcastFetcher/1.0; +https://guau.eus/)"
)


def _parse_itunes_int(el: ET.Element | None) -&gt; int | None:
    if el is None or not el.text:
        return None
    t = str(el.text).strip()
    if t.lstrip("-").isdigit():
        n = int(t)
        return n if n &gt;= 0 else None
    return None


def fetch(url: str, timeout: int, user_agent: str) -&gt; bytes:
    req = urllib.request.Request(
        url,
        headers={"User-Agent": user_agent, "Accept": "*/*"},
    )
    ctx = ssl.create_default_context()
    with urllib.request.urlopen(req, timeout=timeout, context=ctx) as resp:
        return resp.read()


def normalize_guau_url(url: str) -&gt; str:
    p = urlparse(url.strip())
    if not p.scheme:
        p = urlparse("https://" + url.strip().lstrip("/"))
    if p.netloc.lower() in ("guau.eus", "www.guau.eus") and not p.scheme:
        p = p._replace(scheme="https")
    return urlunparse(p)


def discover_rss_url(series_page_html: str) -&gt; str | None:
    # Lehenengo /series/&lt;id&gt;/rss.xml esteka erakusten den sailari dagokio
    # (orrialdearen hydratazioa, erlazionatutako &laquo;related&raquo; blokeak baino lehen).
    matches = re.findall(
        r"https://guau\.eus/series/(\d+)/rss\.xml",
        series_page_html,
    )
    if not matches:
        return None
    sid = matches[0]
    return f"https://guau.eus/series/{sid}/rss.xml"


def parse_rss_items(
    rss_xml: bytes,
) -&gt; list[tuple[str, str, int | None, int | None, int | None]]:
    """Itzultzen du (izenburua, mp3_url, byte_kopurua, denboraldia, atala) &lt;item&gt; bakoitzeko.

    byte_kopurua: RSS-ko length atributua. denboraldia / atala: itunes:season, itunes:episode.
    """
    root = ET.fromstring(rss_xml)
    channel = root.find("channel")
    if channel is None:
        return []
    out: list[tuple[str, str, int | None, int | None, int | None]] = []
    for item in channel.findall("item"):
        title_el = item.find("title")
        title = (title_el.text or "").strip() if title_el is not None else ""
        enc = item.find("enclosure")
        if enc is None:
            continue
        mp3 = enc.get("url") or ""
        if not mp3:
            continue
        raw_len = enc.get("length")
        length: int | None = None
        if raw_len and str(raw_len).strip().isdigit():
            length = int(str(raw_len).strip())
            if length &lt;= 0:
                length = None
        season = _parse_itunes_int(item.find("itunes:season", ITUNES_NS))
        episode = _parse_itunes_int(item.find("itunes:episode", ITUNES_NS))
        out.append((title, mp3, length, season, episode))
    return out


def multi_season_feed(items: list[tuple[str, str, int | None, int | None, int | None]]) -&gt; bool:
    """True, RSS-ak bi denboraldi edo gehiago baditu, edo 1 baino handiago den denboraldi bat (SxxEyy)."""
    seasons = {s for _, _, _, s, _ in items if s is not None}
    if len(seasons) &gt; 1:
        return True
    if seasons and max(seasons) &gt; 1:
        return True
    return False


def http_head_content_length(url: str, timeout: int, user_agent: str) -&gt; int | None:
    req = urllib.request.Request(
        url,
        method="HEAD",
        headers={"User-Agent": user_agent, "Accept": "*/*"},
    )
    ctx = ssl.create_default_context()
    try:
        with urllib.request.urlopen(req, timeout=timeout, context=ctx) as resp:
            cl = resp.headers.get("Content-Length")
            if cl and str(cl).strip().isdigit():
                n = int(str(cl).strip())
                return n if n &gt; 0 else None
    except (urllib.error.HTTPError, urllib.error.URLError, OSError, ValueError):
        pass
    return None


def resolved_expected_bytes(
    mp3_url: str,
    rss_length: int | None,
    timeout: int,
    user_agent: str,
) -&gt; int | None:
    if rss_length is not None:
        return rss_length
    return http_head_content_length(mp3_url, timeout, user_agent)


def safe_filename(
    title: str,
    index: int,
    season: int | None,
    episode: int | None,
    use_season_prefix: bool,
) -&gt; str:
    title_part = re.sub(r'[&lt;&gt;:"/\\|?*\x00-\x1f]', "_", title).strip()
    title_part = re.sub(r"\s+", " ", title_part)
    if not title_part:
        title_part = f"episode_{index:02d}"
    prefix = ""
    if use_season_prefix and season is not None and episode is not None:
        prefix = f"S{season:02d}E{episode:02d} - "
    elif use_season_prefix and season is not None:
        prefix = f"S{season:02d} - "
    elif use_season_prefix and episode is not None:
        prefix = f"E{episode:02d} - "
    base = prefix + title_part
    if not base.lower().endswith(".mp3"):
        base += ".mp3"
    return base


def download_file(url: str, dest: Path, timeout: int, user_agent: str) -&gt; None:
    dest.parent.mkdir(parents=True, exist_ok=True)
    req = urllib.request.Request(
        url,
        headers={"User-Agent": user_agent, "Accept": "*/*"},
    )
    ctx = ssl.create_default_context()
    tmp = dest.with_suffix(dest.suffix + ".part")
    try:
        with urllib.request.urlopen(req, timeout=timeout, context=ctx) as resp:
            total = int(resp.headers.get("Content-Length") or 0)
            chunk = 1024 * 256
            got = 0
            with open(tmp, "wb") as f:
                while True:
                    buf = resp.read(chunk)
                    if not buf:
                        break
                    f.write(buf)
                    got += len(buf)
                    if total and sys.stderr.isatty():
                        pct = 100.0 * got / total
                        sys.stderr.write(f"\r  {pct:5.1f}% ({got // (1024 * 1024)} MiB)")
        if sys.stderr.isatty():
            sys.stderr.write("\n")
        tmp.replace(dest)
    finally:
        if tmp.exists():
            tmp.unlink(missing_ok=True)


def main() -&gt; int:
    ap = argparse.ArgumentParser(
        description="Deskargatu MP3ak guau.eus-etik (/s/&hellip;) edo RSS ofizialetik."
    )
    ap.add_argument(
        "url",
        help="Sailaren orria (https://guau.eus/s/slug) edo RSS jarioa (/series/&lt;id&gt;/rss.xml)",
    )
    ap.add_argument(
        "-o",
        "--output-dir",
        type=Path,
        default=Path("."),
        help="Helburuko karpeta (lehenetsia: uneko direktorioa)",
    )
    ap.add_argument(
        "-n",
        "--dry-run",
        action="store_true",
        help="Erakutsi atalak deskargatu gabe",
    )
    ap.add_argument(
        "--timeout",
        type=int,
        default=120,
        help="HTTP denbora-muga segundotan (lehenetsia: 120)",
    )
    ap.add_argument(
        "--user-agent",
        default=DEFAULT_UA,
        help="HTTP eskaeretarako User-Agent",
    )
    args = ap.parse_args()
    raw = normalize_guau_url(args.url)
    parsed = urlparse(raw)

    rss_url: str | None = None
    if re.search(r"/series/\d+/rss\.xml$", parsed.path or "", re.I):
        rss_url = raw.split("?", 1)[0]
    elif re.search(r"/series/[^/]+/rss\.xml$", parsed.path or "", re.I):
        # Adib. /series/ainguratuak/rss.xml 500 itzuli dezake; saihesten da.
        print(
            "Erabili /s/&lt;slug&gt; orrialdearen URLa edo /series/&lt;zenbaki id&gt;/rss.xml.",
            file=sys.stderr,
        )
        return 1
    elif (parsed.netloc.lower().removesuffix(":443") in GUAU_HOSTS or not parsed.netloc) and (
        parsed.path or ""
    ).startswith("/s/"):
        try:
            html = fetch(raw, args.timeout, args.user_agent).decode(
                "utf-8", errors="replace"
            )
        except urllib.error.HTTPError as e:
            print(f"HTTP errorea orrialdea kargatzean: {e}", file=sys.stderr)
            return 1
        except Exception as e:
            print(f"Sareko errorea: {e}", file=sys.stderr)
            return 1
        rss_url = discover_rss_url(html)
        if not rss_url:
            print(
                "Ezin izan da RSS jarioa aurkitu orrian (series/&lt;id&gt;/rss.xml).",
                file=sys.stderr,
            )
            return 1
        print(f"RSS jarioa: {rss_url}")
    else:
        print(
            "URL ezezaguna. Espero daitekeena: https://guau.eus/s/&lt;slug&gt; "
            "edo https://guau.eus/series/&lt;id&gt;/rss.xml",
            file=sys.stderr,
        )
        return 1

    try:
        rss_body = fetch(rss_url, args.timeout, args.user_agent)
    except urllib.error.HTTPError as e:
        print(f"HTTP errorea RSS-ean: {e}", file=sys.stderr)
        return 1
    except Exception as e:
        print(f"Sareko errorea (RSS): {e}", file=sys.stderr)
        return 1

    items = parse_rss_items(rss_body)
    if not items:
        print("Ez dago enclosure duen atalik RSS-ean.", file=sys.stderr)
        return 1

    use_season_prefix = multi_season_feed(items)
    out_dir: Path = args.output_dir.resolve()
    print(f"{len(items)} atal &mdash; karpeta: {out_dir}")
    if use_season_prefix:
        seasons = sorted({s for _, _, _, s, _ in items if s is not None})
        if seasons:
            print(
                f"Hainbat denboraldi jario berean (denboraldiak: {', '.join(map(str, seasons))}) "
                "&mdash; SxxEyy aurrizkia fitxategi-izenetan."
            )

    for i, (title, mp3_url, rss_length, season, episode) in enumerate(items, start=1):
        name = safe_filename(title, i, season, episode, use_season_prefix)
        dest = out_dir / name
        print(f"[{i}/{len(items)}] {name}")
        if args.dry_run:
            continue
        expected = resolved_expected_bytes(
            mp3_url, rss_length, args.timeout, args.user_agent
        )
        if dest.exists() and dest.is_file():
            local_sz = dest.stat().st_size
            if expected is not None:
                if local_sz == expected:
                    print(f"  badago eta osatuta ({local_sz} B), saltatua")
                    continue
                if local_sz &gt; 0:
                    print(
                        f"  osatu gabe edo zaharkitua ({local_sz}/{expected} B), berriro deskargatzen"
                    )
            else:
                if local_sz &gt; 0:
                    print(
                        "  fitxategia badago, tamaina ezezaguna (RSS-ko length gabe, HEAD-k "
                        f"Content-Length gabe) &mdash; saltatua: {dest}"
                    )
                    continue
        try:
            download_file(mp3_url, dest, args.timeout, args.user_agent)
        except urllib.error.HTTPError as e:
            print(f"  HTTP hutsegitea: {e}", file=sys.stderr)
            return 1
        except Exception as e:
            print(f"  hutsegitea: {e}", file=sys.stderr)
            return 1
        print(f"  OK &rarr; {dest}")

    return 0


if __name__ == "__main__":
    sys.exit(main())
</code></pre>...
        ]]></content>
        <link>https://tfe.eus/blog/guau-en-podkastak-deskargatzeko-scripta.html</link>
        <id>https://tfe.eus/blog/guau-en-podkastak-deskargatzeko-scripta.html></id>
        <media:thumbnail url="/blog_data//2026-05-02_16-44.png-69f60dd3486b3.png?v=1777733075" />
        <category>Blog</category>
        <published>Sat, 02 May 2026 01:01:00 +0000</published>
    </entry>
        <entry>
        <title>Partiturak txertazeko aukera</title>
        <content type="html"><![CDATA[
        <p>"Partiturak txertazeko" aukera gehitzeko <a href="https://partiturak.eus">partiturak.eus</a>-en.</p>
<p><img src="../../../blog_data/mceclip0.png-6966be49e4d0b.png" width="851" height="457"></p>
<p>Ezarpenak batzuk daude:</p>
<ul>
<li>gehitu audio=0 audioa kentzeko</li>
<li>gehitu header=0 titulua kentzeko</li>
<li>gehitu tabs=0 partitura nagusia bakarrik ikusteko. Bestela dantza-ren partitura guztiak agertuko dira, webgunean bezala</li>
</ul>
<p>&nbsp;</p>
<p>&nbsp;</p>...
        ]]></content>
        <link>https://tfe.eus/blog/partiturak-txertazeko-aukera.html</link>
        <id>https://tfe.eus/blog/partiturak-txertazeko-aukera.html></id>
        <media:thumbnail url="/blog_data//2026-01-13_22-29.png-6966b9505f6e7.png?v=1768339792" />
        <category>Blog</category>
        <published>Tue, 13 Jan 2026 01:01:00 +0000</published>
    </entry>
        <entry>
        <title>Grafana, Prometheus, Alloy eta Loki</title>
        <content type="html"><![CDATA[
        <p>Aspalditik nahi izan dut&nbsp;<a href="https://grafana.com/">Grafana</a> probatu, baina ez dut inoiz denbora hartu. Orain, oporretan nagoenez, probat egin dut, eta oso pozik nago emaitzarekin.</p>
<p><img src="../../../blog_data/blobid0.png-6957886a791b4.png" width="824" height="425"></p>
<p>&nbsp;</p>
<h2>Zer dira metrikak eta Zertarako balio duen Prometheusek?</h2>
<p>Metrikak denborarekin lotutako zenbakiak dira. Hau da, sistema baten ezaugarri bat neurtzen duzunean, balioa eta denbora-zigilua gordetzen du.&nbsp;</p>
<p>Adibidez, CPU metrika batean zenbaki horiek izango ditugu:</p>
<p>CPU erabilera: <strong>5%</strong> - Noiz? 2026-01-02 10:02:00-an<br>CPU erabilera: <strong>7%</strong> - Noiz? 2026-01-02 10:02:20-an<br>CPU erabilera: <strong>5.5%</strong> - Noiz? 2026-01-02 10:05:00-an</p>
<p>Prometheus zerbitzari bat da metrikak bildu eta gordetzeko. Adibidez, zure cpu kontsumoa neurtu nahi baldin baduzu, prometheus erabiliko duzu datuak segundu bakoitzean gordetzeko.</p>
<h2>Eta Grafana?</h2>
<p>Grafana tresna bat da <strong>metriketan oinarritutako grafikoak eta panelak</strong> sortzeko. Horrez gain, metrika horietan oinarritutako jakinarazpenak konfiguratzeko aukera ematen du.<br>Baina kontuz, Grafanak ez du <strong>metrikak</strong> sortzen ezta gordetzen: Iturriak irakurtzen ditu grafikoak sortzeko.</p>
<h2>Eta Loki?</h2>
<p>Erakurri duzun bezala, Prometheusek metrikak kudeatzen ditu, baina ezin ditu <strong>log</strong>-daturik gorde. Behar hori asetzeko sortu zuten&nbsp; loki: logak bildu, kudeatu, eta gordetzeko.</p>
<h2>Grafana Alloy</h2>
<p>Alloy grafanaren zerbitzari bat da datuak irakurri, kudeatu, eta gordetzeko.</p>
<p>&nbsp;</p>
<h2>Docker-compose</h2>
<p>Serbitzari hauek guztiak instalatzeko docker-compose.yml artxiboa erabiliko dugu docker irudiak sortzeko. Zerbitzari bakoitza bere dockera izango du horrela.</p>
<pre class="language-markup"><code>networks:
  frontend:
    driver: bridge

services:

  # Grafana basic
  grafana:
    container_name: grafana
    image: grafana/grafana-enterprise
    hostname: grafana
    volumes:
      - grafana_data:/var/lib/grafana
    networks:
    - frontend

  # Node exporter ,for Metrics from the server
  node-exporter:
    container_name: node-exporter
    image: prom/node-exporter
    restart: unless-stopped
    hostname: node-exporter
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    expose:
      - 9100
    networks:
      - frontend
    command:
      - '--path.procfs=/host/proc'
      - '--path.rootfs=/rootfs'
      - '--path.sysfs=/host/sys'
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
    

  # Prometheus to read node-exporter logs and send
  prometheus:
    container_name: prometheus
    image: prom/prometheus
    hostname: prometheus
    volumes:
      - ./data/prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    networks:
      - frontend

  # Grafana loki , to receive log files
  loki:
    container_name: loki
    image: grafana/loki
    hostname: loki
    volumes:
      - loki_data:/loki
    networks:
    - frontend

  # Alloy - to send logs to Loki
  alloy:
    container_name: alloy
    image: grafana/alloy
    hostname: alloy
    volumes:
      - ./data/config.alloy:/etc/alloy/config.alloy
      - alloy_data:/var/lib/alloy/data
      - /var/log:/var/log/:ro
    networks:
    - frontend

volumes:
        prometheus_data:
        grafana_data:
        loki_data:
        alloy_data:
</code></pre>...
        ]]></content>
        <link>https://tfe.eus/blog/grafana-prometheus-alloy-loki.html</link>
        <id>https://tfe.eus/blog/grafana-prometheus-alloy-loki.html></id>
        <media:thumbnail url="/blog_data//2026-01-02_09-50.png-6957885a6d615.png?v=1767344218" />
        <category>Blog</category>
        <published>Fri, 02 Jan 2026 01:01:00 +0000</published>
    </entry>
        <entry>
        <title>Badok-etik abestiak deskargatzeko script-a</title>
        <content type="html"><![CDATA[
        <p>Asko gustatzen zait Badok webgunea, baina hain motel dabilela... php<a href="../../download/scripts/badok_mp3.php/"> script bat</a> sortu dut abestiak deskargatzeko.</p>
<pre class="language-php"><code>&lt;?php

$destination = "/home/tfe/Nextcloud/Musika/Badok";
if(!is_dir($destination))
{
    mkdir($destination, 0755, true);
}

$url = "https://www.badok.eus/azken-kantuak";

$content = file_get_contents($url);
preg_match_all("/&lt;a.*?data-mp3=\"mp3\".*?&gt;&lt;\/a&gt;/", $content, $matches);
foreach($matches[0] as $link)
{
    $tid = preg_replace("/.*data-tid=\"(.*?)\".*/", "$1", $link);

    $image = preg_replace("/.*data-uimg=\"(.*?)\".*/", "$1", $link);
    trim($image);

    $artist = preg_replace("/.*data-artist=\"(.*?)\".*/", "$1", $link);
    trim($artist);
    $artist = preg_replace("/[^a-zA-Z0-9]/", "_", $artist);

    $album = "";
    $izena = preg_replace("/.*data-izena=\"(.*?)\".*/", "$1", $link);
    trim($izena);
    $izena = preg_replace("/[^a-zA-Z0-9]/", "_", $izena);
    if(preg_match("/(.*) - (.*)/", $izena, $matches))
    {
        $album = $matches[1];
        $izena = $matches[2];
    }

    $artist_folder = "$destination/$artist";
    if(!is_dir($artist_folder))
    {
        mkdir($artist_folder, 0755, true);
    }

    $destination_file = "$destination/$artist/$album/$izena.mp3";
    if(!file_exists($destination_file))
    {
        $destination_image = "$destination/$artist/$album/$izena.jpg";
        file_put_contents($destination_image, file_get_contents($image));

        echo "[$artist] $izena ($tid)\n";
        $dl_url = json_decode(file_get_contents("https://www.badok.eus/wp-content/plugins/badok/server_processing.php?play_mp3=" . $tid),true);
        echo "Deskargatzen [$artist] $izena ($tid) - $destination_file\n";
        file_put_contents($destination_file, file_get_contents($dl_url["url"]));
        echo "OK\n";

        exec("eyeD3 --artist \"$artist\" --album \"$album\" --title \"$izena\" --add-image $destination_image:FRONT_COVER $destination_file");
    }
    else {
        echo "[$artist] $izena ($tid) Dagoeneko egina dago.\n";
    }
}


</code></pre>
<p>&nbsp;</p>...
        ]]></content>
        <link>https://tfe.eus/blog/badok-etik-deskargatzeko-scripta.html</link>
        <id>https://tfe.eus/blog/badok-etik-deskargatzeko-scripta.html></id>
        <media:thumbnail url="/blog_data//2025-12-04_10-51.png-69315a25b06a6.png?v=1764842021" />
        <category>Blog</category>
        <published>Thu, 04 Dec 2025 01:01:00 +0000</published>
    </entry>
        <entry>
        <title>Mermaid grafikoak</title>
        <content type="html"><![CDATA[
        <p>Ezagutzen al dituzue Mermaid grafikoak? Ziurrenik baietz, baina konturatu gabe.&nbsp;</p><p>
</p><p><a href="https://mermaid.js.org/">Mermaid</a> empresa bat da, eta berak garatu zuen grafikoak sortzeko javascript liburutegi bat.</p><p>
</p><p>Berrezitasun da grafikoa sortzen dira <strong>deskribapen bidez</strong>.</p><p>
</p><p>Adibidez: deskribapen honekin:</p><p>
</p><pre class="language-java"><code>flowchart TD
    A[Gabonak] --&gt;|Dirua lortu| B(Dendara joan)
    B --&gt; C{Asmatu oparia}
    C --&gt;|500€| D[Ordenagailu bat]
    C --&gt;|200€| E[Mugikor bat ]
    C --&gt;|10000€| F[fa:Auto bat]</code>
</pre><p>
</p><p>Grafiko hori lor dezakezue:</p><p>
</p><p><img src="../../../blog_data/mceclip0.png-68debd633f321.png" width="422" height="363"></p><p>
</p><p>&nbsp;</p><p>
</p><p>Noski, edozein motako grafiko sor dezakezue honekin. Horiek ezagutzeko, gonbidatzen zaitugu <a href="https://mermaid.live/"><strong>mermaid.live</strong></a> webgunera joatera.</p><p>
</p><p><a href="https://mermaid.js.org/ecosystem/integrations-community.html">Askotariko pluginak</a> exisititzen dira: Confluence, Jira, edo Github webguneetarako adibidez.</p><p>
</p><p>&nbsp;</p><p>
</p><p>&nbsp;</p>...
        ]]></content>
        <link>https://tfe.eus/blog/mermaid-grafikoak.html</link>
        <id>https://tfe.eus/blog/mermaid-grafikoak.html></id>
        <media:thumbnail url="/blog_data//mermaid.png-68debc83800ab.png?v=1759427715" />
        <category>Blog</category>
        <published>Sat, 06 Jun 2026 04:46:00 +0000</published>
    </entry>
        <entry>
        <title>Elhuyaren ahotsa entzuteko script bat</title>
        <content type="html"><![CDATA[
        <p>Ez naiz python zalea: Aitortu behar dut scripta sortzeko AA erabili dut.</p>
<p>Dena den: hemen dago emaitza: <a href="../../download/scripts/elhuyar_ahotsa.py/">elhuyar_ahotsa.py</a>.</p>
<p>&nbsp;</p>
<p>Ideia da:</p>
<ul>
<li><a href="https://ttsneuronala.elhuyar.eus/es">https://ttsneuronala.elhuyar.eus/es</a> erabili csrf token bilatzeko</li>
<li>csrftoken erabiliz, <a href="https://ttsneuronala.elhuyar.eus/ajax/get_audio_from_box">https://ttsneuronala.elhuyar.eus/ajax/get_audio_from_box</a> erabili audioaren esteka lortzeko</li>
<li>ffplay aplikazioa erabili esteka entzuteko</li>
</ul>...
        ]]></content>
        <link>https://tfe.eus/blog/elhuyaren-ahotsa-entzuteko-script-bat.html</link>
        <id>https://tfe.eus/blog/elhuyaren-ahotsa-entzuteko-script-bat.html></id>
        <media:thumbnail url="/blog_data//2025-07-21_10-16.png-687df8076b520.png?v=1753085959" />
        <category>Blog</category>
        <published>Mon, 21 Jul 2025 01:01:00 +0000</published>
    </entry>
        <entry>
        <title>[PHP] puntu bat (.) esteka parametroaren izen batean</title>
        <content type="html"><![CDATA[
        <p>Imagina dezagun esteka hori: index.php?<strong>aldagaia.1</strong>=bat&amp;<strong>aldagaia.2</strong>=bi<br>Suposatuko dugu, <strong>$_GET </strong>aldagaiaren barruan hori aurkituko dugu:</p>
<pre class="language-markup"><code>[
  "aldagaia.1"  =&gt; "bat",
  "aldagaia.2" =&gt; "bi"
]
 </code></pre>
<p>Baina EZ! <br>Php-ri ezker (<a href="https://www.php.net/manual/en/language.variables.external.php#language.variables.external.dot-in-names)">https://www.php.net/manual/en/language.variables.external.php#language.variables.external.dot-in-names)</a> &nbsp;puntuak azpimarraz ordezkatzen dira!</p>
<pre class="language-markup"><code>[
  "aldagaia1"  =&gt; "bat",
  "aldagaia_2" =&gt; "bi"
]
 </code></pre>
<p>&nbsp;</p>
<p>Momentuz, ez dago saihesteko konponbiderik, eta benetako parametroak bilatzeko funtzio bat sortu behar dugu:</p>
<pre class="language-php"><code>    function realGET($data)
    {
        $data = preg_replace_callback('/(?:^|(?&lt;=&amp;))[^=[]+/', function($match) {
            return bin2hex(urldecode($match[0]));
        }, $data);
        parse_str($data, $values);
        return array_combine(array_map('hex2bin', array_keys($values)), $values);
    }

         $newGET = realGET($_SERVER['QUERY_STRING']);

</code></pre>...
        ]]></content>
        <link>https://tfe.eus/blog/php-get-aldagaia-puntu-kodigoarekin.html</link>
        <id>https://tfe.eus/blog/php-get-aldagaia-puntu-kodigoarekin.html></id>
        <media:thumbnail url="/blog_data//2025-06-22_11-49.png-6857d1c4a979a.png?v=1750585796" />
        <category>Blog</category>
        <published>Sun, 22 Jun 2025 01:01:00 +0000</published>
    </entry>
        <entry>
        <title>Shadow Tactics: Blades of the Shogun - jokoa euskaraz</title>
        <content type="html"><![CDATA[
        <p>Asko kostatu zait, baina azkenean "<a href="https://store.steampowered.com/app/418240/Shadow_Tactics_Blades_of_the_Shogun/">Shadow Tactics: Blades of the Shogun</a>" jokoa euskaraz itzultzea lortu dut!</p>
<p><img src="../../../blog_data/mceclip0.png-67e0389fa989b.png"></p>
<p>Azalpenak eman baino lehen, hemen duzu behar duzun fitxategia jokoa euskaraz izateko:</p>
<p>&nbsp;</p>
<p><strong>Linuxen:</strong></p>
<p>Deskargatu <a href="../../../download/Blog/jokoak_euskaraz/shadow_tactics/shadowtactics-eu.sh/?download=1">shadowtactics-eu.sh</a> fitxategia, eta:</p>
<pre class="language-php"><code>$ chmod +x shadowtactics-eu.sh
$ ./shadowtactics-eu.sh</code></pre>
<p>&nbsp;</p>
<p><strong>Windowsen:</strong></p>
<p>Deskargatu <a href="../../../download/Blog/jokoak_euskaraz/shadow_tactics/shadow_tactics_euskaraz.exe">shadow_tactics_euskaraz.exe</a> eta exekutatu.</p>
<p>Eskerrik asko <a href="https://ibaios.eus/">ibaios</a>!</p>
<p>&nbsp;</p>
<p><strong>Instalatu ondoren:</strong></p>
<p><span class="HwtZe" lang="eu"><span class="jCAhz ChMk0b"><span class="ryNqvb">A</span></span></span>ukeratu ezkuntza "Espa&ntilde;ol" :</p>
<p><img src="../../../blog_data/mceclip0.png-67e31654817e2.png" width="861" height="478"></p>
<p>&nbsp;</p>
<p><strong>Azalpenak:</strong></p>
<p><a href="https://github.com/nesrak1/UABEA/releases/">UABEA</a> erabiliz, edozein datuak irakur ditzake bideojokotik.&nbsp;</p>
<p><img src="../../../blog_data/mceclip1.png-67e2aa96e3727.png"></p>
<p><strong>resources.assets</strong> fitxategia irekitzean, datu asko daude ikusgai, besteak beste:</p>
<p><img src="../../../blog_data/mceclip2.png-67e2aad56c332.png" width="693" height="530"></p>
<p>&nbsp;</p>
<p>Jokoa itzultzeko, script bat sortu dut, datu horietatik&nbsp; 3 CSV fitxategi sortzeko:</p>
<ul>
<li><a href="../../../download/Blog/jokoak_euskaraz/shadow_tactics/jsons_to_csv.php/">jsons_to_csv.php</a> - jokotik testu arruntak erauzteko</li>
<li><a href="../../../download/Blog/jokoak_euskaraz/shadow_tactics/jsons_to_csv_levels.php/">jsons_to_csv_levels.php</a>&nbsp; - maila gutzien testuak itzultzeko</li>
<li><a href="../../../download/Blog/jokoak_euskaraz/shadow_tactics/jsons_to_csv_vo.php/">jsons_to_csv_vo.php&nbsp;</a> - bideo sekuentzien testuak erauzteko</li>
</ul>
<p>Eta hori esker 3 csv fitxategi sortu ditut:</p>
<ul>
<li><a href="../../../download/Blog/jokoak_euskaraz/shadow_tactics/levelOK.csv/">levelOK.csv</a></li>
<li><a href="../../../download/Blog/jokoak_euskaraz/shadow_tactics/voOK.csv/">voOK.csv</a></li>
<li><a href="../../../download/Blog/jokoak_euskaraz/shadow_tactics/loca_general_Spanish-resources.assets-322.csv/">loca_general_Spanish-resources.assets-322.csv</a></li>
</ul>
<p>Fitxategiak testu guztiak dituzte, eta google translator bidez itzulpen arraunt bat egin dut.</p>
<p>Gero, lerroz-lerro irakuri dut googlen itzulpena, eta saiatu naiz aurkitu ditudan akatzak konpontzen.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>...
        ]]></content>
        <link>https://tfe.eus/blog/Shadow-tactics-euskaraz-itzulpena.html</link>
        <id>https://tfe.eus/blog/Shadow-tactics-euskaraz-itzulpena.html></id>
        <media:thumbnail url="/blog_data//2025-03-23_17-32.png-67e037bf57a0d.png?v=1742747583" />
        <category>Blog</category>
        <published>Sun, 23 Mar 2025 01:01:00 +0000</published>
    </entry>
        <entry>
        <title>Bash aurkikuntzak</title>
        <content type="html"><![CDATA[
        <p>Gaurko #Linux #Bash aurkikuntzak:<br>Baliteke arruntak &nbsp;izatea, baina badaezpada, hemen dituzue:</p>
<p>Antzekoak diren komandoak:<br><code>$ cd /home/$USER</code><br><code>$ cd ~&nbsp;</code><br><code>$ cd</code></p>
<p>Laburpenak:<br><code>$ !vi # Exekutatu "vi"-rekin hasten den azken komandoa .</code></p>
<p>Adibide bat:<br><code>$ cd src/project</code><br><code>$ vi source.c</code><br><code>$ make build</code><br><code>$ ....</code><br><code>$ !v # vi irekitzeko</code><br><code>$ !m # make build exekutatzeko</code></p>
<p>&nbsp;</p>
<div id="simple-translate" class="simple-translate-system-theme">
<div>
<div class="simple-translate-button isShow" style="background-image: url('moz-extension://3cfc37f1-a638-4b67-9c11-9cc27fe77ce7/icons/512.png'); height: 22px; width: 22px; top: 223px; left: 14px;">&nbsp;</div>
<div class="simple-translate-panel " style="width: 300px; height: 200px; top: 0px; left: 0px; font-size: 13px;">
<div class="simple-translate-result-wrapper" style="overflow: hidden;">
<div class="simple-translate-move" draggable="true">&nbsp;</div>
<div class="simple-translate-result-contents">
<p class="simple-translate-result" dir="auto">&nbsp;</p>
<p class="simple-translate-candidate" dir="auto">&nbsp;</p>
</div>
</div>
</div>
</div>
</div>...
        ]]></content>
        <link>https://tfe.eus/blog/bash-aurkikuntzak.html</link>
        <id>https://tfe.eus/blog/bash-aurkikuntzak.html></id>
        <media:thumbnail url="/blog_data//2025-01-23_16-16.png-67925d497c66a.png?v=1737645385" />
        <category>Blog</category>
        <published>Thu, 23 Jan 2025 12:00:00 +0000</published>
    </entry>
        <entry>
        <title>Pid  jokoa - euskaraz</title>
        <content type="html"><![CDATA[
        <p><a href="https://github.com/ZiTAL/JokoakEuskaraz">Zital</a>-i esker bideo-jokoak modu erraz batean itzuli daitezkeela ikasi dut.</p>
<p>Nire lehen proba "<a href="https://store.steampowered.com/app/218740/Pid">Pid</a>" jokoareakin egin dut.</p>
<p>Instalatu ondoren, "<a href="https://github.com/Sergio-Muriel/JokoakEuskaraz/raw/refs/heads/main/Pid/Euskaraz%20itzulpena.exe">Eusaraz itulpena.exe</a>" programa erabil dezakezue, bideo-jokoa euskaraz izateko.<br><br></p>
<p><strong>Oharra:</strong> Baliteke akats izatea, itzultzaile neuronalea baten bidez itzulita dagoelako.</p>
<p><iframe title="YouTube video player" src="https://www.youtube.com/embed/-ZrnasZzL-s?si=D3Qsvjae0dOW1M7x" width="560" height="315" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen"></iframe></p>
<p><img src="../../../blog_data/blobid0.png-674304e80f5ae.png"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<div id="simple-translate" class="simple-translate-light-theme">
<div>
<div class="simple-translate-button isShow" style="background-image: url('moz-extension://c731c07c-aee8-41ed-9cce-db2f2f114dd0/icons/512.png'); height: 22px; width: 22px; top: 70px; left: 92px;">&nbsp;</div>
<div class="simple-translate-panel " style="width: 300px; height: 200px; top: 0px; left: 0px; font-size: 13px;">
<div class="simple-translate-result-wrapper" style="overflow: hidden;">
<div class="simple-translate-move" draggable="true">&nbsp;</div>
<div class="simple-translate-result-contents">
<p class="simple-translate-result" dir="auto">&nbsp;</p>
<p class="simple-translate-candidate" dir="auto">&nbsp;</p>
</div>
</div>
</div>
</div>
</div>...
        ]]></content>
        <link>https://tfe.eus/blog/pid-euskaraz.html</link>
        <id>https://tfe.eus/blog/pid-euskaraz.html></id>
        <media:thumbnail url="/blog_data//2024-11-24_11-51_1.png-67430551715ce.png?v=1732445521" />
        <category>Blog</category>
        <published>Sun, 24 Nov 2024 01:01:00 +0000</published>
    </entry>
    </feed>
