hockyy / miteiru

Miteiru is an open source Electron video player to learn Chinese, Cantonese, and Japanese. It can play all Youtube and HTML 5 supported format (.mkv, .mp4, .mov, and many more) videos, and lots of supports on other subtitle formats (.srt, .ass, .vtt, and many more)
Other
67 stars 0 forks source link

SRS Learning Page #59

Closed hockyy closed 2 months ago

hockyy commented 3 months ago

Radical support and personalization

SRS

Focus more on kanji / hanzi

There are several ways to put up a context:

How to mine

Starting a new session:

'char' : {
   'timeCreated':  1231231,
   'level': {
       'reading': 0,
       'meaning' 0,
       'writing': 0,
   },
   'timeUpdated': {
       'reading': 123123,
       'meaning': 123123,
       'writing': 131231,
   },
}

SRS consists of

Writing has different modes, the behavior is:

Support back

hockyy commented 3 months ago
import { app, ipcMain } from 'electron';
import { Level } from 'level';
import path from 'path';
const SkillConstants {
  'Writing' : 'writing'
  'Conveyance' : 'conveyance'
  'Translation' : 'translation'
}
class Skill {
  skillName: string
  // epoch
  lastUpdated: number;
  // epoch
  level: number;
  constructor (name) {
    this.skillName = name;
    this.lastUpdated = now();
    this.level = 0;
  }
}

class SRSData {
  character: string;
  // epoch
  lastUpdated: number;
  // epoch
  lastCreated: number;
  skills: map<string, Skill>
  constructor (char) {
    this.character = char;
    this.lastUpdated = now();
    this.lastCreated = now();
    for(const auto key of : SkillConstants.keys()) {
      this.skills.insert(SkillConstants[key], Skill());
    }
    this.writing = 
  }
}

class OrderedTree {

  // tuple is basically like c++ tuple, comparator is tie(el[0], el[1], el[2], ..) < tie(other[0], other[1], other[2], ...)
  container: Set
  generateKey: SRSData -> tuple
  constructor(SRSData[] initialData : SRSData[], keyGenerator : (SRSData a) -> tuple) {
    this.generateKey = keyGenerator
    for (const auto curData : initialData) {
      insert(curData)
    }
  }
  insert(SRSData a) {
    currentKey = this.generateKey(curData);
    // Check if key exist, if not then skip
    container.insert(currentKey, a.character)
  }
  erase(SRSData a) {
    currentKey = this.generateKey(curData);
    container.erase(currentKey)
  }
  orderOfKey(SRSData a) {
    currentKey = this.generateKey(curData);
    container.getOrder(currentKey);
    return -1 if not exist
  }
  findByOrder(int index) {
    return null if >= container.size()
    else return container[index];
  }
}

class SRSDatabase {
  static learningTrees: map<string, OrderedTree>
  static srsData: map<string, SRSData>
  static setup() {
    for(const auto key of : SkillConstants.keys()) {
      // classicKeyGen is just refresh by lastUpdated (not yet srs), srs shall compute the perfect srs formula according to the level
      this.learningTrees.add(SkillConstants[key], OrderedTree(srsData.values(), classicKeyGen));
    }

  }

  static loadSRS() {
    const prefix = `srs/${lang}/`; // Define the prefix for keys
        const learningState = {};

        // Define the range for the query
    const queryOptions = {
      gte: prefix, // Start of the range: include the prefix
      lte: `${prefix}\uFFFF`, // End of the range: highest value that still matches the prefix
    };

        // Efficiently iterate over keys within the specified range
    for await (const [key, value] of this.db.iterator(queryOptions)) {
      const strippedKey = key.substring(prefix.length);
      let parsedValue;
      parsedValue = JSON.parse(value) as SRSData
      srsData[strippedKey] = parsedValue;
    }
  }

  static insertNew(character) {
    // now let's just support 1 character srs, 1 terms is a little bit too muhcowkakoawokaw
    SRSData newSrs(character)
  }

  static updateLearningTrees(character a, skills: Skill, newLevel: number) {
    for(const auto key of : SkillConstants.keys()) {
      // classicKeyGen is just refresh by lastUpdated (not yet srs), srs shall compute the perfect srs formula according to the level
      const ptrToSRSData = this.srsData[a];
      oldKey = this.learningTrees[SkillConstants[key]].generateKey(a);
      this.learningTrees[SkillConstants[key]].erase(a);
      ptrToSRSData.skill[SkillName].level = newLevel;
      ptrToSRSData.skill[SkillName].lastUpdated = now();
      ptrToSRSData.lastUpdated = now();
      this.learningTrees[SkillConstants[key]].insert(ptrToSRSData);
    }
  }
}

class Learning {
  static dbPath;
  static db;

  static setup() {
    // Define the path to the database
    this.dbPath = path.join(app.getPath('userData'), 'learningStateDB');
    this.db = new Level(this.dbPath);

  }

  static registerHandler() {
    ipcMain.handle('loadLearningState', async (event, lang) => {
      try {
        const prefix = `${lang}/`; // Define the prefix for keys
        const learningState = {};

        // Define the range for the query
        const queryOptions = {
          gte: prefix, // Start of the range: include the prefix
          lte: `${prefix}\uFFFF`, // End of the range: highest value that still matches the prefix
        };

        // Efficiently iterate over keys within the specified range
        for await (const [key, value] of this.db.iterator(queryOptions)) {
          const strippedKey = key.substring(prefix.length);
          let parsedValue;

          try {
            parsedValue = JSON.parse(value);
            if (typeof parsedValue.level !== 'number' || typeof parsedValue.updTime !== 'number') {
              throw new Error('Invalid format');
            }
          } catch (e) {
            // If parsing fails, assume it's an old format (number)
            parsedValue = {
              level: parseInt(value, 10),
              updTime: Date.now()
            };
            await this.db.put(key, JSON.stringify(parsedValue));
          }

          learningState[strippedKey] = parsedValue;
        }

        return learningState;
      } catch (error) {
        console.error('Error loading learning state:', error);
        return {};
      }
    });

    // Handler to update a specific content's learning state
    ipcMain.handle('updateContent', async (event, content, lang, data) => {
      if (!content) return true;
      try {
        await this.db.put(`${lang}/${content}`, JSON.stringify(data));
        return true; // Indicate success
      } catch (error) {
        console.error('Error updating content:', error);
        return false; // Indicate failure
      }
    });

    // Handler to update a batch of contents' learning states
    ipcMain.handle('updateContentBatch', async (event, contents: LearningStateType, lang) => {
      if (!contents) return true;
      try {
        for (const [key, value] of Object.entries(contents)) {
          const goUpdate = async () => {
            await this.db.put(`${lang}/${key}`, JSON.stringify(value));
          }
          this.db.get(`${lang}/${key}`).then(async val => {
            const parsedVal = JSON.parse(val);
            if (parsedVal.level < value.level) await goUpdate();
          }).catch(async () => {
            await goUpdate();
          })
        }
        return true; // Indicate success
      } catch (error) {
        console.error('Error updating content:', error);
        return false; // Indicate failure
      }
    });
  }
}

export default Learning;
hockyy commented 2 months ago

day 556 finding someone who would help me with this repo