回精華區
作者TKyo.bbs@cpu.tfcis.org (暗黑貴公子) 看板: plan
標題[更新]即時查詢奇摩電影
時間動力核心 (2005/01/19 Wed 19:29:06) Updated: 2006/04/17
更新說明

    1.此次更新修正事宜

        (1) 掉字的 Bug (例 : 北極特快車/台北北區)
        (2) 連線當掉的 Bug (例 : 功夫/台北東區)

    2.此次更新部份
        (1) ymovie.c:CGI_COMMAND3, 補上 \n\n
        (2) ymovie.c:
                write_file()
                http_conn()

        (2) 的部份由於更新幅度相當大, 所以請整個 function 更新即可

簡介 :

    這隻小程式與原先精華區的 "奇摩電影" 功能大不相同

    1.整合 xover 介面, 使用 pans 分頁介面
    2.透過另一隻程式 (ymovie-open.c) 建立之電影資料檔
      再即時查詢其電影的 "放映地區" 及 "放映時刻"
    3.由於整合 xover 介面, 所以要新增/修改的地方很多 :-)


紀錄 :

    1.2004/12/14 完成
    2.2004/12/14 - 2004/12/20 實際上線測試
    3.2004/12/26 - 更新 : ymovie.c:write_file()
                   更新 : ymovie.c:http_conn()

                                                   By CSZone Kyo 2004/12/24

: src/include/global.h : 適當位置加入

+ #ifdef HAVE_NETTOOL
+ #define FN_YMOVIE       "etc/ymovie.dat"        /* 奇摩電影檔 */
+ #endif

: src/include/modes.h : 假設 XO_ZONE 最後為精華區, 需跟 xz[] 同步

  #define XZ_GEM          (XO_ZONE + 13)          /* 精華區 */
+ #ifdef HAVE_NETTOOL
+ #define XZ_YMOVIE       (XO_ZONE + 14)          /* 查詢電影 */
+ #endif

: src/include/struct.h

  #define FILM_GEM        12      /* help message */

+ #ifdef HAVE_NETTOOL
+ #define FILM_YMOVIE     13
+ #endif

/* 適當位置加入 */

+ #ifdef HAVE_NETTOOL

+ #define MOVIE_NOW       0x01
+ #define MOVIE_DOWN      0x02
+ #define MOVIE_NORMAL    0x10

+ typedef struct
+ {
+   int mv_no;              /* 電影編號 */
+   int playmode;           /* 目前放映模式 */
+   char mv_title[30];      /* 電影名稱 */
+   char area_desc[128];    /* 放映地區 (以 ',' 分隔之集合字串) */
+ } YMOVIE;

+ #endif

: src/include/theme.h : 適當位置加入

+ #ifdef HAVE_NETTOOL

+ /* ----------------------------------------------------- */
+ /* 奇摩電影查詢顏色                                      */
+ /* ----------------------------------------------------- */
+ #define COLOR_NOW       "\033[1;31m"    /* 院線片 */
+ #define COLOR_DOWN      "\033[1;32m"    /* 將下檔片 */
+ #define COLOR_OTHER    "\033[0;37m"    /* 其他 */
+ #define CHAR_NOW        "★"
+ #define CHAR_DOWN       "▼"
+ #define CHAR_OTHER     "  "

+ #define NECKER_YMOVIE   "[←]主選單 [→]電影查詢 [↑↓]選擇 [h]說明\n" \
+   COLOR3 "  編號   電影名稱                  放映地區%*s" \
+   "                                   \033[m"
+ #define FEETER_YMOVIE   \
+   COLOR1 " 奇摩電影 " COLOR2 " (ENTER)電影查詢 (↑/↓)上下頁 (←)離開" \
+   "                        \033[m"
+ #endif

: etc/help/ym/ymovie.hlp 新增此目錄及檔案

                             【電影查詢操作說明】
   【基本指令】─────────────────────────────
    (Enter)      電影查詢
    (h)          說明

: src/maple/xover.c : xz[]

- {NULL, NULL, M_GEM, FEETER_GEM}              /* XZ_GEM */
+ {NULL, NULL, M_GEM, FEETER_GEM},             /* XZ_GEM */
+ {NULL, NULL, M_XMODE, FEETER_YMOVIE}         /* XZ_YMOVIE */


: src/maple/menu.c : 適當選單

+ "bin/ymovie.so:ym_main", 0, - M_XMODE,
+ "Movie      ♂ 奇摩電影 ♀",


:src/so/ymovie.c 新增這隻程式

/*-------------------------------------------------------*/
/* ymovie.c          ( NTHU CS MapleBBS Ver 3.10 )       */
/*-------------------------------------------------------*/
/* target : Yahoo/奇摩電影查詢                           */
/* author : kyo.bbs@cszone.org                           */
/* create : 04/12/14                                     */
/* update :   /  /                                       */
/*-------------------------------------------------------*/

#if 0

1.本瀏覽程式需配合 bin/ymovie-open 去抓取資料並建立電影檔
2.根據電影檔資訊即時抓取該部電影放映時段及地點
3.本程式並不需要安裝 lynx

#endif


#include "bbs.h"


#ifdef HAVE_NETTOOL

#define HTTP_PORT       80
#define SERVER_YMOVIE   "tw.movie.yahoo.com"
#define CGI_HTTP_TAG    "http://"
#define CGI_COMMAND     "GET " CGI_HTTP_TAG SERVER_YMOVIE \
                        "/mv_search.html?mname="

#define CGI_COMMAND2    "&area="
#define CGI_COMMAND3    " HTTP/1.0\n\n"
#define BR_PLAY         "\033[m\n          \033[1;36m"
#define BR_COLS         9

/* 注意事項 */
/* ------------------------------------------------------ */
/* 1.pans 分頁介面用, 表示一頁要顯示多少筆資料            */
/* 2.勿設定太多筆, 顯示超過畫面會當機                     */
/* 3.MENU_PAGE 新增或減少時, menu_sub 也得相對新增或減少  */
/* ------------------------------------------------------ */
#define MENU_PAGE       10

extern XZ xz[];
extern char xo_pool[];

static void
url_encode(dst, src)        /* URL encoding */
  uschar *dst;       /* Thor.990331: 要 src 的三倍空間 */
  uschar *src;
{
  for (; *src; src++)
  {
    if (*src == ' ')
      *dst++ = '+';
    else if (is_alnum(*src))
      *dst++ = *src;
    else
    {

/* 原使用 register cc 於 W_ALL 會有警告, 故改成 int */

      int cc = *src;
      *dst++ = '%';
      *dst++ = radix32[cc >> 4];
      *dst++ = radix32[cc & 0xf];
    }
  }
  *dst = '\0';
}

static int
write_file(sockfd, fp, ftmp)
  int sockfd;
  FILE *fp;
  char *ftmp;
{
  char pool[2048], buf_play[512] = {0};
  int cc, i, j, k, fsize;
  char *xhead, *fimage;
  int show, start_show, bufplay;

  char *start_str[] =
  {
    "<td align=\042center\042 class=\042sbody\042>",
    "<td class=\042time\042>",
    "<td class=\042sbody\042>",
    NULL
  };

  char *stop_str[] =
  {
    "</td>",
    "</td>",
    "</a>",
    NULL
  };

  char *insert_str[] =
  {
    "\033[1;37m分    級:\033[35m",
    "\033[1;37m放映時間:\033[36m",
    "\033[1;37m戲院名稱:\033[31m",
    NULL
  };
  FILE *fpw;

  /* Load HTML Page into memory (fimage) */
  fpw = fopen(ftmp, "w");

  while ((cc = read(sockfd, &pool, sizeof(pool))) > 0)
    fwrite(&pool, cc, 1, fpw);

  fclose(fpw);
  close(sockfd);

  fimage = f_map(ftmp, &fsize);
  if (fimage == (char *) -1)
  {
    fclose(fp);
    vmsg("目前無法開啟電影索引檔");
    return -1;
  }

  /* parser HTML from memory (fimage) */
  show = 1;
  bufplay = 0;
  start_show = 0;
  i = 0;

  for (xhead = fimage; *xhead; xhead++)
  {
    if (!start_show)
    {
      k = strlen(start_str[i]) ;
      if (!str_ncmp(xhead, start_str[i], k))
      {
        start_show = 1;
        fputs(insert_str[i], fp);
        xhead += k;
        if (i == 1)
        {
          bufplay = 1;
          j = 0;
        }
      }
    }
    else if (start_show)
    {
      k = strlen(stop_str[i]);
      if (!str_ncmp(xhead, stop_str[i], k))
      {
        start_show = 0;

        switch (i)
        {
          case 0:
          case 2:
            fputs("\033[m\n", fp);
            fputs("\033[1;34m" MSG_SEPERATOR "\033[m\n", fp);
            break;
          case 1:
            {
              char *phead, *pitem;
              int plen, pcount;

              bufplay = 0;
              pcount = 0;
              phead = pitem = buf_play;

              while (pitem = strstr(phead, " "))
              {
                plen = pitem - phead;

                if (plen <= 7)  /* 一般/加註時刻 */
                {
                  *pitem = '\0';
                  if (pcount == (BR_COLS + 1)) /* 到底時立刻換行 */
                  {
                    fputs(BR_PLAY, fp);
                    pcount = 0;
                  }

                  fputs(phead, fp);
                  while (plen < 7 && (pcount != BR_COLS))  /* 補空白 */
                  {
                    fputc(' ', fp);
                    plen++;
                  }
                }
                else
                {
                  if (pcount)       /* 已經換行就不需要再換行 */
                  {
                    fputs(BR_PLAY, fp);
                    pcount = 0;
                  }
                  fputs(phead, fp);
                  break;
                }
                pcount++;
                phead = pitem + 2;
              }
              fputs("\033[m\n", fp);
              memset(&buf_play, 0, sizeof(buf_play));
            }
            break;
        }

        if (++i > 2)
          i = 1;

        xhead += k;
      }
    }

    if (!start_show)
      continue;

    /* 標籤略過 */

    cc = *xhead;
    switch(cc)
    {
    case '<':
      show = 0;
      continue;
    case '>':
      show = 1;
      continue;
    case '\n':
    case '\r':
      continue;
    case ' ':
      continue;
    }

    if (show)
    {
      if (bufplay)
        buf_play[j++] = cc;
      else
        fputc(cc, fp);
    }
  }

  munmap(fimage, fsize);

  if (i == 0)
  {
    fputs("\033[1;34m" MSG_SEPERATOR "\033[m\n", fp);
    fputs("\n沒有查詢到任何資料!\n", fp);
  }
  else
    i = 1;

  return i;
}

static int
http_conn(server, s, extdata)
  char *server;
  char *s;
  char *extdata;
{
  int sockfd, ret;
  FILE *fp;
  char fname[64], ftmp[64];

  if ((sockfd = dns_open(server, HTTP_PORT)) < 0)
  {
    vmsg("無法與伺服器取得連結,查詢失敗");
    return 0;
  }
  else
  {
    outz("正在連接伺服器,請稍後...");
    refresh();
  }
  write(sockfd, s, strlen(s));
  shutdown(sockfd, 1);

  sprintf(fname, "tmp/%s.ymovie", cuser.userid);
  fp = fopen(fname, "w");

  sprintf(ftmp, "tmp/%s.ymovie.tmp", cuser.userid);

  outz("擷取資料中,請稍後...");
  refresh();

  fputs("\033[1;34m" MSG_SEPERATOR "\033[m\n", fp);
  fputs(extdata, fp);

  ret = write_file(sockfd, fp, ftmp);
  fclose(fp);

  if (ret >= 0)
  {
    more(fname, NULL);
    if (ret)
    {
      if (vans("將查詢結果寄回信箱?[N] ") == 'y')
        mail_self(fname, cuser.userid, "[備 忘 錄] 電影查詢", MAIL_READ);
    }
  }
  unlink(fname);
  unlink(ftmp);
  return 1;
}

static void
ym_item(num, ym)
  int num;
  YMOVIE *ym;
{
  prints("%6d %s\033[m%-25.26s %-42.43s\n",
    num,
    (ym->playmode & MOVIE_NOW) ? COLOR_NOW CHAR_NOW :
    (ym->playmode & MOVIE_DOWN) ? COLOR_DOWN CHAR_DOWN :
    COLOR_OTHER CHAR_OTHER,
    ym->mv_title, ym->area_desc);
}

static int
ym_query(xo)
  XO *xo;
{
  YMOVIE *ym;
  int i, j, cc, top;
  char *p, *s;
  char buf[80];
  char menu_sub[10][25] = {0};

  ym = (YMOVIE *) xo_pool + (xo->pos - xo->top);
  top = 0;

  while (1)
  {
    char *menu[] = {"0N",
      menu_sub[0], menu_sub[1], menu_sub[2],
      menu_sub[3], menu_sub[4], menu_sub[5],
      menu_sub[6], menu_sub[7], menu_sub[8],
      menu_sub[9],
      "Q  取消查詢", NULL};

    s = p = ym->area_desc;
    i = 0;
    while (*p)
    {
      if (*p == ',')
      {
        *p = '\0';
        j = (i % MENU_PAGE);

        if (i >= top)
          sprintf(menu_sub[j], "%c  %-s", j + '0', s);

        *p = ',';

        i++;
        s = p + 1;

        if (i >= (top + MENU_PAGE))
          break;
      }
      p++;
    }

    if (*p == 0)
    {
      j = (i % MENU_PAGE);
      sprintf(menu_sub[j], "%c  %-s", j + '0', s);
      i++;
    }

    j = (i % MENU_PAGE);

    if (j)
    {
      menu[++j] = menu[11];
      menu[++j] = menu[12];
    }

    sprintf(buf,
     "選擇放映地區  (←) 換頁║%c║",
     (top / MENU_PAGE) + '1');

    cc = pans(1, 25, buf, menu);
    if ( cc == 'n')
    {
      top += MENU_PAGE;

      if (*p == 0)
        top = 0;

      continue;
    }
    else if (cc == 'q')
    {
      break;
    }
    else
    {
      char uestr[90], sendform[512];

      strcpy(sendform, CGI_COMMAND);
      url_encode(uestr, ym->mv_title);
      strcat(sendform, uestr);
      strcat(sendform, CGI_COMMAND2);
      url_encode(uestr, menu_sub[cc - '0'] + 3);
      strcat(sendform, uestr);
      strcat(sendform, CGI_COMMAND3);
      sprintf(buf, "\033[1;37m電影名稱:\033[32m%s\033[m"
        "\n\033[1;37m放映地區:\033[33m%s\033[m\n",
        ym->mv_title, menu_sub[cc - '0'] + 3);
      http_conn(SERVER_YMOVIE, sendform, buf);
        return XO_HEAD;
    }
  }
  return XO_NONE;
}

static int
ym_body(xo)
  XO *xo;
{
  YMOVIE *ym;
  int max, num, tail;

  max = xo->max;

  if (max <= 0)
  {
    vmsg("尚未建立電影資料");
    return XO_QUIT;
  }

  ym = (YMOVIE *) xo_pool;
  num = xo->top;
  tail = num + XO_TALL;
  if (max > tail)
    max = tail;

  move(3, 0);
  do
  {
    ym_item(++num, ym++);
  } while (num < max);
  clrtobot();

  return XO_FOOT;   /* itoc.010403: 把 b_lines 填上 feeter */
}


static int
ym_head(xo)
  XO *xo;
{
  vs_head("電影查詢", str_site);
  prints(NECKER_YMOVIE, d_cols, "");

  return ym_body(xo);
}


static int
ym_load(xo)
  XO *xo;
{
  xo_load(xo, sizeof(YMOVIE));
  return ym_body(xo);
}


static int
ym_init(xo)
  XO *xo;
{
  xo_load(xo, sizeof(YMOVIE));
  return ym_head(xo);
}

static int
ym_help(xo)
  XO *xo;
{
  /* xo_help("ym"); */  /* 沒有建 help 檔 */

  more("etc/help/ym/ymovie.hlp", NULL);
  return ym_head(xo);
}


static KeyFunc ym_cb[] =
{
  XO_INIT, ym_init,
  XO_LOAD, ym_load,
  XO_HEAD, ym_head,
  XO_BODY, ym_body,

  'r', ym_query,
  'h', ym_help
};

int
ym_main()
{
  XO *xo;

  xz[XZ_YMOVIE - XO_ZONE].xo = xo = xo_new(FN_YMOVIE);
  xz[XZ_YMOVIE - XO_ZONE].cb = ym_cb;
  xo->pos = 0;

  xover(XZ_YMOVIE);

  free(xo);

  return 0;
}

#endif              /* HAVE_NETTOOL */


: src/util/ymovie-open.c 新增這隻程式

/*-------------------------------------------------------*/
/* util/ymovie-open.c   ( NTHU CS MapleBBS Ver 3.10 )    */
/*-------------------------------------------------------*/
/* target : 奇摩電影檔更新                               */
/* create : 04/12/14                                     */
/* update :   /  /                                       */
/* author : kyo.bbs@cszone.twbbs.org                     */
/*-------------------------------------------------------*/

#if 0

# 每天 5:30 AM 建立一次奇摩電影檔
5 30 * * * bin/ymovie-open > /dev/null 2>&1

#endif

#include "bbs.h"

#ifdef HAVE_NETTOOL

#define HTTP_PORT       80
#define SERVER_YMOVIE   "tw.movie.yahoo.com"
#define CGI_HTTP_TAG    "http://"
#define CGI_GET_YMOVIE  CGI_HTTP_TAG SERVER_YMOVIE "/js/search_mv.js"
#define CGI_GET_AREA    CGI_HTTP_TAG SERVER_YMOVIE "/mv_area_js.php?mname="

#undef  DEBUG

void
url_encode(dst, src)        /* URL encoding */
  uschar *dst;       /* Thor.990331: 要 src 的三倍空間 */
  uschar *src;
{
  for (; *src; src++)
  {
    if (*src == ' ')
      *dst++ = '+';
    else if (is_alnum(*src))
      *dst++ = *src;
    else
    {

/* 原使用 register cc 於 W_ALL 會有警告, 故改成 int */

      int cc = *src;
      *dst++ = '%';
      *dst++ = radix32[cc >> 4];
      *dst++ = radix32[cc & 0xf];
    }
  }
  *dst = '\0';
}

static char *
parser_work(sockfd)
  int sockfd;
{
  static char pool[2048];
  static char buf[2048];    /* 所有電影檔及放映地區長字串暫放 */
  int cc, i, j, reload;
  char *xhead, *xtail, *xpos;
  int start_get;

  char *start_str[] =
  {
    "Thi_option0(\042",
    "Thi_option('",
    NULL
  };

  char *stop_str[] =
  {
    "\042);",
    "')",
    NULL
  };

  xhead = pool;
  xtail = pool;
  xpos = buf;
  start_get = 0;
  reload = 0;
  memset(&buf, 0, sizeof(buf));

  for (;;xhead++)
  {
    if (xhead >= xtail || reload)
    {
      if (reload)
      {
        reload = i = 0;

        while (xhead < xtail)
        {
          pool[i] = *xhead;
          xhead++;
          i++;
        }

        xhead = pool;
        cc = read(sockfd, xhead + i, sizeof(pool) - i - 1);
      }
      else
      {
        xhead = pool;
        cc = read(sockfd, xhead, sizeof(pool));
      }
      if (cc <= 0)
        break;
      xtail = xhead + cc;
    }

    if (!start_get)
    {
      for (i = 0; start_str[i] != NULL; i++)
      {
        j = strlen(start_str[i]);
        if ((xtail - xhead) < j)
        {
          xhead--;
          reload = 1;
          continue;
        }
        if (!str_ncmp(xhead, start_str[i], j))
        {
          start_get = 1;
          xhead += j;
          break;
        }
      }
      if (xhead >= xtail)     /* 因為 xhead += j; 的關係 */
        continue;             /* 所以再此再度判斷一次 */
    }
    else if (start_get)
    {
      for (i = 0; stop_str[i] != NULL; i++)
      {
        j = strlen(stop_str[i]);
        if ((xtail - xhead) < j)
        {
          xhead--;
          reload = 1;
          continue;
        }

        if (!str_ncmp(xhead, stop_str[i], j))
        {
          start_get = 0;
          xhead += j;
          break;
        }
      }
      if (xhead >= xtail)     /* 因為 xhead += j; 的關係 */
        continue;             /* 所以再此再度判斷一次 */
    }

    if (!start_get)
      continue;

    *xpos++ = *xhead;
  }
  return buf;
}

static int
http_conn(server, s)
  char *server;
  char *s;
{
  int sockfd;

  sockfd = dns_open(server, HTTP_PORT);
  if (sockfd < 0)
  {
    printf("無法與伺服器取得連結,查詢失敗\n");
    return 0;
  }

  write(sockfd, s, strlen(s));
  shutdown(sockfd, 1);

  return sockfd;
}


int
main()
{
  int sockfd, i;
  char ue_word[90], sendform[512];
  char *p, *str;
  YMOVIE ym;

  chdir(BBSHOME);
  system("rm -f " FN_YMOVIE);       /* 暴力法, 重新建立電影檔 */

  sprintf(sendform, "GET %s HTTP/1.0\n\n", CGI_GET_YMOVIE);

  /* 取電影名稱及模式 */
  if ((sockfd = http_conn(SERVER_YMOVIE, sendform)))
  {
    /* 去掉 " ,,===請選擇電影==;" 及將 ';' 分隔為 '\0' */
    p = strtok(parser_work(sockfd), " ,;");
    close(sockfd);
    i = 0;
    while (p != NULL)
    {
      if ((str = strstr(p, ",,")))
      {
        str += 2;
        memset(&ym, 0, sizeof(YMOVIE));
        ym.mv_no = ++i;

        if (strncmp(str, CHAR_NOW, strlen(CHAR_NOW)) == 0)
          ym.playmode |= MOVIE_NOW;
        else if (strncmp(str, CHAR_DOWN, strlen(CHAR_DOWN)) == 0)
          ym.playmode |= MOVIE_DOWN;
        else
          ym.playmode |= MOVIE_NORMAL;

        if ((ym.playmode & MOVIE_NOW) || (ym.playmode & MOVIE_DOWN))
          str += 2;

        strncpy(ym.mv_title, str, sizeof(ym.mv_title));
        rec_add(FN_YMOVIE, &ym, sizeof(YMOVIE));
      }

      p = strtok (NULL, ";");
    }
  }

  i = 0;

  /* 經由電影名稱抓放映地點 */
  while (!rec_get(FN_YMOVIE, &ym, sizeof(YMOVIE), i))
  {
    url_encode(ue_word, ym.mv_title);
    sprintf(sendform, "GET %s%s HTTP/1.0\n\n", CGI_GET_AREA, ue_word);
    sockfd = http_conn(SERVER_YMOVIE, sendform);
    if ((sockfd = http_conn(SERVER_YMOVIE, sendform)))
    {
      memset(&sendform, 0, sizeof(sendform));   /* 借 sendform 暫存 */
      p = strtok(parser_work(sockfd), ",");
      close(sockfd);
      while (p != NULL)
      {
        if ((str = strstr(p, ",")))
          strcat(sendform, ++str);

        p = strtok (NULL, ";");
      }
      strncpy(ym.area_desc, sendform, sizeof(ym.area_desc));
      rec_put(FN_YMOVIE, &ym, sizeof(YMOVIE), i, NULL);
#ifdef DEBUG
      printf("%s\n", sendform);
#endif
    }
    i++;
  }

  return 0;
}
#endif  /* HAVE_NETTOOL */

--
  Origin:  Maple-itoc˙動力核心  cpu.tfcis.org 
  Author: TKyo cszone.twbbs.org 發表
精華選讀←離開[主題上]主題下(k)上篇(j)下篇S/a搜尋 G串列 TAB精華 ↑↓捲 Pg/Space翻