├── .eslintignore ├── src ├── icons │ ├── Crunchyroll-128.png │ ├── Crunchyroll-150.png │ ├── Crunchyroll-20.png │ ├── Crunchyroll-24.png │ ├── Crunchyroll-40.png │ ├── Crunchyroll-44.png │ ├── Crunchyroll-48.png │ └── Crunchyroll-50.png ├── popup.html ├── manifest.json ├── settings.js ├── popup.css ├── popup.js ├── dashboard.css ├── dashboard.html ├── dashboard.js └── background.js ├── .gitignore ├── test ├── .stylelintrc.json └── .eslintrc.json ├── .github └── workflows │ ├── build.yaml │ └── release.yaml ├── dante └── danted.conf ├── LICENSE ├── package.json ├── README.md └── website └── index.html /.eslintignore: -------------------------------------------------------------------------------- 1 | src/vendor/* 2 | -------------------------------------------------------------------------------- /src/icons/Crunchyroll-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fhuhne/CR-Unblocker/HEAD/src/icons/Crunchyroll-128.png -------------------------------------------------------------------------------- /src/icons/Crunchyroll-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fhuhne/CR-Unblocker/HEAD/src/icons/Crunchyroll-150.png -------------------------------------------------------------------------------- /src/icons/Crunchyroll-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fhuhne/CR-Unblocker/HEAD/src/icons/Crunchyroll-20.png -------------------------------------------------------------------------------- /src/icons/Crunchyroll-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fhuhne/CR-Unblocker/HEAD/src/icons/Crunchyroll-24.png -------------------------------------------------------------------------------- /src/icons/Crunchyroll-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fhuhne/CR-Unblocker/HEAD/src/icons/Crunchyroll-40.png -------------------------------------------------------------------------------- /src/icons/Crunchyroll-44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fhuhne/CR-Unblocker/HEAD/src/icons/Crunchyroll-44.png -------------------------------------------------------------------------------- /src/icons/Crunchyroll-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fhuhne/CR-Unblocker/HEAD/src/icons/Crunchyroll-48.png -------------------------------------------------------------------------------- /src/icons/Crunchyroll-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fhuhne/CR-Unblocker/HEAD/src/icons/Crunchyroll-50.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /public 3 | src.zip 4 | extension.zip 5 | extension.crx 6 | extension.pem 7 | .vscode 8 | *.log 9 | 10 | # OS files 11 | .DS_Store -------------------------------------------------------------------------------- /test/.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard", 3 | "rules": { 4 | "comment-whitespace-inside": null, 5 | "rule-empty-line-before": null, 6 | "selector-list-comma-newline-after": null, 7 | "declaration-empty-line-before": "never", 8 | "indentation": "tab" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: Setup Node.js 15 | uses: actions/setup-node@v3 16 | with: 17 | node-version: 16 18 | cache: 'npm' 19 | - run: npm ci 20 | - run: npm run build 21 | - name: Upload Artifact 22 | uses: actions/upload-artifact@v4 23 | with: 24 | name: extension 25 | path: public/extension.zip 26 | retention-days: 7 27 | -------------------------------------------------------------------------------- /dante/danted.conf: -------------------------------------------------------------------------------- 1 | # This can also be a file if you prefer. 2 | logoutput: syslog 3 | 4 | # Create the crunblocker user first and give it a password using OS commands. 5 | user.privileged: root 6 | user.unprivileged: crunblocker 7 | 8 | # The listening network interface or address. 9 | internal: 0.0.0.0 port=1080 10 | 11 | # The proxying network interface or address. 12 | external: tun0 13 | 14 | # socks-rules determine what is proxied through the external interface. 15 | socksmethod: username 16 | 17 | # client-rules determine who can connect to the internal interface. 18 | clientmethod: none 19 | 20 | client pass { 21 | from: 0.0.0.0/0 to: 0.0.0.0/0 22 | log: connect error disconnect 23 | } 24 | 25 | socks pass { 26 | from: 0.0.0.0/0 to: .crunchyroll.com 27 | log: connect error disconnect 28 | } 29 | -------------------------------------------------------------------------------- /src/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 |28 | 29 | 30 |
31 | 32 | 35 | 36 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Onestay 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cr_unblocker", 3 | "version": "3.2.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "clean": "echo '=> CLEANING' && rm -rf public && mkdir public", 8 | "test": "echo '=> TESTS' && npm run -s lint", 9 | "prebuild": "npm run -s test && npm run -s clean", 10 | "lint": "npm run -s lint:js && npm run -s lint:css", 11 | "lint:js": "echo '-> linting js' && eslint src/*.js", 12 | "lint:css": "echo '-> linting css' && stylelint src/*.css", 13 | "build": "echo '=> BUILD' && npm run -s build:zip", 14 | "build:zip": "echo '-> building for chrome and firefox stores' && cd src; zip -0r ../public/extension.zip ./*; cd ..", 15 | "watch": "watch 'npm run -s build' src/" 16 | }, 17 | "author": "Finn", 18 | "license": "MIT", 19 | "devDependencies": { 20 | "eslint": "^8.16.0", 21 | "stylelint": "^14.8.5", 22 | "stylelint-config-standard": "^25.0.0" 23 | }, 24 | "stylelint": { 25 | "extends": "./test/.stylelintrc.json" 26 | }, 27 | "eslintConfig": { 28 | "extends": "./test/.eslintrc.json" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "CR-Unblocker", 4 | "version": "3.2.0", 5 | "description": "A tool that will proxy the Crunchyroll Session ID through a US server.", 6 | "homepage_url": "https://cr-unblocker.us.to/", 7 | "author": "Finn", 8 | "icons": { 9 | "128": "icons/Crunchyroll-128.png", 10 | "48": "icons/Crunchyroll-48.png", 11 | "24": "icons/Crunchyroll-24.png" 12 | }, 13 | "permissions": [ 14 | "activeTab", 15 | "cookies", 16 | "notifications", 17 | "storage", 18 | "*://*.crunchyroll.com/*", 19 | "*://*.cr-unblocker.us.to/*", 20 | "webRequest", 21 | "webRequestBlocking", 22 | "proxy", 23 | "tabs" 24 | ], 25 | "browser_action": { 26 | "default_icon": { 27 | "128": "icons/Crunchyroll-128.png", 28 | "40": "icons/Crunchyroll-40.png", 29 | "20": "icons/Crunchyroll-20.png" 30 | }, 31 | "default_title": "CR-Unblocker", 32 | "default_popup": "popup.html" 33 | }, 34 | "background": { 35 | "scripts": [ 36 | "settings.js", 37 | "background.js" 38 | ] 39 | }, 40 | "options_ui": { 41 | "page": "dashboard.html" 42 | }, 43 | "browser_specific_settings": { 44 | "gecko": { 45 | "strict_min_version": "91.1.0" 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - name: Setup Node.js 14 | uses: actions/setup-node@v3 15 | with: 16 | node-version: 16 17 | cache: 'npm' 18 | - name: install dependencies 19 | run: npm ci 20 | - name: build release 21 | run: npm run build 22 | - name: get release version 23 | run: echo "::set-output name=version::$(node -p "require('./package.json').version")" 24 | id: version 25 | - name: release 26 | uses: actions/create-release@v1 27 | id: create_release 28 | with: 29 | draft: true 30 | prerelease: false 31 | release_name: ${{ steps.version.outputs.version }} 32 | tag_name: ${{ github.ref }} 33 | env: 34 | GITHUB_TOKEN: ${{ github.token }} 35 | - name: upload artifact 36 | uses: actions/upload-release-asset@v1 37 | env: 38 | GITHUB_TOKEN: ${{ github.token }} 39 | with: 40 | upload_url: ${{ steps.create_release.outputs.upload_url }} 41 | asset_path: ./public/extension.zip 42 | asset_name: cr-unblocker.zip 43 | asset_content_type: application/zip 44 | -------------------------------------------------------------------------------- /src/settings.js: -------------------------------------------------------------------------------- 1 | var browser = browser || chrome; 2 | 3 | /** 4 | * Export function 5 | * @param {Object} global The Object that should receive the exported functions. 6 | */ 7 | ((global) => { 8 | // Settings object with default settings 9 | let settings = { 10 | switchRegion: true, 11 | keepAlive: false, 12 | proxyCustom: false, 13 | proxyHost: '', 14 | proxyPort: 1080, 15 | proxyUser: '', 16 | proxyPass: '', 17 | proxyType: 'socks' 18 | } 19 | let validSettings = Object.keys(settings); 20 | 21 | /** 22 | * Load saved settings 23 | */ 24 | browser.storage.local.get({ settings: null }, (item) => { 25 | if (item.settings !== null) { 26 | // Merge saved settings with default settings overwriting the default ones 27 | settings = Object.assign(settings, item.settings); 28 | } else { 29 | // Save default settings 30 | browser.storage.local.set({ settings: settings }); 31 | } 32 | }); 33 | 34 | /** 35 | * Saves settings validating keys and sending an update message 36 | * @param {Object} keys Object containing the settings to change 37 | */ 38 | function saveSettings(keys) { 39 | let changed = {}; 40 | for (let key of Object.keys(keys)) { 41 | if (validSettings.indexOf(key) !== -1) { 42 | // Update settings object 43 | settings[key] = keys[key]; 44 | changed[key] = keys[key]; 45 | } 46 | } 47 | browser.runtime.sendMessage({ event: 'settingsChanged', changed: changed, settings: settings }); 48 | browser.storage.local.set({ settings: settings }); 49 | } 50 | 51 | /** 52 | * Gets settings as copy 53 | * @return {Object} Object containing settings 54 | */ 55 | function getSettings() { 56 | return Object.assign({}, settings); 57 | } 58 | 59 | if (!global.settings) { 60 | global.settings = { 61 | save: saveSettings, 62 | get: getSettings 63 | }; 64 | } 65 | })(this || {}); 66 | 67 | /** 68 | * Export object through messages 69 | */ 70 | browser.runtime.onMessage.addListener((message, sender, sendResponse) => { 71 | if (message.action === 'saveSettings') { 72 | this.settings.save(message.settings); 73 | } else if (message.action === 'getSettings') { 74 | sendResponse(this.settings.get()); 75 | } 76 | }); 77 | -------------------------------------------------------------------------------- /src/popup.css: -------------------------------------------------------------------------------- 1 | body { 2 | width: 100vw; 3 | max-width: 350px; /* or your desired desktop max width */ 4 | min-width: 200px; /* set a minimum for usability */ 5 | margin: 0 auto; 6 | font-size: 0.95rem; 7 | text-align: center; 8 | color: white; 9 | background-color: #f78c25; 10 | font-family: Montserrat, sans-serif; 11 | padding: 0.5em; 12 | box-sizing: border-box; 13 | } 14 | 15 | hr { 16 | margin: 0 0.3em 1em; 17 | border: solid 0.08em rgb(255 255 255 / 20%); 18 | } 19 | 20 | .bigger { 21 | font-size: 1.2em; 22 | } 23 | 24 | .cr-topbar { 25 | padding: 0.5em 0; 26 | display: block; 27 | } 28 | 29 | .cr-footerbar { 30 | font-size: 0.75em; 31 | padding: 0.8em 0; 32 | display: block; 33 | color: white; 34 | background-color: #dc7c20; 35 | } 36 | 37 | .cr-topbar p { 38 | font-size: 1.2em; 39 | margin: 0.25em 0.2em 0; 40 | font-weight: bold; 41 | font-family: Roboto, sans-serif; 42 | } 43 | 44 | .cr-footerbar p { 45 | margin: 0; 46 | } 47 | 48 | .cr-checkbox { 49 | font-size: 1em; 50 | vertical-align: middle; 51 | } 52 | 53 | .cr-button { 54 | font-size: 1em; 55 | color: #f38022; 56 | width: 85%; 57 | max-width: 18em; 58 | min-width: 6em; 59 | cursor: pointer; 60 | display: block; 61 | margin: 0.8em auto 1em; 62 | padding: 0.65em 1.2em; 63 | border-radius: 0.33em; 64 | border: none; 65 | background-color: white; 66 | text-align: center; 67 | transition: background 0.2s, color 0.2s; 68 | box-sizing: border-box; 69 | } 70 | 71 | .cr-button:active { 72 | background-color: #ffb268; 73 | color: white; 74 | } 75 | 76 | .blink { 77 | animation: blink-animation 3s alternate both ease-in-out; 78 | } 79 | 80 | .blink p { 81 | animation: fade-animation 3s alternate both ease-in-out; 82 | } 83 | 84 | @keyframes blink-animation { 85 | from { 86 | background-color: #f38022; 87 | } 88 | 15% { 89 | background-color: #ffb268; 90 | } 91 | 85% { 92 | background-color: #ffb268; 93 | } 94 | to { 95 | background-color: #f38022; 96 | } 97 | } 98 | 99 | @keyframes fade-animation { 100 | from { 101 | opacity: 0; 102 | } 103 | 20% { 104 | opacity: 1; 105 | } 106 | 80% { 107 | opacity: 1; 108 | } 109 | to { 110 | opacity: 0; 111 | } 112 | } 113 | 114 | body.mobile-popup .cr-button, 115 | body.mobile-popup .bigger { 116 | font-size: 1.2em; 117 | padding: 1em 0.5em; 118 | width: 100%; 119 | } 120 | body.mobile-popup label { 121 | font-size: 1.15em; 122 | } 123 | -------------------------------------------------------------------------------- /src/popup.js: -------------------------------------------------------------------------------- 1 | var browser = browser || chrome 2 | 3 | let proxyStatusInterval = null 4 | 5 | function setProxyStatusSection(show) { 6 | const section = document.getElementById('proxy-status-section'); 7 | section.style.display = show ? 'block' : 'none'; 8 | if (!show) { 9 | document.getElementById('proxyStatus').textContent = ''; 10 | } 11 | } 12 | function testProxyStatus() { 13 | const statusEl = document.getElementById('proxyStatus'); 14 | statusEl.textContent = 'Connecting...'; 15 | statusEl.style.color = 'white'; 16 | statusEl.style.backgroundColor = 'transparent'; 17 | 18 | browser.runtime.sendMessage({ 19 | action: 'testCurrentProxy' 20 | }); 21 | } 22 | 23 | function handleSwitchRegionChange(enabled) { 24 | if (enabled) { 25 | setProxyStatusSection(true); 26 | testProxyStatus(); 27 | if (!proxyStatusInterval) { 28 | proxyStatusInterval = setInterval(testProxyStatus, 15000); 29 | } 30 | } else { 31 | setProxyStatusSection(false); 32 | if (proxyStatusInterval) { 33 | clearInterval(proxyStatusInterval); 34 | proxyStatusInterval = null; 35 | } 36 | } 37 | } 38 | 39 | /** 40 | * Open new tab on button click 41 | */ 42 | document.getElementById('open-cr').addEventListener('click', () => { 43 | browser.tabs.create({ url: 'http://crunchyroll.com/' }); 44 | }); 45 | 46 | /** 47 | * Open dashboard on button click 48 | */ 49 | document.getElementById('open-dashboard').addEventListener('click', () => { 50 | browser.tabs.create({ url: browser.runtime.getURL('dashboard.html') }); 51 | }); 52 | 53 | /** 54 | * Adds event listener for checkbox that saves a settings value 55 | * @param {String} id ID of checkbox and name of setting 56 | * @param {Function} callback Optional callback to call with new state of setting 57 | */ 58 | function addSettingCheckbox(id, callback) { 59 | document.getElementById(id).addEventListener('change', (ev) => { 60 | let settings = {}; 61 | settings[id] = ev.target.checked; 62 | browser.runtime.sendMessage({ action: 'saveSettings', settings: settings }); 63 | if (typeof callback === 'function') { 64 | // eslint-disable-next-line callback-return 65 | callback(ev.target.checked); 66 | } 67 | }); 68 | } 69 | 70 | /** 71 | * Save checkbox states 72 | */ 73 | addSettingCheckbox('switchRegion'); 74 | 75 | /** 76 | * Display settings in DOM 77 | */ 78 | browser.runtime.sendMessage({ action: 'getSettings' }, (settings) => { 79 | document.getElementById('switchRegion').checked = settings.switchRegion; 80 | handleSwitchRegionChange(settings.switchRegion) 81 | }); 82 | 83 | browser.runtime.onMessage.addListener((message) => { 84 | if (message.event === 'settingsChanged') { 85 | handleSwitchRegionChange(message.settings.switchRegion) 86 | } 87 | }) 88 | 89 | browser.runtime.onMessage.addListener((message) => { 90 | if (message.event === 'proxyTestResult') { 91 | const output = document.getElementById('proxyStatus'); 92 | 93 | if (message.success) { 94 | output.textContent = `✅ Proxy[${message.proxy}] is working!`; 95 | } else { 96 | output.textContent = `❌ Proxy[${message.proxy}] failed: ${message.error || 'Unavailable'}`; 97 | } 98 | } 99 | }); 100 | window.addEventListener('unload', () => { 101 | if (proxyStatusInterval) { 102 | clearInterval(proxyStatusInterval) 103 | } 104 | }) 105 | 106 | if (/android|iphone|ipad|mobile/i.test(navigator.userAgent)) { 107 | document.body.classList.add('mobile-popup'); 108 | } 109 | -------------------------------------------------------------------------------- /src/dashboard.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | height: 100vh; 5 | font-family: Montserrat, sans-serif; 6 | display: flex; 7 | flex-direction: column; 8 | background-color: #f78c25; 9 | color: white; 10 | } 11 | 12 | .tab-area { 13 | display: flex; 14 | flex-direction: column; 15 | height: 100vh; 16 | } 17 | 18 | .tabs { 19 | padding: 0 0 0 10px; 20 | margin: 0; 21 | border-bottom: 1px solid #fff8; 22 | display: flex; 23 | align-items: center; 24 | flex-wrap: wrap; 25 | background-color: #f78c25; 26 | } 27 | 28 | .tabs li { 29 | list-style: none; 30 | margin: 0 5px; 31 | position: relative; 32 | bottom: -1px; 33 | } 34 | 35 | .tabs li a { 36 | text-decoration: none; 37 | color: white; 38 | padding: 5px 15px; 39 | margin-top: 8px; 40 | border-radius: 3px 3px 0 0; 41 | border: 1px solid #fff8; 42 | background-color: transparent; 43 | transition: background 0.2s, color 0.2s; 44 | } 45 | 46 | .tabs li a:hover, 47 | .tabs li.active a { 48 | background-color: white; 49 | color: #f38022; 50 | border-bottom: 1px solid #f78c25; 51 | padding: 9px 15px; 52 | margin-top: 0; 53 | } 54 | 55 | .brand { 56 | font-family: Roboto, sans-serif; 57 | font-size: 1.2rem; 58 | color: white; 59 | } 60 | .tabs li p.brand { 61 | margin-top: 4px; 62 | padding: 5px 15px; 63 | } 64 | .tabs li p.brand span { 65 | font-size: 0.8rem; 66 | } 67 | 68 | .tab-content { 69 | flex: 1 1 auto; 70 | overflow-y: auto; 71 | padding: 15px; 72 | min-height: 0; 73 | background-color: #f78c25; 74 | color: white; 75 | } 76 | 77 | .tab { 78 | display: none; 79 | } 80 | .tab.active { 81 | display: block; 82 | } 83 | 84 | h1, h2, h3, h4, h5, h6 { 85 | font-weight: bold; 86 | font-family: Roboto, sans-serif; 87 | color: white; 88 | } 89 | .tab :first-child { 90 | margin-top: 0; 91 | } 92 | 93 | .cr-button { 94 | cursor: pointer; 95 | display: block; 96 | padding: 10px; 97 | margin: 10px 0 15px; 98 | border-radius: 5px; 99 | border: none; 100 | font-size: 1rem; 101 | background-color: white; 102 | color: #f38022; 103 | min-width: 80%; 104 | text-align: center; 105 | transition: background 0.2s, color 0.2s; 106 | } 107 | .cr-button:hover { 108 | background-color: #ffb268; 109 | color: white; 110 | } 111 | 112 | .tab-content::-webkit-scrollbar { 113 | width: 8px; 114 | } 115 | .tab-content::-webkit-scrollbar-thumb { 116 | background-color: rgb(255 255 255 / 30%); 117 | border-radius: 4px; 118 | } 119 | 120 | @media (max-width: 600px) { 121 | html, body { 122 | font-size: 16px; 123 | padding: 0; 124 | min-width: 0; 125 | } 126 | 127 | .tab-area, .tab-content { 128 | padding: 5px; 129 | } 130 | 131 | .tabs { 132 | flex-direction: column; 133 | padding: 0; 134 | } 135 | 136 | .tabs li { 137 | margin: 2px 0; 138 | width: 100%; 139 | } 140 | 141 | .tabs li a, .tabs li.active a, .tabs li p.brand { 142 | width: 100%; 143 | display: block; 144 | padding: 10px 5px; 145 | text-align: left; 146 | font-size: 1rem; 147 | } 148 | 149 | .tab-content { 150 | padding: 8px; 151 | } 152 | 153 | table { 154 | width: 100%; 155 | font-size: 1em; 156 | } 157 | 158 | table tr { 159 | display: flex; 160 | flex-direction: column; 161 | margin-bottom: 10px; 162 | } 163 | 164 | table td { 165 | padding: 4px 0; 166 | display: block; 167 | width: 100%; 168 | } 169 | 170 | input[type="text"], input[type="password"], select { 171 | width: 100%; 172 | font-size: 1em; 173 | padding: 10px; 174 | box-sizing: border-box; 175 | } 176 | 177 | .cr-button { 178 | min-width: 100%; 179 | font-size: 1em; 180 | padding: 12px 0; 181 | margin: 12px 0; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CR-Unblocker 3.0 2 | 3 | CR-Unblocker helps accessing region locked anime on Crunchyroll without the need for a VPN. This extension will only proxy the request to obtain the session token but not other traffic. This way the site stays fast and responsive! 4 | 5 | ## I've heard it isn't safe? 6 | We only proxy the token request through a proxy - no logging or similar! If you do not trust our servers, you are free to configure your own SOCKS proxy in the extension settings. Please note that we can not be held responsible for compromised accounts. 7 | 8 | ## Installing 9 | You can just install the Firefox Extension (also working on Android) from [here](https://addons.mozilla.org/firefox/addon/crunchy-unblocker). 10 | 11 | Unfortunately the Chrome and Edge Stores have removed the unblocker from their stores, reuploading would probably not work so this option will no longer be there. 12 | 13 | Alternatively you can just download the source and package it yourself. The extension is tested in Firefox, but it should also work in all other browsers supporting WebExtensions API. 14 | 15 | ## How to Install CR-Unblocker on Android (Two Methods) 16 | 17 | ### **Option 1: Easiest Way (Desktop View in Firefox Mobile)** 18 | 19 | 1. Open Firefox on your Android device. 20 | 2. Go to the [CR-Unblocker Add-on page](https://addons.mozilla.org/en-US/firefox/addon/crunchy-unblocker/). 21 | 3. Switch your browser to **Desktop view** (tap the three dots menu and select "Desktop site"). 22 | 4. You should now see the **"Add to Firefox"** button—tap it to install the add-on. 23 | 24 | > *Tested and working on Firefox version 138 (Android).* 25 | 26 | --- 27 | 28 | ### **Option 2: Using Firefox Nightly with Custom Add-on Collections** 29 | 30 | **Requirements:** 31 | 32 | * Android version 5.0 or higher 33 | * Mozilla Firefox account 34 | 35 | #### Steps: 36 | 37 | 1. **Create a Firefox Account:** 38 | Go to the [Mozilla Firefox site](https://support.mozilla.org/en-US/kb/access-mozilla-services-firefox-account) and create an account. 39 | You'll need this account to [create a custom collection](https://support.mozilla.org/en-US/kb/how-use-collections-addonsmozillaorg). 40 | 41 | 2. **Create a Collection:** 42 | After logging in, create a collection and add the CR-Unblocker extension to it. 43 | Your collection's **user ID** and **collection name** can be found [here](https://addons.mozilla.org/en-US/firefox/collections/). 44 | 45 | 3. **Install Firefox Nightly:** 46 | Download and install [Firefox Nightly](https://play.google.com/store/apps/details?id=org.mozilla.fenix&hl=en_US) from the Play Store. 47 | 48 | 4. **Add Your Collection to Firefox Nightly:** 49 | Follow [this guide](https://blog.mozilla.org/addons/2020/09/29/expanded-extension-support-in-firefox-for-android-nightly/) to add your collection to the app. 50 | 51 | 5. **Install the Add-on:** 52 | Once your collection is added, you'll be able to install CR-Unblocker directly from your collection in Firefox Nightly. 53 | 54 | #### **Tips for Using the Add-on** 55 | 56 | * **To adjust settings:** 57 | Tap the three dots (`⋮`) → Add-ons → CR-Unblocker → Make your changes. 58 | 59 | * **To open Crunchyroll via CR-Unblocker:** 60 | Open any website → Tap the three dots (`⋮`) → Add-ons → CR-Unblocker → Open Crunchyroll → Enjoy! 61 | 62 | ## Requirements 63 | 64 | It is not strictly required, but to run the helper commands for testing and packing the extension you should have these installed: 65 | 66 | * nodejs 16 67 | 68 | ## Building 69 | 70 | To pack the extension for the extension store you need to follow these steps: 71 | 72 | ```bash 73 | npm run build 74 | ``` 75 | 76 | ## Using a private proxy 77 | If you really don't trust us or the server is offline you can point the extension to any SOCKS proxy. See the extension settings. 78 | 79 | ## Contributing 80 | The extension is always under development. Some features might be added later. If you have any idea on what to add feel free to contribute to the project or open an issue. 81 | -------------------------------------------------------------------------------- /test/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 2018 4 | }, 5 | "env": { 6 | "es6": true, 7 | "node": true, 8 | "browser": true, 9 | "webextensions": true 10 | }, 11 | "extends": "eslint:recommended", 12 | "rules": { 13 | "no-extra-parens": ["warn", "all", { "nestedBinaryExpressions": false }], 14 | "accessor-pairs": "warn", 15 | "array-callback-return": "error", 16 | "complexity": "warn", 17 | "consistent-return": "off", 18 | "curly": ["error", "multi-line", "consistent"], 19 | "dot-location": ["error", "property"], 20 | "dot-notation": "error", 21 | "eqeqeq": "error", 22 | "no-empty-function": "error", 23 | "no-floating-decimal": "error", 24 | "no-implied-eval": "error", 25 | "no-invalid-this": "error", 26 | "no-lone-blocks": "error", 27 | "no-multi-spaces": "error", 28 | "no-new-func": "error", 29 | "no-new-wrappers": "error", 30 | "no-new": "error", 31 | "no-octal-escape": "error", 32 | "no-return-assign": "error", 33 | "no-self-compare": "error", 34 | "no-sequences": "error", 35 | "no-throw-literal": "error", 36 | "no-unmodified-loop-condition": "error", 37 | "no-unused-expressions": "error", 38 | "no-useless-call": "error", 39 | "no-useless-concat": "error", 40 | "no-useless-escape": "error", 41 | "no-void": "error", 42 | "no-warning-comments": "warn", 43 | "wrap-iife": "error", 44 | "yoda": "error", 45 | "no-label-var": "error", 46 | "no-shadow": "error", 47 | "no-undef-init": "error", 48 | "callback-return": "error", 49 | "handle-callback-err": "error", 50 | "no-mixed-requires": "error", 51 | "no-new-require": "error", 52 | "no-path-concat": "error", 53 | "no-process-env": "error", 54 | "array-bracket-spacing": "error", 55 | "block-spacing": "error", 56 | "brace-style": ["error", "1tbs", { "allowSingleLine": true }], 57 | "camelcase": "error", 58 | "comma-dangle": "error", 59 | "comma-spacing": "error", 60 | "comma-style": "error", 61 | "computed-property-spacing": "error", 62 | "consistent-this": "error", 63 | "eol-last": "error", 64 | "func-names": "error", 65 | "func-style": ["error", "declaration", { "allowArrowFunctions": true }], 66 | "id-length": ["error", { "exceptions": ["i", "j", "a", "b", "e", "m","c"] }], 67 | "indent": ["error", "tab", { "SwitchCase": 1 }], 68 | "key-spacing": "error", 69 | "keyword-spacing": ["error", { 70 | "overrides": { 71 | "if": { "after": true }, 72 | "for": { "after": true }, 73 | "while": { "after": true }, 74 | "catch": { "after": true }, 75 | "switch": { "after": true } 76 | } 77 | }], 78 | "max-depth": "error", 79 | "max-nested-callbacks": ["error", { "max": 4 }], 80 | "max-statements-per-line": ["error", { "max": 2 }], 81 | "new-cap": "error", 82 | "newline-per-chained-call": ["error", { "ignoreChainWithDepth": 3 }], 83 | "no-array-constructor": "error", 84 | "no-bitwise": "warn", 85 | "no-inline-comments": "error", 86 | "no-lonely-if": "error", 87 | "no-mixed-operators": "error", 88 | "no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }], 89 | "no-new-object": "error", 90 | "no-spaced-func": "error", 91 | "no-trailing-spaces": "error", 92 | "no-unneeded-ternary": "error", 93 | "no-whitespace-before-property": "error", 94 | "object-curly-newline": "warn", 95 | "object-curly-spacing": ["error", "always"], 96 | "operator-assignment": "error", 97 | "operator-linebreak": ["error", "before"], 98 | "padded-blocks": ["error", "never"], 99 | "quote-props": ["error", "as-needed"], 100 | "quotes": ["error", "single", { "avoidEscape": true, "allowTemplateLiterals": true }], 101 | "semi-spacing": "error", 102 | "space-before-blocks": "error", 103 | "space-before-function-paren": ["error", "never"], 104 | "space-in-parens": "error", 105 | "space-infix-ops": "error", 106 | "space-unary-ops": "error", 107 | "spaced-comment": "error", 108 | "unicode-bom": "error", 109 | "arrow-spacing": "error", 110 | "no-duplicate-imports": "error", 111 | "no-useless-computed-key": "error", 112 | "no-useless-constructor": "error", 113 | "prefer-arrow-callback": "error", 114 | "prefer-rest-params": "error", 115 | "prefer-spread": "error", 116 | "prefer-template": "error", 117 | "rest-spread-spacing": "error", 118 | "template-curly-spacing": "error", 119 | "yield-star-spacing": "error", 120 | "no-console": "off" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/dashboard.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 || 38 | 39 | | 40 |41 | 45 | | 46 |
| 49 | | 50 | |
| 53 | | 54 | |
| 57 | | 58 | |
| 61 | | 62 | |
| 65 | | 66 | |
Firefox Users can get the extension here.
14 |Hello everyone!
17 |18 | It's been about 5 years since I started hosting the unblocker server and updating the app from time to time 19 | when needed. 20 |
21 |22 | While the project itself does not require a lot of time anymore (just an occasional restart of the server), 23 | it does take *some* time and more importantly resources from my side. The truth is, I haven't been 24 | using this extension by myself for quite some time now. Crunchyrolls catalogue in Germany keeps growing and 25 | there are only few titles not available. I know this is not true for every country, so YMMV. But ever since 26 | moving to Japan I can't access Crunchyroll without a VPN at all, so this way of unlocking titles became 27 | unavailable to me. 28 |
29 |30 | For this reason I decided to end hosting the unblocker server by the end 31 | of the current season (~ end of June 2025). 32 |
33 |34 | Good news: one of you decided to step in! Starting with the 3.2.0 Update 35 | MeGaNeKoS will operate a new unblock server and take care of the 36 | extension. 37 |
38 |This release changes the default proxy as per the announcement message and some improvements for the proxy 41 | handling.
42 |This release adds support for HTTP proxies. It also features some UI improvements together with an 49 | availability check for the current proxy server.
50 |Up unitl now, every time the proxy server had issues users were presented with never ending loading screens 52 | on Crunchyroll. This should be handled better now.
53 |This release fixes some bugs and removes the code used for "old crunchyroll".
59 |CR-Unblocker now supports the new Crunchyroll Beta. This means no longer being limited to non logged in 67 | usage.
68 |Most issues seem to be errors getting new sessions. I think this is largely related to the reload loop. To 76 | improve on this I implemented a timer to only reload once a minute - if the session can not be obtained, it 77 | is unlikely to work a few seconds after.
78 |Good news: unblocking while logged in is back!
83 |Some of you have hinted me a way but I could not replicate it for some reason. Thanks for telling me, and 84 | sorry for not getting this working sooner :)
85 |This is just a small update, sorry still no solution for logged in users :(
91 |But I removed the unblock servers that do not work anymore, this should make switching faster.
92 |Also, region switching won't happen while logged in. This way you can login/logout depending on the anime you 93 | want to watch. Still inconvenient, but better than nothing!
94 |Hi everyone! Maintainer from the Firefox extension here.
96 |Looks like the original creator (who is hosting the unblock server) of this project has no interest in 97 | keeping this up. This is why I'm taking over from here on.
98 |He said in his "farewell" message to just torrent anime. Please do not do that. Support the anime you love to 99 | watch, even more in these challenging times. I'll add some servers to the unblocker pool to "fix" the 100 | current problems of session IDs getting issued. Please keep in mind, my resources are limited. If anyone 101 | wants to help running unblock servers get in touch with me. 102 |
103 |Greetings from Germany. Stay safe and healthy!
104 |