├── data └── .gitkeep ├── lib ├── syobocal │ ├── version.rb │ ├── comment │ │ ├── element │ │ │ ├── root.rb │ │ │ ├── text.rb │ │ │ ├── blank.rb │ │ │ ├── list.rb │ │ │ ├── header2.rb │ │ │ ├── header1.rb │ │ │ ├── link.rb │ │ │ ├── row.rb │ │ │ └── text_node.rb │ │ ├── staff.rb │ │ ├── cast.rb │ │ ├── music.rb │ │ ├── music_data.rb │ │ ├── person.rb │ │ ├── helper │ │ │ └── fragment.rb │ │ ├── parser.rb │ │ └── section.rb │ ├── sub_titles │ │ ├── sub_title.rb │ │ └── parser.rb │ ├── util.rb │ ├── rss2.rb │ ├── rss.rb │ ├── calchk.rb │ ├── json.rb │ └── db.rb └── syobocal.rb ├── .gitignore ├── Gemfile ├── bin ├── setup └── console ├── Rakefile ├── scripts ├── download_db_title_lookup.rb └── parse_comment.rb ├── test ├── comment │ ├── test_person.rb │ ├── element │ │ └── test_text_node.rb │ ├── test_section.rb │ └── test_parser.rb ├── samples │ ├── 116.json │ ├── 2077.json │ └── 91.json ├── sub_titles │ └── test_parser.rb └── test_db.rb ├── exe ├── syobocal-anime └── syobocal ├── LICENSE.txt ├── syobocal.gemspec ├── CODE_OF_CONDUCT.md └── README.md /data/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/syobocal/version.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | VERSION = "0.15.0" 3 | end 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | # Specify your gem's dependencies in syobocal.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rake/testtask" 3 | 4 | Rake::TestTask.new(:test) do |t| 5 | t.libs << "test" 6 | t.libs << "lib" 7 | t.test_files = FileList["test/**/test_*.rb"] 8 | end 9 | 10 | task :default => :test 11 | -------------------------------------------------------------------------------- /lib/syobocal/comment/element/root.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | module Comment 3 | module Element 4 | class Root 5 | attr_reader :elements 6 | 7 | def initialize(elements) 8 | @elements = elements 9 | end 10 | 11 | def ==(other) 12 | other.elements == elements 13 | end 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/syobocal/comment/staff.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | module Comment 3 | class Staff 4 | attr_reader :role, :people 5 | 6 | def initialize(role, people) 7 | @role, @people = role, people 8 | end 9 | 10 | def ==(other) 11 | other.instance_of?(self.class) && other.role == role && other.people == people 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /scripts/download_db_title_lookup.rb: -------------------------------------------------------------------------------- 1 | require "syobocal" 2 | 3 | path = "data/db_title_lookup.json" 4 | 5 | if File.exist? path 6 | puts "Nothing to do. '#{path}' already exists." 7 | else 8 | puts "Downloading to '#{path}'." 9 | 10 | open(path, "w") do |f| 11 | f.print JSON.pretty_generate(Syobocal::DB::TitleLookup.get({ "TID" => "*" })) 12 | end 13 | 14 | puts "Completed." 15 | end 16 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "syobocal" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start(__FILE__) 15 | -------------------------------------------------------------------------------- /lib/syobocal/sub_titles/sub_title.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | module SubTitles 3 | class SubTitle 4 | attr_reader :episode, :title 5 | 6 | def initialize(episode, title) 7 | @episode, @title = episode, title 8 | end 9 | 10 | def ==(other) 11 | other.instance_of?(self.class) && other.episode == episode && other.title == title 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/syobocal/comment/cast.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | module Comment 3 | class Cast 4 | attr_reader :character, :people 5 | 6 | def initialize(character, people) 7 | @character, @people = character, people 8 | end 9 | 10 | def ==(other) 11 | other.instance_of?(self.class) && other.character == character && other.people == people 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /scripts/parse_comment.rb: -------------------------------------------------------------------------------- 1 | require "syobocal" 2 | require "pp" 3 | 4 | path = "data/db_title_lookup.json" 5 | 6 | json = JSON.parse(File.read(path)) 7 | 8 | sections = [] 9 | 10 | json.each do |title| 11 | puts title["tid"] 12 | 13 | parser = Syobocal::Comment::Parser.new(title["comment"]) 14 | 15 | pp parser.parse 16 | pp parser.staffs 17 | pp parser.casts 18 | pp parser.musics 19 | pp parser.links 20 | end 21 | -------------------------------------------------------------------------------- /lib/syobocal/comment/element/text.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | module Comment 3 | module Element 4 | class Text 5 | attr_reader :str 6 | 7 | def initialize(str) 8 | @str = str 9 | end 10 | 11 | def ==(other) 12 | other.instance_of?(self.class) && other.str == str 13 | end 14 | 15 | def self.create(text) 16 | new(text) 17 | end 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/syobocal/comment/music.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | module Comment 3 | class Music 4 | attr_reader :title, :category, :data_list 5 | 6 | def initialize(title, category, data_list) 7 | @title, @category, @data_list = title, category, data_list 8 | end 9 | 10 | def ==(other) 11 | other.instance_of?(self.class) && other.title == title && other.category == category && other.data_list == data_list 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/syobocal/util.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | module Util 3 | def self.format_params(params) 4 | return "" if params.length == 0 5 | 6 | "?" + params.to_a.map { |tuple| 7 | tuple[0].to_s + "=" + tuple[1].to_s 8 | }.join("&") 9 | end 10 | 11 | def self.format_params_amp(params) 12 | return "" if params.length == 0 13 | 14 | "&" + params.to_a.map { |tuple| 15 | tuple[0].to_s + "=" + tuple[1].to_s 16 | }.join("&") 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/syobocal/comment/element/blank.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | module Comment 3 | module Element 4 | class Blank 5 | def initialize; end 6 | 7 | def ==(other) 8 | other.instance_of?(self.class) 9 | end 10 | 11 | def inner_text 12 | "" 13 | end 14 | 15 | def self.match?(line) 16 | line.match(/\A\s*\Z/) 17 | end 18 | 19 | def self.parse(line) 20 | Element::Blank.new 21 | end 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/syobocal/sub_titles/parser.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | module SubTitles 3 | class Parser 4 | FORMAT_REGEXP = /\A\*(.*?)\*(.*)\Z/ 5 | 6 | def initialize(sub_titles) 7 | @sub_titles = sub_titles 8 | end 9 | 10 | def parse 11 | return @parse if defined? @parse 12 | 13 | @sub_titles.each_line.each_with_object([]) { |line, array| 14 | m = FORMAT_REGEXP.match(line) 15 | 16 | array << SubTitle.new(m[1], m[2]) if m 17 | } 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /test/comment/test_person.rb: -------------------------------------------------------------------------------- 1 | require "minitest/autorun" 2 | require "syobocal" 3 | 4 | class TestPerson < MiniTest::Test 5 | include Syobocal::Comment 6 | 7 | def test_parse 8 | expect = Person.new("hoge", "piyo") 9 | actual = Person.parse("hoge(piyo)") 10 | 11 | assert_equal expect, actual 12 | end 13 | 14 | def test_valid? 15 | assert_equal false, Person.new("", "piyo").valid? 16 | assert_equal false, Person.new(nil, "piyo").valid? 17 | assert_equal true, Person.new("hoge", "piyo").valid? 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /test/comment/element/test_text_node.rb: -------------------------------------------------------------------------------- 1 | require "minitest/autorun" 2 | require "syobocal" 3 | 4 | class TestTextNode < MiniTest::Test 5 | include Syobocal::Comment::Element 6 | 7 | def test_inner_text 8 | node = TextNode.new([Text.new("hoge"), Link.new("piyo", "http://example.com")]) 9 | 10 | assert_equal "hogepiyo", node.inner_text 11 | end 12 | 13 | def test_split 14 | node = TextNode.new([Text.new("hoge、"), Link.new("piyo、", "http://example.com"), Text.new("、foo(bar、baz)")]) 15 | 16 | assert_equal ["hoge", "piyo、", "foo(bar、baz)"], node.split 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/syobocal/comment/music_data.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | module Comment 3 | class MusicData 4 | attr_reader :attr, :attr_text, :attr_note, :value, :people 5 | 6 | def initialize(attr, attr_text, attr_note, value, people) 7 | @attr, @attr_text, @attr_note, @value, @people = attr, attr_text, attr_note, value, people 8 | end 9 | 10 | def ==(other) 11 | other.instance_of?(self.class) && other.attr == attr && other.attr_text == attr_text && other.attr_note == other.attr_note && other.value == value && other.people == people 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/syobocal/comment/element/list.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | module Comment 3 | module Element 4 | class List 5 | attr_reader :text_node 6 | 7 | def initialize(text_node) 8 | @text_node = text_node 9 | end 10 | 11 | def ==(other) 12 | other.instance_of?(self.class) && other.text_node == text_node 13 | end 14 | 15 | def self.match?(line) 16 | line.start_with?("-") 17 | end 18 | 19 | def self.parse(line) 20 | txt = line.match(/\A-(.*)\Z/)[1] 21 | 22 | Element::List.new(Element::TextNode.parse(txt)) 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/syobocal/comment/element/header2.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | module Comment 3 | module Element 4 | class Header2 5 | attr_reader :text_node 6 | 7 | def initialize(text_node) 8 | @text_node = text_node 9 | end 10 | 11 | def ==(other) 12 | other.instance_of?(self.class) && other.text_node == text_node 13 | end 14 | 15 | def self.match?(line) 16 | line.start_with?("**") 17 | end 18 | 19 | def self.parse(line) 20 | txt = line.match(/\A\*\*(.*)\Z/)[1] 21 | 22 | Element::Header2.new(Element::TextNode.parse(txt)) 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/syobocal/comment/element/header1.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | module Comment 3 | module Element 4 | class Header1 5 | attr_reader :text_node 6 | 7 | def initialize(text_node) 8 | @text_node = text_node 9 | end 10 | 11 | def ==(other) 12 | other.instance_of?(self.class) && other.text_node == text_node 13 | end 14 | 15 | def self.match?(line) 16 | !Header2.match?(line) && line.start_with?("*") 17 | end 18 | 19 | def self.parse(line) 20 | txt = line.match(/\A\*(.*)\Z/)[1] 21 | 22 | Element::Header1.new(Element::TextNode.parse(txt)) 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/syobocal/comment/element/link.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | module Comment 3 | module Element 4 | class Link 5 | attr_reader :str, :url 6 | 7 | def initialize(str, url) 8 | @str, @url = str, url 9 | end 10 | 11 | def ==(other) 12 | other.instance_of?(self.class) && other.str == str && other.url == url 13 | end 14 | 15 | def self.create(text) 16 | inner_str = text.match(/\A\[\[(.*)\]\]\Z/)[1] 17 | 18 | sep = inner_str.split(/ /) 19 | 20 | if ["http://", "https://", "archive://"].any? { |scheme| sep.last.start_with?(scheme) } 21 | link = sep.pop 22 | new(sep.join(" "), link) 23 | else 24 | new(sep.join(" "), nil) 25 | end 26 | end 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/syobocal/comment/person.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | module Comment 3 | class Person 4 | PERSON_SEPARATOR = "、" 5 | ENCODED_SEPARATOR = "\t" 6 | 7 | attr_reader :name, :note 8 | 9 | def initialize(name, note) 10 | @name, @note = name, note 11 | end 12 | 13 | def ==(other) 14 | other.instance_of?(self.class) && other.name == name && other.note == note 15 | end 16 | 17 | def valid? 18 | !name.nil? && !name.empty? 19 | end 20 | 21 | def self.parse(str) 22 | _, name, note = *(str.match(/\A([^\(\)]+?)(?:\((.*?)\))?\Z/).to_a) 23 | 24 | Person.new(name&.strip, note&.strip) 25 | end 26 | 27 | def self.multi_parse(str) 28 | Helper::Fragment.parse(str).to_a.map { |f| 29 | name = f.text 30 | note = f&.child&.to_s 31 | 32 | Person.new(name, note) 33 | } 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /test/comment/test_section.rb: -------------------------------------------------------------------------------- 1 | require "minitest/autorun" 2 | require "syobocal" 3 | 4 | class TestSection < MiniTest::Test 5 | include Syobocal::Comment 6 | 7 | def test_staff_section? 8 | section = Section.new( 9 | Element::Header1.new( 10 | Element::TextNode.new( 11 | [Element::Text.new("スタッフ")] 12 | ) 13 | ), 14 | [] 15 | ) 16 | 17 | assert section.staff_section? 18 | end 19 | 20 | def test_cast_section? 21 | section = Section.new( 22 | Element::Header1.new( 23 | Element::TextNode.new( 24 | [Element::Text.new("キャスト")] 25 | ) 26 | ), 27 | [] 28 | ) 29 | 30 | assert section.cast_section? 31 | end 32 | 33 | def test_link_section? 34 | section = Section.new( 35 | Element::Header1.new( 36 | Element::TextNode.new( 37 | [Element::Text.new("リンク")] 38 | ) 39 | ), 40 | [] 41 | ) 42 | 43 | assert section.link_section? 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /exe/syobocal-anime: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby 2 | # coding:utf-8 3 | 4 | require 'syobocal' 5 | 6 | def format_time(time) 7 | h = time.hour 8 | h += 24 if h < 5 9 | m = time.min 10 | 11 | sprintf("%2d:%02d", h, m) 12 | end 13 | 14 | params = {"days" => "1"} 15 | result = Syobocal::CalChk.get(params) 16 | 17 | puts "これから放送されるアニメ@首都圏\n" 18 | 19 | result.select{|prog| 20 | # 現在から 21 | st = Time.now 22 | 23 | # 次の朝5時まで 24 | day = Time.now.hour < 5 ? Date.today : Date.today + 1 25 | ed = Time.new(day.year, day.month, day.day, 5) 26 | 27 | # 首都圏のチャンネルで放送されるアニメ 28 | syutoken_ch = [ 29 | 1, # NHK総合 30 | 2, # NHK Eテレ 31 | 3, # フジテレビ 32 | 4, # 日本テレビ 33 | 5, # TBS 34 | 6, # テレビ朝日 35 | 7, # テレビ東京 36 | 8, # TVK 37 | 13, # チバテレビ 38 | 14, # テレ玉 39 | 19, # TOKYO MX 40 | ] 41 | 42 | st < prog[:st_time] and prog[:st_time] < ed and syutoken_ch.include?(prog[:ch_id]) 43 | }.sort_by{|prog| 44 | prog[:st_time] # 放送開始日時で降順に並べ替え 45 | }.each{|prog| 46 | puts "#{format_time(prog[:st_time])} [#{prog[:ch_name]}] #{prog[:title]} / #{prog[:sub_title]}" 47 | } 48 | -------------------------------------------------------------------------------- /lib/syobocal/comment/element/row.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | module Comment 3 | module Element 4 | class Row 5 | attr_reader :attr_node, :value_node 6 | 7 | def initialize(attr_node, value_node) 8 | @attr_node, @value_node = attr_node, value_node 9 | end 10 | 11 | def ==(other) 12 | other.instance_of?(self.class) && other.attr_node == attr_node && other.value_node == value_node 13 | end 14 | 15 | def self.match?(line) 16 | line.start_with?(":") 17 | end 18 | 19 | def self.parse(line) 20 | if line.scan(":").length == 1 21 | # NOTE :が1つしか含まれない行は:以降が値となる 22 | m = line.match(/\A:(.*)\Z/) 23 | attr = Element::Blank.new 24 | value = Element::TextNode.parse(m[1]) 25 | else 26 | m = line.match(/\A:([^:]*?):(.*)\Z/) 27 | attr = Element::TextNode.parse(m[1]) 28 | value = Element::TextNode.parse(m[2]) 29 | end 30 | 31 | Element::Row.new(attr, value) 32 | end 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 xmisao 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/syobocal.rb: -------------------------------------------------------------------------------- 1 | require "open-uri" 2 | require "rexml/document" 3 | require "delegate" 4 | require "json" 5 | require "rss" 6 | require "syobocal/version" 7 | 8 | require "syobocal/util" 9 | require "syobocal/calchk" 10 | require "syobocal/db" 11 | require "syobocal/json" 12 | require "syobocal/rss" 13 | require "syobocal/rss2" 14 | require "syobocal/comment/element/blank" 15 | require "syobocal/comment/element/header1" 16 | require "syobocal/comment/element/header2" 17 | require "syobocal/comment/element/link" 18 | require "syobocal/comment/element/list" 19 | require "syobocal/comment/element/row" 20 | require "syobocal/comment/element/text" 21 | require "syobocal/comment/element/text_node" 22 | require "syobocal/comment/element/root" 23 | require "syobocal/comment/helper/fragment" 24 | require "syobocal/comment/parser" 25 | require "syobocal/comment/staff" 26 | require "syobocal/comment/cast" 27 | require "syobocal/comment/person" 28 | require "syobocal/comment/music" 29 | require "syobocal/comment/music_data" 30 | require "syobocal/comment/section" 31 | require "syobocal/sub_titles/sub_title" 32 | require "syobocal/sub_titles/parser" 33 | 34 | module Syobocal; end 35 | -------------------------------------------------------------------------------- /test/samples/116.json: -------------------------------------------------------------------------------- 1 | { 2 | "tid": 116, 3 | "last_update": "2009-10-26 20:57:09 +0900", 4 | "title": "まっすぐにいこう。", 5 | "short_title": null, 6 | "title_yomi": "まっすぐにいこう", 7 | "title_en": null, 8 | "comment": "*リンク\n[['03夏 公式 http://www.ytv.co.jp/massugu03/]]\n*メモ\n-第一期。全4話\n-AT-Xの再放送は金21:00、火15:00、27:00\n*スタッフ\n:原作:きら「まっすぐにいこう。」\n:掲載誌:集英社「コーラス」\n:企画:斎藤朋之\n:監督・演出・絵コンテ:井硲清高\n:脚本:大和屋暁、伊丹あき、三原光尋 \n:構成:加藤陽一\n:キャラクターデザイン・総作画監督:つなきあき\n:作画監督:森田奈苗、高橋英吉\n:美術監督:坂本信人\n:色彩設計:有尾由紀子\n:撮影監督:松崎信也\n:編集:辺見俊夫\n:音楽:大島ミチル\n:音響プロデューサー:中野徹\n:音響監督:岩浪美和\n:音響効果:神保大介\n:音響制作:HALF H・P STUDIO\n:制作進行:佐藤嘉進、補永直樹\n:制作担当:坪井葉子\n:制作デスク:大塚峰子\n:アシスタントプロデューサー:北田修一\n:プロデューサー:斎藤朋之)よみうりテレビ)、山口聰(ゆめ太カンパニー)\n:チーフプロデューサー:諏訪道彦(よみうりテレビ)\n:制作:よみうりテレビ、ゆめ太カンパニー\n*オープニングテーマ「どろぼう」\n:歌:aiko\n*エンディングテーマ「花火」\n:歌:aiko\n*キャスト\n:マメタロウ:吉野裕行\n:はなこ:小島幸子\n:若月郁子:折笠富美子\n:秋吉純一:野島健児\n:伊集院たけし:鈴村健一\n:早乙女直:下屋則子\n:渡部京介:森川智之\n:セバスチャン:鈴木真仁\n:空:半場友恵\n:ハシブト:大浜直樹\n:友情出演:辻谷耕史、渡辺久美子\n", 9 | "cat": 10, 10 | "title_flag": 0, 11 | "first_year": 2003, 12 | "first_month": 8, 13 | "first_end_year": 2003, 14 | "first_end_month": 8, 15 | "first_ch": "よみうりテレビ", 16 | "keywords": null, 17 | "user_point": 1, 18 | "user_point_rank": 5151, 19 | "sub_titles": "*001*学校へいこう!\n*002*コンビニへいこう!\n*003*川原へいこう!\n*004*正直にいこう!\n" 20 | } 21 | -------------------------------------------------------------------------------- /lib/syobocal/rss2.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | module RSS2 3 | def get(params = {}) 4 | parse(URI.open(url(params))) 5 | end 6 | 7 | def url(params = {}) 8 | "http://cal.syoboi.jp/rss2.php" + Syobocal::Util.format_params(params) 9 | end 10 | 11 | def parse(rss) 12 | rss = REXML::Document.new(rss) 13 | 14 | result = Result.new 15 | 16 | channel = rss.elements["rss/channel"] 17 | result.title = channel.elements["title"].text 18 | result.link = channel.elements["link"].text 19 | result.dc_language = channel.elements["dc:language"].text 20 | result.pub_date = Time.parse(channel.elements["pubDate"].text) 21 | 22 | rss.elements.each("rss/channel/item") { |item| 23 | result << { 24 | :title => item.elements["title"].text, 25 | :link => item.elements["link"].text, 26 | :description => item.elements["description"].text, 27 | :pub_date => Time.parse(item.elements["pubDate"].text), 28 | } 29 | } 30 | 31 | result 32 | end 33 | 34 | module_function :get, :url, :parse 35 | 36 | class Result < DelegateClass(Array) 37 | attr_accessor :title, :link, :dc_language, :pub_date 38 | 39 | def initialize 40 | super([]) 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /syobocal.gemspec: -------------------------------------------------------------------------------- 1 | lib = File.expand_path("../lib", __FILE__) 2 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 3 | require "syobocal/version" 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = "syobocal" 7 | spec.version = Syobocal::VERSION 8 | spec.authors = ["xmisao"] 9 | spec.email = ["mail@xmisao.com"] 10 | 11 | spec.summary = "Simple gem for Syoboi Calendar" 12 | spec.description = "Syoboi Calendar is the oldest and biggest ANIME information site, supported and hosted by anime fans in Japan. This gem make it easy to download information using web APIs of this site." 13 | spec.homepage = "https://github.com/xmisao/syobocal" 14 | spec.license = "MIT" 15 | 16 | # Specify which files should be added to the gem when it is released. 17 | # The `git ls-files -z` loads the files in the RubyGem that have been added into git. 18 | spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do 19 | `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 20 | end 21 | spec.bindir = "exe" 22 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 23 | spec.require_paths = ["lib"] 24 | 25 | spec.add_development_dependency "bundler", "~> 2.0" 26 | spec.add_development_dependency "rake", ">= 13.0" 27 | spec.add_development_dependency "minitest", "~> 5.0" 28 | spec.add_development_dependency "rufo", "~> 0.7" 29 | spec.add_dependency "rexml", "~> 3.2" 30 | spec.add_dependency "rss", "~> 0.2" 31 | 32 | spec.required_ruby_version = ">= 3.1.0" 33 | end 34 | -------------------------------------------------------------------------------- /lib/syobocal/comment/element/text_node.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | module Comment 3 | module Element 4 | class TextNode 5 | attr_reader :text_elements 6 | 7 | SUBJECT_SEPARATOR = "、" 8 | ENCODED_SEPARATOR = "\t" 9 | 10 | def initialize(text_elements) 11 | @text_elements = text_elements 12 | end 13 | 14 | def inner_text 15 | text_elements.map(&:str).join("") 16 | end 17 | 18 | def ==(other) 19 | other.instance_of?(self.class) && other.text_elements == text_elements 20 | end 21 | 22 | def split 23 | buffer = "" 24 | 25 | text_elements.each do |node| 26 | case node 27 | when Element::Text 28 | buffer << node.str.gsub(SUBJECT_SEPARATOR, ENCODED_SEPARATOR) 29 | when Element::Link 30 | buffer << node.str 31 | end 32 | end 33 | 34 | buffer.scan(/[^#{ENCODED_SEPARATOR}\(]+(?:\(.*?\))?/).map { |str| str.gsub(ENCODED_SEPARATOR, SUBJECT_SEPARATOR).strip } 35 | end 36 | 37 | def self.match?(line) 38 | true 39 | end 40 | 41 | def self.parse(text) 42 | elements = text.split(/(\[\[[^\[\]]*?\]\])/).select { |s| !s.empty? }.map do |s| 43 | if s.match(/\A\[\[[^\[\]]*?\]\]\Z/) 44 | Link.create(s) 45 | else 46 | Text.create(s) 47 | end 48 | end 49 | 50 | Element::TextNode.new(elements) 51 | end 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /test/sub_titles/test_parser.rb: -------------------------------------------------------------------------------- 1 | require "minitest/autorun" 2 | require "syobocal" 3 | 4 | module Syobocal 5 | module SubTitles 6 | class TestParser < MiniTest::Test 7 | def test_parse 8 | sub_titles = < item.attribute("rdf:about").to_s, 25 | :title => item.elements["title"].text, 26 | :link => item.elements["link"].text, 27 | :description => item.elements["description"].text, 28 | :dc_date => Time.parse(item.elements["dc:date"].text), 29 | :dc_publisher => item.elements["dc:publisher"].text, 30 | :tv_genre => tv.elements["tv:genre"].text, 31 | :tv_start_datetime => Time.parse(tv.elements["tv:startDatetime"].text), 32 | :tv_end_datetime => Time.parse(tv.elements["tv:endDatetime"].text), 33 | :tv_iepg_url => tv.elements["tv:iepgUrl"].text, 34 | :tv_performer => tv.elements["tv:performer"].text, 35 | } 36 | } 37 | 38 | result 39 | end 40 | 41 | module_function :get, :url, :parse 42 | 43 | class Result < DelegateClass(Array) 44 | attr_accessor :title, :link, :description 45 | 46 | def initialize 47 | super([]) 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/syobocal/calchk.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | module CalChk 3 | def get(params = {}) 4 | parse(URI.open(url(params))) 5 | end 6 | 7 | def url(params = {}) 8 | "http://cal.syoboi.jp/cal_chk.php" + Syobocal::Util.format_params(params) 9 | end 10 | 11 | def parse(xml) 12 | xml = REXML::Document.new(xml) 13 | 14 | result = Result.new 15 | 16 | syobocal = xml.elements["syobocal"] 17 | result.url = syobocal.attribute("url").to_s 18 | result.version = syobocal.attribute("version").to_s 19 | result.last_update = Time.parse(syobocal.attribute("LastUpdate").to_s) 20 | result.spid = syobocal.attribute("SPID").to_s 21 | result.spname = syobocal.attribute("SPNAME").to_s 22 | 23 | xml.elements.each("syobocal/ProgItems/ProgItem") { |item| 24 | result << { 25 | :pid => item.attribute("PID").to_s.to_i, 26 | :tid => item.attribute("TID").to_s.to_i, 27 | :st_time => Time.parse(item.attribute("StTime").to_s), 28 | :ed_time => Time.parse(item.attribute("EdTime").to_s), 29 | :ch_name => item.attribute("ChName").to_s, 30 | :ch_id => item.attribute("ChID").to_s.to_i, 31 | :count => item.attribute("Count").to_s.to_i, 32 | :st_offset => item.attribute("StOffset").to_s.to_i, 33 | :sub_title => item.attribute("SubTitle").to_s, 34 | :title => item.attribute("Title").to_s, 35 | :prog_comment => item.attribute("ProgComment").to_s, 36 | } 37 | } 38 | 39 | result 40 | end 41 | 42 | module_function :get, :url, :parse 43 | 44 | class Result < DelegateClass(Array) 45 | attr_accessor :url, :version, :last_update, :spid, :spname 46 | 47 | def initialize 48 | super([]) 49 | end 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /lib/syobocal/comment/helper/fragment.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | module Comment 3 | module Helper 4 | class Fragment 5 | CHILD_BEGIN = "(" 6 | CHILD_END = ")" 7 | SEPARATOR = "、" 8 | 9 | attr_reader :text, :child, :following 10 | 11 | def initialize(text, child, following) 12 | @text, @child, @following = text, child, following 13 | end 14 | 15 | def self.parse(str) 16 | chars = str.each_char.to_a 17 | 18 | parse_chars(chars) 19 | end 20 | 21 | def self.parse_chars(chars) 22 | text = "" 23 | child = nil 24 | following = nil 25 | 26 | until chars.empty? 27 | c = chars.shift 28 | 29 | case c 30 | when CHILD_BEGIN 31 | child = parse_chars(chars) 32 | when CHILD_END 33 | return Fragment.new(text, child, following) 34 | when SEPARATOR 35 | following = parse_chars(chars) 36 | else 37 | text << c 38 | end 39 | end 40 | 41 | Fragment.new(text, child, following) 42 | end 43 | 44 | def pretty_output(level = 0) 45 | line = level.times.map { " " }.join 46 | 47 | line << text 48 | 49 | puts line 50 | 51 | child&.pretty_output(level + 1) 52 | 53 | following&.pretty_output(level) 54 | end 55 | 56 | def to_a 57 | array = [self] 58 | target = self 59 | 60 | while target.following 61 | array << target.following 62 | target = target.following 63 | end 64 | 65 | array 66 | end 67 | 68 | def to_s 69 | to_a.map { |f| 70 | if f.child 71 | "#{f.text}(#{f.child.to_s})" 72 | else 73 | "#{f.text}" 74 | end 75 | }.join(SEPARATOR) 76 | end 77 | end 78 | end 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /test/samples/2077.json: -------------------------------------------------------------------------------- 1 | { 2 | "tid": 2077, 3 | "last_update": "2013-12-02 02:17:55 +0900", 4 | "title": "魔法少女まどか☆マギカ", 5 | "short_title": "まどか☆マギカ", 6 | "title_yomi": "まほうしょうじょまどかまぎか", 7 | "title_en": "PUELLA MAGI MADOKA MAGICA", 8 | "comment": "*リンク\n-[[公式 http://www.madoka-magica.com/tv/]]\n-[[MBS http://www.mbs.jp/madoka-magica/]]\n-[[ニコニコチャンネル http://ch.nicovideo.jp/ch260]]\n-[[TOKYO MX http://www.mxtv.co.jp/madoka_magica/]]\n-[[BS11デジタル http://www.bs11.jp/anime/2360/]]\n*メモ\n-アニマックスでのリピート放送は放送日の27:00~と日曜26:00~\n*スタッフ\n:原作:Magica Quartet\n:監督:新房昭之\n:キャラクター原案:蒼樹うめ\n:キャラクターデザイン:岸田隆宏\n:シリーズ構成・脚本:虚淵玄(ニトロプラス)\n:シリーズディレクター:宮本幸裕\n:総作画監督:谷口淳一郎、高橋美香\n:アクションディレクター:阿部望、神谷智大\n:レイアウト設計:牧孝雄\n:異空間設計:劇団イヌカレー\n:美術監督:稲葉邦彦、金子雄司\n:美術設定:大原盛仁\n:色彩設計:日比野仁、滝沢いづみ\n:ビジュアルエフェクト:酒井基\n:撮影監督:江藤慎一郎\n:編集:松原理恵\n:音響監督:鶴岡陽太\n:音響制作:楽音舎\n:音楽:梶浦由記\n:音楽制作:アニプレックス\n:アニメーション制作:SHAFT\n:製作:Magica Partners(アニプレックス、芳文社、博報堂DYメディアパートナーズ、ニトロプラス、ムービック、SHAFT)、毎日放送\n*オープニングテーマ「コネクト」\n:作詞・作曲:渡辺翔\n:主題歌協力:外村敬一\n:歌:ClariS\n:使用話数:#1~#9、#11\n-#10、#12はオープニングテーマなし、エンディングテーマとして使用\n*エンディングテーマ「Magia」\n:作詞・作曲・編曲:梶浦由記\n:音楽プロデューサー:森康哲\n:歌:Kalafina\n:使用話数:#3~#8、#11(Blu-ray/DVDのみ)\n-#1、#2、#9、#11(放送版)はエンディングテーマなし\n-#1、#2、#10は挿入歌として使用\n*キャラクターエンディングソング1「また あした」\n:作詞・作曲:hanawaya\n:編曲:流歌、田口智則\n:歌:鹿目まどか(悠木碧)\n:使用話数:#1~#2(Blu-ray/DVDのみ)\n*キャラクターエンディングソング2「and I'm home」\n:作詞・作曲:wowaka\n:編曲:どく、wowaka\n:歌:美樹さやか(喜多村英梨)、佐倉杏子(野中藍)\n:使用話数:#9(Blu-ray/DVDのみ)\n*挿入歌「コネクト (ゲームインスト)」\n:作曲:渡辺翔\n:使用話数:#6\n*キャスト\n:鹿目まどか:悠木碧\n:暁美ほむら:斎藤千和\n:巴マミ/鹿目タツヤ:水橋かおり\n:美樹さやか:喜多村英梨\n:佐倉杏子:野中藍\n:キュゥべえ:加藤英美里\n:志筑仁美:新谷良子\n:鹿目詢子:後藤邑子\n:鹿目知久:岩永哲哉\n:上条恭介:吉田聖子\n:早乙女和子:岩男潤子\n*予告イラスト\n:#1:ハノカゲ\n:#2:氷川へきる\n:#3:津路参汰(ニトロプラス)\n:#4:小林尽\n:#5:ゆーぽん(ニトロプラス)\n:#6:ウエダハジメ\n:#7:天杉貴志\n:#8:藤真拓哉\n:#9:なまにくATK(ニトロプラス)\n:#10:ムラ黒江\n:#11:ブリキ\n*エンドイラスト\n:#12:蒼樹うめ", 9 | "cat": 10, 10 | "title_flag": 0, 11 | "first_year": 2011, 12 | "first_month": 1, 13 | "first_end_year": 2011, 14 | "first_end_month": 4, 15 | "first_ch": "MBS", 16 | "keywords": "wikipedia:魔法少女まどか☆マギカ", 17 | "user_point": 173, 18 | "user_point_rank": 313, 19 | "sub_titles": "*01*夢の中で会った、ような・・・・・\n*02*それはとっても嬉しいなって\n*03*もう何も恐くない\n*04*奇跡も、魔法も、あるんだよ\n*05*後悔なんて、あるわけない\n*06*こんなの絶対おかしいよ\n*07*本当の気持ちと向き合えますか?\n*08*あたしって、ほんとバカ\n*09*そんなの、あたしが許さない\n*10*もう誰にも頼らない\n*11*最後に残った道しるべ\n*12*わたしの、最高の友達" 20 | } 21 | -------------------------------------------------------------------------------- /lib/syobocal/comment/parser.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | module Comment 3 | class Parser 4 | ELEMENT_CLASSES = [ 5 | Element::Header2, 6 | Element::Header1, 7 | Element::List, 8 | Element::Row, 9 | Element::Blank, 10 | Element::TextNode, # NOTE Sentinel 11 | ] 12 | 13 | def initialize(comment) 14 | @comment = comment 15 | end 16 | 17 | def parse 18 | return @parse if defined? @parse 19 | 20 | return @parse = Element::Root.new([]) unless @comment 21 | 22 | elements = @comment.each_line.map do |line| 23 | line.chomp! 24 | ELEMENT_CLASSES.find { |clazz| clazz.match?(line) }.parse(line) 25 | end 26 | 27 | @parse = Element::Root.new(elements) 28 | end 29 | 30 | def staffs 31 | return @staffs if defined? @staffs 32 | 33 | rows = sections.find { |section| section.staff_section? }&.rows || [] 34 | 35 | @staffs = create_staff_list(rows) 36 | end 37 | 38 | def casts 39 | return @casts if defined? @casts 40 | 41 | rows = sections.find { |section| section.cast_section? }&.rows || [] 42 | 43 | @casts = create_cast_list(rows) 44 | end 45 | 46 | def musics 47 | return @musics if defined? @musics 48 | 49 | @musics = create_musics(all_sections) 50 | end 51 | 52 | def links 53 | return @links if defined? @links 54 | 55 | @links = sections.find { |section| section.link_section? }&.links || [] 56 | end 57 | 58 | def sections 59 | return @sections if defined? @sections 60 | 61 | @sections = Section.create_sections(parse.elements) 62 | end 63 | 64 | def all_sections 65 | return enum_for(:all_sections) unless block_given? 66 | 67 | sections.each do |section| 68 | yield section 69 | 70 | section.sub_sections.each do |sub_section| 71 | yield sub_section 72 | end 73 | end 74 | end 75 | 76 | private 77 | 78 | def create_staff_list(rows) 79 | staffs = rows.map do |row| 80 | role = row.attr_node.inner_text 81 | people = row.value_node.split.map { |str| Person.parse(str) } 82 | 83 | Staff.new(role, people.select(&:valid?)) 84 | end 85 | 86 | staffs.select { |staff| !staff.people.empty? } 87 | end 88 | 89 | def create_cast_list(rows) 90 | casts = rows.map do |row| 91 | character = row.attr_node.inner_text 92 | people = row.value_node.split.map { |str| Person.parse(str) } 93 | 94 | Cast.new(character, people.select(&:valid?)) 95 | end 96 | 97 | casts.select { |cast| !cast.people.empty? } 98 | end 99 | 100 | def create_musics(sections) 101 | sections.each_with_object([]) do |section, musics| 102 | musics << section.to_music if section.music_section? 103 | end 104 | end 105 | end 106 | end 107 | end 108 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at mail@xmisao.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /lib/syobocal/json.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | module JSON 3 | module TitleMedium 4 | def get(params = {}) 5 | parse(URI.open(url(params))) 6 | end 7 | 8 | def url(params = {}) 9 | "http://cal.syoboi.jp/json.php?Req=TitleMedium" + Syobocal::Util.format_params_amp(params) 10 | end 11 | 12 | def parse(json) 13 | ::JSON.load(json) 14 | end 15 | 16 | module_function :get, :url, :parse 17 | end 18 | 19 | module TitleLarge 20 | def get(params = {}) 21 | parse(URI.open(url(params))) 22 | end 23 | 24 | def url(params = {}) 25 | "http://cal.syoboi.jp/json.php?Req=TitleLarge" + Syobocal::Util.format_params_amp(params) 26 | end 27 | 28 | def parse(json) 29 | ::JSON.load(json) 30 | end 31 | 32 | module_function :get, :url, :parse 33 | end 34 | 35 | module TitleFull 36 | def get(params = {}) 37 | parse(URI.open(url(params))) 38 | end 39 | 40 | def url(params = {}) 41 | "http://cal.syoboi.jp/json.php?Req=TitleFull" + Syobocal::Util.format_params_amp(params) 42 | end 43 | 44 | def parse(json) 45 | ::JSON.load(json) 46 | end 47 | 48 | module_function :get, :url, :parse 49 | end 50 | 51 | module ProgramByPID 52 | def get(params = {}) 53 | parse(URI.open(url(params))) 54 | end 55 | 56 | def url(params = {}) 57 | "http://cal.syoboi.jp/json.php?Req=ProgramByPID" + Syobocal::Util.format_params_amp(params) 58 | end 59 | 60 | def parse(json) 61 | ::JSON.load(json) 62 | end 63 | 64 | module_function :get, :url, :parse 65 | end 66 | 67 | module ProgramByCount 68 | def get(params = {}) 69 | parse(URI.open(url(params))) 70 | end 71 | 72 | def url(params = {}) 73 | "http://cal.syoboi.jp/json.php?Req=ProgramByCount" + Syobocal::Util.format_params_amp(params) 74 | end 75 | 76 | def parse(json) 77 | ::JSON.load(json) 78 | end 79 | 80 | module_function :get, :url, :parse 81 | end 82 | 83 | module ProgramByDate 84 | def get(params = {}) 85 | parse(URI.open(url(params))) 86 | end 87 | 88 | def url(params = {}) 89 | "http://cal.syoboi.jp/json.php?Req=ProgramByDate" + Syobocal::Util.format_params_amp(params) 90 | end 91 | 92 | def parse(json) 93 | ::JSON.load(json) 94 | end 95 | 96 | module_function :get, :url, :parse 97 | end 98 | 99 | module SubTitles 100 | def get(params = {}) 101 | parse(URI.open(url(params))) 102 | end 103 | 104 | def url(params = {}) 105 | "http://cal.syoboi.jp/json.php?Req=SubTitles" + Syobocal::Util.format_params_amp(params) 106 | end 107 | 108 | def parse(json) 109 | ::JSON.load(json) 110 | end 111 | 112 | module_function :get, :url, :parse 113 | end 114 | 115 | module ChFilter 116 | def get(params = {}) 117 | parse(URI.open(url(params))) 118 | end 119 | 120 | def url(params = {}) 121 | "http://cal.syoboi.jp/json.php?Req=ChFilter" + Syobocal::Util.format_params_amp(params) 122 | end 123 | 124 | def parse(json) 125 | ::JSON.load(json) 126 | end 127 | 128 | module_function :get, :url, :parse 129 | end 130 | 131 | module ChIDFilter 132 | def get(params = {}) 133 | parse(URI.open(url(params))) 134 | end 135 | 136 | def url(params = {}) 137 | "http://cal.syoboi.jp/json.php?Req=ChIDFilter" + Syobocal::Util.format_params_amp(params) 138 | end 139 | 140 | def parse(json) 141 | ::JSON.load(json) 142 | end 143 | 144 | module_function :get, :url, :parse 145 | end 146 | end 147 | end 148 | -------------------------------------------------------------------------------- /lib/syobocal/comment/section.rb: -------------------------------------------------------------------------------- 1 | module Syobocal 2 | module Comment 3 | class Section 4 | attr_reader :header, :elements, :sub_sections 5 | 6 | class RestrictedOperation < StandardError; end 7 | 8 | def initialize(header, elements) 9 | @header, @elements = header, elements 10 | @sub_sections = [] 11 | end 12 | 13 | def sub_section? 14 | @header.instance_of? Element::Header2 15 | end 16 | 17 | def add_subsection(sub_section) 18 | raise RestrictedOperation if sub_section? 19 | raise RestrictedOperation unless sub_section.sub_section? 20 | 21 | @sub_sections << sub_section 22 | end 23 | 24 | STAFF_WORD = "スタッフ" 25 | CAST_WORD = "キャスト" 26 | LINK_WORD = "リンク" 27 | MUSIC_WORDS = %w(テーマ ソング 歌 曲) 28 | MUSIC_TITLE_REGEXP = /\A(.*)「(.+?)」\Z/ 29 | 30 | def staff_section? 31 | header.text_node.inner_text.include?(STAFF_WORD) 32 | end 33 | 34 | def cast_section? 35 | header.text_node.inner_text.include?(CAST_WORD) 36 | end 37 | 38 | def link_section? 39 | header.text_node.inner_text.include?(LINK_WORD) 40 | end 41 | 42 | def music_section? 43 | str = header.text_node.inner_text 44 | 45 | MUSIC_WORDS.any? { |keyword| str.include?(keyword) } && str.match(MUSIC_TITLE_REGEXP) 46 | end 47 | 48 | def to_music 49 | m = header.text_node.inner_text.match(MUSIC_TITLE_REGEXP) 50 | title = m[2] 51 | category = m[1] 52 | 53 | data_list = rows.map do |row| 54 | attr = row.attr_node.inner_text 55 | 56 | attr_fragment = Helper::Fragment.parse(attr) 57 | 58 | if attr_fragment.to_a.size == 1 59 | attr_text = attr_fragment.text 60 | attr_note = attr_fragment&.child&.to_s 61 | else 62 | attr_text = attr_fragment.to_s 63 | attr_note = nil 64 | end 65 | 66 | value = row.value_node.inner_text 67 | people = Person.multi_parse(value) 68 | 69 | MusicData.new(attr, attr_text, attr_note, value, people) 70 | end 71 | 72 | Music.new(title, category, data_list) 73 | end 74 | 75 | def rows 76 | elements.select { |element| element.is_a? Element::Row } 77 | end 78 | 79 | def links 80 | elements.each_with_object([]) { |element, links| 81 | case element 82 | when Element::List 83 | links << element.text_node.text_elements.select { |elm| elm.instance_of? Element::Link } 84 | when Element::TextNode 85 | links << element.text_elements.select { |elm| elm.instance_of? Element::Link } 86 | end 87 | }.flatten 88 | end 89 | 90 | def self.create_sections(elements) 91 | sections = [] 92 | current_header = nil 93 | buffered_elements = [] 94 | 95 | elements.each do |element| 96 | case element 97 | when Element::Header1, Element::Header2 98 | if current_header 99 | sections << Section.new(current_header, buffered_elements) 100 | buffered_elements = [] 101 | end 102 | 103 | current_header = element 104 | else 105 | buffered_elements << element if current_header 106 | end 107 | end 108 | 109 | if current_header 110 | sections << Section.new(current_header, buffered_elements) 111 | end 112 | 113 | structure_sections(sections) 114 | end 115 | 116 | def self.structure_sections(sections) 117 | root_sections = [] 118 | parent_section = nil 119 | 120 | sections.each do |section| 121 | if section.sub_section? 122 | # NOTE parent_sectionがないパターンは不正なので無視する 123 | parent_section.add_subsection(section) if parent_section 124 | else 125 | root_sections << section 126 | parent_section = section 127 | end 128 | end 129 | 130 | root_sections 131 | end 132 | 133 | private_class_method :structure_sections 134 | end 135 | end 136 | end 137 | -------------------------------------------------------------------------------- /exe/syobocal: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby 2 | # coding:utf-8 3 | 4 | require 'syobocal' 5 | require 'pp' 6 | 7 | command = ARGV[0] 8 | params = {} 9 | params = eval(ARGV[1]) if ARGV[1] 10 | 11 | $SYOBOCAL_STRICT = true 12 | 13 | case command 14 | when "CalChk" 15 | pp Syobocal::CalChk.get(params) 16 | when "DB::TitleLookup" 17 | puts Syobocal::DB::TitleLookup.url(params) 18 | result = Syobocal::DB::TitleLookup.get(params) 19 | puts "Code: " + result.code.to_s 20 | puts "Message: " + (result.message || "") 21 | pp result 22 | when "DB::ProgLookup" 23 | puts Syobocal::DB::ProgLookup.url(params) 24 | result = Syobocal::DB::ProgLookup.get(params) 25 | puts "Code: " + result.code.to_s 26 | puts "Message: " + (result.message || "") 27 | pp result 28 | when "DB::ChLookup" 29 | puts Syobocal::DB::ChLookup.url(params) 30 | result = Syobocal::DB::ChLookup.get(params) 31 | puts "Code: " + result.code.to_s 32 | puts "Message: " + (result.message || "") 33 | pp result 34 | when "DB::ChGroupLookup" 35 | puts Syobocal::DB::ChGroupLookup.url(params) 36 | result = Syobocal::DB::ChGroupLookup.get(params) 37 | puts "Code: " + result.code.to_s 38 | puts "Message: " + (result.message || "") 39 | pp result 40 | when "DB::TitleViewCount" 41 | puts Syobocal::DB::TitleViewCount.url(params) 42 | result = Syobocal::DB::TitleViewCount.get(params) 43 | puts "Code: " + result.code.to_s 44 | puts "Message: " + (result.message || "") 45 | if result.code == 200 46 | puts "Title: " + result.title 47 | puts "Type: " + result.type 48 | puts "Columns: " + result.columns.join(', ') 49 | end 50 | pp result 51 | when "DB::TitleRankHistory" 52 | puts Syobocal::DB::TitleRankHistory.url(params) 53 | result = Syobocal::DB::TitleRankHistory.get(params) 54 | puts "Code: " + result.code.to_s 55 | puts "Message: " + (result.message || "") 56 | if result.code == 200 57 | puts "Title: " + result.title 58 | puts "Type: " + result.type 59 | puts "Columns: " + result.columns.join(', ') 60 | end 61 | pp result 62 | when "DB::TitlePointHistory" 63 | puts Syobocal::DB::TitlePointHistory.url(params) 64 | result = Syobocal::DB::TitlePointHistory.get(params) 65 | puts "Code: " + result.code.to_s 66 | puts "Message: " + (result.message || "") 67 | if result.code == 200 68 | puts "Title: " + result.title 69 | puts "Type: " + result.type 70 | puts "Columns: " + result.columns.join(', ') 71 | end 72 | pp result 73 | when "DB::TitlePointTop" 74 | puts Syobocal::DB::TitlePointTop.url(params) 75 | result = Syobocal::DB::TitlePointTop.get(params) 76 | puts "Code: " + result.code.to_s 77 | puts "Message: " + (result.message || "") 78 | if result.code == 200 79 | puts "Title: " + result.title 80 | puts "Type: " + result.type 81 | puts "Columns: " + result.columns.join(', ') 82 | end 83 | pp result 84 | when "JSON::TitleMedium" 85 | puts Syobocal::JSON::TitleMedium.url(params) 86 | result = Syobocal::JSON::TitleMedium.get(params) 87 | pp result 88 | when "JSON::TitleLarge" 89 | puts Syobocal::JSON::TitleLarge.url(params) 90 | result = Syobocal::JSON::TitleLarge.get(params) 91 | pp result 92 | when "JSON::TitleFull" 93 | puts Syobocal::JSON::TitleFull.url(params) 94 | result = Syobocal::JSON::TitleFull.get(params) 95 | pp result 96 | when "JSON::ProgramByPID" 97 | puts Syobocal::JSON::ProgramByPID.url(params) 98 | result = Syobocal::JSON::ProgramByPID.get(params) 99 | pp result 100 | when "JSON::ProgramByCount" 101 | puts Syobocal::JSON::ProgramByCount.url(params) 102 | result = Syobocal::JSON::ProgramByCount.get(params) 103 | pp result 104 | when "JSON::ProgramByDate" 105 | puts Syobocal::JSON::ProgramByDate.url(params) 106 | result = Syobocal::JSON::ProgramByDate.get(params) 107 | pp result 108 | when "JSON::SubTitles" 109 | puts Syobocal::JSON::SubTitles.url(params) 110 | result = Syobocal::JSON::SubTitles.get(params) 111 | pp result 112 | when "JSON::ChFilter" 113 | puts Syobocal::JSON::ChFilter.url(params) 114 | result = Syobocal::JSON::ChFilter.get(params) 115 | pp result 116 | when "JSON::ChIDFilter" 117 | puts Syobocal::JSON::ChIDFilter.url(params) 118 | result = Syobocal::JSON::ChIDFilter.get(params) 119 | pp result 120 | when "RSS" 121 | puts Syobocal::RSS.url(params) 122 | result = Syobocal::RSS.get(params) 123 | puts "Title: " + result.title 124 | puts "link: " + result.link 125 | puts "Description: " + result.description 126 | pp result 127 | when "RSS2" 128 | puts Syobocal::RSS2.url(params) 129 | result = Syobocal::RSS2.get(params) 130 | puts "Title: " + result.title 131 | puts "link: " + result.link 132 | puts "Language: " + result.dc_language 133 | puts "PubDate: " + result.pub_date.to_s 134 | pp result 135 | else 136 | $stderr.puts "Unknown command or command was not specified." 137 | $stderr.puts "Example: syobocal CalChk" 138 | end 139 | -------------------------------------------------------------------------------- /test/test_db.rb: -------------------------------------------------------------------------------- 1 | require "minitest/autorun" 2 | require "syobocal" 3 | 4 | module TestDB 5 | class TestTitleLookup < MiniTest::Test 6 | def setup 7 | @sample_params = { "TID" => "2077" } 8 | end 9 | 10 | def test_url 11 | expected = "http://cal.syoboi.jp/db.php?Command=TitleLookup&TID=2077" 12 | actural = Syobocal::DB::TitleLookup.url(@sample_params) 13 | 14 | assert_equal expected, actural 15 | end 16 | 17 | def test_get 18 | result = Syobocal::DB::TitleLookup.get(@sample_params) 19 | 20 | assert_equal 200, result.code 21 | assert result.count > 0 22 | assert result.is_a? Syobocal::DB::LookupResult 23 | end 24 | end 25 | 26 | class TestProgLookup < MiniTest::Test 27 | def setup 28 | @sample_params = { "Range" => "20090401_000000-20090402_000000" } 29 | end 30 | 31 | def test_url 32 | expected = "http://cal.syoboi.jp/db.php?Command=ProgLookup&Range=20090401_000000-20090402_000000" 33 | actural = Syobocal::DB::ProgLookup.url(@sample_params) 34 | 35 | assert_equal expected, actural 36 | end 37 | 38 | def test_get 39 | result = Syobocal::DB::ProgLookup.get(@sample_params) 40 | 41 | assert_equal 200, result.code 42 | assert result.count > 0 43 | assert result.is_a? Syobocal::DB::LookupResult 44 | end 45 | end 46 | 47 | class TestChLookup < MiniTest::Test 48 | def setup 49 | @sample_params = { "ChID" => "1" } 50 | end 51 | 52 | def test_url 53 | expected = "http://cal.syoboi.jp/db.php?Command=ChLookup&ChID=1" 54 | actural = Syobocal::DB::ChLookup.url(@sample_params) 55 | 56 | assert_equal expected, actural 57 | end 58 | 59 | def test_get 60 | result = Syobocal::DB::ChLookup.get(@sample_params) 61 | 62 | assert_equal 200, result.code 63 | assert result.count > 0 64 | assert result.is_a? Syobocal::DB::LookupResult 65 | end 66 | end 67 | 68 | class TestChGroupLookup < MiniTest::Test 69 | def setup 70 | @sample_params = { "ChGID" => "1" } 71 | end 72 | 73 | def test_url 74 | expected = "http://cal.syoboi.jp/db.php?Command=ChGroupLookup&ChGID=1" 75 | actural = Syobocal::DB::ChGroupLookup.url(@sample_params) 76 | 77 | assert_equal expected, actural 78 | end 79 | 80 | def test_get 81 | result = Syobocal::DB::ChGroupLookup.get(@sample_params) 82 | 83 | assert_equal 200, result.code 84 | assert result.count > 0 85 | assert result.is_a? Syobocal::DB::LookupResult 86 | end 87 | end 88 | 89 | class TestTitleViewCount < MiniTest::Test 90 | def setup 91 | @sample_params = { "TID" => "2077" } 92 | end 93 | 94 | def test_url 95 | expected = "http://cal.syoboi.jp/db.php?Command=TitleViewCount&TID=2077" 96 | actural = Syobocal::DB::TitleViewCount.url(@sample_params) 97 | 98 | assert_equal expected, actural 99 | end 100 | 101 | def test_get 102 | result = Syobocal::DB::TitleViewCount.get(@sample_params) 103 | 104 | assert_equal 200, result.code 105 | assert result.count > 0 106 | assert result.is_a? Syobocal::DB::TableResult 107 | end 108 | end 109 | 110 | class TestTitleRankHistory < MiniTest::Test 111 | def setup 112 | @sample_params = { "TID" => "2077" } 113 | end 114 | 115 | def test_url 116 | expected = "http://cal.syoboi.jp/db.php?Command=TitleRankHistory&TID=2077" 117 | actural = Syobocal::DB::TitleRankHistory.url(@sample_params) 118 | 119 | assert_equal expected, actural 120 | end 121 | 122 | def test_get 123 | result = Syobocal::DB::TitleRankHistory.get(@sample_params) 124 | 125 | assert_equal 200, result.code 126 | assert result.count > 0 127 | assert result.is_a? Syobocal::DB::TableResult 128 | end 129 | end 130 | 131 | class TestTitlePointHistory < MiniTest::Test 132 | def setup 133 | @sample_params = { "TID" => "2077" } 134 | end 135 | 136 | def test_url 137 | expected = "http://cal.syoboi.jp/db.php?Command=TitlePointHistory&TID=2077" 138 | actural = Syobocal::DB::TitlePointHistory.url(@sample_params) 139 | 140 | assert_equal expected, actural 141 | end 142 | 143 | def test_get 144 | result = Syobocal::DB::TitlePointHistory.get(@sample_params) 145 | 146 | assert_equal 200, result.code 147 | assert result.count > 0 148 | assert result.is_a? Syobocal::DB::TableResult 149 | end 150 | end 151 | 152 | class TestTitlePointTop < MiniTest::Test 153 | def setup 154 | @sample_params = {} 155 | end 156 | 157 | def test_url 158 | expected = "http://cal.syoboi.jp/db.php?Command=TitlePointTop" 159 | actural = Syobocal::DB::TitlePointTop.url(@sample_params) 160 | 161 | assert_equal expected, actural 162 | end 163 | 164 | def test_get 165 | result = Syobocal::DB::TitlePointTop.get(@sample_params) 166 | 167 | assert_equal 200, result.code 168 | assert result.count > 0 169 | assert result.is_a? Syobocal::DB::TableResult 170 | end 171 | end 172 | end 173 | -------------------------------------------------------------------------------- /test/comment/test_parser.rb: -------------------------------------------------------------------------------- 1 | require "minitest/autorun" 2 | require "syobocal" 3 | 4 | module Syobocal 5 | module Comment 6 | class TestParser < MiniTest::Test 7 | def test_parse 8 | comment = < :int, 70 | "LastUpdate" => :time, 71 | "Title" => :str, 72 | "ShortTitle" => :str, 73 | "TitleYomi" => :str, 74 | "TitleEN" => :str, 75 | "Comment" => :str, 76 | "Cat" => :int, 77 | "TitleFlag" => :int, 78 | "FirstYear" => :int, 79 | "FirstMonth" => :int, 80 | "FirstEndYear" => :int, 81 | "FirstEndMonth" => :int, 82 | "FirstCh" => :str, 83 | "Keywords" => :str, 84 | "UserPoint" => :int, 85 | "UserPointRank" => :int, 86 | "SubTitles" => :str, 87 | } 88 | end 89 | end 90 | end 91 | 92 | module ProgLookup 93 | def get(params = {}) 94 | parse(URI.open(url(params))) 95 | end 96 | 97 | def url(params) 98 | "http://cal.syoboi.jp/db.php?Command=ProgLookup" + Syobocal::Util.format_params_amp(params) 99 | end 100 | 101 | def parse(xml) 102 | xml = REXML::Document.new(xml) 103 | 104 | result = LookupResult.new 105 | 106 | result.code = xml.elements["ProgLookupResponse/Result/Code"].text.to_i 107 | result.message = xml.elements["ProgLookupResponse/Result/Message"].text 108 | 109 | xml.elements.each("ProgLookupResponse/ProgItems/ProgItem") { |item| 110 | mapper = Mapper.new 111 | result << mapper.map(item) 112 | } 113 | 114 | result 115 | end 116 | 117 | module_function :get, :url, :parse 118 | 119 | class Mapper 120 | include Syobocal::DB::Mapper 121 | 122 | def initialize 123 | @map = { 124 | "LastUpdate" => :time, 125 | "PID" => :int, 126 | "TID" => :int, 127 | "StTime" => :time, 128 | "StOffset" => :int, 129 | "EdTime" => :time, 130 | "Count" => :int, 131 | "SubTitle" => :str, 132 | "ProgComment" => :str, 133 | "Flag" => :int, 134 | "Deleted" => :int, 135 | "Warn" => :int, 136 | "ChID" => :int, 137 | "Revision" => :int, 138 | "STSubTitle" => :str, 139 | } 140 | end 141 | end 142 | end 143 | 144 | module ChLookup 145 | def get(params = {}) 146 | parse(URI.open(url(params))) 147 | end 148 | 149 | def url(params) 150 | "http://cal.syoboi.jp/db.php?Command=ChLookup" + Syobocal::Util.format_params_amp(params) 151 | end 152 | 153 | def parse(xml) 154 | xml = REXML::Document.new(xml) 155 | 156 | result = LookupResult.new 157 | 158 | result.code = xml.elements["ChLookupResponse/Result/Code"].text.to_i 159 | result.message = xml.elements["ChLookupResponse/Result/Message"].text 160 | 161 | xml.elements.each("ChLookupResponse/ChItems/ChItem") { |item| 162 | mapper = Mapper.new 163 | result << mapper.map(item) 164 | } 165 | 166 | result 167 | end 168 | 169 | module_function :get, :url, :parse 170 | 171 | class Mapper 172 | include Syobocal::DB::Mapper 173 | 174 | def initialize 175 | @map = { 176 | "LastUpdate" => :time, 177 | "ChID" => :int, 178 | "ChName" => :str, 179 | "ChiEPGName" => :str, 180 | "ChURL" => :str, 181 | "ChEPGURL" => :str, 182 | "ChComment" => :str, 183 | "ChGID" => :int, 184 | "ChNumber" => :int, 185 | } 186 | end 187 | end 188 | end 189 | 190 | module ChGroupLookup 191 | def get(params = {}) 192 | parse(URI.open(url(params))) 193 | end 194 | 195 | def url(params) 196 | "http://cal.syoboi.jp/db.php?Command=ChGroupLookup" + Syobocal::Util.format_params_amp(params) 197 | end 198 | 199 | def parse(xml) 200 | xml = REXML::Document.new(xml) 201 | 202 | result = LookupResult.new 203 | 204 | result.code = xml.elements["ChGroupLookupResponse/Result/Code"].text.to_i 205 | result.message = xml.elements["ChGroupLookupResponse/Result/Message"].text 206 | 207 | xml.elements.each("ChGroupLookupResponse/ChGroupItems/ChGroupItem") { |item| 208 | mapper = Mapper.new 209 | result << mapper.map(item) 210 | } 211 | 212 | result 213 | end 214 | 215 | module_function :get, :url, :parse 216 | 217 | class Mapper 218 | include Syobocal::DB::Mapper 219 | 220 | def initialize 221 | @map = { 222 | "LastUpdate" => :time, 223 | "ChGID" => :int, 224 | "ChGroupName" => :str, 225 | "ChGroupComment" => :str, 226 | "ChGroupOrder" => :int, 227 | } 228 | end 229 | end 230 | end 231 | 232 | module TitleViewCount 233 | def get(params = {}) 234 | parse(URI.open(url(params))) 235 | end 236 | 237 | def url(params) 238 | "http://cal.syoboi.jp/db.php?Command=TitleViewCount" + Syobocal::Util.format_params_amp(params) 239 | end 240 | 241 | def parse(xml) 242 | Syobocal::DB.parse_table_data(xml) 243 | end 244 | 245 | module_function :get, :url, :parse 246 | end 247 | 248 | module TitleRankHistory 249 | def get(params = {}) 250 | parse(URI.open(url(params))) 251 | end 252 | 253 | def url(params) 254 | "http://cal.syoboi.jp/db.php?Command=TitleRankHistory" + Syobocal::Util.format_params_amp(params) 255 | end 256 | 257 | def parse(xml) 258 | Syobocal::DB.parse_table_data(xml) 259 | end 260 | 261 | module_function :get, :url, :parse 262 | end 263 | 264 | module TitlePointHistory 265 | def get(params = {}) 266 | parse(URI.open(url(params))) 267 | end 268 | 269 | def url(params) 270 | "http://cal.syoboi.jp/db.php?Command=TitlePointHistory" + Syobocal::Util.format_params_amp(params) 271 | end 272 | 273 | def parse(xml) 274 | Syobocal::DB.parse_table_data(xml) 275 | end 276 | 277 | module_function :get, :url, :parse 278 | end 279 | 280 | module TitlePointTop 281 | def get(params = {}) 282 | parse(URI.open(url(params))) 283 | end 284 | 285 | def url(params) 286 | "http://cal.syoboi.jp/db.php?Command=TitlePointTop" + Syobocal::Util.format_params_amp(params) 287 | end 288 | 289 | def parse(xml) 290 | Syobocal::DB.parse_table_data(xml) 291 | end 292 | 293 | module_function :get, :url, :parse 294 | end 295 | 296 | def self.parse_table_data(xml) 297 | xml = REXML::Document.new(xml) 298 | 299 | result = TableResult.new 300 | 301 | result.code = xml.elements["TableData/Result/Code"].text.to_i 302 | result.message = xml.elements["TableData/Result/Message"].text 303 | 304 | result.columns = [] 305 | if result.code == 200 306 | result.title = xml.elements["TableData/Title"].text 307 | result.type = xml.elements["TableData/Type"].text 308 | 309 | xml.elements.each("TableData/Columns/Value") { |item| 310 | result.columns << item.text 311 | } 312 | 313 | xml.elements.each("TableData/Line") { |line| 314 | line_data = {} 315 | line.elements.each_with_index { |value, index| 316 | key = result.columns[index] 317 | if key == "Date" 318 | line_data[key] = Date.parse(value.text) 319 | else 320 | line_data[key] = value.text ? value.text.to_i : nil 321 | end 322 | } 323 | result << line_data 324 | } 325 | end 326 | 327 | result 328 | end 329 | 330 | class LookupResult < DelegateClass(Array) 331 | attr_accessor :code, :message 332 | 333 | def initialize 334 | super([]) 335 | end 336 | end 337 | 338 | class TableResult < DelegateClass(Array) 339 | attr_accessor :code, :message, :title, :type, :columns 340 | 341 | def initialize 342 | super([]) 343 | end 344 | end 345 | end 346 | end 347 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # syobocal 2 | 3 | ## 概要 4 | 5 | syobocalは[しょぼいカレンダー](http://cal.syoboi.jp/)からデータを取得するためのRubyスクリプトです。`sch_upload`を除いて仕様が公開されている以下20種類のエンドポイントすべてに対応しています。 6 | 7 | - cal_chk.php 8 | - db.php 9 | - TitleLookup 10 | - ProgLookup 11 | - ChLookup 12 | - ChGroupLookup 13 | - TitleViewCount 14 | - TitleRankHistory 15 | - TitlePointHistory 16 | - TitlePointTop 17 | - json.php 18 | - TitleMedium 19 | - TitleLarge 20 | - TitleFull 21 | - ProgramByPID 22 | - ProgramByCount 23 | - ProgramByDate 24 | - SubTitles 25 | - ChFilter 26 | - ChIDFilter 27 | - rss.php 28 | - rss2.php 29 | 30 | ## インストール 31 | 32 | ~~~~ 33 | # gem install syobocal 34 | ~~~~ 35 | 36 | ## 使い方 37 | 38 | エンドポイントごとにモジュールを切っています。 39 | 例えば`db.php`の`TitleLookup`に対応したモジュールは`Syobocal::DB::TitleLookup`です。 40 | 各モジュールには以下のモジュール関数が定義されています。 41 | 42 | - get() -- データを取得してパースして返す 43 | - url() -- データを取得するURLを生成して返す 44 | - parse() -- 取得したデータをパースする 45 | 46 | このうち`url()`と`parse()`は`get()`の内部処理のためのモジュール関数です。 47 | 基本的には`get()`にパラメータをハッシュで渡してやればデータが取得できます。 48 | XMLやRSSをパースした結果は値が適切な型に変換されたハッシュの配列です。 49 | JSONをパースした結果はJSONのデータ構造をそのまま返します。 50 | 51 | いくつか典型的だろう使い方を紹介します。 52 | 53 | 以下は`db.php`からアニメごとに採番された`TID`とタイトルを一覧で取得する例です。 54 | 55 | ```ruby 56 | require 'syobocal' 57 | require 'pp' 58 | 59 | pp Syobocal::DB::TitleLookup.get({'TID' => '*', 'Fields' => 'TID,Title'}) 60 | # [{:tid=>1, :title=>"魔法遣いに大切なこと"}, 61 | # {:tid=>2, :title=>"ソニックX"}, 62 | # {:tid=>3, :title=>"デ・ジ・キャラットにょ"}, 63 | # {:tid=>1224, :title=>"魔法先生ネギま!(実写版)"}, 64 | # {:tid=>3601, :title=>"新妹魔王の契約者"}, 65 | # {:tid=>3588, :title=>"アイドルマスター シンデレラガールズ"}, 66 | # {:tid=>4, :title=>"妄想科学シリーズ ワンダバスタイル"}, 67 | # {:tid=>1086, :title=>"ひとひら"}, 68 | # {:tid=>5, :title=>"DEARBOYS"}, 69 | # {:tid=>6, :title=>"LAST EXILE"}, 70 | # ... 71 | ``` 72 | 73 | 以下は`TID`指定で特定のアニメのデータを取得する例です。 74 | 75 | ```ruby 76 | require 'syobocal' 77 | require 'pp' 78 | 79 | title = Syobocal::DB::TitleLookup.get({'TID' => '2077'}) 80 | pp title.first 81 | # {:tid=>2077, 82 | # :last_update=>2013-12-02 02:17:55 +0900, 83 | # :title=>"魔法少女まどか☆マギカ", 84 | # :short_title=>"まどか☆マギカ", 85 | # :title_yomi=>"まほうしょうじょまどかまぎか", 86 | # :title_en=>"PUELLA MAGI MADOKA MAGICA", 87 | # :comment=> 88 | # "*リンク\n" + 89 | # "-[[公式 http://www.madoka-magica.com/tv/]]\n" + 90 | # "-[[MBS http://www.mbs.jp/madoka-magica/]]\n" + 91 | # "-[[ニコニコチャンネル http://ch.nicovideo.jp/ch260]]\n" + 92 | # "-[[TOKYO MX http://www.mxtv.co.jp/madoka_magica/]]\n" + 93 | # "-[[BS11デジタル http://www.bs11.jp/anime/2360/]]\n" + 94 | # "*メモ\n" + 95 | # "-アニマックスでのリピート放送は放送日の27:00~と日曜26:00~\n" + 96 | # "*スタッフ\n" + 97 | # ":原作:Magica Quartet\n" + 98 | # ":監督:新房昭之\n" + 99 | # ":キャラクター原案:蒼樹うめ\n" + 100 | # ":キャラクターデザイン:岸田隆宏\n" + 101 | # ... 102 | ``` 103 | 104 | APIリクエスト時のパラメータは`syobocal`ライブラリとして解釈しません。しょぼいカレンダーのAPI仕様を参照して下さい。また`'TID' => '*'`に代表される大量のデータ取得はしばしば10MB以上の巨大なレスポンスを返します。しょぼいカレンダー側も大変だろうと思うので、使い方は考えて下さい。 105 | 106 | ### コメントパーサ(ベータ版) 107 | 108 | しょぼいカレンダーの`comment`属性にはスタッフやキャストなど多岐に渡る情報が専用の記法で格納されています。バージョン`0.11`以降の`syobocal`ライブラリはコメントパーサ(`Syobocal::Comment::Parser`)を備え、それらの情報を意味的に扱いやすい形で取り出すことができます。コメントパーサはベータ版です。インタフェースは変更される可能性があります。 109 | 110 | ```ruby 111 | require 'syobocal' 112 | require 'pp' 113 | 114 | title = Syobocal::DB::TitleLookup.get({'TID' => '2077'}) 115 | comment = title.first[:comment] 116 | parser = Syobocal::Comment::Parser.new(comment) 117 | 118 | pp parser.links.first 119 | # # 122 | 123 | pp parser.staffs.first 124 | # #], 129 | # @role="原作"> 130 | 131 | pp parser.casts.first 132 | # #]> 136 | 137 | pp parser.musics.first 138 | # #], 147 | # @value="渡辺翔">, 148 | # #], 154 | # @value="外村敬一">, 155 | # #], 163 | # @value="ClariS">, 164 | # #, 170 | # #], 171 | # @value="#1~#9、#11">], 172 | # @title="コネクト"> 173 | ``` 174 | 175 | ### サブタイトルパーサ(ベータ版) 176 | 177 | しょぼいカレンダーの`sub_titles`属性をパースして扱いやすい形にして取り出すパーサです。 178 | 179 | ```ruby 180 | require 'syobocal' 181 | require 'pp' 182 | 183 | title = Syobocal::DB::TitleLookup.get({'TID' => '2077'}) 184 | sub_titles = title.first[:sub_titles] 185 | parser = Syobocal::SubTitles::Parser.new(sub_titles) 186 | 187 | pp parser.parse 188 | # [#, 191 | # #, 194 | # #, 197 | # #, 200 | # #, 203 | # #, 206 | # #, 209 | # #, 212 | # #, 215 | # #, 218 | # #, 221 | # #] 224 | ``` 225 | 226 | ## おまけ 227 | 228 | テスト用の`syobocal`コマンドと、これから放送される首都圏のアニメを表示する`anime`コマンドを同梱しています。 229 | 230 | ### syobocal 231 | 232 | 各モジュールの機能をテストできるスクリプトです。 233 | 以下のようにして使います。 234 | 詳しくはソースを見て下さい。 235 | 236 | ~~~~ 237 | $ syobocal DB::TitleLookup "{'TID' => '2077'}" 238 | http://cal.syoboi.jp/db.php?Command=TitleLookup&TID=2077 239 | Code: 200 240 | Message: 241 | [{:tid=>2077, 242 | :last_update=>2013-12-02 02:17:55 +0900, 243 | :title=>"魔法少女まどか☆マギカ", 244 | :short_title=>"まどか☆マギカ", 245 | :title_yomi=>"まほうしょうじょまどかまぎか", 246 | :title_en=>"PUELLA MAGI MADOKA MAGICA", 247 | :comment=> 248 | "*リンク\n-[[公式 http://www.madoka-magica.com/tv/]]\n-[[MBS http://www.mbs.jp/madoka-magica/]]\n-[[ニコニコチャンネル http://ch.nicovideo.jp/ch260]]\n-[[TOKYO MX http://www.mxtv.co.jp/madoka_magica/]]\n-[[BS11デジタル http://www.bs11.jp/anime/2360/]]\n*メモ\n-アニマックスでのリピート放送は放送日の27:00~と日曜26:00~\n*スタッフ\n:原作:Magica Quartet\n:監督:新房昭之\n:キャラクター原案:蒼樹うめ\n:キャラクターデザイン:岸田隆宏\n:シリーズ構成・脚本:虚淵玄(ニトロプラス)\n:シリーズディレクター:宮本幸裕\n:総作画監督:谷口淳一郎、高橋美香\n:アクションディレクター:阿部望、神谷智大\n:レイアウト設計:牧孝雄\n:異空間設計:劇団イヌカレー\n:美術監督:稲葉邦彦、金子雄司\n:美術設定:大原盛仁\n:色彩設計:日比野仁、滝沢いづみ\n:ビジュアルエフェクト:酒井基\n:撮影監督:江藤慎一郎\n:編集:松原理恵\n:音響監督:鶴岡陽太\n:音響制作:楽音舎\n:音楽:梶浦由記\n:音楽制作:アニプレックス\n:アニメーション制作:SHAFT\n:製作:Magica Partners(アニプレックス、芳文社、博報堂DYメディアパートナーズ、ニトロプラス、ムービック、SHAFT)、毎日放送\n*オープニングテーマ「コネクト」\n:作詞・作曲:渡辺翔\n:主題歌協力:外村敬一\n:歌:ClariS\n:使用話数:#1~#9、#11\n-#10、#12はオープニングテーマなし、エンディングテーマとして使用\n*エンディングテーマ「Magia」\n:作詞・作曲・編曲:梶浦由記\n:音楽プロデューサー:森康哲\n:歌:Kalafina\n:使用話数:#3~#8、#11(Blu-ray/DVDのみ)\n-#1、#2、#9、#11(放送版)はエンディングテーマなし\n-#1、#2、#10は挿入歌として使用\n*キャラクターエンディングソング1「また あした」\n:作詞・作曲:hanawaya\n:編曲:流歌、田口智則\n:歌:鹿目まどか(悠木碧)\n:使用話数:#1~#2(Blu-ray/DVDのみ)\n*キャラクターエンディングソング2「and I'm home」\n:作詞・作曲:wowaka\n:編曲:どく、wowaka\n:歌:美樹さやか(喜多村英梨)、佐倉杏子(野中藍)\n:使用話数:#9(Blu-ray/DVDのみ)\n*挿入歌「コネクト (ゲームインスト)」\n:作曲:渡辺翔\n:使用話数:#6\n*キャスト\n:鹿目まどか:悠木碧\n:暁美ほむら:斎藤千和\n:巴マミ/鹿目タツヤ:水橋かおり\n:美樹さやか:喜多村英梨\n:佐倉杏子:野中藍\n:キュゥべえ:加藤英美里\n:志筑仁美:新谷良子\n:鹿目詢子:後藤邑子\n:鹿目知久:岩永哲哉\n:上条恭介:吉田聖子\n:早乙女和子:岩男潤子\n*予告イラスト\n:#1:ハノカゲ\n:#2:氷川へきる\n:#3:津路参汰(ニトロプラス)\n:#4:小林尽\n:#5:ゆーぽん(ニトロプラス)\n:#6:ウエダハジメ\n:#7:天杉貴志\n:#8:藤真拓哉\n:#9:なまにくATK(ニトロプラス)\n:#10:ムラ黒江\n:#11:ブリキ\n*エンドイラスト\n:#12:蒼樹うめ", 249 | :cat=>10, 250 | :title_flag=>0, 251 | :first_year=>2011, 252 | :first_month=>1, 253 | :first_end_year=>2011, 254 | :first_end_month=>4, 255 | :first_ch=>"MBS", 256 | :keywords=>"wikipedia:魔法少女まどか☆マギカ", 257 | :user_point=>265, 258 | :user_point_rank=>256, 259 | :sub_titles=> 260 | "*01*夢の中で会った、ような・・・・・\n*02*それはとっても嬉しいなって\n*03*もう何も恐くない\n*04*奇跡も、魔法も、あるんだよ\n*05*後悔なんて、あるわけない\n*06*こんなの絶対おかしいよ\n*07*本当の気持ちと向き合えますか?\n*08*あたしって、ほんとバカ\n*09*そんなの、あたしが許さない\n*10*もう誰にも頼らない\n*11*最後に残った道しるべ\n*12*わたしの、最高の友達"}] 261 | ~~~~ 262 | 263 | ### syobocal-anime 264 | 265 | これから放送される首都圏のアニメを表示するサンプルスクリプトです。 266 | 267 | ~~~~ 268 | $ syobocal-anime 269 | これから放送されるアニメ@首都圏 270 | 25:25 [フジテレビ] 暗殺教室(第2期) / 卒業の時間 271 | 25:58 [TBS] 少年メイド / 少年よ、大志を抱け 272 | 26:28 [TBS] 坂本ですが? / ぬくもりはいらない/1-2メモリーズ 273 | 26:35 [テレビ東京] あにむす! / 274 | 26:58 [TBS] マギ シンドバッドの冒険 / 天空都市アルテミュラ 275 | ~~~~ 276 | 277 | ## 作者 278 | 279 | - [xmisao](https://www.xmisao.com/) 280 | 281 | ## ライセンス 282 | 283 | This library is distributed under the MIT license. 284 | -------------------------------------------------------------------------------- /test/samples/91.json: -------------------------------------------------------------------------------- 1 | { 2 | "tid": 91, 3 | "last_update": "2019-03-16 11:33:12 +0900", 4 | "title": "クレヨンしんちゃん", 5 | "short_title": null, 6 | "title_yomi": "くれよんしんちゃん", 7 | "title_en": null, 8 | "comment": "*リンク\n-[[テレビ朝日 http://www.tv-asahi.co.jp/shinchan/]]\n-[[双葉社 http://www.futabasha.com/sinchan/]]\n-[[シンエイ動画 http://www.shin-ei-animation.jp/modules/products/index.php?id=6]]\n\n*メモ\n-2005/04/22からHD放送\n-回数は一部ファンサイト[[[1] http://homepage2.nifty.com/crashin/]][[[2] http://www5a.biglobe.ne.jp/~crashin/sub/index.htm]][[[3] http://www.ctt.ne.jp/~ao-m/kureyonsintyan.html]]のカウントによるものです。\n双葉社サイトの表現と異なります(2008.11で双葉社-6)\nスペシャルはカウントしていません。\n\n*スタッフ\n:原作:臼井儀人(らくだ社)\n:掲載誌:まんがタウン(双葉社)\n:制作:テレビ朝日、ADK、シンエイ動画\n*オープニングテーマ7「ダメダメのうた」\n:作詞:LADY Q\n:作曲:LADY Q\n:編曲:森俊也\n:歌:LADY Q&野原しんのすけ(矢島晶子)&野原みさえ(ならはしみき)\n:使用期間:2000年6月~2003年1月11日\n*オープニングテーマ8「PLEASURE」\n:作詞:黒須チヒロ\n:作曲:細井かおり\n:編曲:清水信之\n:歌:華原朋美\n:使用期間:2003年1月18日~2004年10月16日\n*オープニングテーマ9「ユルユルでDE-O!」\n:作詞:ムトウユージ\n:作曲:中村康就\n:編曲:中村康就\n:歌:野原しんのすけ(矢島晶子)\n:使用期間:2004年10月22日 - 2007年2月23日、2007年7月6日~2009年10月16日\n*オープニングテーマ10「ユルユルで DE-O! 2007クレヨンフレンズVersion」\n:作詞:ムトウユージ\n:作曲:中村康就\n:編曲:岩崎貴文\n:歌:野原しんのすけ(矢島晶子)&クレヨンフレンズ from AKB48\n:使用期間:2007年3月9日~2007年6月22日\n*オープニングテーマ11「ハピハピ」\n:作詞:ベッキー♪♯\n:作曲:Splash Candy\n:編曲:本田優一郎\n:歌:ベッキー♪♯\n:使用期間:2009年10月23日~2010年7月30日\n*オープニングテーマ12「Hey baby!」\n:作詞:倖田來未\n:作曲:井上慎二郎\n:編曲:田中直\n:歌:倖田來未\n:使用期間:2010年8月6日~2011年1月28日\n*オープニングテーマ13「T.W.L」\n:作詞・作曲:北川悠仁\n:編曲:野間康介\n:歌:関ジャニ∞\n:使用期間:2011年2月4日~2011年10月28日\n*オープニングテーマ14「希望山脈」\n:作詞:秋元康\n:作曲:ray.m\n:編曲:増田武史\n:歌:渡り廊下走り隊7\n:使用期間:2011年11月4日~2012年3月16日\n*オープニングテーマ15「キミに100パーセント」\n:作詞・作曲・編曲:中田ヤスタカ\n:歌:きゃりーぱみゅぱみゅ\n:使用期間:2012年10月19日~\n\n*エンディングテーマ13「ママとのお約束条項の歌」\n:作詞:臼井儀人&浅田有理\n:作曲:小杉保夫\n:編曲:斉藤英夫\n:歌:野原しんのすけ(矢島晶子)&野原みさえ(ならはしみき)\n:使用期間:2002年11月2日~2003年9月27日、2004年10月16日\n*エンディングテーマ14「ありの歌」\n:作詞:りお\n:作曲:りお\n:編曲:パパダイスケ\n:歌:やなわらばー\n:使用期間:2004年10月22日~2005年12月16日\n*SHIN-MEN主題歌「来て来てSHIN-MEN」\n:作詞:湯浅政明\n:作曲:荒川敏行\n:歌:杉並児童合唱団\n\n*キャスト\n:しんのすけ:矢島晶子(~2018/6/29)、小林由美子(2018/7/6~)\n:みさえ:ならはしみき\n:ひろし:藤原啓治(~2016/8/12)、森川智之(2016/8/26~)\n:ひまわり:こおろぎさとみ\n:シロ、風間くん:真柴摩利\n:ネネちゃん:林玉緒\n:マサオくん:一龍斎貞友\n:ボーちゃん:佐藤智恵\n:酢乙女あい:川澄綾子\n:よしなが先生:高田由美(~2009/10/23)、七緒はるひ(寺田はるひ)(2009/11/20~)\n:まつざか先生:富沢美智恵\n:上尾先生:三石琴乃\n:園長先生:納谷六朗(~2014/10/17)、森田順平 (2015/10/9~)\n:大原ななこ:紗ゆり(~2012/3/30)、伊藤静(2013/5/24~)\n:アクション仮面:玄田哲章\n:カンタム・ロボ:大滝進矢\n:ぶりぶりざえもん:塩沢兼人(~2000/5)、神谷浩史(2016/5/13~)\n:風間ママ:玉川紗己子\n:ネネママ:萩森侚子\n:マサオママ:大塚智子\n:まつざか母:麻生まどか\n:もえP:野川さくら\n:むさえ:根谷美智子\n**SHIN-MEN\n:ゴゥ:矢島晶子\n:ヒュー:水島大宙\n:スィ:ゆきじ\n:ニョキ:吉野裕行\n:カン:斎賀みつき\n:パラボン:かないみか\n:ブリットニー:甲斐田裕子\n:赤ぶりぶり:中田譲治\n:黒ぶりぶり:小杉十郎太\n:金ぶりぶり:拡森信吾(広森信吾)\n:青ぶりぶり:辻親八\n:白ぶりぶり:楠見尚己", 9 | "cat": 1, 10 | "title_flag": 0, 11 | "first_year": 1992, 12 | "first_month": 4, 13 | "first_end_year": null, 14 | "first_end_month": null, 15 | "first_ch": "テレビ朝日", 16 | "keywords": "クレヨンしんちゃん,wikipedia:クレヨンしんちゃん_(アニメ)", 17 | "user_point": 15, 18 | "user_point_rank": 2874, 19 | "sub_titles": "*0514*おそるべし女の闘いだゾ/和服はビューティフォーだゾ\n*0515*お子様映画はつらいゾ/松坂先生もついに結婚?だゾ\n*0516*オラカゼっぽいゾ/コッソリ直しちゃうゾ\n*0517*お昼はコレが食べたいゾ/外でアソブとあったかいゾ\n*0518*マサオくんがやっちゃった!ゾ/準備はオッケーだゾ\n*0519*コタツの長い一日だゾ/マサオ君のカケモチ人生だゾ\n*0520*みさえ刑事純情派だゾ/燃えるオーディションだゾ\n*0521*子ネコを拾ったゾ/オラ美人になるゾ\n*0522*ハナ水が止まらないゾ/母ちゃんケーキを作るゾ\n*0523*合コンするゾ/かわいいひまにゴ用心!だゾ\n*0524*ありがた~いお寿司だゾ/先生たちの健康診断だゾ\n*0525*お弁当コンクールだゾ/インタビューに答えるゾ\n*0526*もてもてマサオくんだゾ/ムリして若返っちゃうゾ\n*0527*明日からダイエットだゾ/テレビ朝日を見学するゾ\n*0528*ひろしのときめきの日々だゾ/ふってふってふってだゾ\n*0529*アイ・ラブ・ミッチーだゾ/あこがれのゴージャス・パフェだゾ\n*0530*父ちゃんとお散歩だゾ/車に乗れないゾ\n*0531*父ちゃんは風船がお好き!?だゾ/上尾先生がご指導するゾ\n*0532*グレてやる!?だゾ/ケータイの番をするゾ\n*0533*口笛はヒュ~ゥだゾ/お好み焼きを作るゾ\n*0534*もえPに大変身だゾ/まつざか先生見ちゃいや~んだゾ\n*0535*サインしちゃうゾ/ひまわりが耳鼻科に行くゾ\n*0536*母ちゃんを看護するゾ/隠れ家レストランに行くゾ\n*0537*めんどうくさいゾ/竹馬作って乗っちゃうゾ\n*0538*ズッキンでドッキン!だゾ/ジュースが飲みたいゾ\n*0539*CDショップへ行くゾ/ゆかたで花火大会だゾ\n*0540*オオクワガタを捕るゾ 1/オオクワガタを捕るゾ 2\n*0541*かたたたき券だゾ/あつくるしいゾ\n*0542*あつくる先生、登場!だゾ/ウォーキング・デートだゾ\n*0543*サッカーで勝負だゾ/ひまわりがのっけちゃうゾ\n*0544*エキスパンダーできたえるゾ/アロマ~な気分だゾ\n*0545*プチファイヤ~ ポだゾ/アクセサリーをつくるゾ\n*0546*ネネちゃんがおヘソを曲げちゃったゾ/まつたけをもらったゾ\n*0547*早起きは三文のトク!?だゾ/苦手をコクフクするゾ\n*0548*運動会でファイヤー!だゾ/かすかべ防衛隊ファイヤー!だゾ\n*0549*ギックリ腰でも食べたいゾ/さよならファイヤー!だゾ\n*0550*ペンキを塗るゾ/ひまのお気に入りだゾ\n*0551*合コンデビューだゾ/頼むからひとりにしてだゾ\n*0552*シロもおしゃれをするゾ/湯豆腐を食べたいゾ\n*0553*食べ過ぎちゃったゾ/オラの心はエリートだゾ\n*0554*かあちゃんは子育て中/また女子大に行ったゾ\n*0555*オラをスキーに連れてってだゾ(前編/後編)\n*0556*しいぞう先生と雪遊びだゾ/ひまわりはお姫様だゾ\n*0557*行列のラーメン屋に行くゾ/まつざか先生に送ってもらうゾ\n*0558*人の話を聞きなさいだゾ/ファミリースキーだゾ\n*0559*母ちゃんはお掃除名人だゾ/新車を買いに行くゾ\n*0560*ミニパトのおねいさんだゾ/父ちゃんの大事なシャツだゾ\n*0561*おばちゃんが来たゾ/おっきな公園へ行くゾ\n*0562*チョコビをかせぐゾ/大変!今日から家族がふえたゾ\n*0563*未知とのそーぐーだゾ/グータラ姉妹だゾ\n*0564*負けるな、むさえちゃん!だゾ/風間くんとシロのお散歩だゾ\n*0565*むさえちゃんにお説教!だゾ/電車でおでかけだゾ\n*0566*結成!新さいたま紅さそり隊だゾ 前編・後編\n*0567*お洗濯するゾ/未来のオラ達だゾ\n*0568*クリーニング屋さんにお使いだゾ/お米をゲットするゾ\n*0569*女3人寄っちゃったゾ/ゴルフ練習場に行くゾ\n*0570*ワールドカップで盛り上がるゾ/サッカーで盛り上がるゾ\n*0571*むさえちゃんのお見合いだゾ/おためし英会話だゾ\n*0572*迷子の友だゾ/むさえちゃんと幼稚園に行くゾ\n*0573*むさえちゃん流生き方だゾ/ラブ★コンだゾ\n*0574*知らない人にはついていかないゾ/コピーをとるゾ\n*0575*虫とり美少女だゾ/留守番はむさえちゃんにおまかせだゾ\n*0576*甲子園にムチューだゾ/女がふたり集まると…だゾ\n*0577*ブリーフパンダだゾ1/ブリーフパンダだゾ2\n*0578*ここ一番に弱いゾ/なぐられうさぎたびたびだゾ\n*0579*オラ2歳児だゾ/むさえちゃんvs秋田のじいちゃんだゾ\n*0580*初めての歯医者さんだゾ/銀ちゃんの休日だゾ\n*0581*オラ、ラッキーボーイだゾ/イモ掘り対決だゾ\n*0582*野原家三代だゾ/おじょうさまとお呼び!だゾ\n*0583*温水プールはパラダイスだゾ/シロがお薬をのまないゾ\n*0584*紅サソリ隊 友情のアルバイトだゾ/おニューのスーツを買うゾ\n*0585*秋の遠足はパニックだゾ 前編・後編\n*0586*ひまわりを寝かせるゾ/コタツを出すのは大変だゾ\n*0587*ゴミは誰のもの!?だゾ/むさえちゃんの子守歌だゾ\n*0588*みんなでアミアミだゾ/自転車はどこ?だゾ\n*0589*心配がイッパイだゾ/働く四郎さんを応援するゾ\n*0590*キライなものはキライだゾ/ふとんカバーを取り替えるゾ\n*0591*父ちゃんの出張をおいかけるゾ1/父ちゃんの出張をおいかけるゾ2\n*0592*わすれられたお散歩だゾ/初めてのおわかれだゾ\n*0593*あ~した天気にするゾ/むさえちゃんのヤキニクロードだゾ\n*0594*マサオくんにはえちゃったゾ/いい湯だゾ\n*0595*一家そろって発熱だゾ~むさえちゃんが看病するゾ~/オラ流 世界水泳だゾ\n*0596*さがしものをなくしたゾ/三日坊主かーちゃんだゾ\n*0597*マサオくんをこっそり応援するゾ/ひまわりのCMオーディションだゾ\n*0598*幼稚園をお助けするゾ/オラお寿司屋さんだゾ\n*0599*幼稚園にニューフェイスだゾ 前編・後編\n*0600*あいちゃんとゴーカなおままごとだゾ/ヒーローでおかたづけだゾ\n*0601*2歳児しんちゃん第5弾 オラがオラになった日だゾ/ご町内をパトロールするゾ\n*0602*マッサージは疲れるゾ/ひとりぼっちのオラだゾ\n*0603*ネネちゃんの一大事だゾ/おとしものですよぉ!だゾ\n*0604*シュミを楽しむゾ/父ちゃんにプレゼントだゾ\n*0605*夏といえば水泳大会だゾ/宇宙飛行士になるゾ\n*0606*紙袋はすっごく大事だゾ/オラんちのピーマンだゾ\n*0607*なくなるお届け物だゾ/涼しくすごすゾ\n*0608*行列のできるドーナッツ屋さんだゾ/シロのしあわせだゾ\n*0609*おもいスイカちゃんだゾ/雨宿りしませませだゾ\n*0610*オラとカッパと夏のおわりだゾ 前編・後編\n*0611*九州のじいちゃんが来たゾ 前編・後編\n*0612*やる気になってみたゾ/今夜はバーベキューだゾ\n*0613*ゴルフで勝負だゾ/ケッサクを撮りまくるゾ\n*0614*マッサージでくたくただゾ/イソーローにイソーローだゾ\n*0615*ラッキーガールネネちゃんだゾ/日曜日のオランちだゾ\n*0616*ハナカミ王子だゾ/アルバイトしてみたゾ\n*0617*秋田に里帰りだゾ 前編・後編\n*0618*祝!!ナント!青春の旅立ちだゾ 前編・後編\n*0619*父ちゃんと遊んであげるゾ/おかえり、むさえちゃん!だゾ\n*0620*目指せ世界チャンピオンだゾ/母ちゃん大革命だゾ\n*0621*風間君とお留守番だゾ/忘れ物に気を付けるゾ\n*0622*マサオくんを手伝うゾ/お見合い写真を撮るゾ\n*0623*こども美容室だゾ/エコ!でいくゾ\n*0624*ハシゴの下で大変だゾ/冷蔵庫が壊れちゃったゾ\n*0625*ネネちゃんのモウソウだゾ/芸術はおえかきだゾ\n*0626*スシ王子!だゾ 前編・後編\n*0627*お店屋さんごっこだゾ/ウォーキングだゾ\n*0628*オラ、ボディーガードになるゾ/新茶をもらったゾ\n*0629*ヒミツのキャラクター弁当だゾ/オラたちが決めるゾ\n*0630*そしてみんな犯人になったゾ/靴下を探せ!だゾ\n*0631*詰めホーダイに弱いゾ/父ちゃんが出てったゾ\n*0632*恋するネネちゃんだゾ/ヤキトリ屋さんに行ったゾ/たまには言ってみたゾ\n*0633*シロのお散歩は大変!だゾ/母ちゃんは遅いゾ/やっと買ったゾ\n*0634*紅さそり隊の保育園だゾ/ひまわりのおツメを切るゾ/くすぐったいのはイャ~ンだゾ\n*0635*アポなしでお誕生会だゾ/ガソリンギリギリだゾ/風間くんも聞いてほしいゾ\n*0636*組長のヒミツの愛だゾ/たたたた宝クジが!だゾ/怒ってなんかいないゾ\n*0637*旅に出るとおいしいゾ/雨の日曜日だゾ/あじつけを教わるゾ\n*0638*合コンでシクヨロだゾ/昆虫ハンターひろしだゾ/うさぎもイッパイ飲みたいゾ\n*0639*鵜飼いを見るゾ/ヤツの心を読むゾ\n*0640*なんちゃってキャンプだゾ/ジョリジョリが気持ちいいゾ/ウンチク語っちゃうゾ\n*0641*カミナリゴロゴロだゾ/日焼け父ちゃんだゾ/シュミを見てほしいゾ\n*0642*今日はとことんツイてないゾ/わたしもとことんツイてないわだゾ/やっぱりとことんツイてないゾ\n*0643*母ちゃんが家出したゾ/家出のつづきだゾ\n*0644*仲直りしたら?だゾ/地デジはいいゾ\n*0645*ロード・トゥ・運動会だゾ/朝からいるゾ/夜でもいるゾ\n*0646*お風呂でシュワッとするゾ/ゴミを分別するゾ/戦いはむなしいゾ\n*0647*幼稚園のおかたづけだゾ/ひまのおもちゃだゾ/間がわるいゾ\n*0648*しわよせの黄色いハンカチだゾ/桜田のそにょだゾ\n*0649*ご本のおつかいだゾ/マサオくんのアシスタントだゾ/相談を聞いてほしいゾ\n*0650*笑う?園長先生だゾ/アルバイト母ちゃんだゾ/見ちゃいや~んだゾ\n*0651*お歌でお悩みだゾ/謎のしんこちゃんだゾ\n*0652*風間くんのお見舞いだゾ/手作りベビー服だゾ\n*0653*フリマで稼いじゃうゾ/父ちゃんとシロの散歩だゾ/新婚でラブラブだゾ\n*0654*みんなで熊本だゾ/気が合う三人だゾ\n*0655*熊本でお見合いだゾ/野原家オープンゴルフだゾ/しみじみ飲んじゃうゾ\n*0656*どこでも見つけちゃうゾ/また!しんこちゃんだゾ/持たせて安心だゾ\n*0657*ママサミットだゾ/ちょっぴりの雪だゾ/気分はメルヘンだゾ\n*0658*師匠のさがし物だゾ/ラップでお掃除だゾ/ケータイは禁止だゾ\n*0659*メールは一日にしてならずだゾ/またまたしんこちゃんだゾ\n*0660*あいちゃんがダイエットだゾ/オラ、旅立つゾ\n*0661*さすらいのハスラーだゾ/さいごの?しんこちゃんだゾ\n*0662*オラのラディッシュちゃんだゾ/かくれてかくすゾ\n*0663*ななこおねいさんとお留守番だゾ/父ちゃんと棚をつくるゾ\n*0664*あいちゃんの家出だゾ/迷子を捕まえろ!だゾ\n*0665*シロのおとまりだゾ/いいところなのに・・・だゾ/まほー少女もえP「地デジがみたい」の巻\n*0666*粘土をコネコネするゾ/それはヒミツ!だゾ/まだつづいてたゾ\n*0667*まつざか先生がハマッたゾ/ひまわりをあずけるゾ/あずかりたくないゾ\n*0668*結婚式でノリノリだゾ/真夜中のドライブだゾ/かっこいいトオルちゃんだゾ\n*0669*ラブレター大作戦だゾ/ゴムをとりかえるゾ/リアルな話をしたいゾ\n*0670*親指さん、こんにちはだゾ/ザリガニ大脱走だゾ/大事なおやゆびちゃんだゾ\n*0671*犯人はダレだ!だゾ/ぎゃるり野原だゾ/この皿は!!だゾ\n*0672*弁当男子だゾ/ひまのプール日記だゾ/がんばれ川口のヤツ!だゾ\n*0673*お留守番でおそうじだゾ/週刊埼玉県庁だゾ/ユメを語るゾ\n*0674*Dr.カザマだゾ/銀行ではお静かにだゾ/バラッドニュース\n*0675*屈底母娘の家出だゾ/石が気持ちを伝えるゾ/バラッドニュース\n*0676*ツルの恩返しだゾ/映画「BALLAD 名もなき恋のうた」公開記念 戦国しんちゃんシリーズ第一章 伝説の軍扇をさがせだゾ1/バラッドニュース\n*0677*シロが夏バテだゾ/映画「BALLAD 名もなき恋のうた」公開記念 戦国しんちゃんシリーズ第ニ章 伝説の軍扇をさがせだゾ2/バラッドニュース\n*0678*夜の鉄棒だゾ/映画「BALLAD 名もなき恋のうた」公開記念 戦国しんちゃんシリーズ第三章 天下を取るゾ/バラッドニュース\n*0679*映画「BALLAD 名もなき恋のうた」公開記念 戦国しんちゃんシリーズ最終章 恋の戦国メモリーだゾ/バラッドニュース/戦国しちゃうゾ\n*0680*ネネちゃんの相手は大変だゾ/鍵をなくしたゾ/バラッドニュース\n*0681*コンカツだゾ/ホームランをねらうゾ/コンカツを企画するゾ\n*0682*四葉のクローバーだゾ/おイモをもらったゾ/クレしんインタビュー\n*0683*しんこちゃんたびたびだゾ/母ちゃんvsひまわりだゾ/ひまがやっちゃうゾ/正体は!だゾ\n*0684*スースーが苦手だゾ/書道はたのしいゾ/5才児におそわるゾ\n*0685*移動動物園が来たゾ/箸の持ち方を気にするゾ/作詞がブームだゾ\n*0686*ネネちゃんにお手紙だゾ/仲良くして!だゾ/またまた正体は!だゾ\n*0687*つけまつげでパワーアップだゾ/ワンワンカレンダーに出るゾ/カレンダーがくやしいゾ\n*0688*上尾先生の秘密の週末だゾ/幼稚園でバザーだゾ/マフラーがほしいゾ\n*0689*園長先生の災難だゾ/高級チョコをもらったゾ/将来は!?だゾ\n*0690*しんこちゃんまたまた!だゾ/段ボール箱を片づけるゾ/将来は!?だゾ2\n*0691*チョコビの工場見学だゾ/アクション仮面タオルだゾ/将来は!?だゾ3\n*0692*思い出のパース旅行だゾ 前編・後編/将来は!?だゾ4\n*0693*カスカベ大連合だゾ/家事代行屋さんは見た!だゾ\n*0694*コートの下は…だゾ/流れ星にお願いするゾ\n*0695*出たとこドライブだゾ/どーしてもヤキイモが食べたいゾ\n*0696*春我部タイムパトロール隊 オラの花嫁がみたいゾ\n*0697*懸賞に応募するゾ/まつざか先生、ついにだゾ\n*0698*帰ってきたサラリーマンしんのすけだゾ/シロがゆくえふめいだゾ\n*0699*夢のサイン会だゾ/ボーちゃんとひまわりだゾ\n*0700*すごろくは遠いゾ/フィットネス父ちゃんだゾ\n*0701*ベビーカーをデコるゾ/ネネちゃんをエスコートだゾ/デコるゾ\n*0702*お留守番がドキドキだゾ ちょっと怖いゾ編/ひまのイベントに行くゾ/グチるゾ\n*0703*風間くんの髪が…だゾ/ファミレスは最高だゾ/知らないうちに…だゾ\n*0704*ロック魂だゾ/サッカー太郎の鬼退治だゾ\n*0705*うるさい歩数計だゾ/デカすぎるゾ/一日5000歩だゾ\n*0706*恋する四郎さんだゾ/カーペットを守るゾ/恋とは!?だゾ\n*0707*シールをもらうゾ/ひまとお留守番はドキドキだゾ\n*0708*さよならミッチーヨシリンだゾ 前編・後編\n*0709*タコさんはたくさんだゾ/金魚すくいで勝負だゾ\n*0710*ボーちゃんのほしいものだゾ/水まきするゾ\n*0711*五千円のジュースだゾ/チョコビワールドだゾ\n*0712*ちゃぶ台鬼だゾ/シロの一日だゾ\n*0713*線の上を歩くゾ/初コインランドリーだゾ\n*0714*おウチでお弁当だゾ/父ちゃんの秘密の夜だゾ/20周年!だゾ\n*0715*組長イメチェン大作戦だゾ/秋田のじいちゃんがまた来たゾ\n*0716*もらった服のゆううつだゾ/高級ケーキ屋さんだゾ\n*0717*ワケあり商品だゾ/父ちゃんの子守りだゾ\n*0718*悪役はかっこいいゾ/キノコ狩りへ行くゾ\n*0719*子どもも買いだゾ/ななこおねいさんを追いかけるゾ\n*0720*うさぎが家にやってきたゾ/お弁当箱の戦いだゾ\n*0721*シネコンに行くゾ/勝手にランキングだゾ\n*0722*枯れ葉よ?、だゾ/あいちゃんの試練だゾ\n*0723*先生のお手伝いだゾ/リモコンがいっぱいだゾ/スパイって何?だゾ\n*0724*もえPの誕生日だゾ/SHIN-MEN 3 カンのヒミツ\n*0725*おモチつきだゾ/名前をつけるゾ\n*0726*秘密の部屋はパラダイスだゾ/きれいな部屋は苦しいゾ\n*0727*シロとおつかいだゾ/SHIN-MEN 4 ヒューの恋\n*0728*ばら組をスパイするゾ/自転車通勤するゾ\n*0729*きょうだいが欲しいゾ/母ちゃんの日記だゾ\n*0730*風間くんのパパが帰ってくるゾ/妄想するゾ\n*0731*スケバンママだゾ/読み聞かせだゾ\n*0732*花いっぱい大作戦だゾ/名刺を作るゾ\n*0733*大物を釣るゾ/SHIN-MEN 5 スィと女王様/まだまだやってるゾ\n*0734*犬の飼い方教えるゾ/お金をひろったゾ/クレヨンしんちゃん名作劇場「冬の国の冒険だゾ」(再)\n*0735*パイレーツオブアリビアンだゾ/ヒップホップダンスだゾ/小さいオラだゾ\n*0736*シロがうらやましいゾ/先生たちのお買い物だゾ/おとまりっていいゾ\n*0737*地デジになったゾ/フツーの女子高生タイムだゾ/みんなでえらぶゾ\n*0738*赤ズキンと紫ズキンだゾ(再)/SHIN-MEN 6 ニョキん家\n*0739*ふしぎな指輪だゾ/ウチではたらく父ちゃんだゾ/しっかり仕事して!だゾ\n*0740*王子様のクツだゾ/車でランチだゾ/王子さま♡だゾ\n*0741*風間くんの夏休みだゾ/ベビーカーにハマッたゾ\n*0742*ガキ大将むさえだゾ/田舎にとまるゾ\n*0743*ネネちゃんのシールだゾ/お宝を探すゾ\n*0744*カスカベ忍者隊だゾ 天の巻・地の巻\n*0745*オラはサラリーマンしんのすけだゾ(再)/こわ~いDVDだゾ\n*0746*古着で遊んじゃうゾ/父ちゃんのランチは大変だゾ\n*0747*オラはプロ野球選手だゾ/ひまわりを激写だゾ\n*0748*鑑識しんちゃんだゾ/名作ランキングベスト5\n*0749*たいやき屋さんだゾ/財布を落としたゾ\n*0750*オラの車が吠える時!だゾ/SHIN-MEN 7 リバースごにょごにょ大作戦! 前編\n*0751*箱の中身は何でしょねだゾ/SHIN-MEN 7 リバースごにょごにょ大作戦! 後編\n*0752*自由なあいちゃんだゾ/銀河の危機をお救いするゾ\n*0753*ちくわともやしだゾ/こだわりのコーヒーショップだゾ\n*0754*あいちゃんに初体験させるゾ/上尾先生、なんかヘンだゾ\n*0755*玄関の大そうじだゾ/SHIN-MEN 8 カレーでガッカリ~大作戦!\n*0756*今年こそは!だゾ/父ちゃんのお土産大作戦だゾ\n*0757*野原家脱出大作戦だゾ/マサオくんに花をだゾ\n*0758*おひとりさまだゾ/SHIN-MEN 9 お風呂でゴーゴー大作戦!\n*0759*カイロであったカイロだゾ/父ちゃんの日曜日だゾ\n*0760*金魚とボーちゃんだゾ/ズボンがピンチだゾ\n*0761*まつざか先生のお買い物だゾ/ボーちゃんといっしょだゾ\n*0762*ごはんをたくゾ/SHIN-MEN 10 みんなでソロ~っと大作戦!\n*0763*母ちゃんが捻挫したゾ/ひまわりはベビーシッター?だゾ\n*0764*シロ王子だゾ/宇宙家族ノハラだゾ・前編\n*0765*ネネちゃんちでお泊まり会だゾ/缶ケリウォーズだゾ\n*0766*花見で迷子だゾ/SHIN-MEN 11 育児でバブッと大作戦!\n*0767*二千円で福引きだゾ/シャッターチャンスを狙うゾ\n*0768*母ちゃんはBNPだゾ/SHIN-MEN 12 ヘーックショイ大作戦!\n*0769*金メダルへの道だゾ/パパとこどものお料理教室だゾ\n*0770*イクじい対決だゾ/どっちが先に帰るかだゾ\n*0771*オラ、ひとりで寝るゾ/覆面父ちゃんだゾ\n*0772*シロを探すゾ/ポスターを守るゾ\n*0773*アクション仮面を裏切らないゾ/気になってる風間くんだゾ\n*0774*シロを特訓だゾ/恋する水族館だゾ\n*0775*引っ越し先を探すゾ/夏を先取りするゾ\n*0776*アクション仮面を忘れないゾ/女子力をあげるゾ\n*0777*海の家でアルバイトだゾ/早起きしちゃったゾ\n*0778*カユ~イおジャマ虫だゾ/防衛隊の手帖だゾ\n*0779*流しサラリーマンそうめんだゾ/水もしたたるシロだゾ\n*0780*エビまつりだゾ/あいちゃんのおつかいだゾ\n*0781*スキマにはまったゾ/夏の帰り道だゾ\n*0782*夫婦の危機を相談されるゾ/まさかの時に備えるゾ\n*0783*今夜はいろいろややこしいゾ/SHIN-MEN 13 制服で征服?大作戦!\n*0784*狼男カスカベだゾ/カーテンを洗っちゃうゾ\n*0785*泥んこあそびは気持ちいいゾ/母ちゃんのお昼寝は内緒だゾ\n*0786*トンボの野原ひろしだゾ/シールを取り返すゾ\n*0787*着物で幼稚園に来ちゃったゾ/引き出物選びはモメモメだゾ\n*0788*本を売りたいゾ/おもちゃ箱はキツキツだゾ\n*0789*モテキじゃなくイレキだゾ/父ちゃんとブンツーだゾ\n*0790*ひまわりと耳おれクマだゾ/カスコンだゾ\n*0791*防衛隊の隊長を決めるゾ/見たい夢を見るゾ\n*0792*ほどけないゾ/園長先生を怒らせたのは誰?だゾ\n*0793*スカイツリーでキメるゾ/きゃりー来航だゾ\n*0794*草津へ家族旅行だゾ 前編・後編\n*0795*リサイクルショップについて行くゾ/今日は寒~いゾ\n*0796*シロとコンテストだゾ/引っ越しのお手伝いだゾ\n*0797*中学生が来たゾ/熱血!アスレチック公園だゾ\n*0798*B級グルメの塔だゾ1/B級グルメの塔だゾ2\n*0799*夢のカレー大会だゾ/恐怖!怪人歯医者だゾ\n*0800*友情の鍵探しだゾ/布団を圧縮するゾ\n*0801*ネネちゃんがスランプだゾ/紅サソリ隊は恋愛禁止だゾ\n*0802*お父様の万年筆はどこ?だゾ/じいちゃんの参観日だゾ\n*0803*シガイセンはこわいゾ/対決!忍者ランドだゾ\n*0804*かすかべ防衛隊の解散だゾ/ピーマンを返すゾ\n*0805*潮干狩りへゴー!だゾ/傘がいっぱいだゾ\n*0806*母ちゃんが入院して退院したゾ/母ちゃんが退院して入院したゾ\n*0807*入院生活をマンキツするゾ/真夏の雪だるまだゾ\n*0808*子連れサラリーマンだゾ/母ちゃんが退院するゾ\n*0809*幼稚園の階段だゾ/父ちゃんとプールだゾ\n*0810*謎のかぐやさんだゾ/シロのキャットタワーだゾ\n*0811*ゆれるブランコ少女だゾ/CMが見たいゾ\n*0812*母ちゃんのお宝を探すゾ/大回転マダムだゾ\n*0813*犯人は風間くんだゾ/手作りギョーザだゾ\n*0814*ボウリング対決だゾ/母ちゃんの過去だゾ\n*0815*友情のアイスだゾ/今度こそやせたいゾ\n*0816*スペインでお宝ゲットだゾ\n*0817*風間くんに追われてるゾ/フリマで売りたいゾ\n*0818*大そうじはバタバタだゾ/夢のカフェをつくるゾ\n*0819*オラたち家電製品だゾ\n*0820*かけっこ教室だゾ/シロのお散歩ともだちだゾ\n*0821*ワカサギ釣りだゾ/アクション仮面が行方不明だゾ\n*0822*ランチでアート対決だゾ/父ちゃんの日曜はツイてないゾ\n*0823*ゴーちゃん。が来たゾ/父ちゃんよりいっぱい歩くゾ\n*0824*ガマン対決だゾ/父ちゃんをいやすゾ\n*0825*宇宙警察犬・ロボドッグだゾ\n*0826*マサオ兄ちゃんだゾ/代休父ちゃんだゾ\n*0827*まいごのコブタだゾ/レンタルショップのソムリエだゾ\n*0828*ファザー牧場にいくゾ/お天気お兄さんだゾ\n*0829*あいちゃんのお弁当をいただくゾ/引越しで仕分けるゾ\n*0830*執事喫茶だゾ/シロとチェンジだゾ\n*0831*お塾を体験するゾ/ドミノで遊ぶゾ\n*0832*突然!となりの昼ごはんだゾ/なかなか爪が切れないゾ\n*0833*今日も雨だゾ/冒険水着を買うゾ\n*0834*ねのねのねだゾ/LEDにかえるゾ\n*0835*絶対に負けられないサッカー対決だゾ\n*0836*さすらいの防衛隊だゾ/地獄のセールスレディ・リターンズだゾ\n*0837*むさえちゃんの自動車教習だゾ/オラは見た!カスカベ都市伝説 ガチャガチャ人間だゾ\n*0838*むさえちゃんは免許を取れるのカ~だゾ/レジェンド オブ なぐられうさぎだゾ\n*0839*プラネタリウムに行くゾ/アイドルをプロデュースするゾ\n*0840*ボーちゃんが気になるゾ/晩ごはんはまかせろだゾ\n*0841*おゆうぎ会の準備だゾ/ひまわりの怖いものだゾ\n*0842*サトイモ掘りだゾ/毒舌で人気者だゾ\n*0843*女子校の学園祭にいくゾ/レアなボイスがききたいゾ\n*0844*ネネちゃんのエコバッグを追え!だゾ/おもちゃをかたづけるゾ\n*0845*父ちゃんと磯釣りだゾ/原稿はどこへいった!?だゾ\n*0846*携帯電話をなくしたゾ/スマホにするゾ\n*0847*スマホでドキドキだゾ/防衛隊の忘年会だゾ\n*0848*湯たんぽでぬくぬくだゾ/ゼッタイに拾いたい石だゾ\n*0849*雪の宿のミステリーだゾ 前編・後編\n*0850*アゲアゲ母ちゃん29号だゾ/愛のみかんだゾ\n*0851*サンキューベリードッグだゾ/風間くんとおつかいだゾ\n*0852*インコがきた!だゾ/疑惑!?父ちゃんの写真だゾ\n*0853*テニスで対決だゾ/本屋さんでサイン会だゾ\n*0854*ひまわりが眠れないゾ/床下のヒミツだゾ\n*0855*かぶってるゾ/掃除機を買うゾ\n*0856*たけのこ大襲撃だゾ 前編・後編\n*0857*家に入れないゾ/インターネットでお電話するゾ\n*0858*シロの引越し物語だゾ/ちんあなごを見たいゾ\n*0859*骨折父ちゃんだゾ/ゆるキャラをプロデュースするゾ\n*0860*~骨折父ちゃん~つきそいするゾ/バッグを拾ったゾ\n*0861*~骨折父ちゃん~ギプスをはずすゾ/忍ちゃんとお留守番だゾ\n*0862*双子が来たゾ 前編・後編\n*0863*春我部は今日も雨だったゾ/紅さそり隊解散だゾ\n*0864*待ち受け画像を撮るゾ/ザキヤマさんが来~る~だゾ\n*0865*ドローンは見てたゾ/地獄のセールスレディ・逆襲だゾ\n*0866*夏のおでかけ会議だゾ/ぬか漬けデビューだゾ\n*0867*テーブルを回すゾ/ラジオ体操で早起きだゾ\n*0868*純情かき氷だゾ/熱血!修造先生とプールだゾ\n*0869*キャンプへ行くゾ\n*0870*ひみつちゃんと勝負だゾ/ひみつちゃんは友だちだゾ\n*0871*幻のセミを捕るゾ/なぐられうさぎ<泡>だゾ\n*0872*動物園はウキウッキーだゾ\n*0873*つみ木で対決だゾ/たこパーでドキドキだゾ\n*0874*母ちゃんは捨てる女だゾ/恋の交通安全戦士だゾ\n*0875*父ちゃんと紙相撲だゾ/マサオくんを心配するゾ\n*0876*勇者のけん玉だゾ/呪いのあみだくじだゾ\n*0877*警察犬シロだゾ/あいちゃんがイメチェンだゾ\n*0878*父ちゃんはど~こだ?だゾ/おならが出そうだゾ\n*0879*ゴロ寝主婦を卒業するゾ/静電気でビビビ!だゾ\n*0880*イルミネーションを見にいくゾ/手袋で真剣勝負だゾ\n*0881*恋のスケート大作戦だゾ/恐怖のアプリだゾ\n*0882*秘湯に行くゾ 前編・後編\n*0883*ネネちゃんのピアノリサイタルだゾ/父ちゃんのとりあえず主夫だゾ\n*0884*お風呂はイヤイヤ!だゾ/オラと雪の合戦だゾ\n*0885*シロが太っちゃったゾ/オラはファッションリーダーだゾ\n*0886*ガラス越しの恋だゾ/しゃっくりを止めたいゾ\n*0887*スーパー実演販売士がきたゾ/おもちつきが楽しみだゾ\n*0888*オラたちのハザードマップだゾ/お肉をキャッチするゾ\n*0889*男の沽券にかかわるゾ/ななこおねいさんを探しに行くゾ\n*0890*父ちゃんが起きない!?だゾ 前編・後編\n*0891*寝ている間にアートだゾ/まつざか先生を応援するゾ\n*0892*隣のおじさんの顔が見たいゾ/風間くんと怒られちゃったゾ\n*0893*お出かけするのもひと苦労だゾ/マサオくんの正夢だゾ\n*0894*田植え体験だゾ/ぶりぶりざえもんの冒険 覚醒編\n*0895*洗濯物を集めるゾ/ぶりぶりざえもんの冒険 閃光編\n*0896*よしなが先生がクビ!?だゾ/ボーちゃんはこだわるゾ\n*0897*こわ~い歯医者さんだゾ/シロとひな鳥だゾ\n*0898*泥んこでただいまだゾ/雨の日は転職日和だゾ\n*0899*父ちゃんが坊主頭だゾ 前編・後編\n*0900*思い出したいマサオくんだゾ/父ちゃんと地獄のセールスレディだゾ\n*0901*見~た~わ~ね~!だゾ/しんのすけ対シン・ゴジラだゾ\n*0902*枝豆を育てるゾ/ぶりぶりざえもんの冒険 天下統一編\n*0903*冷蔵庫のおかたづけだゾ/タクシードライバーしんのすけだゾ\n*0904*ユーとリーが来たゾ/金魚をもらったゾ\n*0905*着せ放題がアツいゾ/くじを引きたい風間くんだゾ\n*0906*ギリギリで買い物するゾ/思いが重いマサオくんだゾ\n*0907*手作りブローチをあげるゾ/冷やし中華が食べたいゾ\n*0908*ななこおねいさんと手をつなぎたいゾ/手品を見せたいゾ\n*0909*父ちゃんと会社ごっこだゾ/サイコロ電車だゾ\n*0910*お風呂は戦闘だゾ 前編・後編\n*0911*グラタンは熱~いゾ/おやつをシェアするゾ\n*0912*カニカニバトルだゾ/うちわで応援するゾ\n*0913*野原家新車を買う① 車が壊れたゾ/カスカベ国際警察特捜班 怪盗眠りネコを追え!だゾ\n*0914*野原家新車を買う② 外車は男のマロンだゾ/紅さそり隊に入りたいゾ\n*0915*野原家新車を買う③ とうとう車を買うゾ/むさえちゃん家を大捜索だゾ\n*0916*ななこ先生だゾ/勝負!フィギュアスケート城だゾ\n*0917*どうしたネネちゃん!?だゾ/見たいのに見られないゾ\n*0918*オラのクツが行方不明だゾ/早起きしてトクしたいゾ\n*0919*秋田でスキーだゾ 前編・後編\n*0920*本屋さんをお助けするゾ/ひまわりがカゼをひいたゾ\n*0921*商店街のカオだゾ/新しい枕がほしいゾ\n*0922*ひまわり組の組長先生だゾ/カスカベ国際警察特捜班 チョコレートパニックだゾ\n*0923*恐怖のフランス人形だゾ/寒くても外で遊ぶゾ\n*0924*まつざか先生のシロだゾ/あいちゃんのSPが変わったゾ\n*0925*消防署を見学するゾ/家族写真を撮ってもらうゾ\n*0926*宇宙カスカベ防衛隊だゾ 前編・後編\n*0927*回転ずしでフィーバーだゾ/カエルの財布がピンチだゾ\n*0928*サヤマーランドで茶摘みだゾ/宇宙ブタで大もうけだゾ\n*0929*窓をピカピカにするゾ/ゴルフで勝負だゾ\n*0930*カミングアウト大会だゾ/断れない母ちゃんだゾ\n*0931*オラたち双子だゾ/父ちゃんと相撲だゾ\n*0932*コインランドリーはらくらくだゾ/となりのおばさんちであそぶゾ\n*0933*すき焼きをゲットだゾ/運をためたいゾ\n*0934*家庭教師を探すゾ/父ちゃんをむかえにいくゾ\n*0935*カスカベ都市伝説シリーズ 謎の行列だゾ/シロのゴーカな家だゾ\n*0936*カスカベ都市伝説シリーズ 恐怖のエレベーターだゾ/ゴミ箱ウォーズだゾ\n*0937*じゅん散歩がやってきたゾ/オラのらくがき部屋だゾ\n*0938*ナゾの人魚伝説だゾ 前編・後編\n*0939*ペンギンが来たゾ/なぐられうさぎ<醒>だゾ\n*0940*父ちゃんとアイスを作るゾ/3匹のぶりぶりだゾ\n*0941*新そばをたべに行くゾ/またまた地獄のセールスレディだゾ\n*0942*オラんちのマツタケ会議だゾ/あいちゃんはひとすじだゾ\n*0943*サラリーマンバーベキューだゾ/ドッグヨガを体験するゾ\n*0944*必殺技を考えるゾ/ボーちゃんの秘密だゾ\n*0945*オラたちアヒルだゾ/メニューが一緒だゾ\n*0946*秋のスペシャル企画第1弾!単身赴任が決まったゾ/ピンチを切り抜けるゾ\n*0947*ネネちゃんの家出だゾ/秋の特別企画 しんのすけ対シン・ゴジラだゾ\n*0948*ベビーシッターは大変だゾ/秋のスペシャル企画第2弾!単身赴任はお気楽だゾ\n*0949*どうしてもパフェが食べたいゾ/秋のスペシャル企画第3弾!単身赴任はドキドキだゾ\n*0950*むさえちゃんと大掃除だゾ/ぶりぶりざえもんの冒険 伝説のクツ編\n*0951*ななこおねいさんとおもちつきだゾ/お宝をそろえたいゾ\n*0952*雪まつりでミステリーだゾ 前編・後編\n*0953*カエルさん帰るだゾ/みんながシロの散歩だゾ\n*0954*冬はやっぱりコタツだゾ/スタンプラリーだゾ\n*0955*ありがた~いアレを取りに行くゾ/紅さそり隊にあこがれるゾ\n*0956*風間くんと離れられないゾ/寒い日は父ちゃんと遊ぶ?だゾ\n*0957*パンフレットの写真をとるゾ/マニキュアを塗りたい母ちゃんだゾ\n*0958*食リポに挑戦するゾ/オラは卒業するゾ\n*0959*父ちゃんのサラサラヘアーだゾ/ひまわりが来ちゃったゾ\n*0960*親子三代スイーツの旅だゾ/上尾先生をおたすけするゾ\n*0961*パンのシールを集めるゾ/アクション仮面に変身だゾ\n*0962*AIロボがやってきたゾ/オラんちでグランピングだゾ\n*0963*はり絵ではり合うゾ/幹事をまかされたゾ\n*0964*ふろくを作るゾ/黒磯さんの素顔を見たいゾ\n*0965*くんせいにチャレンジするゾ/カゼをひいてタイクツだゾ\n*0966*父ちゃんの健康診断だゾ/ダチョウとたわむれるゾ\n*0967*ミラクルドッグ・シロだゾ/マサオくんの\"当て期\"だゾ\n*0968*野菜を育てるゾ/DVDを借りにいくゾ\n*0969*野原家プリンウォーズだゾ/防衛隊の会議だゾ\n*0970*ソフトクリームはおいしいゾ/お手伝いポイントを貯めるゾ\n*0971*うなぎのつかみどりだゾ/夏の都市伝説シリーズ おともだちができたゾ\n*0972*夏といえば体操だゾ/夏の都市伝説シリーズ 行き先のわからないバスだゾ\n*0973*ニワトリとバトルだゾ/夏の都市伝説シリーズ キョーフ扇風機だゾ\n*0974*まだまだ地獄のセールスレディだゾ/ボール奪還作戦だゾ\n*0975*オラたち金魚だゾ/おパンツを買いにいくゾ\n*0976*背が高くなりたいゾ/この家事はダレがする?だゾ\n*0977*しつこいお味をさぐるゾ/親子マラソンだゾ\n*0978*オラのうちにはテレビがないゾ/スクープするゾ\n*0979*おシリをさがすゾ/上尾先生をドキドキさせるゾ\n*0980*もえPステッキはステキだゾ/幼稚園ごっこだゾ\n*0981*スマホで売りたいゾ/センターをねらえ!だゾ\n*0982*荷造りを手伝うゾ/いきなりじいちゃんだゾ\n*0983*いきなりもうひとりのじいちゃんだゾ/ケッサクを運ぶゾ\n*0984*激アツ!焼き芋対決だゾ/母ちゃんと卓球するゾ\n*0985*アクション仮面をお助けするゾ/あいちゃんとお鍋だゾ\n*0986*マサオくんちの床暖房だゾ/モチ売りの少女だゾ\n*0987*すごいお城をつくるゾ/押入れを整理するゾ\n*0988*みのむし風間くんだゾ/ファミレスごっこだゾ\n*0989*お風呂を楽しむゾ/いちごをおいしく食べたいゾ\n*0990*バーゲンでホールインワンだゾ/恋の歯科検診だゾ\n*0991*コツメカワウソのドーヤくんだゾ/むさえちゃんが看病するゾ\n*0992*春をさがすゾ/防衛隊のお花見だゾ\n*0993*シロと箱だゾ/トレビあ~んだゾ" 20 | } 21 | --------------------------------------------------------------------------------