Friday, 4 September 2026

Goin' To Town (3 Stars)


Mae West: delicious.

"Goin' to Town": not so much.





Thursday, 3 September 2026

Crazy Samurai Musashi (4 Stars)


"Crazy Samurai Musashi" is a fight rather than a story. There's not much plot, but that's hardly the point; Tak Sakaguchi spends most of the film fighting his way through one opponent after another.

The choreography suffers from the decision to film the main fight in a single take. The sheer length makes the action less impressive than it might have been with more conventional editing; as another reviewer has pointed out, there's not a single decapitation or severed arm. The fight is often said to last 77 minutes, but on my Blu-ray the uninterrupted sequence runs for 74 minutes and 43 seconds. That's still one hell of a fight!


The film has been released with different titles. I'll stick to "Crazy Samurai Musashi", which is written on the film's title screen. 

Order from Amazon.com
Order from Amazon.co.uk

Wednesday, 2 September 2026

The Angry River (4 Stars)


Don't get too excited when you see Jackie Chan's name in the credits. As in all his early films, it was a very minor non-speaking role. If you blink you'll miss him.

"The Angry River" is a good film, although I didn't enjoy it as much as "Hapkido". It's Angela Mao's first film, and it's very different from the martial-arts films she would make afterwards.

Mao plays Lan Feng, whose father is poisoned by the villainous King Hell, and the only cure is a rare herb found in a remote mountain region. Lan Feng sets off to find it, encountering various dangers along the way. When she finally obtains the herb, she's temporarily deprived of her martial-arts abilities, but after returning home and discovering that her father has died, she takes the herb herself and regains her fighting skills.

Unfortunately, I didn't find the fights as impressive as those in Mao's later films. Even after she has regained her abilities, the action doesn't have the speed and impact of "Hapkido" or "Lady Whirlwind". That's understandable, since "The Angry River" was her first film and is much more of a traditional wuxia fantasy than the hard-hitting martial-arts films she would soon become known for.

It's also an historically important film. "The Angry River" was the first film made by Golden Harvest, although "The Invincible Eight" was released first. The cast and crew include several people who would become major figures in Hong Kong cinema, including Sammo Hung, Jackie Chan and Lam Ching-ying, while the action choreography was by the highly influential Han Ying-chieh.

Watching it now, it's fascinating to see Golden Harvest and Angela Mao right at the beginning of their careers. Only a few months after "The Angry River", Golden Harvest would release "The Big Boss", and Hong Kong cinema would change dramatically.


The film also has some wonderfully old-fashioned fantasy effects. I was particularly amused by the monster on the mountain. There's no mistaking that it's a man in a rubber costume, but somehow that only makes the scene more entertaining. It's the sort of thing that adds to the charm of an early-1970's wuxia film rather than spoiling it.

So "The Angry River" isn't my favourite Angela Mao film, but it's an enjoyable piece of Hong Kong cinema history and an interesting glimpse of both Mao and Golden Harvest before they became famous.

Tuesday, 1 September 2026

Alphabetical List of Posts




After having passed its beta test, this is the new version of my code for creating an alphabetical list of blog posts. It's not just a modified version, it's completely new code written by AI. I'm impressed. The old app needed 36 seconds to list my 6348 posts. The new app only needs 6 seconds. I've now removed the page with the old code that I posted in 2015. I strongly recommend that everyone should update.

Unlike the old version, no configuration is required. Copy the code into a stand-alone blogspot page, and away you go.

The code is free to copy and use. All I ask is that you leave a comment telling me the address of your blog and how many posts you currently have. I'm particularly interested in hearing from bloggers who have more posts than me.



Addendum on 3. September 2026

It's been pointed out to me that if a post has no title it's missing from the list. This is deliberate. The code is for sorting posts, and if a post has no title there's nothing to sort. Please remember to give all of your posts titles!





<!-- Blogger Alphabetical Post Index - Version 2.14
     Paste this into the HTML view of a Blogger Page.

     Version 2.14:
     - Based on the working Version 2.11 retrieval method.
     - Shows publication date before each post title.
     - Sorts alphabetically by title.
     - If titles are identical, older posts come first.
     - Uses Blogger's lightweight JSONP summary feed.
     - Requests 100 posts at a time using fixed start-index positions.
     - Loads 5 batches in parallel for speed.
     - No API key, blog ID or blog URL is required.
-->

<div id="alphabetical-post-index">
  <p id="api-status">Loading posts... 0 found</p>
  <ul id="api-post-list"></ul>
</div>

<style>
#alphabetical-post-index {
  width: 100%;
}

#api-status {
  margin: 0 0 1em 0;
}

#api-post-list {
  margin: 0;
  padding-left: 1.5em;
}

#api-post-list li {
  margin: 0.2em 0;
}

#api-post-list .post-date {
  display: inline-block;
  margin-right: 0.8em;
  white-space: nowrap;
  color: #777;
}

#api-post-list a {
  text-decoration: none;
}

#api-post-list a:hover {
  text-decoration: underline;
}
</style>

<script>
(function () {
  "use strict";

  const BATCH_SIZE = 100;
  const PARALLEL_REQUESTS = 5;
  const RETRIES = 2;

  const status = document.getElementById("api-status");
  const list = document.getElementById("api-post-list");

  const postsByUrl = new Map();

  let callbackCounter = 0;

  function blogRoot() {
    return window.location.protocol + "//" + window.location.host + "/";
  }

  function updateProgress() {
    status.textContent =
      "Loading posts... " +
      postsByUrl.size.toLocaleString("en-GB") +
      " found";
  }

  function canonicaliseUrl(url) {
    try {
      const u = new URL(url, blogRoot());
      u.hash = "";

      if (u.host === window.location.host) {
        u.protocol = window.location.protocol;
      }

      return u.href;
    } catch (error) {
      return url;
    }
  }

  function formatDate(dateString) {
    if (!dateString) {
      return "";
    }

    const date = new Date(dateString);

    if (Number.isNaN(date.getTime())) {
      return "";
    }

    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const day = String(date.getDate()).padStart(2, "0");

    return year + "-" + month + "-" + day;
  }

  function jsonp(url) {
    return new Promise(function (resolve, reject) {
      callbackCounter++;

      const callbackName =
        "__alphabeticalIndexCallback" + callbackCounter;

      const script = document.createElement("script");
      let finished = false;

      const timer = setTimeout(function () {
        if (finished) {
          return;
        }

        finished = true;
        cleanup();
        reject(new Error("Blogger feed request timed out."));
      }, 15000);

      function cleanup() {
        clearTimeout(timer);

        try {
          delete window[callbackName];
        } catch (error) {
          window[callbackName] = undefined;
        }

        if (script.parentNode) {
          script.parentNode.removeChild(script);
        }
      }

      window[callbackName] = function (data) {
        if (finished) {
          return;
        }

        finished = true;
        cleanup();
        resolve(data);
      };

      script.onerror = function () {
        if (finished) {
          return;
        }

        finished = true;
        cleanup();
        reject(new Error("Blogger feed request failed."));
      };

      script.src =
        url +
        (url.indexOf("?") >= 0 ? "&" : "?") +
        "callback=" +
        encodeURIComponent(callbackName);

      script.async = true;
      document.body.appendChild(script);
    });
  }

  async function jsonpWithRetry(url) {
    let lastError = null;

    for (let attempt = 0; attempt <= RETRIES; attempt++) {
      try {
        return await jsonp(url);
      } catch (error) {
        lastError = error;

        if (attempt < RETRIES) {
          await new Promise(function (resolve) {
            setTimeout(resolve, 300 * (attempt + 1));
          });
        }
      }
    }

    throw lastError;
  }

  function feedUrl(startIndex) {
    return (
      blogRoot() +
      "feeds/posts/summary" +
      "?alt=json" +
      "&start-index=" + startIndex +
      "&max-results=" + BATCH_SIZE
    );
  }

  function getAlternateLink(entry) {
    if (!entry.link) {
      return "";
    }

    for (const link of entry.link) {
      if (link.rel === "alternate") {
        return canonicaliseUrl(link.href);
      }
    }

    return "";
  }

  function addEntries(data) {
    if (!data || !data.feed) {
      throw new Error("Blogger returned an invalid feed.");
    }

    const entries = data.feed.entry || [];

    for (const entry of entries) {
      const title =
        entry.title &&
        typeof entry.title.$t === "string"
          ? entry.title.$t.trim()
          : "";

      const url = getAlternateLink(entry);

      const published =
        entry.published &&
        entry.published.$t
          ? entry.published.$t
          : "";

      if (!title || !url) {
        continue;
      }

      if (!postsByUrl.has(url)) {
        postsByUrl.set(url, {
          title: title,
          url: url,
          published: published
        });
      }
    }

    updateProgress();

    return entries.length;
  }

  function getTotalResults(data) {
    try {
      return Number(
        data.feed["openSearch$totalResults"].$t
      ) || 0;
    } catch (error) {
      return 0;
    }
  }

  async function loadBatch(startIndex) {
    const data =
      await jsonpWithRetry(feedUrl(startIndex));

    addEntries(data);

    return data;
  }

  async function worker(queue, failures) {
    while (queue.length) {
      const startIndex = queue.shift();

      try {
        await loadBatch(startIndex);
      } catch (error) {
        console.warn(
          "Feed batch failed at start-index",
          startIndex,
          error
        );

        failures.push(startIndex);
      }
    }
  }

  function comparePosts(a, b) {
    const result = a.title.localeCompare(
      b.title,
      "en",
      {
        sensitivity: "base",
        numeric: true
      }
    );

    if (result !== 0) {
      return result;
    }

    return new Date(a.published) - new Date(b.published);
  }

  function renderPosts() {
    const posts =
      Array.from(postsByUrl.values());

    posts.sort(comparePosts);

    const fragment =
      document.createDocumentFragment();

    for (const post of posts) {
      const li =
        document.createElement("li");

      const date =
        document.createElement("span");

      date.className = "post-date";
      date.textContent =
        formatDate(post.published);

      const link =
        document.createElement("a");

      link.href = post.url;
      link.textContent = post.title;

      li.appendChild(date);
      li.appendChild(link);
      fragment.appendChild(li);
    }

    list.replaceChildren(fragment);

    status.textContent =
      posts.length.toLocaleString("en-GB") +
      " published posts";
  }

  async function start() {
    const firstData =
      await loadBatch(1);

    const totalResults =
      getTotalResults(firstData);

    if (!totalResults) {
      throw new Error(
        "Blogger did not report the total number of posts."
      );
    }

    const queue = [];

    for (
      let startIndex = 1 + BATCH_SIZE;
      startIndex <= totalResults;
      startIndex += BATCH_SIZE
    ) {
      queue.push(startIndex);
    }

    const failures = [];
    const workers = [];

    const workerCount = Math.min(
      PARALLEL_REQUESTS,
      queue.length
    );

    for (let i = 0; i < workerCount; i++) {
      workers.push(
        worker(queue, failures)
      );
    }

    await Promise.all(workers);

    if (failures.length > 0) {
      const retryList = failures.slice();
      failures.length = 0;

      for (const startIndex of retryList) {
        try {
          await loadBatch(startIndex);
        } catch (error) {
          failures.push(startIndex);
        }
      }
    }

    renderPosts();

    if (failures.length > 0) {
      status.textContent =
        postsByUrl.size.toLocaleString("en-GB") +
        " posts found; " +
        failures.length.toLocaleString("en-GB") +
        " feed batch" +
        (failures.length === 1 ? "" : "es") +
        " couldn't be read.";
    }
  }

  start().catch(function (error) {
    console.error(
      "Alphabetical Post Index:",
      error
    );

    status.textContent =
      "Loading stopped after " +
      postsByUrl.size.toLocaleString("en-GB") +
      " posts.";

    status.style.fontWeight = "bold";
  });
})();
</script>


Monday, 31 August 2026

Lady Whirlwind (4 Stars)


I watched "Lady Whirlwind" tonight, having watched "Hapkido" yesterday. They're both Angela Mao films from 1972, although "Lady Whirlwind" was actually made first. The two films are also connected by the fact that Angela Mao, Sammo Hung and Hwang In-shik learned hapkido (the fighting style) while making it, before going on to make "Hapkido" (the film).

I didn't enjoy "Lady Whirlwind" as much as "Hapkido". The biggest problem for me is the story. There's a lot of confusion surrounding Tien's search for Shih-Hao. We eventually learn that Shih-Hao had abandoned Tien's pregnant sister, but his explanation is simply that circumstances forced him to leave her. What those circumstances were is never properly explained. It seems likely that Tung Ku was involved, but the film leaves us to work that out for ourselves.

I'm also not sure why Tien goes to the casino looking for Shih-Hao. She seems to believe that the casino owner and Tung Ku are hiding him, but the film never makes it clear why she thinks this. It's the sort of thing that probably made sense to the filmmakers, but they're unable to explain it to the viewer.

Fortunately, the fight scenes are good enough to make up for the film's shortcomings. There's plenty of action and some excellent martial-arts choreography. Angela Mao is good, although her fighting skills aren't as prominently displayed as they are in "Hapkido". Hwang In-shik and Sammo Hung also add plenty to the action.

So "Lady Whirlwind" works better for me as a martial-arts film than as a story. The action makes it worth watching, but I found the plot much less satisfying than "Hapkido".

Sunday, 30 August 2026

Hapkido (5 Stars)


Last week I bought two box sets of films starring Angela Mao. Six films, and I have to start somewhere, so "Hapkido" is a wonderful place to start. I'm working my way through the films, and after seeing this one I'm already looking forward to the other five.

The film introduces us to three capable martial artists, Kao Chang (Carter Wong), Fan Wei (Sammo Hung) and Ying (Angela Mao). They're all good fighters, but Ying is clearly the best, and that's established right at the beginning during the training sequence. It's a nice touch, because the later fights live up to what we've already been shown. Angela Mao is excellent, and the action is terrific throughout.

My only slight criticism is the introduction of Ying's Head Brother about twenty minutes before the end. By that stage the film has already established its main characters and story, so bringing in an important new character so late feels rather awkward.

That's a minor blemish, though. Hapkido is an exciting, beautifully staged martial-arts film and, for me, it's almost perfect.


Who likes lobby cards? I miss them.



Look at the following poster. I'm amused by the tagline, "Don't let your girlfriend see this film". At first I thought it meant that she'd be jealous if I sat for 90 minutes ogling the beautiful Angela Mao. But it's probably a warning that my girlfriend would follow Angela's example and beat me up. Maybe both.


Saturday, 29 August 2026

Becoming Led Zeppelin (5 Stars)


I wrote about "Becoming Led Zeppelin" when I saw it in the cinema last year. I don't have much more to say about the film itself. But there was one thing I didn't put into my original review: while I was sitting in the cinema watching it, I realised that it was the best documentary I'd ever seen.

That's not because of the subject matter. Obviously, I'm interested in Led Zeppelin, but it's the way the story is told that makes this documentary so good.

Most documentaries are watched by people who are already interested in the subject, so the people watching them will already be familiar with the details being presented. "Becoming Led Zeppelin" is different. It's genuinely informative. I'm a Led Zeppelin fan, yet it still told me things I didn't know.

I knew that Jimmy Page had played with the Yardbirds, for example, but I didn't know that Page, Robert Plant, John Paul Jones and John Bonham had gone on tour together in 1968 as the Yardbirds. They could have recorded their first album together as the sixth Yardbirds album, but they wanted to make a new start.

I also found the story of their early popularity fascinating. They were playing to sold-out concerts in America, then flying back to England where hardly anybody knew them. It took at least a year for their popularity to pick up in England.

The documentary ends with Led Zeppelin playing at London's Royal Albert Hall on 9th January 1970. This was when they'd truly made it big. This was when they became Led Zeppelin.

Is this really the best documentary ever made?

That's what I thought last year, and it's what I still think today.


One last point. On Wikipedia it's written "Led Zeppelin evolved from a previous band, the Yardbirds, and were originally named the New Yardbirds".

This is incorrect. They were never officially named the New Yardbirds. They were the Yardbirds, as proved by the contract shown above. It can also be verified by the posters for the various stages of their Scandinavian tour. "New Yardbirds" is a nickname that wasn't given to them until after they changed their name to Led Zeppelin.

Don't believe everything you read in Wikipedia.

Order from Amazon.com
Order from Amazon.co.uk
Order from Amazon.de